Search code examples
javascriptangularjshighchartshighcharts-ng

Synchronize multiple chart's x-axis


In Angular Highcharts, I wanted to do synchronized X-axis in multiple charts in the dashboard. I found a solution for x-axis synchronize for two chart(pasted below). But I stuck with multi chart synchronize. I have 8 charts in my dashboard. I want to synchronize all of them.

Existing code for two charts in highcharts-ng

$scope.chart1 = {
    options: {
        chart: {
            events: {
                selection: function(event) {
                    self.onZoomHandler(event, 'chart2');
                }
            }
        }
    }
}

self.onZoomHandler = function(event, chartToUpdate) {
    var chart = $scope[chartToUpdate].getHighcharts();
    if (event.xAxis) {
        chart.xAxis[0].setExtremes(event.xAxis[0].min, event.xAxis[0].max);
        if (!chart.resetZoomButton) {
            chart.showResetZoom();
        }
    } else {
        chart.xAxis[0].setExtremes(null, null);
        if (chart.resetZoomButton) {
            chart.resetZoomButton = chart.resetZoomButton.destroy();
        }
    }
};

How could we do this in multiple charts?


Solution

  • You can synchronise tooltips and zoom for any number of charts inside a containing element that has id="container":

    <div id="container">
      <div class="row">
        <highchart id="chart1" config="chartConfig" class="span10"></highchart>
      </div>
      <div class="row">
        <highchart id="chart2" config="chartConfig2" class="span10"></highchart>
      </div>
      <div class="row">
        <highchart id="chart3" config="chartConfig3" class="span10"></highchart>
      </div>
    </div>
    

    with the following JS functions:

    /**
     * In order to synchronize tooltips and crosshairs, override the
     * built-in events with handlers defined on the parent element.
     */
    $('#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];
        event = chart.pointer.normalize(e.originalEvent); // Find coordinates within the chart
        point = chart.series[0].searchPoint(event, true); // Get the hovered point
    
        if (point) {
          point.highlight(e);
        }
      }
    });
    
    /**
     * Override the reset function, we don't need to hide the tooltips and crosshairs.
     */
    Highcharts.Pointer.prototype.reset = function() {
      return undefined;
    };
    
    /**
     * Highlight a point by showing tooltip, setting hover state and draw crosshair
     */
    Highcharts.Point.prototype.highlight = function(event) {
      this.onMouseOver(); // Show the hover marker
      this.series.chart.tooltip.refresh(this); // Show the tooltip
      this.series.chart.xAxis[0].drawCrosshair(event, this); // Show the crosshair
    };
    /**
     * Synchronize zooming through the setExtremes event handler.
     */
    function syncExtremes(e) {
      var thisChart = this.chart;
    
      if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
        Highcharts.each(Highcharts.charts, function(chart) {
          if (chart !== thisChart) {
            if (chart.xAxis[0].setExtremes) { // It is null while updating
              chart.xAxis[0].setExtremes(e.min, e.max, undefined, false, {
                trigger: 'syncExtremes'
              });
            }
          }
        });
      }
    }
    

    (sourced from http://www.highcharts.com/demo/synchronized-charts)

    Then just set the syncExtremes function in the setExtremes callback.

    $scope.chartConfig = {
      ...
      xAxis: {
        events: {
          setExtremes: syncExtremes
        },
      ...
    }
    

    Here's a jsfiddle example with 3 charts: http://jsfiddle.net/nLguvp7m/4/