Retrieving HTML Element Linked to Bokeh JS Object from Bokeh.documents in JavaScript

Question: Is it possible to retrieve the HTML element linked to a Bokeh JS object from the Bokeh.documents? I would like to access the HTML button created with a Bokeh widget in Python in order to trigger a click event over it using JavaScript.

Details: I’m working with Bokeh to create interactive visualizations in Python. I have created a Bokeh widget, specifically a button, which generates corresponding HTML elements when rendered in a web browser. Now, I want to manipulate this button using JavaScript, but I’m unsure how to access the HTML element that corresponds to the Bokeh JS object from the Bokeh.documents array in JavaScript.

Additional Context: I have explored the Bokeh.documents array in the browser’s console and can see the structure of the document, but I’m struggling to understand how to map the Bokeh JS object to its corresponding HTML element. Any guidance or code snippets demonstrating how to achieve this would be greatly appreciated.

Current Approach: Here’s what I’ve tried so far:

const bokehDocs = Bokeh.documents;
const myWidget = bokehDocs[0].get_model_by_name("myButtonWidget");
// Now, how can I get the corresponding HTML element linked to this Bokeh JS object (myWidget)?

Expected Outcome: I expect to be able to retrieve the HTML element linked to the Bokeh JS object, such as a button, so that I can manipulate it using JavaScript. This could involve triggering events like clicks or updating its properties dynamically.

Any help or insights into how to achieve this would be greatly appreciated. Thank you in advance!

Impossible? Probably not impossible. But this information is not currently exposed in any supported, public way, and different widgets handle their DOM manipulation in different ways (e.g. some widgets may delete and re-create existing DOM nodes entirely from time to time). You’d probably need to study the BokehJS source to code for a specific given widget to see if there is some way to get at what you want. Alternatively, it is possible to create your own Bokeh custom extensions though this is an advanced topic.

In either case be advised that this is all still often internal and/or not-fully baked API, so things might change in the future requiring you to adapt your code. Realistically, if you need this level of DOM control/access, then Bokeh may not be the appropriate tool (Bokeh was created originally for Python programmers specifically so they could avoid dealing with DOM and JS directly).

1 Like

@Lou If you attach a css_class to the Bokeh button and use querySelector or querySelectorAll of the shadowRoot of the Bokeh elements? Would that work? I have no experience with this.

button = Button(
    label="Click",
    button_type="success",
    name = "button2",
    css_classes=['myBkBtn']
)
const bk_sroot = document.getElementsByClassName(topmost bk class name)[0].shadowRoot;
const bk_btn_div = bk_sroot.querySelectorAll('div.myBkBtn');
...
2 Likes

@Jonas_Grave_Kristens idea is a reasonable one to try, obviously the DOM structure of different widgets is different, so you might need to search around inside whatever actually has the class attached to get what you really want.

2 Likes

Thank you, @Jonas_Grave_Kristens and @Bryan, for your insights. While the concept of using css_classes is intriguing, it unfortunately doesn’t align well with my specific requirements. I’ll delve deeper into exploring custom extensions, although I must admit some frustration that such functionality isn’t built from the ground up. Implementing this feature from scratch would undoubtedly enhance Bokeh’s versatility, particularly in its integration with other frameworks.

One option in order to trigger an event associated with a Bokeh widget is the use execute() on the CustomJS callback itself. Get the script by its name and execute the script.

button.js_on_event(
    "button_click",
    CustomJS(
        code = "console.log('BK button: click!');",
        name = 'btn_script'
    ))
const bokehDocs = Bokeh.documents;
const btn_script = bokehDocs[0].get_model_by_name("btn_script");
btn_script.execute();
1 Like

Assuming Python code like this:

p = figure(tools=TOOLS, name="my_plot")

Then this will retrieve the associated view and its DOM element:

> Bokeh.index.query_one((view) => view.model.name == "my_plot").el
<div class=​"bk-Figure">​…​</div>​
1 Like

@mateusz is .el consistent everywhere and available in all cases? Is it documented anywhere?

Just to reiterate what I said above: the original motivation for Bokeh was as a Python tool, and all of the "JS side* was hidden implementation details that no users were expected to interact with. Over time, people came to express interest in using BokehJS directly, but that was years later, and even today, is still secondary.

I would love to see BokehJS become a viable, fully supported standalone option for JS devs, but the very best thing that could help make that happen (or happen much faster) is for motivated front-end devs with opinions and experience about how JS libraries should be used, to decide to become involved contributors.

1 Like

is .el consistent everywhere and available in all cases?

.el is available on all views that inherit from DOMView, which is all UI components and DOM nodes.

Is it documented anywhere?

Strongly doubt it’s documented anywhere.

Thank you, @mateusz, for providing this valuable information. It perfectly aligns with what I was searching for at that time.

I understand Bokeh’s original focus on Python and the subsequent interest in using BokehJS directly. While I primarily work with Python for graph creation, I find myself needing to enhance interactivity and style using JS. While I’m not currently skilled enough to contribute, I’m grateful for Bokeh’s capabilities. Thanks for your efforts in developing such a valuable tool.

1 Like