plotly / plotly/plotly.js

Feature Request: Plotly.Fx.hover() should support unified hover mode with categorical x-axes

Open
#7,682 3 comments 0 reactions 1 assignee View on GitHub

@timdonohue-aerology is already working on this.

Since Mar 27, 2026.

Dominant language
JavaScript
Stars
18.3k
Forks
2k
Avg merge
2d 12h
Merged PRs (30d)
28

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 hovermode configuration
  • ✅ 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)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.