javascriptchart.jsvue-chartjs

Chart.js hover over label


I have a bar chart in Chart.js (using the latest version), and I want to make some visual change when the mouse is hovering over a category label. How would I implement either or both of the following visual changes?

  1. Make the cursor be a pointer while hovering over a label.
  2. Make the label be in a different color while it is being hovered on.

A related question is here: How to detect click on chart js 3.7.1 axis label?. However, my question is about hovering over a label, without clicking on the label.

In the example below, I want something to happen when hovering on these texts: Item A, Item B, Item C.

window.onload = function() {
  var ctx = document.getElementById('myChart').getContext('2d');
  window.myBar = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: ['Item A', 'Item B', 'Item C'],
      datasets: [{
        data: [1, 2, 3],
        backgroundColor: 'lightblue'
      }]
    },
    options: {
      responsive: true,
      indexAxis: 'y',
      plugins: {
        legend: {
          display: false
        },
        tooltip: {
          enabled: false
        },
      }
    }
  });
};
.chart-container {
  position: relative;
  height: 90vh;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.2.0"></script>

<div class="chart-container">
  <canvas id="myChart"></canvas>
</div>


Solution

  • To make the cursor a pointer while hovering over a label, you can try to assign a CSS cursor value to event.native.target.style.cursor when hover is triggered.

    event.native.target.style.cursor = 'pointer';
    

    To make the label a different color while it is being hovered on, you can try this

    myChart.config.options.scales.y.ticks.color = hoverColors; // ['black','red','black'], ['black','black','red'], ['red','black','black']
    

    UPDATE

    Thanks to LeeLenalee for giving an almost correct answer. I've edited the code above so it fits what is required in the problem. Don't forget to change source of the library in the HTML from :

    https://cdn.jsdelivr.net/npm/chart.js@4.2.0
    

    to :

    https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js
    

    Updated code:

    window.onload = function() {
      const findLabel = (labels, evt) => {
          let found = false;
          let res = null;
          try {
              labels.forEach(l => {
                  l.labels.forEach((label, index) => {
                      if (evt.x > label.x && evt.x < label.x2 && evt.y > label.y && evt.y < label.y2) {
                          res = {
                              label: label.label,
                              index
                          };
                          found = true;
                      }
                  });
              });
          } catch (e) {}
    
          return [found, res];
      };
    
      const getLabelHitboxes = (scales) => {
          try {
              return Object.values(scales).map((s) => ({
                  scaleId: s.id,
                  labels: s._labelItems.map((e, i) => ({
                      x: e.translation[0] - s._labelSizes.widths[i],
                      x2: e.translation[0] + s._labelSizes.widths[i] / 2,
                      y: e.translation[1] - s._labelSizes.heights[i] / 2,
                      y2: e.translation[1] + s._labelSizes.heights[i] / 2,
                      label: e.label,
                      index: i
                  }))
              }));
          } catch (e) {}
      };
    
    
      const changeCursorAndLabelColor = (event, chart, index, hoverMode) => {
          // your hover color here
          // const hoverColor = '#ff0000';
          const hoverColor = 'red';
          const hoverColors = [];
    
          for (let i = 0; i < myChart.data.datasets[0].data.length; i++) {
              if (hoverMode) {
                  // change cursor
                  event.native.target.style.cursor = 'pointer';
                  if (index === i) {
                      hoverColors.push(hoverColor);
                  } else {
                      hoverColors.push(defaultLabelColor);
                  }
              } else {
                  // change cursor
                  event.native.target.style.cursor = 'default';
                  hoverColors.push(defaultLabelColor);
              }
          }
          // change label to your hover color
          myChart.config.options.scales.y.ticks.color = hoverColors;
    
          // update chart when hover is triggered
          myChart.update();
      }
    
      let foundMode = false;
      const plugin = {
          id: 'customHover',
          afterEvent: (chart, event, opts) => {
              const evt = event.event;
              if (evt.type !== 'mousemove') {
                  return;
              }
              const [found, labelInfo] = findLabel(getLabelHitboxes(chart.scales), evt);
              if (found && myChart.data.labels.includes(labelInfo.label)) {
                  changeCursorAndLabelColor(evt, chart, labelInfo.index, true);
                  foundMode = true;
              } else {
                  if (foundMode) changeCursorAndLabelColor(evt, chart, null, false);
                  foundMode = false;
              }
    
          }
      }
    
      Chart.register(plugin);
      var ctx = document.getElementById('myChart');
      const myChart = new Chart(ctx, {
          type: 'bar',
          data: {
              labels: ['Item A', 'Item B', 'Item C'],
              datasets: [{
                  label: 'My Data',
                  data: [1, 2, 3],
                  backgroundColor: 'lightblue'
              }]
          },
          options: {
              responsive: true,
              indexAxis: 'y',
              plugins: {
                  legend: {
                      display: false
                  },
                  tooltip: {
                      enabled: false
                  },
              },
              onHover: (event, chart) => {
                  if (foundMode) changeCursorAndLabelColor(event, chart, null, false);
                  foundMode = false;
              }
          }
      });
      const defaultLabelColor = myChart.config.options.scales.y.ticks.color;
    };
    .chart-container {
      position: relative;
      height: 90vh;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js"></script>
    <div class="chart-container">
      <canvas id="myChart"></canvas>
    </div>