Black Lives Matter. Please consider donating to Black Girls Code today.

'clickData' callback not called when multiple inputs

When I have an additional input other than just clickData, the callback isn’t called when the user clicks on the graph. The callback is called correctly after the user also updates the second input.

As an example, I have:
@app.callback(
Output(‘coin_dist_graph’, ‘figure’),
[Input(‘average_coins_graph’, ‘clickData’), Input(‘coin_type_dropdown’, ‘value’)]
)
def display_click_data(click_data, coin_type):

display_click_data is only called if the user picks a value in the coin_type_dropdown after clicking on the graph.

Can you please create a small, reproducable example? I can not replicate this issue. I tried:

import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import json

app = dash.Dash()

app.layout = html.Div([
    dcc.Graph(
        id='graph',
        figure={
            'data': [{
                'x': [1, 2, 3],
                'y': [3, 1, 2]
            }]
        }
    ),
    dcc.Dropdown(
        id='dropdown',
        options=[{'label': i, 'value': i} for i in ['a', 'b', 'c']]
    ),
    html.Pre(id='output')
])

@app.callback(Output('output', 'children'), [
    Input('graph', 'clickData'),
    Input('dropdown', 'value')])
def update_output(*args):
    return json.dumps(args, indent=2)


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

It’s possible to reproduce it when creating the figure with the fixedrange: True option (nearly same code as above):

import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import json

app = dash.Dash()

app.layout = html.Div([
    dcc.Graph(
        id='graph',
        figure={
            'data': [{
                'x': [1, 2, 3],
                'y': [1, 10, 100]
            }],
            'layout': {
                'yaxis': {
                    'fixedrange': True,
                },
                'xaxis': {
                    'fixedrange': True,
                }
            }
        }
    ),
    dcc.Dropdown(
        id='dropdown',
        options=[{'label': i, 'value': i} for i in ['a', 'b', 'c']]
    ),
    html.Pre(id='output')
])

@app.callback(Output('output', 'children'), [
    Input('graph', 'clickData'),
    Input('dropdown', 'value')])
def update_output(*args):
    return json.dumps(args, indent=2)


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

After some investigation, it seems to be related to an issue with plotly.js itself: https://github.com/plotly/plotly.js/issues/2272. A fixed has been merged 3 days ago: https://github.com/plotly/plotly.js/pull/2279

The hoverData event seems to work fine however.
Any chance to get a new version of Dash Components (0.18.1) to include plotly.js 1.33.1? (https://github.com/plotly/plotly.js/releases/tag/v1.33.1)

PR ready! https://github.com/plotly/dash-core-components/pull/150

Thanks so much for investigating. dash-core-components==0.18.1 is now available.

1 Like

Thanks a lot for looking into it! I’ve tried with 0.18.1 and this works fine with fixedrange

But my issue turned out to be, I was returning None for the figure output of a second graph when the page is first loaded with no clickData. When I return {} instead, it works fine

In case you want to see, here’s a test case:

import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import json

from app import app

app = dash.Dash()

app.layout = html.Div([
    dcc.Graph(
        id='graph',
        figure={
            'data': [{
                'x': [1, 2, 3],
                'y': [3, 1, 2]
            }],
        }
    ),
    dcc.Dropdown(
        id='dropdown',
        options=[{'label': i, 'value': i} for i in ['a', 'b', 'c']],
        value='a'
    ),
    dcc.Graph(
        id='graph2'
    )
])

i = 0

@app.callback(Output('graph2', 'figure'), [
    Input('graph', 'clickData'),
    Input('dropdown', 'value')])
def update_output(*args):
    global i
    if i == 0:
        i = 1
        return None
    else:
        return {
            'data': [{
                'x': [1, 2, 3],
                'y': [3, 1, 2]
            }],
        }


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