ea42gh
February 12, 2026, 5:07pm
1
Wanted: a slider that updates a display on either mouse click or drag.
I wanted the update to only happen on the mouse up event.
While I could make dragging the slider work, clicking on the slider only worked every k’th click (typically 2, 3 or 4, depending on the method I tried)
The only version that worked is
import panel as pn
from bokeh.models import Slider, Div, CustomJS
from bokeh.layouts import column
pn.extension()
class SliderMouseupBokehJS(pn.viewable.Viewer):
def __init__(self, **params):
super().__init__(**params)
self.slider = Slider(start=0, end=10, value=0, step=0.1, title="Drag Me")
self.div = Div(text="mouseup value: (none)")
self.slider.js_on_change(
"value_throttled",
CustomJS(args=dict(div=self.div), code="""
const v = cb_obj.value_throttled;
div.text = `mouseup value: ${v.toFixed(3)}`;
""")
)
self.layout = column(self.slider, self.div)
def __panel__(self):
return pn.pane.Bokeh(self.layout)
SliderMouseupBokehJS()
what am I missing?
This ought to be simple.
ea42gh
February 12, 2026, 6:58pm
2
Still struggling with this: the following code at times responds to clicks by updating the slider, at other times not.
The debug fields do show events in both cases, though!
import panel as pn
from bokeh.models import Slider as BkSlider, TextInput as BkTextInput, CustomJS
from bokeh.layouts import column
pn.extension()
class DragOnlySliderDemo(pn.viewable.Viewer):
def __init__(self, **params):
super().__init__(**params)
self.slider = BkSlider(title="Drag Only", start=0, end=100, value=50, step=1)
self.debug_event = BkTextInput(title="Slider Events", value="")
self.debug_thr = BkTextInput(title="Throttled Event", value="")
self._install_js_bridge(self.slider)
self.layout = column(
self.slider,
self.debug_event,
self.debug_thr,
)
def _install_js_bridge(self, slider):
bridge = BkTextInput(value="", visible=False)
counter = BkTextInput(value="0", visible=False)
last_val = BkTextInput(value=str(slider.value), visible=False)
squelch = BkTextInput(value="0", visible=False)
slider.js_on_change(
"value",
CustomJS(args={"counter": counter, "squelch": squelch}, code="""
if (squelch.value === "1") {
squelch.value = "0";
return;
}
const c = parseInt(counter.value || "0");
counter.value = (c + 1).toString();
""")
)
slider.js_on_change(
"value_throttled",
CustomJS(args={
"bridge": bridge,
"counter": counter,
"last": last_val,
"squelch": squelch,
"dbg": self.debug_event,
"dbg_thr": self.debug_thr,
}, code="""
const c = parseInt(counter.value || "0");
if (c > 1) {
bridge.value = cb_obj.value_throttled.toString();
last.value = cb_obj.value_throttled.toString();
} else {
squelch.value = "1";
cb_obj.value = parseFloat(last.value);
}
const kind = (c > 1) ? "drag" : "click";
dbg.value = `${kind}, count=${c}`;
dbg_thr.value = `throttled -> ${cb_obj.value_throttled}, count=${c}`;
counter.value = "0";
""")
)
self._bridge_panes = [
pn.pane.Bokeh(bridge),
pn.pane.Bokeh(counter),
pn.pane.Bokeh(last_val),
pn.pane.Bokeh(squelch),
]
def __panel__(self):
return pn.pane.Bokeh(self.layout)
DragOnlySliderDemo()