-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
Use Case
I'm building a Dash app with synchronized hover between an AG Grid table and a Plotly chart. When a user hovers over a table row, I want to trigger the chart's hover tooltip at the corresponding time point to show all trace values (runway configuration probabilities over time).
The chart uses layout.hovermode = "x unified" to show all traces together in one tooltip when hovering naturally with the mouse.
Current Behavior & Limitations
Attempt 1: Point-based hover
// Shows only a single trace, not unified
Plotly.Fx.hover(plotElement, [{curveNumber: 0, pointNumber: 5}]);Result: Shows tooltip for only trace 0, ignores hovermode: "x unified"
Attempt 2: X-value based hover
// Fails with categorical x-axis (time strings like "01/03 12:00")
Plotly.Fx.hover(plotElement, {xval: "01/03 12:00"}, ['xy']);Result: Logs Fx.hover failed - doesn't work with categorical/string x-axis values
Attempt 3: Multiple points
// Shows 5 separate tooltips instead of unified
const points = [];
for (let i = 0; i < plotElement.data.length; i++) {
points.push({curveNumber: i, pointNumber: 5});
}
Plotly.Fx.hover(plotElement, points);Result: Shows individual tooltip per trace (not unified)
Current Workaround
I'm using synthetic MouseEvent dispatch with pixel coordinate calculation:
// Calculate pixel position using internal APIs
const layout = plotElement._fullLayout; // ⚠️ Internal API
const xaxis = layout.xaxis;
const yaxis = layout.yaxis;
const xPixel = xaxis.l2p(rowIndex) + xaxis._offset; // ⚠️ l2p() is internal
const yPixel = yaxis._offset + yaxis._length / 2;
// Dispatch synthetic mouse event
const plotArea = plotElement.querySelector('.nsewdrag'); // ⚠️ Internal class
const rect = plotElement.getBoundingClientRect();
plotArea.dispatchEvent(new MouseEvent('mousemove', {
bubbles: true,
cancelable: true,
clientX: rect.left + xPixel,
clientY: rect.top + yPixel,
view: window
}));This works but is fragile because it relies on:
_fullLayout(internal object)xaxis.l2p()(internal method)xaxis._offset,yaxis._offset,yaxis._length(internal properties).nsewdrag(internal CSS class)
These could break on Plotly.js upgrades.
Proposed Solution
Enhance Plotly.Fx.hover() to respect layout.hovermode when triggered programmatically:
Option 1: Auto-detect from layout
// When layout.hovermode = "x unified", show unified tooltip
Plotly.Fx.hover(plotElement, {xval: "01/03 12:00"});Option 2: Explicit unified flag
Plotly.Fx.hover(plotElement, {xval: "01/03 12:00"}, {unified: true});Option 3: Support categorical x-values with point index
// Use point index for categorical axes, auto-triggers unified mode
Plotly.Fx.hover(plotElement, {pointIndex: 5, unified: true});Benefits
- ✅ Uses public API instead of internal DOM/pixel manipulation
- ✅ Won't break on Plotly.js version upgrades
- ✅ Enables common use cases: table/chart sync, timeline controls, external hover triggers
- ✅ Respects existing
hovermodeconfiguration - ✅ More intuitive API (matches how manual hover works)
Environment
- Plotly.js version: 6.5.0 (via Dash 3.3.0)
- Browser: Chrome/Safari (reproduced in both)
- Chart type: Area chart with categorical x-axis (time strings)
- Hovermode:
"x unified"
Willingness to Contribute
I'm willing to submit a PR for this if the approach is acceptable to maintainers. Please advise on preferred API design before I start implementation.
Related Use Cases
This would benefit anyone needing programmatic hover control:
- Synchronized charts and data tables
- Timeline scrubbing controls
- External search/filter highlighting
- Keyboard navigation of data points
- Accessibility features (screen reader integration)