[Chartjs]-Chart.js hover over label

-1πŸ‘

βœ…

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>

0πŸ‘

You can just use the custom plugin from that question and ignore everything but mousemove events instead of ignoring everything but click events:

const findLabel = (labels, evt) => {
  let found = false;
  let res = null;

  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;
      }
    });
  });

  return [found, res];
};

const getLabelHitboxes = (scales) => (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
  }))
})));

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) {
      console.log(labelInfo);
    }

  }
}

Chart.register(plugin);

const options = {
  type: 'line',
  data: {
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [{
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        borderColor: 'pink'
      },
      {
        label: '# of Points',
        data: [7, 11, 5, 8, 3, 7],
        borderColor: 'orange'
      }
    ]
  },
  options: {}
}

const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
  <canvas id="chartJSContainer" width="600" height="400"></canvas>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js"></script>
</body>

0πŸ‘

To change the cursor to a pointer when hovering over a category label in a Chart.js bar chart, you can add:

options: {
  plugins: {
    tooltip: {
      mode: 'index',
      intersect: false
    },
  },
  interaction: {
    mode: 'index',
    intersect: false
  },
  onHover: function(evt, elements) {
    if (elements.length) {
      document.getElementById("myChart").style.cursor = "pointer";
    } else {
      document.getElementById("myChart").style.cursor = "default";
    }
  },
  // ...
}

To change the color of a label when it is being hovered on, you can add:

options: {
  plugins: {
    tooltip: {
      mode: 'index',
      intersect: false
    },
  },
  interaction: {
    mode: 'index',
    intersect: false
  },
  onHover: function(evt, elements) {
    if (elements.length) {
      var chart = evt.chart;
      var datasetIndex = elements[0].datasetIndex;
      var index = elements[0].index;
      chart.data.labels[index] = '<span style="color: red;">' + chart.data.labels[index] + '</span>';
      chart.update();
    } else {
      var chart = evt.chart;
      chart.data.labels = ['Item A', 'Item B', 'Item C'];
      chart.update();
    }
  },
  // ...
}

Leave a comment