2

I am using react-highchart for a project. And displaying two charts: 1) Line Chart with 2 series data, it will render two lines on same chart. 2) Bar or Column Chart.

Now when I hover over a point it should enable tooltip on both lines in 1st chart and in column chart as well. X-axis is datetime. It should active point on both lines like this:

enter image description here

In react-highchart, I have used shared: true attribute, but it is not making both lines active.

tooltip: {
  enabled: true,
  useHTML: true,
  shared: true,
  backgroundColor: 'rgba(255,255,255,1)',
  borderRadius: 3,
  shape: 'rectangle'
}

And is there a way to make another chart's tooltip active as well?

EDIT

After a suggestion, I was checking synchronized-charts in highcharts, but the code example was in jQuery, I need it in react-highcharts. Still I tried to convert the code to react and did this:

import ReactHighcharts from 'react-highcharts/ReactHighcharts';

/**
 * Override the reset function, we don't need to hide the tooltips and
 * crosshairs.
*/
ReactHighcharts.Highcharts.Pointer.prototype.reset = function reset() {
 return undefined;
};

ReactHighcharts.Highcharts.Point.prototype.highlight = function highlight(event) {
 event = this.series.chart.pointer.normalize(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
};

After chart render callback:

['mousemove', 'touchmove', 'touchstart'].forEach(eventType => {
  const container = document.getElementById('tab__charts');
  container.removeEventListener(eventType, this.handleMouseMove);
  container.addEventListener(eventType, this.handleMouseMove);
});

Handle Mouse move and syncExtreme:

handleMouseMove(e) {
  for (let i = 0; i < ReactHighcharts.Highcharts.charts.length; i += 1) {
    const chart = ReactHighcharts.Highcharts.charts[i];
    if (chart) {
      // Find coordinates within the chart
      const event = chart.pointer.normalize(e);
      // Get the hovered point
      const point = chart.series[0].searchPoint(event, true);

      if (point) {
        point.highlight(e);
      }
    }
  }
}

syncExtremes(e) {
  const thisChart = this.chart;

  if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
    ReactHighcharts.Highcharts.each(ReactHighcharts.Highcharts.charts, (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' },
          );
        }
      }
    });
  }
}

Now when I hover over the the point it is giving error:

enter image description here

But Somehow It worked for second chart, If I hover over second chart point, it is showing tooltip on both chart. Not working for first chart. Plus first chart has two series. I am getting closer to solution.

EDIT 2: Solution I figured out that what was causing Tooltips to be synchronised only if hover over second Chart. It was due to that console error, which was breaking the code (For loop inside handleMouseMove()). So after putting that error into try catch, it fixed the problem.

 if (point) {
   try {
     point.highlight(e);
   } catch (err) {
      // pass;
   }
 }

Not the best way, but it works. The only problem now is, first chart has two Series line (Check the above image), and only first one getting active circle, not the second one.

EDIT 3: Solution for Highlighting Multiple series.

After reading the code, I found this line, whhich was causing only first series to highlight point:

point = chart.series[0].searchPoint(event, true)

this line is only taking first series. Bad code. It should be:

chart.series.forEach(series => {
    const point = series.searchPoint(event, true);
    if (point) {
        try {
            point.highlight(e);
        } catch (err) {
            // pass;
        }
    }
});

The only issue is now this try catch, without it getting Can not read property category of undefined.

Rahul Sagore
  • 1,588
  • 2
  • 26
  • 47
  • It requires synchronized charts, check this example https://www.highcharts.com/demo/synchronized-charts. – Wojciech Chmiel Nov 08 '18 at 10:25
  • @WojciechChmiel Thanks for the right option, I tried it, and updated the question. Is there a react example for this? – Rahul Sagore Nov 12 '18 at 05:41
  • @WojciechChmiel Somehow It worked for second chart, If I hover over second chart point, it is showing tooltip both chart. Not working for first chart. Plus first chart has two series. I am getting closer to solution. – Rahul Sagore Nov 12 '18 at 06:53

1 Answers1

2

I recommend you to use highcharts-react-official wrapper. Below you can find an example of synchronized charts:

import React from "react";
import { render } from "react-dom";
// Import Highcharts
import Highcharts from "highcharts/highstock";
//import HighchartsReact from "./HighchartsReact.min.js";
import HighchartsReact from "highcharts-react-official";

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

  /**
   * Highlight a point by showing tooltip, setting hover state and draw crosshair
   */
  H.Point.prototype.highlight = function(event) {
    event = this.series.chart.pointer.normalize(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
  };

  H.syncExtremes = function(e) {
    var thisChart = this.chart;

    if (e.trigger !== "syncExtremes") {
      // Prevent feedback loop
      Highcharts.each(Highcharts.charts, function(chart) {
        if (chart && chart !== thisChart) {
          if (chart.xAxis[0].setExtremes) {
            // It is null while updating
            chart.xAxis[0].setExtremes(e.min, e.max, undefined, false, {
              trigger: "syncExtremes"
            });
          }
        }
      });
    }
  };
})(Highcharts);

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      options: {
        chart: {
          type: "line",
          zoomType: "x",
          panning: true,
          panKey: "shift"
        },
        xAxis: {
          events: {
            setExtremes: function(e) {
              Highcharts.syncExtremes(e);
            }
          }
        },
        series: [
          {
            data: [
              29.9,
              71.5,
              106.4,
              129.2,
              144.0,
              176.0,
              135.6,
              148.5,
              216.4,
              194.1,
              95.6,
              54.4
            ]
          }
        ]
      },
      options2: {
        chart: {
          zoomType: "x"
        },
        series: [
          {
            data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
          }
        ],
        xAxis: {
          events: {
            setExtremes: function(e) {
              Highcharts.syncExtremes(e);
            }
          }
        }
      }
    };
  }

  componentDidMount() {
    ["mousemove", "touchmove", "touchstart"].forEach(function(eventType) {
      document
        .getElementById("container")
        .addEventListener(eventType, function(e) {
          var chart, point, i, event;

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

              if (point) {
                point.highlight(e);
              }
            }
          }
        });
    });
  }

  inputChange(e) {
    this.setState({
      options: {
        series: [{ data: [1, 1, 1] }, { data: [2, 2, 2] }]
      }
    });
  }

  render() {
    return (
      <div id="container">
        <HighchartsReact
          constructorType={"chart"}
          highcharts={Highcharts}
          options={this.state.options}
        />
        <HighchartsReact
          constructorType={"chart"}
          highcharts={Highcharts}
          options={this.state.options2}
        />
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

Live demo: https://codesandbox.io/s/jl8mrq2m53

ppotaczek
  • 36,341
  • 2
  • 14
  • 24
  • This works, but my First chart has two Line series, and I want to add hover circle on both lines if any of them is hovered, check the screenshot I added in question. Only the hovered line is getting active circle on point. – Rahul Sagore Dec 04 '18 at 07:46
  • And, on over it gives console error `TypeError: Cannot read property 'category' of undefined`, when calling `point.highlight(e);` function – Rahul Sagore Dec 04 '18 at 07:54
  • 1
    Figured it out, this line `point = chart.series[0].searchPoint(event, true);` only taking first series. After looping over it fixed the issue. – Rahul Sagore Dec 04 '18 at 08:18