Repository navigation
Customized Click, Hover, and Selection Styles or Traces #1847
Description
Plotly.js has default styles for hovering (tooltips) and for selection (dimmed traces).
In Dash, users want to be able to customize these styles. While they can customize these styles themselves through Dash callbacks, it's slow (roundtrip to server). And while I could write this behaviour into the Dash Graph component, it would be great if this was standard behaviour that everyone could benefit from.
Here are some examples that customized interaction styles or traces could enable.
As requested in the Dash community forum
For this example, there was some "animation fading" implemented, this is, why the line seems to be slightly behind the mouse. But I guess it's clear to see what kind of performance, I was hoping to get by Dash.
I think, it would be really nice, if there are efforts to improve these kind of animations, because I think they are used in many data visualization tools (having a moving maker I mean).
There is this built-in Plotly "Toggle Spike Lines" function in each Graph, that does nearly what I want and is pretty fast, but not customizable.
In addition to this example, users might want to:
- Make the points larger
- Make the lines that they're hovering over thicker
- Add text to the points they're hovering over
- Add shapes the points their hovering over (vertical or horizontal line shapes)
- Outline countries in maps
In this Uber Rides Dash demo created by @alishobeiri, the selected bars are replotted to be white. This persistent style informs the user which bars are selected and also matches the color palette of app itself.
While selected markers have a "dimming" effect, some users will want to customize the style of the selected points and the unselected points. For example, they might want to:
- Hide the unselected points
- Display text to the selected points (
mode: "markers+text") - Modify the colors of either selection
- Add an outline to points or countries instead of fading
In BI platforms, "clicking" is often used for drill downs. While we support click events, we don't modify the look and feel of the graph after clicking. For example, users might want to display constant text when clicking on a point.
In addition, there are a few other default style changes for clickable points (this could be a separate issue):
- Modify the hover interaction of the point to make it seem more "clickable": adding
cursor: pointerand making the point a little bit larger - Displaying an "x" over the point so that the user can "unclick" a point
I'm sure there are many more examples out there.







Activity
chriddyp commentedon Jul 4, 2017
I haven't thought about the attribute specs that much yet. One way to get some of these interactions would be through a
:hoversuffix like in CSS:That doesn't cover the case for drawing a vertical or horizontal line over points.
chriddyp commentedon Jul 4, 2017
With custom hover attributes, perhaps users could create their tooltips by using a combination of annotations, shapes, and images that are triggered on and off through custom
hoverstyles.chriddyp commentedon Jul 4, 2017
cc @charleyferrari who might have some additional examples too
chriddyp commentedon Jul 4, 2017
Example from @cpsievert 's work. In this case, highlighting across subplots. That type of linking hover across traces might be out of the scope of this.
https://plotcon17.cpsievert.me/workshop/day2/#18
monfera commentedon Jul 4, 2017
Indeed a solution for this would be useful (needed) for the crossfilter as we discussed in #1762
WARNING: totally my personal ramblings and biases only; in part informed by the crossfiltered dashboard work:
If possible, it'd be great to separate the activating affordances (e.g. Click, Hover, Selection, or crossfiltering some points away from another plot) from the specific temporary restyling ie. visual channel updates, as it may depend, as @chriddyp you write, on the intent such as clicking could indicate various intents, e.g. drilling down, including/excluding from filter, showing atomic data for point or jumping to an external link. A dashboard may have various inputs external to the plotly widgets or concepts, e.g. streaming stock prices would highlight the ticker (and time series line) of the last traded stock, or a UI tour or storytelling, so it might be beneficial to make these work and be assignable depending on how the context requires it.
Having made this separation, I think the affordances part is trivial as these
plotly.jsevents mostly exist or can be extended. So the task is reduced to applying arbitrary styling changes. This is the harder part:With the current
plotly.jsAPIOne option is to simply call
Plotly.restyleand/orPlotly.relayout- which has the following issues:restyleandrelayoutcalls; currently can't be batchedPlotly.newPlotpretty much throws out everything so it's not fast either, and resets user selections or other user navigated state (zoom, pan etc.)plotly.js-style column array updates are great when only one thing changes, but (I think) it's not possible to update one or a few points out of eg. 1000 data points, and even if we make such an API, making it faster than a full vector update is still a challenge.With CSS restyling
It's a quick win b/c the desired effect can be achieved (limited only by the standard), and no code change is needed. It can be made as fast as the DOM allows. Yet it has some drawbacks:
plotly.jsscenegraph, e.g. structuring, classnames etc. may changeplotly.jsie. not part of a 'contract' with the userYet I think it's an attractive direction as
OK then what?
Maybe @etpinard @alexcjohnson @chriddyp @jackparmer @cpsievert @rreusser @charleyferrari or someone else perhaps has clearer views than I. One option is to provide preconceived restyles (e.g. 'increase salience' - 'decrease salience' etc). Another option could be to somehow open up and declaratively expose the scene graph and commit to class names and using / not using certain CSS styles on certain elements - sounds very laborious.
Solving the incremental recalc with reactive streams
Recalculate data and update the DOM for only what's needed. E.g. move the sliders under
Model tweaksorLayout tweaksin this older experimentVega is also using reactive streams for much the same purpose. There are very nice papers and videos that describe it, e.g. this one from Arvind Satyanarayan, Ryan Russell, Jane Hoffswell and Jeffrey Heer - thanks Arvind for reminding me of this paper recently.
We've started to use a similar solution with the
crossfilterwork but as it usesplotly.jscharts it won't make those faster. For this reason, there's auser exitfacility (SAP terminology which is a huge standard system that needs highly client dependent extensions) that'd allow custom styling and even support for adding and updating non-plotly.jsplots.What about
reactandreduxDespite the name,
reactis not reactive in itself. It can be made reactive, but even then, by its nature, it still regenerates the entire scene graph (as virtual DOM) and does a lot of DOM diffing even where these won't lead to an update. There are some solutions e.g. using immutability and identity equality forshouldComponentUpdatechecks but using an immutable library for our rather large JSON specs (incl. data) and more importantly, the large amounts of scenegraph data generated, is quite expensive too. Caching vdom fragments (subtrees) is also a possibility to reduce vdom tree construction. It could work but it has speed limits.A more serious limitation with
reduxfrom a viewpoint of continuous interactions - it's great with predictable changes to an essentially global object, yet due to its conventions (e.g. using strings for dispatching actions), best practices eg. immutability, and our need to perform cascading changes, it gets expensive fast. Basically, each mouse move would rebuild the entire dashboard state. For this reason,reduxisn't often used for high-frequency interactions, and a half-solution won't be a solution. I wrote on this in more detail in the last three paragraphs of this and here and here so it deff. makes me an FRP fanboy; glad to learn about other approaches though.There are some attempts to solve the core issue with
reduxinvolved, e.g. reselect and various caching approaches which might have some memory leak risks. Btw.reduxcan be combined with reactive data streams / FRP just fine, so havingreduxfor overall app state while doing data updates and user interactions via FRP is OK butreduxdoesn't add much for the interactivity / 60FPS update aspect so it's not relevant for this discussion.etpinard commentedon Jul 4, 2017
That sounds painful. We could do better.
etpinard commentedon Jul 4, 2017
We could maybe attach
methodandargsattributes to traces similar toupdatemenusbuttons andslidersteps which act as a callback on hover and/or click. For example,etpinard commentedon Jul 4, 2017
monfera commentedon Jul 4, 2017
monfera commentedon Jul 5, 2017
#1762 is closed; an illustration of the crossfiltered styling in plots other than the one where we do the box selection is here
monfera commentedon Sep 22, 2017
#1943 is now closed but its comments are pertinent to this discussion.
jackparmer commentedon Sep 25, 2017
I'd love to be able to set the color of an individual bar or scatter point on hover.
For example, in a monotone chart like this:
I'd love to set the hover color of a bar to an accent color like pink.
10 remaining items
kevalshah90 commentedon Jul 29, 2020
I am trying to figure this out. I'd like to update modal popup based on scatter mapbox marker click event data. How did you do it?
jackparmer commentedon Sep 10, 2020
This issue has been tagged with
NEEDS SPON$ORA community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort.
Sponsorship range: $45k-$50k
What Sponsorship includes:
Please include the link to this issue when contacting us to discuss.
inselbuch commentedon Nov 6, 2020
Would like to chime in on the possibility of simply disabling the tooltips. I still want the click behavior to return the closest point. I just don't want the tooltip at all.
alexcjohnson commentedon Nov 6, 2020
@inselbuch that should just be
hoverinfo: 'none'https://plotly.com/javascript/reference/scatter/#scatter-hoverinfoinselbuch commentedon Nov 6, 2020
jjrob13 commentedon Nov 17, 2020
Hey sorry to jump in here, hoping my usecase is already supported as the previous commenter's was.
I often use plotly to visualize many series with different ranges on a shared timescale. One feature I've been searching for (and I'm not sure if it's part of this issue or not - so pardon my asking) is the ability to have clicks persist the hover dialog.
For example, I'd love to be able to do something like this by clicking in a few locations, to easily highlight how these many traces are changing over time (and possibly to set some default clickData to show some default hoverinfo for a few points when users first open the page):

Please let me know if this is already supported, or if it's part of this pending feature request.
Thank you very much!
Andrej4156 commentedon Apr 19, 2021
I linked here from part 4 of the tutorial .
I just wanted to chime in that it is possible to customize the hover interactions while using a selection box by changing the code in the tutorial in the "Generic Crossfilter Recipe" section:
[p['customdata'] for p in selected_data['points']])to[p['pointNumber'] for p in selected_data['points']])and then getting rid of
customdata=df.index,inupdate_traces.p['pointNumber']already has point indices so settingp['customdata']todf.indexmakesp['customdata']identical top['pointNumber']and needlessly occupiesp['customdata']which prevents the user from customizing the hover interaction using the method outlined here here.I'm not sure if this addresses all of the issues presented here or if someone else has already mentioned it before but at the very least the tutorial should probably be updated to use
pointNumberinstead ofcustomdataas a matter of good practice (unless there is some other utility for it that I am missing).nicolaskruchten commentedon Apr 19, 2021
@Andrej4156 thanks! I think the
pointNumberwill not match up correctly withdf.indexif you have multiple traces in the same figure, which is why the recipe is set up this way.EWChina999 commentedon Jun 12, 2022
ECharts is powerful, has a feature of "hover on legend to highlight the respective series and dim the rest of series", please check apache/echarts#17200.