How to respond to click events for polylines, shapes, etc?

If I have a map with a bunch of points and connecting lines, which are generated dynamically based on input parameters - how do I respond to mouse clicks on the points and lines?

The only examples I can find require a callback referencing the specific id of a given object (like a polyline), but I want a way to capture any click and get from the context what was clicked.

Hi @zack_nc , you can capture click events and access detailed context about what was clicked using the clickData property.

Thanks ,

I got that somewhat working - if I click on the map or on someone in the geojson object’s data, I can capture the event.

But if I click on a polyline, which is added to the parent map object, that does not trigger the event.

I see that I can explicitly capture the clickData on a specific polyline by ID, but I’m adding the polylines dynamically.

How can I capture click events for lines in this case - where the line isn’t even added until after the layout()?

Hi @zack_nc ,

Then, what you can do is to generate your own clickData in a callback. Here there is an example:

from dash import Dash, html, dcc, Input, Output
import plotly.graph_objects as go
import numpy as np

app = Dash()

# Random dataset
def generate_group_data(n_groups=3, n_points=10):
    groups = []
    for i in range(n_groups):
        x = np.linspace(0, 10, n_points)
        y = np.random.rand(n_points) + i
        groups.append({
            "x": x,
            "y": y,
            "label": f"Group {chr(65 + i)}"
        })
    return groups

app.layout = html.Div([
    html.H3("Dynamic Lines with Click Tracking"),
    
    html.Button("Generate New Lines", id="generate-btn", n_clicks=0),
    
    dcc.Graph(id='my-graph'),
    
    html.Div(id='click-output', style={'marginTop': '20px', 'fontWeight': 'bold'})
])


@app.callback(
    Output('my-graph', 'figure'),
    Input('generate-btn', 'n_clicks')
)
def update_figure(n_clicks):
    grouped_data = generate_group_data()

    fig = go.Figure()

    for group in grouped_data:
        fig.add_trace(go.Scatter(
            x=group["x"],
            y=group["y"],
            name=group["label"],
            mode="lines+markers",
            customdata=[group["label"]] * len(group["x"]),
            hoverinfo='text',
            hovertext=[f"{group['label']}<br>X={x:.1f}<br>Y={y:.2f}" for x, y in zip(group["x"], group["y"])]
        ))

    fig.update_layout(title="Click on Any Point to See Info", height=500)

    return fig


@app.callback(
    Output('click-output', 'children'),
    Input('my-graph', 'clickData')
)
def display_click_data(clickData):
    if not clickData:
        return "Click on a point to see details."

    point = clickData['points'][0]
    trace_index = point['curveNumber']
    x = point['x']
    y = point['y']
    group = point.get('customdata', 'Unknown')

    return f"You clicked on {group} (Trace #{trace_index}) — X: {x}, Y: {y}"


if __name__ == '__main__':
    app.run(debug=True)

1 Like

I think you misunderstood my question.

My polyline has data embedded - but clicking on it does not trigger any callback.

I could do something like this:


@app.callback(
    Output("click-output", "children"),
    Input("my-polyline", "n_clicks"),
    State("my-polyline", "clickData"),
    prevent_initial_call=True,
)
def handle_polyline_click(n_clicks, click_data):
    if click_data:
        lat = click_data["latlng"][0]
        lon = click_data["latlng"][1]
        return f"Polyline clicked at: Lat {lat:.2f}, Lon {lon:.2f}"
    return ""

But the issue is that the polylines are added dynamically, so I don’t know the IDs in advance.

Does that make sense?

Hey @zack_nc, could you creta a minimal example for us?