tenebris silentio
tenebris silentio

Reputation: 519

JavaScript Chart.JS - Custom tooltips to add description of specific data point when hovering

I am creating a chart in Chart.JS and want viewers to be able to hover over a specific part of a donut chart and get both the data and a customized description of the data. For the example below, we looked for relevant research projects via the web and through a series of interviews. Right now, it shows the name of the field and the corresponding data. In addition to that, I want to define the field for a user. For example, when someone hovers over Web-Based Search I want them to see:

Web-based Search: 75

We extracted a scan of relevant research projects through web-scraping.

Similarly, I want to add a description defining the interviews field. I've added tooltips before, however it's always been something like [static string] + [data]. I haven't had to create custom definitions for each data point before. Any help is appreciated. See my CodePen also: https://codepen.io/tenebris_silentio/pen/abdgXqg

    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Project Overview</title>
        <script src="https://code.jquery.com/jquery-3.4.1.slim.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.js"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.js"></script>
        <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.css" rel="stylesheet"/>
        <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet"/>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
    </head>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.js"></script>
    <!-- Load plotly.js into the DOM -->
    <script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
  </head>
  <div class="container">
   <div class="row my-3">
          <div class="col">
            <p class="sansserif">
              <h3>Project Overview</h3></p>
          </div>
      </div>

      <div class="row py-2">
          <div class="col-md-4 py-1">
              <div class="card" style="width: 33rem;">
                  <div class="card-body">
                      <canvas id="chDonut1"></canvas>
                  </div>
              </div>
          </div>

  <script>
  /* chart.js chart examples */
  Chart.pluginService.register({
  beforeDraw: function(chart) {
  var width = chart.chart.width,
  height = chart.chart.height,
  ctx = chart.chart.ctx;

  ctx.restore();
  var fontSize = (height / 124).toFixed(2);
  ctx.font = fontSize + "em sans-serif";
  ctx.textBaseline = "middle";

  var text = "";

  switch(chart.chart.canvas.id){
    case "chDonut1":
       text = "Method";
       break;
  }

  var textX = Math.round((width - ctx.measureText(text).width) / 2),
  textY = height / 2;

  ctx.fillText(text, textX, textY);
  ctx.save();
  }
  });
  // chart colors
  var colors = ['#007bff','#28a745','#333333','#c3e6cb','#dc3545','#6c757d'];

  var donutOptions = {
  cutoutPercentage: 85,
  legend: {position:'bottom', padding:5, labels: {pointStyle:'circle', usePointStyle:true}}
  };

  // donut 1
  var chDonutData1 = {
  labels: ['Web-based search', 'Interviews'],
  datasets: [
    {
      backgroundColor: colors.slice(0,2),
      borderWidth: 0,
      data: [75, 25]
    }
  ]
  };

  var chDonut1 = document.getElementById("chDonut1");
  if (chDonut1) {
  new Chart(chDonut1, {
    type: 'pie',
    data: chDonutData1,
    options: donutOptions
  });
  }



  </script>



Upvotes: 0

Views: 1159

Answers (1)

uminder
uminder

Reputation: 26150

You can define a set of tooltip callback functions to obtain the desired result. This could look as follows:

tooltips: {
  callbacks: {
    title: (tooltipItems, data) => data.labels[tooltipItems[0].index],
    label: (tooltipItems, data) => 'Count: ' + data.datasets[0].data[tooltipItems.index],
    footer: (tooltipItems, data) => ['', 'Infos:'].concat(data.datasets[0].info[tooltipItems[0].index])
  }
}

Note that in the footer callback, I return text that I defined in an array named info defined inside the data object.

datasets: [{
  ...
  info: [
    ['This is the description', 'for Web-based search ...'],
    ['This is the description', 'for Interviews ...']
  ]
}]

Please have a look at your amended code below:

<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Project Overview</title>
  <script src="https://code.jquery.com/jquery-3.4.1.slim.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.js"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.js"></script>
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.css" rel="stylesheet" />
  <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.js"></script>
  <!-- Load plotly.js into the DOM -->
  <script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
</head>
<div class="container">
  <div class="row my-3">
    <div class="col">
      <p class="sansserif">
        <h3>Project Overview</h3>
      </p>
    </div>
  </div>

  <div class="row py-2">
    <div class="col-md-4 py-1">
      <div class="card" style="width: 33rem;">
        <div class="card-body">
          <canvas id="chDonut1"></canvas>
        </div>
      </div>
    </div>

    <script>
      /* chart.js chart examples */
      Chart.pluginService.register({
        beforeDraw: function(chart) {
          var width = chart.chart.width,
            height = chart.chart.height,
            ctx = chart.chart.ctx;

          ctx.restore();
          var fontSize = (height / 124).toFixed(2);
          ctx.font = fontSize + "em sans-serif";
          ctx.textBaseline = "middle";

          var text = "";

          switch (chart.chart.canvas.id) {
            case "chDonut1":
              text = "Method";
              break;
          }

          var textX = Math.round((width - ctx.measureText(text).width) / 2),
            textY = height / 2;

          ctx.fillText(text, textX, textY);
          ctx.save();
        }
      });
      // chart colors
      var colors = ['#007bff', '#28a745', '#333333', '#c3e6cb', '#dc3545', '#6c757d'];

      var donutOptions = {
        cutoutPercentage: 85,
        legend: {
          position: 'bottom',
          padding: 5,
          labels: {
            pointStyle: 'circle',
            usePointStyle: true
          }
        },
        tooltips: {
          callbacks: {
            title: (tooltipItems, data) => data.labels[tooltipItems[0].index],
            label: (tooltipItems, data) => 'Count: ' + data.datasets[0].data[tooltipItems.index],
            footer: (tooltipItems, data) => ['', 'Infos:'].concat(data.datasets[0].info[tooltipItems[0].index])
          }
        }
      };

      // donut 1
      var chDonutData1 = {
        labels: ['Web-based search', 'Interviews'],
        datasets: [{
          backgroundColor: colors.slice(0, 2),
          borderWidth: 0,
          data: [75, 25],
          info: [
            ['This is the description', 'for Web-based search ...'],
            ['This is the description', 'for Interviews ...']
          ]
        }]
      };

      var chDonut1 = document.getElementById("chDonut1");
      if (chDonut1) {
        new Chart(chDonut1, {
          type: 'pie',
          data: chDonutData1,
          options: donutOptions
        });
      }
    </script>

</html>

Upvotes: 1

Related Questions