Loading js file to change color on pn object

Hi All,

I am working on a dashboard for some monthly metrics on my team. I need to change the font color on a pn object (HTML or Dataframe) depending on their value.

For what I have been able to investigate, the recommended way to do this is with JS.

I have tried to initialize the external js file but it is not working as the font color is not changing.

Here is an example

#imports
import panel as pn

#js files association. The path is a local path on the server
pn.extension(js_files = {’$’ : ‘/data/coe_dashboard/styles/table_style.js’})

#pn object creation
html_pane = pn.pane.HTML("""

This is an Example Code

January February March
15 10 8
2 3 34
""")

html_pane

Also here is the content of the js file

td_array = document.getElementsByTagName(“td”);

for (i = 0; i < td_array.length; i++){
if (td_array[i].textContent > 10){
td_array[i].style.color = “red”;
};
};

Any guidance will be highly appreciated

1 Like

Hi @guidozamora.

There is a similar question here and some answers.

Additionally the Panel Dataframe widget is a bokeh DataTable and can use formatting documented on the bokeh site like this.

I haven’t done this my self yet. So don’t know what the best solution is.

Let me know if it is useful or you need something else.

1 Like

Hi @guidozamora

The problem with you script is that it is included and run at the top of your document. I.e. before the table is in the document. Thus it fails. Something like the below works.

image

import panel as pn
html_pane = pn.pane.HTML("""
<h1>This is an Example Code</h1>
<table>
  <tbody>
<tr>
    <th>January</th>
    <th>February</th>
    <th>March</th>
  </tr>
  <tr>
    <td>15</td>
    <td>10</td>
    <td>8</td>
  </tr>
  <tr>
    <td>2</td>
    <td>3</td>
    <td>34</td>
  </tr>
</tbody>
</table>
""")

js_pane = pn.pane.HTML("""
<script>
td_array = document.getElementsByTagName("td");
for (i = 0; i < td_array.length; i++){
    if (td_array[i].textContent > 10){
        td_array[i].style.color = "red";
    };
};
</script>
""", width=0, height=0, margin=0, sizing_mode="fixed")

pn.Column(
    html_pane, js_pane
).servable()

Please note that if you update the table dynamically you will have to remove (js_pane.object="") and add the JS again (js_pane.object=JS) after you updated the table(s).

Hi @guidozamora

If you control the HTML generation you can style it using the HTML class attribute and some css.

For example

import panel as pn
STYLE="""
td.large-number {
    background: red;
    color: gray;
}
"""
pn.config.raw_css.append(STYLE)

html_pane = pn.pane.HTML("""
<h1>This is an Example Code</h1>
<table>
  <tbody>
<tr>
    <th>January</th>
    <th>February</th>
    <th>March</th>
  </tr>
  <tr>
    <td class="large-number">15</td>
    <td>10</td>
    <td>8</td>
  </tr>
  <tr>
    <td>2</td>
    <td>3</td>
    <td class="large-number">34</td>
  </tr>
</tbody>
</table>
""")

pn.Column(
    html_pane
).servable()

image

For me that would be more robust and simple than using javascript. But with javascript you might be able to do more complex things.

1 Like

@Marc Thanks a lot for your reply! It was really helpful.

As I am trying to apply a conditional color style based on value, I am going with the JS option.

As you predicted, in a dynamic table it works ok only the first time. I have been trying to understand in which part of my code I have to remove and add the js_pane.object.

I have tried a lot of options but nothing has worked.

I would really appreciate if you can give me some guidance.

Below is my code.

#imports
import panel as pn
pn.extension()

#report lists creation
reports_list=[‘Report 1’,‘Report 2’]

#widget creation
dd_reports = pn.widgets.Select(name=‘Select the report:’, options=reports_list)

#HTML tables creation
report_1 = pn.pane.HTML("""

Report 1


















January February March
15 10 8
2 3 34

“”")
report_2 = pn.pane.HTML("""

Report 2


















May June July
17 10 65
67 3 71

“”")

#JS
js_pane = pn.pane.HTML("""

“”")

@pn.depends(dd_reports)

#function to return the report depending on user selection
def display_report(report):
global js_pane
js_pane.object=""
js_pane.object = ("""

“”")
if report == ‘Report 1’:
return report_1
else:
return report_2

#arrange all elements on a pane Row
final_report = pn.Row(dd_reports, display_report,js_pane)

final_report.show()

Thanks again for your help !!!