plotly / plotly.js

Open-source JavaScript charting library behind Plotly and Dash
https://plotly.com/javascript/
MIT License
16.74k stars 1.83k forks source link

Browser hangs on relayout on hover events (adding/removing annotations) #1640

Closed dictaartisan closed 1 month ago

dictaartisan commented 7 years ago

This is probably related with #1446 and #1467 (sorry for duplicate comment)

I see browse hangs (stack overflow) when drawing/removing an annotation on hover and unhover events. Here's a codepen (shamelessly copying etpinard's code)

http://codepen.io/dictaartisan/pen/XRRYwe

Hover in and out of the points often enough and browser will hang - Chrome seems to recover after a while in this case but on another more complex plot I have to kill the browser

dictaartisan commented 7 years ago

In fact, within that codepen, the infinite recursion seems to be triggered when hovering quickly over the top point (6,4) which triggers the rescaling of the y axis when the annotation is inserted at the top of the chart

The culprit appears to be the gd._fullLayout._rehover function, which appears to create an chain reaction of hover/unhover events

alexcjohnson commented 6 years ago

This is a bit of an old issue, but adding some thoughts to it that arose while thinking about #2644: rehover was created for cases like streaming data, where something not driven by immediate user action is altering the data in a plot. Without recalculating the hover state with the new data, we'd either lose hover entirely (if streaming is fast you'd never be able to see hover info) or you'd be looking at out-of-date points.

It becomes tricky though when you're using a hover event to modify the plot. The issue here is that, as you noted, the y axis is rescaled by adding the new annotation, so after adding it the mouse is no longer over that point and you get a new hover event to reflect that. As a result you remove the annotation, the y axis rescales again, and the mouse IS over the point again. ∞

So what's the right behavior, and where should we break this loop? We can't say "no hover events if the hover was from rehover" or the displayed and reacted-to hover information would get out of sync. I suppose we could annotate the event data, to let you know that it came from a rehover and allow you to react accordingly. We could also probably try to detect such an infinite loop and throw an error rather than locking up the page, or perhaps add a small delay somewhere so instead of freezing you'll just see flashing and be able to move your mouse away. I'm thinking maybe if the hover is triggered by rehover, add a delay after displaying the tooltip but before emitting the hover event. That way normal hover events still come immediately but once we get into a potential infinite loop we slow things down.

Thinking about this from an application standpoint, I'm not sure what behavior you would want anyway - if autoranging to the annotation is important, would you want to stop at hover (and annotation) displayed, even though the mouse is not over the point anymore? But then a small mouse move will confirm that the mouse is not over the point, and remove the annotation... so that would replace the infinite loop with one iteration of the loop per mousemove event. Best would be to avoid this altogether, perhaps by disabling autorange when adding this annotation, or I suppose in principle we could add an annotation attribute to tell us not to consider this annotation in autorange calculations... it will still display as long as the arrowhead is pointing to displayed coordinates.

etpinard commented 6 years ago

I'm thinking maybe if the hover is triggered by rehover, add a delay after displaying the tooltip but before emitting the hover event. That way normal hover events still come immediately but once we get into a potential infinite loop we slow things down.

I'm a fan of this.

I suppose in principle we could add an annotation attribute to tell us not to consider this annotation in autorange calculations...

I'm also a fan of this. This could be a thing for traces too.

KNejad commented 5 years ago

Strangely, I'm getting a very similar issue with an on click event. It's happening on a large 3d plot (works fine on 2d). I want to add an annotation when the user clicks yet it seems to freeze the browser and call the plotly_click event in an infinite loop. Basically all I am doing is calling a relayout inside of a plotly_click callback.

I wouldn't expect the suspected cause of this issue to cause a problem with plotly_click since there should only be one click event.

gvwilson commented 1 month ago

Hi - this issue has been sitting for a while, so as part of our effort to tidy up our public repositories I'm going to close it. If it's still a concern, we'd be grateful if you could open a new issue (with a short reproducible example if appropriate) so that we can add it to our stack. Cheers - @gvwilson