Data on tap tool event/cb

I’m encountering an issue with a dissonance between what the TapTool registers versus what is selected on the CDS. With the following code:

taptool =
    taptool.callback = CustomJS(args=

Where cb is:

    data =;
    item =[cb_data.source.selected.indices[0]];
    if(item != undefined) {
        // do something

Most of the time, a click (on a point on the figure) is registered, but selected is empty! I have to be hovering perfectly over it in order for selected and TapTool to agree. Am I accessing data from the wrong selected var?


This may just be an order of operations concern. Things have to happen in some order, and offhand I have no idea if there’s any reason to suspect whether the CDS selection must be set before or after a tap tool callback is executed. A complete small example would be useful to do some quick investigation.

Ah ok, happy to make one, but then are you saying that checking the ‘selected’ is the correct way of figuring out what was tapped? It seems weird to me that the taptool is capable of only activating when a glyph is clicked, yet it isn’t capable of providing the data/index that glyph represents.


Actually looking at the code the callback should always happen after, so what’s really needed is a complete reproducer.

Ok, will do!

Alright, this produces “failed” (which given it’s inside a taptool callback, shouldn’t happen)

from bokeh.document import Document
from import curdoc
from bokeh.models import ColumnDataSource, TapTool, CustomJS
from bokeh.plotting import figure
import pandas as pd

# ------------------ Data Prep ------------------ #
doc = curdoc()
args = doc.session_context.request.arguments

p = figure(tools="tap,save",
           background_fill_color='gray', background_fill_alpha=0.3,
           match_aspect=True, plot_height=500, plot_width=2000)
df = pd.DataFrame({"x": [1, 2, 3, 4, 5], "y": [1, 2, 3, 4, 5]})

source = ColumnDataSource(df)

line = p.line(x="x", y="y", source=source,
circle ="x", y="y", source=source)

taptool =
taptool.callback = CustomJS(args=
data =;
val_x = data.x[cb_data.source.selected.indices[0]];
val_y = data.y[cb_data.source.selected.indices[0]];
if(val_x != undefined && val_y != undefined) {
    console.log("something clicked" + " x: " + val_x + ", y: " + val_y);    
else {


Can just bokeh serve it!

Wait, is the tap tool also registering taps on the line? That may be it!

By default unless you specify an explicit renderers property on a selection type tool, it will try to operate on eery available glyph.

1 Like

To be explicit for anyone viewing this post, as Bryan described this fixes the issue:

taptool.renderers = [circle]
1 Like