score:6

Accepted answer

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.

score: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();  
      }
    }

Related Query