Hiding Bokeh elements

Hello,
maybe it will be just an easy CSS fix, but I only now the basics, so I came to ask here.

What I want to do:
Hide/show sliders (or other bokeh objects) depending on the user input.
I use bokeh serve.

What I did so far:
Back in old Bokeh I used a Javascript callback where I looked for elements with a certain class name.
Meanwhile it’s far easier by just using the css_classes attribute. Let’s add "hidden" to the css classes of things I want to hide.
In the templates/styles.css file I used

div.bk-slider.bk-layout-fixed.hidden{
	display: none;
}

What’s the problem?
It’s not really a problem but a “dirty” way I guess.
Now, since the structure of new Bokeh has changed quite a bit, I use

div.bk.slider.hidden{
	display: none !important;
}

This works, but I read that !important is considered bad practice and should rather not be used. As far as I’ve found out is, that CSS uses some sort of priority hierarchy (ids > class names > elements).
So is there any way to achieve the same hide functionality without !important by exploiting Bokeh’s structure?

Complete Example + what I tried

main.py


from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, Slider, RadioGroup, Button
from bokeh.io import curdoc
from bokeh.layouts import column, row

def r_callback(attr, old, new):
    no_hide = list(range(0,3))
    no_hide.pop(new)
    s_list[new].css_classes = ["slider", "hidden"]
    for i in no_hide:
        s_list[i].css_classes = ["slider"]


def b_callback():
    for i in range(0,3):
        s_list[i].css_classes = ["slider"]


s_top = Slider(title="top slider", value=0, start=0, end=1, step=0.1, css_classes=["slider"])
s_mid = Slider(title="middle slider", value=0, start=0, end=1, step=0.1, css_classes=["slider"])
s_bot = Slider(title="bottom slider", value=0, start=0, end=1, step=0.1, css_classes=["slider"])

s_list = [s_top, s_mid, s_bot] # put them in a list for easier handling

r_group = RadioGroup(labels=["top", "middle", "bottom"], inline=True)
b = Button(label="show all")

r_group.on_change("active", r_callback)
b.on_click(b_callback)

curdoc().add_root(row(column(r_group, b), column(s_top, s_mid, s_bot)))

templates/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    {{ bokeh_css }}
    {{ bokeh_js }}
    <style type="text/css">{% include 'styles.css' %}</style>
  </head>
  <body>
    {{ plot_div|indent(8) }}
    {{ plot_script|indent(8) }}
  </body>
</html>

To be honest, I don’t know what all this is doing and if it is really necessary (except for including the styles file)… I just copied this html from somewhere :sweat_smile:

templates/styles.css

div.bk.slider.hidden{
/* display: none; */ /* strange behavior, slider goes to height 0 */
display: none !important;
/* visibility: hidden; */
}

Using display: none as in the old version does not work anymore, I think the reason is the new structure and thus resulting new hierarchies for the css part. Strangely the height of the slider will be set to zero when using this, apart from that nothing happens.

As mentioned above, using display: none !important works fine. A small gap will stay where the element was, but I think it also was like this in the old version and is not such a big deal in my application.

Using visibility: hidden works in the way, that the element will not be displayed. However a gap in the size of the full element will stay.

I’m not sure if changing the css_classes is a good idea but you could easily use visible = False to hide the sliders.
I’ve modified the b_callback() function to do this.

from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, Slider, RadioGroup, Button
from bokeh.io import curdoc
from bokeh.layouts import column, row

# def r_callback(attr, old, new):
#     no_hide = list(range(0,3))
#     no_hide.pop(new)
#     s_list[new].css_classes = ["slider", "hidden"]
#     for i in no_hide:
#         s_list[i].css_classes = ["slider"]


def b_callback():
    active_rb = r_group.active
    if active_rb==0:
        s_top.visible = False
        s_mid.visible = True 
        s_bot.visible = True
    elif active_rb==1:
        s_top.visible = True
        s_mid.visible = False 
        s_bot.visible = True
    elif active_rb==2:
        s_top.visible = True
        s_mid.visible = True 
        s_bot.visible = False
    else:
        s_top.visible = True
        s_mid.visible = True 
        s_bot.visible = True

    # for i in range(0,3):
    #     s_list[i].css_classes = ["slider"]


s_top = Slider(title="top slider", value=0, start=0, end=1, step=0.1, css_classes=["slider"])
s_mid = Slider(title="middle slider", value=0, start=0, end=1, step=0.1, css_classes=["slider"])
s_bot = Slider(title="bottom slider", value=0, start=0, end=1, step=0.1, css_classes=["slider"])

s_list = [s_top, s_mid, s_bot] # put them in a list for easier handling

r_group = RadioGroup(labels=["top", "middle", "bottom"], inline=True)
b = Button(label="show all")

# r_group.on_change("active", r_callback)
b.on_click(b_callback)

curdoc().add_root(row(column(r_group, b), column(s_top, s_mid, s_bot)))

You could also use s_mid.disabled = True to make them disabled but keep them visible.

Edit: Fixed s_mid.visible to s_mid.disabled

Thanks, that makes it even easier :sweat_smile: And also better in terms of spacing :+1:

I wasn’t aware that there is a visibility property now for models. Back in Bokeh 1.0.2 hiding via css_classes was the only option.
Checking the changelog on the bokeh github page it seems that this feature was introduced in Bokeh 1.1.0.
I should have read it before posting^^

Thank you very much :slight_smile:


Edit:
by

I think you mean s_mid.disabled = True? Yes, this was a feature I used a lot, but here explicit hiding was the goal :slight_smile:

No problem. Fixed the s_mid.disabled mistake.