How to obtain the 'color' value the user is hovering over, from a stacked bar chart in Dash, via a callback

Hi all,

I am relatively new to Dash and have a question on how to get the ‘color’ information, via a callback, where a user is hovering over a (stacked) bar chart.

I need the ‘color’ value that the user is hovering over, in order to trigger another action, using a callback function of course.

I created a simplified example version to illustrate the issue.

Here’s the layout and callback code:

app.layout = html.Div([
    
    # main bar plot (stacked)
    dcc.Graph(
       id='bar_chart',
       figure=fig
    ),
    
    # display the info for the plot element the user is hovering over
    html.Div([
        html.Pre(id='hover_data', style={'paddingTop':35})
        ], style={'width':'30%'}
    ),
    
    
])
@app.callback(
    Output('hover_data', 'children'),
    Input('bar_chart', 'hoverData'),
)
def show_user_hover_data(hoverData):
    return json.dumps(hoverData, indent=2)

Also, here is my code for the figure:

fig = px.bar(df, x="day", y="count", color="fruit",
            orientation='v', color_discrete_sequence=coloring)

When I hover over, lets’ say x axis = “Tue” and y = 1.5 … this is the hoverData shown from the callback:

{
  "points": [
    {
      "curveNumber": 0,
      "pointNumber": 5,
      "pointIndex": 5,
      "x": "Tue",
      "y": 1,
      "label": "Tue",
      "value": 1,
      "bbox": {
        "x0": 356.62,
        "x1": 551.98,
        "y0": 260.2,
        "y1": 260.2
      }
    }
  ]
}

So, it is easy for me to extract the x and y information. That’s no problem.

However - I also need to know the ‘color’ information on the chart - ie, the “fruit” value.

When I hover over that cell w/ my mouse – it shows me:

fruit=apple
day=Tue
count=1

All of that is correct. The problem is - hoverData does not contain the value of “fruit” (in this case, it is “apple”).
I need to get that information in a callback.

How can I do that?

thanks in advance,
Craig

ps - attached is a snapshot of the dash page running and the info shown when I hover over that element
(and after that, a snippet portion of the dataframe / df I used)

Hi Craig,

Welcome to the community! :slightly_smiling_face:

The hoverData contains a curveNumber item, which is the trace id for the particular trace being hovered. In your particular case, bars of different colors belong to different traces, which you can see in fig.data.

Since you are defining the color sequence manually, I believe you could simply get the element idx = hoverData["points"][0]["curveNumber"] from coloring as the color. In a general case, you can pass State("bar_chart", "hoverData") to the callback (the current figure) and use the same value in fig["data"][idx]["marker"]["color"], in other words, use curveNumber to index the traces in fig.data and get the marker color from the hovered trace.

Be mindful that hoverData is None when the app starts, so you have to handle that case before you try to access it as a dict.

Hope this helps!

1 Like

Thanks! This is very helpful info. I will try this out soon.

Also, just as another possible option for this, I talked to a colleague at work who’s been working with Dash for a while, and he suggested this option … to add “customdata” to the hover (& hovertemplate) and access that data to get what I need. URL doc. link is below:

thanks again & regards,
Craig

1 Like

Just to note a solution that worked well, with minimal updates to the code:

Updated the figure creation to add a customdata arg…
Doc page is here: plotly.express.bar — 5.8.2 documentation

fig = px.bar(df, x="day", y="count", color="fruit",
             custom_data=["fruit"],
             orientation='v', color_discrete_sequence=coloring)

Now, an example of how to get / display the info in a callback

@app.callback(
    Output('hover_data', 'children'),
    Input('bar_chart', 'hoverData'),
    prevent_initial_call=True,
)
def show_user_hover_data(hoverData):
    xval = hoverData['points'][0]['x']
    yval = hoverData['points'][0]['y']
    curveNum = hoverData['points'][0]['curveNumber']
    fruit = hoverData['points'][0]['customdata'][0]
    jsd =  json.dumps(hoverData, indent=2) 

    retv = jsd + f"\n X = {xval} ; Y = {yval} ; fruit = {fruit} ; curveNumber = {curveNum}"
    return retv

… and last, here is the output (which you can compare to the previous json dump above)

  • you can see, we now have the “fruit” value available to us in the new “customdata” field
{
  "points": [
    {
      "curveNumber": 0,
      "pointNumber": 5,
      "pointIndex": 5,
      "x": "Tue",
      "y": 1,
      "label": "Tue",
      "value": 1,
      "bbox": {
        "x0": 339.02,
        "x1": 521.5799999999999,
        "y0": 260.2,
        "y1": 260.2
      },
      "customdata": [
        "apple"
      ]
    }
  ]
}
 X = Tue ; Y = 1 ; fruit = apple ; curveNumber = 0