admin管理员组

文章数量:1326478

I am trying to synchronize shared tooltip across multiple charts, each having multiple series.

The problem is in the below example, the tooltip always shows the 3 series, even though at that particular point there are only two series present.

1) How do I make sure that a series is shown in tooltip only when it is actually present?

2) How do I make sure the tooltip is closed when we move out of the chart?

JSFiddle: /

Code for synchronization in fiddle:

$('#container').bind('mousemove touchmove touchstart', function (e) {
    var chart,
        point,
        i,
        event;

    for (i = 0; i < Highcharts.charts.length; i = i + 1) {
        chart = Highcharts.charts[i];
        var points = [];
        // Find coordinates within the chart
        event = chart.pointer.normalize(e.originalEvent);
        // Get the hovered point
        for(var j=0; j<chart.series.length; j++) {
           point = chart.series[j].searchPoint(event, true);
           points.push(point);  
        }

        chart.tooltip.refresh(points);

    }
});

I am trying to synchronize shared tooltip across multiple charts, each having multiple series.

The problem is in the below example, the tooltip always shows the 3 series, even though at that particular point there are only two series present.

1) How do I make sure that a series is shown in tooltip only when it is actually present?

2) How do I make sure the tooltip is closed when we move out of the chart?

JSFiddle: https://jsfiddle/qoL7fx27/1/

Code for synchronization in fiddle:

$('#container').bind('mousemove touchmove touchstart', function (e) {
    var chart,
        point,
        i,
        event;

    for (i = 0; i < Highcharts.charts.length; i = i + 1) {
        chart = Highcharts.charts[i];
        var points = [];
        // Find coordinates within the chart
        event = chart.pointer.normalize(e.originalEvent);
        // Get the hovered point
        for(var j=0; j<chart.series.length; j++) {
           point = chart.series[j].searchPoint(event, true);
           points.push(point);  
        }

        chart.tooltip.refresh(points);

    }
});
Share Improve this question asked May 25, 2018 at 18:04 user2133404user2133404 1,8576 gold badges35 silver badges67 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 6

Here is my solution. It's perfectly working for me. I made adjustments based on Synchronisation of multiple charts

Demo here

The following code shows/hides the tooltip and makes sure they are aligned on mousemove and mouseleave.

Note that I found that I only need to find the first point searched and use it to show/hide the tooltip. It's because all my time series share the same x values.

$("#container").bind("mousemove mouseleave", function(e) {
  for (let i = 0; i < Highcharts.charts.length; ++i) {
    let hart = Highcharts.charts[i];
    let event = chart.pointer.normalize(e.originalEvent); // Find coordinates within the chart
    let point;
    for (let j = 0; j < chart.series.length && !point; ++j) {
      point = chart.series[j].searchPoint(event, true);
    }
    if (!point) return;

    if (e.type === "mousemove") {
       point.onMouseOver();
      chart.xAxis[0].drawCrosshair(event, point); // Show the crosshair
    } else {
      point.onMouseOut();
      chart.tooltip.hide(point);
      chart.xAxis[0].hideCrosshair();
    }

  }
});

Keep reseting the reset function so as to disallow HighCharts resetting the points -- we take over the control.

Highcharts.Pointer.prototype.reset = function() {
  return undefined;
};

1) How do I make sure that a series is shown in tooltip only when it is actually present?

The unwanted behavior is caused by searchPoint function - it returns the nearest point even though the x position doesn't mach with other points. So if the series has only one point it'll be always found.

Solution:

Manually select points to display in tooltip.formatter:

        formatter: function() {
          var outputString = '';

          this.points.forEach(function(point) {
            if (point.x === this.x) {
              outputString += "<span style='color:" + point.color + "'>\u25CF</span> " + point.series.name + ": <b>" + point.y + "</b><br/>";
            }
          }, this);
          return outputString;
        }

API reference: https://api.highcharts./highcharts/tooltip.formatter


2) How do I make sure the tooltip is closed when we move out of the chart?

Restore the default Highcharts.Pointer.prototype.reset function by removing these lines:

Highcharts.Pointer.prototype.reset = function() {
  return undefined;
};


Demo for both questions: https://jsfiddle/BlackLabel/2mxxrk5n/


Update:

I posted the wrong answer for the second question. This code hides tooltips:

$('#container').bind('mouseout', function(e) {
  Highcharts.charts.forEach(function(chart) {
    chart.tooltip.hide();
    // undo point highlight
    chart.series.forEach(function(series) {
        series.points.forEach((point) => point.setState(''));
    });
  });
});

can you please tell me how to highlight the corresponding points in each chart? As of now, the tooltip shows correctly, however the points are not highlighted in three charts

This piece of highlights points:

points.forEach(function(point_) {
  if (point_) {
    point_.highlight(e);
  }
}, this);

To achieve the desired behavior you must provide a logic for filtering points that should be Highlighted. Here's a very simplified example adjusted to this particular case:

// provide a logic for filtering points
if(points[0] && points[1].x > 0) {
    points.pop(); // remove the unwanted point
}

These approaches (all stemming from the Highcharts demo for synchronized charts) did not work for me for the simple reason I had my charts in a 2x2 grid layout, and apparently because these rely on the mouse event and specific coordinates, the syncing doesn't work when the charts are say, side-by-side (as in my case) instead of all in one vertically aligned column.

I've taken a simpler approach using the built-in point events, and assuming the chart's x-axis values are all the same (and presumably they should be given you want to sync them) then this method will just use the hovered-over point's x-value to make the other points (across all charts and series) with the same x-value appear hovered-over as well.

Here is all the code that is required (none of those other overrides/customizations are needed):

function syncActivePoints(hovPt) {
  for (let i = 0; i < Highcharts.charts.length; ++i) {
    let chart = Highcharts.charts[i];
    let point;
    for (let j = 0; j < chart.series.length; ++j) {
      point = chart.series[j].points[hovPt.x]
      if (!point) return;
      point.setState('hover');
      chart.tooltip.refresh(chart.series[j].data[hovPt.x]);
      chart.xAxis[0].drawCrosshair(null, point);
    }
  }
}

function clearActivePoints(outPt) {

  for (let i = 0; i < Highcharts.charts.length; ++i) {
    let chart = Highcharts.charts[i];
    chart.xAxis[0].hideCrosshair();
    chart.tooltip.hide();

    let point;
    for (let j = 0; j < chart.series.length; ++j) {
      point = chart.series[j].points[outPt.x]
      if (!point) return;
      point.setState();
    }
  }
}

   // this will attach those functions globally (i.e. to all charts)
   // otherwise, you can just add the events to each specfic chart config that you want the syncing behavior to apply to
Highcharts.setOptions({
  plotOptions: {
     series: {
        point: {
            events: {
                mouseOver: function () {
                  syncActivePoints(this);
                },
                mouseOut: function () {
                  clearActivePoints(this);
                },
              }
            }
          }
        }
});

本文标签: javascriptHighcharts synchronize tooltip on multiple charts with multiple seriesStack Overflow