9

I've made a pie chart using Chart.js, and I'd like to detect when a segment is hovered over. I've found plenty of documentation regarding manipulating the tooltips that appear when hovering over segments, but nothing regarding doing something else when a tooltip would appear. Is this possible?

Hydrothermal
  • 4,851
  • 7
  • 26
  • 45
  • Have you tried using [JQuery hover](https://api.jquery.com/hover/)? You could bind a function to the element hover event, like: `$("#chart).hover(function(){ //code here })` – jmartins Mar 26 '15 at 19:40
  • 3
    @jmartins I'm looking to trap hover events on the individual segments. Unfortunately, Chart.js uses Canvas, so the whole chart is just one element. – Hydrothermal Mar 26 '15 at 19:41

6 Answers6

17

I know this has already been given an accepted answer, and im not sure if this does satisfy your use case but Chart js released an update (maybe a month ago or so) that allowed for custom tooltips. This allows for a custom function to run when a tooltip would normally have been drawn. They have an example in the samples section of git hub

in short you define a custom function

 Chart.defaults.global.customTooltips = function(tooltip){//do what you want}

here is the example they give in the samples with an extra bit of text added to an html tooltip. The only annoying thing I see is that don't provide the segment/point/bar that triggered this tooltip which would be really handy as then you could do some thing to the graph knowing this information but you are given the tooltip data which means you can do something with that instead.

Chart.defaults.global.customTooltips = function (tooltip) {
      // Tooltip Element
      var tooltipEl = $('#chartjs-tooltip');
      // Hide if no tooltip
      if (!tooltip) {
          tooltipEl.css({
              opacity: 0
          });
          return;
      }
      // Set caret Position
      tooltipEl.removeClass('above below');
      tooltipEl.addClass(tooltip.yAlign);
      // Set Text
      tooltipEl.html(tooltip.text+ " anythign custom you want");
      // Find Y Location on page
      var top;
      if (tooltip.yAlign == 'above') {
          top = tooltip.y - tooltip.caretHeight - tooltip.caretPadding;
      } else {
          top = tooltip.y + tooltip.caretHeight + tooltip.caretPadding;
      }
      // Display, position, and set styles for font
      tooltipEl.css({
          opacity: 1,
          left: tooltip.chart.canvas.offsetLeft + tooltip.x + 'px',
          top: tooltip.chart.canvas.offsetTop + top + 'px',
          fontFamily: tooltip.fontFamily,
          fontSize: tooltip.fontSize,
          fontStyle: tooltip.fontStyle,
      });
  };
  var pieData = [{
      value: 300,
      color: "#F7464A",
      highlight: "#FF5A5E",
      label: "Red"
  }, {
      value: 50,
      color: "#46BFBD",
      highlight: "#5AD3D1",
      label: "Green"
  }, {
      value: 100,
      color: "#FDB45C",
      highlight: "#FFC870",
      label: "Yellow"
  }, {
      value: 40,
      color: "#949FB1",
      highlight: "#A8B3C5",
      label: "Grey"
  }, {
      value: 120,
      color: "#4D5360",
      highlight: "#616774",
      label: "Dark Grey"
  }];

  var ctx1 = document.getElementById("chart-area1").getContext("2d");
  window.myPie = new Chart(ctx1).Pie(pieData);
  var ctx2 = document.getElementById("chart-area2").getContext("2d");
  window.myPie = new Chart(ctx2).Pie(pieData);
#canvas-holder {
       width: 100%;
       margin-top: 50px;
       text-align: center;
   }
   #chartjs-tooltip {
       opacity: 1;
       position: absolute;
       background: rgba(0, 0, 0, .7);
       color: white;
       padding: 3px;
       border-radius: 3px;
       -webkit-transition: all .1s ease;
       transition: all .1s ease;
       pointer-events: none;
       -webkit-transform: translate(-50%, 0);
       transform: translate(-50%, 0);
   }
   #chartjs-tooltip.below {
       -webkit-transform: translate(-50%, 0);
       transform: translate(-50%, 0);
   }
   #chartjs-tooltip.below:before {
       border: solid;
       border-color: #111 transparent;
       border-color: rgba(0, 0, 0, .8) transparent;
       border-width: 0 8px 8px 8px;
       bottom: 1em;
       content:"";
       display: block;
       left: 50%;
       position: absolute;
       z-index: 99;
       -webkit-transform: translate(-50%, -100%);
       transform: translate(-50%, -100%);
   }
   #chartjs-tooltip.above {
       -webkit-transform: translate(-50%, -100%);
       transform: translate(-50%, -100%);
   }
   #chartjs-tooltip.above:before {
       border: solid;
       border-color: #111 transparent;
       border-color: rgba(0, 0, 0, .8) transparent;
       border-width: 8px 8px 0 8px;
       bottom: 1em;
       content:"";
       display: block;
       left: 50%;
       top: 100%;
       position: absolute;
       z-index: 99;
       -webkit-transform: translate(-50%, 0);
       transform: translate(-50%, 0);
   }
<script src="https://raw.githack.com/chartjs/Chart.js/v1.1.1/Chart.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="canvas-holder">
    <canvas id="chart-area1" width="50" height="50" />
</div>
<div id="canvas-holder">
    <canvas id="chart-area2" width="300" height="300" />
</div>
<div id="chartjs-tooltip"></div>
Quince
  • 14,790
  • 6
  • 60
  • 69
6

The way I'm doing it is slightly more simple: assuming you already have some code defining the canvas like canvas = document.getElementById("chart"); and your pie chart is window.myPie. You can use the onmousemove javascript event, or jQuery hover

canvas.onmousemove = function(evt) {
    var el = window.myPie.getElementsAtEvent(evt);
    //do something with the el object to display other information
    //elsewhere on the page
}

In my case highlight a table row based on the value of el[0]._index

rajuGT
  • 6,224
  • 2
  • 26
  • 44
Artesea
  • 71
  • 1
  • 2
5

No...

There's nothing in the ChartJS API to override or extend the tooltip,

But, a workaround...

You can modify the draw method of the Chart.Tooltip class. This would allow you to "do something else" when the tooltip would normally be rendered by ChartJS.

The draw method you want to tie into starts at line 1351 of the source here:

https://github.com/nnnick/Chart.js/blob/master/src/Chart.Core.js

markE
  • 102,905
  • 11
  • 164
  • 176
5

For v2.0 version:

Chart.defaults.global.hover.onHover = function(x) {
  if(x[0]) {
    var index = x[0]._index;
    // Place here your code
  }
};
rjurado01
  • 5,337
  • 3
  • 36
  • 45
  • Sadly this approach broke in v2.5 and newer (v2.7 currently). Given `options: { hover: { mode: true } }` it will call `onHover` again, but won't give `x` with access to the current hover element the same way it did in v2.4. Probably best to solve it with a custom label approach (even if your not drawing any labels). – Cwista Sep 28 '17 at 10:36
  • From `v2.5` onwards parameters of `onHover` callback has changed. See [chart.js onHover not work](https://stackoverflow.com/a/47547499/4816207) – Prashant Pokhriyal Nov 29 '17 at 07:57
4

UPDATE 2020 : For Chartjs 3.5

Here is a quick fix that I used with Chartjs 3.5 to trigger events on hover over Doughnut or Pie parts. It relies on using the existing onHover option :

onHover : (event, activeElements) => {

    if (activeElements.length > 0) {

            // get active chart element
            let elt = activeElements[0];

            // retrieve element label
            let lbl = elt._model.label;

            // Get element value
            let elt_index = elt._chart.tooltip._data.labels.indexOf(lbl);
            let val = elt._chart.tooltip._data.datasets[0].data[elt_index];

           // trigger your event here :
           // ex for vuejs : this.$emit('element-hovered', { label : lbl, value : val });

       }
       else {
           // No active elements
       }

   }

Working fine so far :)

thiout_p
  • 717
  • 11
  • 15
2

If found a small trick using customTooltip, too. I searched for a solution to get an Event if the user moves with the mouse over a Value and a Tooltip is shown. Mainly i liked to present in a different frame some detail informations besides the raw Plot values.

var options = {            
  customTooltips: function (tooltip)
  {
    if (!tooltip) return;

    tooltip.custom = false;
    tooltip.draw();
    OnEntrySelected(tooltip.title);
    tooltip.custom = this;
  }
}

The custom tooltip is drawn using tooltip.draw(), but this calls the custom method. I set it to false to avoid a recursive loop, call the default behavior and take the data i need for my callback (OnEntrySelected) which is in this case the string of the x-Axis label. This event is triggered whenever the tooltip is shown.