Using CustomJS in Bokehjs

Hi,

I am using BokehJS, and I am trying to attach a callback to some figure. Below is a minimal example:

var plt = Bokeh.Plotting;

var x = Bokeh.LinAlg.linspace(-0.5, 20.5, 10);
var y = x.map(function (v) { return v * 0.5 + 3.0; });
var source = new Bokeh.ColumnDataSource({ data: { x: x, y: y } });

// make the plot and add some tools
var tools = "pan,wheel_zoom,box_zoom,reset,save";
var p = plt.figure({ title: "Bla Bla", tools: tools });

// add a line with data from the source
const line = p.line(
{ field: "x" },
{ field: "y" },
{ source: source, line_width: 2}
);

p.js_event_callbacks['tap'] = new Bokeh.CustomJS({args: [], code: "console.log('Tap event occurred at x-position: ' + cb_obj.x)"});

// show the plot
plt.show(p);

I am expecting to get some output in the console (when pressing with the LMB on the figure), but nothing happens. What am I missing here?

Thanks in advance,
Amit

You should’ve seen an error there.

js_event_callbacks is not an object that maps event -> callback. It’s an object that maps event -> callbacks_array. Just wrap your newly created CustomJS in an array.

By the way, your code is for an old version of BokehJS. Here’s its modern variant that works with BokehJS 2.0.2:

const plt = Bokeh.Plotting;
const array_util = Bokeh.require('core/util/array');
const x = array_util.linspace(-0.5, 20.5, 10);
const y = x.map(function (v) { return v * 0.5 + 3.0; });
const ColumnDataSource = Bokeh.Models('ColumnDataSource');
const source = new ColumnDataSource({ data: { x: x, y: y } });

// make the plot and add some tools
const tools = "pan,wheel_zoom,box_zoom,reset,save";
const p = plt.figure({ title: "Bla Bla", tools: tools });

// add a line with data from the source
const line = p.line(
    { field: "x" },
    { field: "y" },
    { source: source, line_width: 2}
);

const CustomJS = Bokeh.Models('CustomJS');
p.js_event_callbacks['tap'] = [new CustomJS({args: [], code: "console.log('Tap event occurred at x-position: ' + cb_obj.x)"})];

// show the plot
plt.show(p);

Note that it requires bokeh-api.js, which is a separate JS file. It’s available along with the main bokeh.js just as well.

Thank you, it worked!
I am using version 1.4 (this is what I have installed at work).

Btw, is this the correct way to add ‘tap’ (or any BokehEvent) callbacks? It looks like an imitation of the python syntax (i.e. attaching JS code in a string) instead of a direct callback.

Also, is there a plan to make BokehJS a standalone library? If so, I believe that more documentation will help its adaptation.

Thanks,
Amit

js_event_callbacks has type p.Property<{[key: string]: CallbackLike0<BokehEvent>[]}>.
We can disregard p.Property for now - what’s important is the part within <...>. [key: string] is exactly what it looks like - it’s just a string that denotes an event name in this case. CallbackLike0<BokehEvent> is an array of CallbackLike<BokehEven, [], void>. Which, in turn, is just an object with just a single execute method that accepts the event and returns nothing.
The only thing that CustomJS does is wrap that JS code string in a function along with the args dict.

To sum it all up, in your case something like

{ execute: (event) => { console.log('Tap event occurred at x-position: ' + cb_obj.x); }}

should work just fine if used instead of that new CustomJS(...).

Thanks. For the record, it works as:

{ execute: (event) => { console.log('Tap event occurred at x-position: ’ + event.x); }}

1 Like

So, I would say that BokehJS is technically already a standalone library (published to NPM) that folks can and do use without any of the Python parts. But it’s absolutely correct that it is currently lacking in documentation, examples, howtos, and that having these things would help spur adoption of BokehJS as a thing in its own right. Further, I am very interested in increasing usage of BokehJS directly by JS devs, because I hope that might attract new JS devs to become contributors. But this is also just an area where we could really use some help, both due to limited time/resources as well as lack of knowledge about what JS devs would expect a really good experience to be. If this is something you’d like to participate in moving forward, the Development category here is a great place to open topics to discuss what manageable concrete steps are.

I guess a nice step in this direction will be to translate the python Bokeh gallery to BokehJS.

Hi there!

Thank you very much for this beautiful piece of software :slightly_smiling_face:

Right now, I’m struggling at adapting the above solution to a callback on a x_range.start property change. Could someone more intelligent please help me out with that? Thank you!

So far I tried the following in the above example to log the x_range.start property to the browser’s console:

p.x_range.js_property_callbacks['change:start'] = [new CustomJS({args: [], code: "console.log('Change in x_range.start: ' + cb_obj.start)"})];

Unfortunately it doesn’t give me any output to the console.

The example above works btw.

THanks and Regards
Chris

Try using

p.x_range.property('start').change.connect((_args, x_range) => console.log(x_range.start))