Is it possible to make tooltips clickable URL?

Your example doesn’t use a Legend at all, but rather a hovertool…

You can certainly build a custom html tooltip for your hovertool that has hyperlinks attached:

from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, HoverTool, CustomJSHover

src = ColumnDataSource(data={'x':[0,1,2],'y':[2,3,1],'link':['https://google.com','https://reddit.com','https://twitter.com']})

tt = '''
    <div>
        <a href=@link{safe}}>@link{safe}</a>
    </div>
    '''
f = figure()
r = f.scatter(x='x',y='y',source=src)
hvr = HoverTool(renderers=[r],tooltips=tt)
f.add_tools(hvr)
show(f)

I mostly took this from the guide → Configuring plot tools — Bokeh 2.4.3 Documentation

However, this won’t accomplish what I think you seek because users won’t be able to click on the hyperlink in the tooltip because as soon as they move the mouse to go click on it, the hover will disappear. Trawling through github discussions/issues, it appears there is currently no “easy” way to make a HoverTool “sticky” (Feature Request: HoverTool tooltips stick to a point on click. · Issue #5724 · bokeh/bokeh · GitHub).

One proposed workaround for this would be to implement the above, but also add a TapTool with a callback that retrieves the url string from the tapped glyph, and window.open (see Window.open() - Web APIs | MDN) to open it. This way you can open the url link stored in the CDS:

from bokeh.plotting import figure, show, save
from bokeh.models import ColumnDataSource, HoverTool, TapTool, CustomJS

src = ColumnDataSource(data={'x':[0,1,2],'y':[2,3,1],'link':['https://google.com','https://reddit.com','https://twitter.com']})

toolt = '''
    <div>
        <a href=@link{safe}}>@link{safe}</a>
    </div>
    '''
f = figure()
r = f.scatter(x='x',y='y',source=src)
hvr = HoverTool(renderers=[r],tooltips=toolt)
f.add_tools(hvr)

tap_cb = CustomJS(code='''
                  var l = cb_data.source.data['link'][cb_data.source.inspected.indices[0]]
                  window.open(l)
                  ''')
tapt = TapTool(renderers=[r],callback=tap_cb,behavior='inspect')
f.add_tools(tapt)

show(f)
2 Likes