Highlight Line Series on legend hover
Asked Answered
L

2

6

Really liking chart.js, but I am struggling to create custom events (not overly experienced so still learning)

I am trying to highlight a complete line, or bar series when the legend tooltip is hovered, essentially show all elements in a dataset to the hover style.

I am trying to do this via the legend onHover callback, but not sure if this is correct? I have managed to find the dataset being hovered but struggling to be able to run the updateHoverStyle to highlight the series.

Any help or pointers as to where I should be looking?

This is where I've got to so far...

legend: {
      labels: {
        usePointStyle: true
      },
      onHover: function(event, legendItem) {
        var me = this;
        var options = me.options || {};
        var hoverOptions = options.hover;
        var index = legendItem.datasetIndex;
        var ci = this.chart;
        var elements = ci.getDatasetMeta(index).data;
        ci.updateHoverStyle(elements, hoverOptions.mode, true)
      }
    }
Lakesha answered 6/3, 2017 at 8:47 Comment(2)
Provide a working example.Macromolecule
@SahilDhir I don't have a working example with chart.js, I am trying to mimic a feature when I use Google Charts Visualizations. If you see in the below the blue line has a highlight shadow when I hover over the blue legend label (example with Google Charts) linkLakesha
F
6

You were certainly on the right track, but let me help you along with a working example for a bar chart along with some explanation.

To implement this behavior, you definitely want to make use of the legend onHover property (just like you did). However, once you update the hover style you must re-render the chart for the change to take effect. Here is an example.

legend: {
  labels: {
    usePointStyle: true
  },
  onHover: function(event, legendItem) {
    var options = this.options || {};
    var hoverOptions = options.hover || {};
    var ci = this.chart;
    hoveredDatasetIndex = legendItem.datasetIndex;
    ci.updateHoverStyle(ci.getDatasetMeta(hoveredDatasetIndex).data, hoverOptions.mode, true);
    ci.render();
  }
}

With that working, now we need a way to unset or clear the hover style once the legend item is no longer hovered. Otherwise, each time the user hovers over a legend item the series in the graph will get darker and darker until its just black.

So we need some way to clear the hover style. It would have been great if there was a legend onMouseLeave property, but alas...there isn't. So to get around this we end up having to "trick" chart.js to doing what we want. The trick is to use the tooltips custom function. Here is an example below.

tooltips: {
  mode: 'index',
  intersect: false,
  custom: function(tooltip) {
    if (hoveredDatasetIndex != -1) {
      var options = this.options || {};
      var hoverOptions = options.hover || {};
      var ci = this._chartInstance.chart.controller;
      ci.updateHoverStyle(ci.getDatasetMeta(hoveredDatasetIndex).data, hoverOptions.mode, false);
      hoveredDatasetIndex = -1;
      ci.render();
    }
  }
}

What this is doing is clearing the hover style (by passing in false to the last argument of updateHoverStyle). Since we are outside of the context of the legend, I simply used a variable external to my callbacks to store the previously hovered dataset index.

The reason this 'hack' works is because the tooltips callback is called each time the mouse is moved anywhere on the entire chart (but not the legend). So it represents everything except the legend. Because of this, we can use it just like we would have used the non-existent but handy legend onMouseLeave callback.

Hopefully this all makes sense. Here is a working codepen to demonstrate the full solution.

Flanch answered 6/3, 2017 at 19:8 Comment(4)
Brilliant, happy to know I was on the right track... I may of worked out the re-rendering, but using tooltips.custom to reverse it I would never of worked out :) Thank you very much, solution seems to work on line & bar which is what I wanted!Lakesha
This worked for me after I copied clearing logic (from tooltips section) into the beginning of the highlighting logic (legend section). Otherwise, all series would end up highlighted.Adachi
wouldn't it be enough to remove highlighting of all traces but the one which the mouse is hovering on?Yonit
People reading this answer should be aware that they added a onLeave parameter to newer versions of chart.JS. So no longer need to do this hack.Tartarous
Y
0

There is only one thing missing in your code: chart refresh using ci.render();

legend: {
      labels: {
        usePointStyle: true
      },
      onHover: function(event, legendItem) {
        var me = this;
        var options = me.options || {};
        var hoverOptions = options.hover;
        var index = legendItem.datasetIndex;
        var ci = this.chart;
        var elements = ci.getDatasetMeta(index).data;
        ci.updateHoverStyle(elements, hoverOptions.mode, true)
        ci.render();  //    <<---- commit changes
      }
    }

But this will end up highlighting all the traces: you must remove highlight of all other traces before highlighting the hovered one, bt cycling through them:

legend: {
      labels: {
        usePointStyle: true
      },
      onHover: function(event, legendItem) {
        var me = this;
        var options = me.options || {};
        var hoverOptions = options.hover;
        var index = legendItem.datasetIndex;
        var ci = this.chart;
        for (var i=0; i < ci.datasets.length-1; i++) {
           var elements = ci.getDatasetMeta(i).data;
           ci.updateHoverStyle(elements, hoverOptions.mode, false) ; // <<<--- turn off higlight
        }
        var elements = ci.getDatasetMeta(index).data;
        ci.updateHoverStyle(elements, hoverOptions.mode, true) // <<-- Turn on 
        ci.render();  
      }
    }
Yonit answered 15/2, 2022 at 13:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.