Changing tooltip color in Chart.js

10,264

Solution 1

Pretty much anytime you want to customize the look and feel of the tooltip beyond the configuration options provided by chart.js, you will have to use custom tooltips. The good thing about custom tooltips is that they are html/css based so your styling options are endless.

In this case, since you are trying to change the tooltip background color based upon the value of your data, you can simply define some css classes to handle both styling options and then implement logic in your custom tooltip function to assign the appropriate css class based upon a value.

Here is an example.

custom: function(tooltip) {
  // get the tooltip element
  var tooltipEl = document.getElementById('chartjs-tooltip');

  // create one if it does not yet exist
  if (!tooltipEl) {
    tooltipEl = document.createElement('div');
    tooltipEl.id = 'chartjs-tooltip';
    tooltipEl.innerHTML = "<table></table>"
    document.body.appendChild(tooltipEl);
  }

  // hide the tooltip and restore the cursor
  // if there isn't one to display yet
  if (tooltip.opacity === 0) {
    tooltipEl.style.opacity = 0;
    $(this._chart.canvas).css("cursor", "default");
    return;
  } else {
    // otherwise change the cursor to pointer
    $(this._chart.canvas).css("cursor", "pointer");
  }

  // clear all classes and add the correct background color
  tooltipEl.classList.remove('active', 'normal');
  if (tooltip.dataPoints[0].yLabel === 2) {
    tooltipEl.classList.add('active');
  } else {
    tooltipEl.classList.add('normal');
  }

  function getBody(bodyItem) {
    return bodyItem.lines;
  }

  // set tooltip text
  if (tooltip.body) {
    var titleLines = tooltip.title || [];
    var bodyLines = tooltip.body.map(getBody);
    var innerHtml = '<thead>';

    titleLines.forEach(function(title) {
      innerHtml += '<tr><th>' + title + '</th></tr>';
    });
    innerHtml += '</thead><tbody>';

    bodyLines.forEach(function(body, i) {
      // map the number that is going to be displayed state value
      if (tooltip.dataPoints[0].yLabel === 2) {
        body = 'Active';
      } else {
        body = 'Normal';
      }

      var colors = tooltip.labelColors[i];
      var style = 'background:' + colors.backgroundColor;
      style += '; border-color:' + colors.borderColor;
      style += '; border-width: 2px'; 
      var span = '<span class="chartjs-tooltip-key" style="' + style + '"></span>';
      innerHtml += '<tr><td>' + span + body + '</td></tr>';
    });
    innerHtml += '</tbody>';

    var tableRoot = tooltipEl.querySelector('table');
    tableRoot.innerHTML = innerHtml;
  }

  // get the position of tooltip
  var position = this._chart.canvas.getBoundingClientRect();

  // set display, position, and font styles
  tooltipEl.style.opacity = 1;
  tooltipEl.style.left = position.left + tooltip.caretX + 'px';
  tooltipEl.style.top = position.top + tooltip.caretY + 'px';
  tooltipEl.style.fontFamily = tooltip._fontFamily;
  tooltipEl.style.fontSize = tooltip.fontSize;
  tooltipEl.style.fontStyle = tooltip._fontStyle;
  tooltipEl.style.padding = tooltip.yPadding + 'px ' + tooltip.xPadding + 'px';
}

It also sounds like you want to customize the point color based upon the data value. This is actually quite simple to do and does not require using any sort of callback. You can simply pass an array of colors to the pointBackgroundColor and pointBorderColor properties where each index in the array maps to the index in your data array.

For example, if you want the first and third points to be a different color then your array might look like this [blue, red, blue, red]. You can just build your color array while you are building your chart data object (e.g. in the prepareDataForChart() function.

Here is an example of that.

// this function processess our raw data and converts
// it to a format that chart.js can understand
var prepareDataForChart = function(data) {
  var chartData = {
    data: [],
    pointColors: [],
  };

  data.forEach(function(d) {
    var yValue;
    var pointColor;

    // since we cannot use a category scale on the y-axis,
    // lets map the eventState to an arbitrary numerical value
    // and set the point color
    if (d.eventState === 'A') {
      yValue = 2;
      pointColor = chartColors.red;
    } else if (d.eventState === 'N') {
      yValue = 1;
      pointColor = chartColors.blue;
    }

    // we have to use the 'alternate' dataset format
    // in order to graph this correctly
    chartData.data.push({
      x: d.eventTime,
      y: yValue,
    });

    // add our point color to the colors array
    chartData.pointColors.push(pointColor);
  });

  return chartData;
};

Then when you create you chart just use the pointColors array that was returned by your function.

var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
  type: 'line',
  data: {
    datasets: [{
      label: "Object 1",
      fill: false,
      borderColor: chartColors.red,
      borderWidth: 2,
      backgroundColor: chartColors.white,
      pointBorderColor: chartData.pointColors,
      pointBackgroundColor: chartData.pointColors,
      pointBorderWidth: 1,
      pointHoverRadius: 5,
      pointHitRadius: 20,
      steppedLine: true,
      data: chartData.data,
    }]
  },
// ...rest of chart options...

Here is a codepen example demonstrating everything that was discussed (colored tooltips and points).

Solution 2

Alternatively, you can use the label Color function.

tooltips: {
     callbacks: {
       labelColor: function(tooltipItem,data) {
           if (tooltipItem.datasetIndex === 0) {
                return {
                    borderColor: "#FFFFFF",
                    backgroundColor: "#FFCD2E"
                        };
                 }
Share:
10,264
Maresia
Author by

Maresia

Updated on June 15, 2022

Comments

  • Maresia
    Maresia almost 2 years

    trying to customized the tooltip based on the value of the data. Also there are these small circular connectors or joins on the corner of a line chart, when the line goes up or down, that I am also trying to change its color and for those I don't even know where to start. Here is a portion of the code I am using where I am trying to return and change the tooltip color using Chart.js

            tooltips: {
              mode: 'index',
              intersect: false,
              callbacks: {
    
                label: function(tooltipItem, data) {
                  if (tooltipItem.yLabel === 1) {
    
        //return 'Normal';
                return { 'Normal',
                 tooltipFillColor: 'rgb(255, 99, 132)', // red
            };
    
                  } else if (tooltipItem.yLabel === 2) {
    
                   // return 'Active';
            return { 'Acitve',
                 tooltipFillColor: 'rgb(75, 192, 192)', // green
            };
    
                  }
                }
              }
            },
    

    Thanks for all the help!

  • Maresia
    Maresia about 7 years
    Hi Jordan, I see that the code you posted would do very close to what I am trying, but if I could alter the values of these four items that is in the dataset: [{ pointBorderColor: '#000000', pointBackgroundColor: "#ff0000", pointBorderWidth: 1, pointHoverRadius: 5, ... }] based on the value of "tooltipItem.yLabel" it would do the trick for me. Thats what I am trying to do in the "return" from the "if" statement.
  • jordanwillis
    jordanwillis about 7 years
    You can actually just set the color of the points individually by passing an array to pointBackgroundColor and pointBorderColor. See my updated answer which explains all this (I updated the codepen too).
  • Maresia
    Maresia about 7 years
    Thats great, simpler than how I was trying, creating another function just to get the colors based on the values of d.eventState. I found an issue where if I only have one value, how came the chat doesn't show any line for that value, just the tooltip show: codepen.io/anon/pen/yMQNaM