Search code examples
javascriptcsshighchartsmouseeventmouseover

Highcharts mouseOver: highlight relevant yAxis and de-emphasize the rest


I have a chart with several series with different colors and yAxes on both sides. It looks like this:

example

As shown here, colors are shared between the points, lines, legend and yAxis labels, to distinguish between Series.

What I want to achieve, is to extend the normal behavior of Highcharts regarding mouseOvers: namely, to highlight relevant elements and de-emphasize the rest.

Highcharts does this as far as the datapoints and lines are concerned: the line under the mouse gets widened, while others lose opacity (the same happens when the mouse is over the corresponding legend). yAxis labels, however, remain the same. I would like to embolden the corresponding labels and lower the opacity for the rest... so far I only managed to do the embolden part, but not the rest. It looks like this:

highlight

It seems that creating functions for the plotOptions.series.events.mouseOver and plotOptions.series.events.mouseOut elements is key. Inside those functions the this object seems to be available, which can be used to affect the elements corresponding to the highlighted series but, crucially, not the rest.

How can I affect the labels from all other series not highlighted? Moreover, can I also modify the behavior of mouseOver for the legend elements?

Here's a jsfiddle with the example: https://jsfiddle.net/sv6bcfw2/

Here's the code:

Highcharts.chart('container', {
  series: [{
    data: [1, 2, 3]
  }, {
    data: [18, 4, 15, 7],
    yAxis: 1
  }, {
    data: [100, 200, 81],
    yAxis: 2
  }, {
    data: [2, 5, 9],
    yAxis: 3
  }, {
    data: [19, 29, 23],
    yAxis: 4
  }, {
    data: [100, 0, -100],
    yAxis: 5
  }],

  plotOptions: {
    series: {
      events: {
        mouseOver: function() {
          this.yAxis.update({
            labels: {
              style: {
                fontWeight: "bold"
              }
            }
          });
        },

        mouseOut: function() {
          this.yAxis.update({
            labels: {
              style: {
                fontWeight: "normal"
              }
            }
          });
        }
      }
    }
  },

  yAxis: [{
    title: null,
    opposite: true,
    color: '#7cb5ec',
    labels: {
      style: {
        color: '#7cb5ec',
        fontWeight: "normal"
      }
    }
  }, {
    title: null,
    color: '#434348',
    labels: {
      style: {
        color: '#434348',
        fontWeight: "normal"
      }
    }
  }, {
    title: null,
    opposite: true,
    color: '#90ed7d',
    labels: {
      style: {
        color: '#90ed7d',
        fontWeight: "normal"
      }
    }
  }, {
    title: null,
    color: '#f7a35c',
    labels: {
      style: {
        color: '#f7a35c',
        fontWeight: "normal"
      }
    }
  }, {
    title: null,
    opposite: true,
    color: '#8085e9',
    labels: {
      style: {
        color: '#8085e9',
        fontWeight: "normal"
      }
    }
  }, {
    title: null,
    color: '#f15c80',
    labels: {
      style: {
        color: '#f15c80',
        fontWeight: "normal"
      }
    }
  }]
});

Thanks for your time!


Solution

  • Your current implementation for mouseOver/mouseOut events is perfect. I've just found a small error - all of your yAxis labels were bold on start, so hovering on them and updating the fontWeight to bold didn't do much.

    Starting with manipulating the labels of the other series' axes, on this keyword you also have access to the chart object, which stores all of the axes. With that, you can grab the hovered series axis index (from this.yAxis.userOptions.index), and affect the rest of them.

      plotOptions: {
        series: {
          events: {
            mouseOver: function() {
              const hoveredAxisIndex = this.yAxis.userOptions.index;
              // Update hovered axis
              this.yAxis.update({
                labels: {
                  style: {
                    fontWeight: "bold"
                  }
                }
              });
              // Update the rest of axes
              this.chart.yAxis.forEach(axis => {
                if (axis.userOptions.index === hoveredAxisIndex) return;
                axis.update({
                  labels: {
                    style: {
                      opacity: 0.2
                    }
                  }
                })
              });
            },
    
            mouseOut: function() {
              // Apply the default axes styling
              this.chart.yAxis.forEach(axis => {
                axis.update({
                  labels: {
                    style: {
                      opacity: 1,
                      fontWeight: "normal"
                    }
                  }
                })
              });
            }
          }
        }
      },
    

    When it comes to enabling the same event on legend hover, there is no built-in event like on the series, but it's fairly easy to add it by yourself on the chart.load event.

    DEMO: https://jsfiddle.net/BlackLabel/x4pcLkns/

    Since we use the same functions again, I would suggest a simple refactor: https://jsfiddle.net/BlackLabel/j9pbku5h/