Is it possible to access the exact xaxis position of the cursor in a highcharts tooltip?

Viewed 29

I'm working on a highcharts solution which includes several different graph types in one single chart. Is it possible to have the exact time of mouse position displayed in the tooltip instead of a calculated range? (We're using highstock together with the boost and xrange module)

Also i need to always show the newest value of a series left from cursor position. Due to having an xRange Series i needed to refresh the tooltip with my own implementation since stickytracking doesnt work with xrange. But right now the display of the green temperature series constantly switches from the actual value to the first value in the series (e.g when hovering around March 11th it constantly changes from 19.85°C back to 19.68°C which is the very first entry)

So i'm having 2 issues:

  1. displaying the exact time in tooltip
  2. displaying specific values in tooltip

I guess both could be solved with having the exact x position of cursor and for displaying the values i guess i did somewhat the right thing already with refreshing the tooltip on mousemove. Still the values won't always display properly.

I understand that Highcharts makes a best guess on the tooltip time value by displaying the range but to me it seems like it orients itself around the xRange Series. I already tries to tinker around with the plotoptions.series.stickyTracking and tooltip.snap values but this doesn't really help me at all.

I understand too that this.x in tooltip formatter function will be bound to the closest point. Still i need it to use the current mouse position. In a first attempt i was filtering through the series in the tooltip formatter itself before i changed to calculating the points on the mousemove event. But there i also couldn't get the right values since x was a rough estimate anyways.

Is there any solution to that?

At the moment i'm using following function onMouseMove to refresh the tooltip:

chart.container.addEventListener('mousemove', function(e) {
  const xValue = chart.xAxis[0].toValue(chart.pointer.normalize(e).chartX);
  const points = [];  
  
  chart.series.filter(s => s.type === "xrange").forEach(s => {
    s.points.forEach(p => {
        const { x, x2 } = p;
      if (xValue >= x && xValue <= x2) points.push(p);
    })
  })
  
  chart.series.filter(s => s.type !== "xrange").forEach(s => {
    const point = s.points.reverse().find(p => p.x <= xValue);
    if(point) points.push(point);
  })

  if (points.length) chart.tooltip.refresh(points, chart.pointer.normalize(e));
})

also i'm using this tooltip configuration and formatter:

tooltip: {
    shared: true,
    followPointer: true,
    backgroundColor: "#FFF",
    borderColor: "#AAAAAA",
    borderRadius: 5,
    shadow: false,
    useHTML: true,
    formatter: function(){
        const header = createHeader(this.x)
      
      return `
        <table>
            ${header}
        </table>
      `
    }
},


const createHeader = x => {
    const headerFormat = '%d.%m.%Y, %H:%M Uhr';
  const dateWithOffSet = x - new Date(x).getTimezoneOffset() * 60 * 1000;
  return `<tr><th colspan="2" style="text-align: left;">${Highcharts.dateFormat(headerFormat, dateWithOffSet)}</th></tr>`
}

See following jsFiddle for my current state (just remove formatter function to see the second issue in action): jsFiddle

(including the boost module throws a script error in jsFiddle. Don't know if this is important so i disabled it for now)

2 Answers

finally found a solution to have access to mouse position in my tooltip: extending Highcharts with a module (kudos to Torstein Hønsi):

(function(H) {
  H.Tooltip.prototype.getAnchor = function(points, mouseEvent) {
    var ret,
      chart = this.chart,
      inverted = chart.inverted,
      plotTop = chart.plotTop,
      plotLeft = chart.plotLeft,
      plotX = 0,
      plotY = 0,
      yAxis,
      xAxis;

    points = H.splat(points);

    // Pie uses a special tooltipPos
    ret = points[0].tooltipPos;

    // When tooltip follows mouse, relate the position to the mouse
    if (this.followPointer && mouseEvent) {
      if (mouseEvent.chartX === undefined) {
        mouseEvent = chart.pointer.normalize(mouseEvent);
      }
      ret = [
        mouseEvent.chartX - chart.plotLeft,
        mouseEvent.chartY - plotTop
      ];
    }
    // When shared, use the average position
    if (!ret) {
      H.each(points, function(point) {
        yAxis = point.series.yAxis;
        xAxis = point.series.xAxis;
        plotX += point.plotX + (!inverted && xAxis ? xAxis.left - plotLeft : 0);
        plotY += (point.plotLow ? (point.plotLow + point.plotHigh) / 2 : point.plotY) +
          (!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151
      });

      plotX /= points.length;
      plotY /= points.length;

      ret = [
        inverted ? chart.plotWidth - plotY : plotX,
        this.shared && !inverted && points.length > 1 && mouseEvent ?
        mouseEvent.chartY - plotTop : // place shared tooltip next to the mouse (#424)
        inverted ? chart.plotHeight - plotX : plotY
      ];
    }
        
    // Add your event to Tooltip instances
        this.event = mouseEvent;

    return H.map(ret, Math.round);
  }
})(Highcharts)

http://jsfiddle.net/2h951hdj/

Also you can wrap dragStart on the pointer and get exactly mouse position, in this case when you click on the chart area you will have the mouse position on the x-axis.

(function(H) {
  H.wrap(H.Pointer.prototype, 'dragStart', function(proceed, e) {
    let chart = this.chart;

    chart.mouseIsDown = e.type;
    chart.cancelClick = false;
    chart.mouseDownX = this.mouseDownX = e.chartX;
    chart.mouseDownY = this.mouseDownY = e.chartY;
    chart.isZoomedByDrag = true;

    console.log(chart.mouseDownX);
  });
}(Highcharts));

Highcharts.chart('container', {
  chart: {
    events: {
      load: function() {
        let chart = this,
          tooltip = chart.tooltip;
        console.log(tooltip);
      }
    }
  },
  series: [{
    data: [2, 5, 2, 3, 6, 5]
  }],

});

Live demo: https://jsfiddle.net/BlackLabel/1b8rf9hc/

Related