[Chartjs]-Chartjs stacked bar separate tooltip for all stacked

2👍

to get the total of each stack, you can use the dataPoints found in the tooltip context
and use the dataset labels to group by each stack

        // group stacks
        const groups = {};
        tooltip.dataPoints.forEach(function (point) {
          if (groups.hasOwnProperty(barChartData.datasets[point.datasetIndex].label)) {
            groups[barChartData.datasets[point.datasetIndex].label] += parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
          } else {
            groups[barChartData.datasets[point.datasetIndex].label] = parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
          }
        });

e.g. –> {"Product 2":492.53,"Product ":2202.4700000000003}

then use the external option to create a custom tooltip

see following working snippet…

$(document).ready(function() {
  var barChartData = {
    labels: ["2022-01-17","2022-01-18","2022-01-19","2022-01-20","2022-01-21","2022-01-22","2022-01-23","2022-01-24","2022-01-25","2022-01-26","2022-01-27","2022-01-28","2022-01-29","2022-01-30"],
    datasets: [{"label":"Product 2","data":["292.53","328.5","273.83","305.44","260.33","251.87","118.15","253.95","86.64","87.78","116.68","295.49","61.32","83.78"],"backgroundColor":"#66bb6a","borderColor":"#66bb6a","pointBackgroundColor":"#66bb6a","stack":"Stack 0"},{"label":"Product ","data":["1522.27","1844.83","1581.01","2767.68","2821.36","2940.31","2876.1","2037.79","1593.01","1900.86","1607.21","2188.92","2428.74","2508.81"],"backgroundColor":"#1b5e20","borderColor":"#1b5e20","pointBackgroundColor":"#1b5e20","stack":"Stack 0"},{"label":"Product 2","data":["200","4.14","28.51","13.68","0","0","19.93","0","0","0","10.47","23.05","9.42","10.58"],"backgroundColor":"#ffcdd2","borderColor":"#ffcdd2","pointBackgroundColor":"#ffcdd2","stack":"Stack 1"},{"label":"Product ","data":["680.2","536.51","524.41","479.69","453.19","521.87","530.57","485.13","440.25","591.29","722.73","711.58","686.63","510.72"],"backgroundColor":"#ef9a9a","borderColor":"#ef9a9a","pointBackgroundColor":"#ef9a9a","stack":"Stack 1"}]
  };

  var ctx = document.getElementById("canvas").getContext("2d");
  var myBar = new Chart(ctx, {
    type: 'bar',
    data: barChartData,
    options: {
      interaction: {
        intersect: false,
        mode: 'index',
      },
      plugins: {
        tooltip: {
          enabled: false,
          position: 'nearest',
          external: function (context) {
            // init
            const {chart, tooltip} = context;

            // remove old tooltip
            var container = chart.canvas.parentNode.querySelector('.tooltip');
            if (container) {
              chart.canvas.parentNode.removeChild(container);
            }

            // determine if tooltip exists
            if (tooltip.opacity === 0) {
              return;
            }

            // group stacks
            const groups = {};
            tooltip.dataPoints.forEach(function (point) {
              if (groups.hasOwnProperty(barChartData.datasets[point.datasetIndex].label)) {
                groups[barChartData.datasets[point.datasetIndex].label] += parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
              } else {
                groups[barChartData.datasets[point.datasetIndex].label] = parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
              }
            });

            // build tooltip rows
            var rows = '';
            Object.keys(groups).forEach(function (groupName) {
              rows += renderTemplate('template-tooltip-row', {
                group: groupName,
                value: groups[groupName].toLocaleString(undefined, {minimumFractionDigits: 2})
              });
            });

            // build tooltip
            chart.canvas.parentNode.insertAdjacentHTML('beforeEnd', renderTemplate('template-tooltip', {
              rows: rows,
              title: tooltip.title[0]
            }));

            // position tooltip
            const {offsetLeft: positionX, offsetTop: positionY} = chart.canvas;
            container = chart.canvas.parentNode.querySelector('.tooltip');
            container.style.left = positionX + tooltip.caretX + 'px';
            container.style.top = positionY + tooltip.caretY + 'px';
            container.style.font = tooltip.options.bodyFont.string;
            container.style.padding = tooltip.options.padding + 'px ' + tooltip.options.padding + 'px';
          }
        }
      }
    }
  });

  /**
   * render html template
   * @param {string} templateId - id of html template
   * @param {object} templateValues - values for each template placeholder
   * @return {string} template content
   */
  function renderTemplate(templateId, templateValues) {
    var propHandle;     // property key
    var templateText;   // html template content
    var templateValue;  // value for template placeholder

    // get template content, replace each placeholder with value
    templateText = document.querySelector('#' + templateId).innerHTML;
    if (templateValues) {
      for (propHandle in templateValues) {
        if (templateValues.hasOwnProperty(propHandle)) {
          templateValue = '';

          // convert template value to string
          if (templateValues[propHandle] !== null) {
            if (templateValues[propHandle].hasOwnProperty('results')) {
              templateValue = encodeURIComponent(JSON.stringify(templateValues[propHandle].results));
            } else {
              templateValue = templateValues[propHandle].toString();
            }
          }

          // handle dollar sign in template value
          if (templateValue.indexOf('$') > -1) {
            templateValue = templateValue.replace(new RegExp('\\$', 'g'), '$$$');
          }

          // replace template placeholder(s) with template value
          if (templateText.indexOf('{{' + propHandle + '}}') > -1) {
            templateText = templateText.replace(
              new RegExp('{{' + propHandle + '}}', 'g'),
              templateValue
            );
          }
        }
      }
    }
    return templateText.trim();
  }
});
.align-right {
  text-align: right;
}

.table {
  border-collapse: separate;
  border-spacing: 0vw 0vw;
  display: table;
}

.table-body {
  display: table-row-group;
}

.table-cell {
  display: table-cell;
  padding: 4px;
}

.table-foot {
  display: table-footer-group;
}

.table-head {
  display: table-header-group;
}

.table-row {
  display: table-row;
}

.title {
  font-weight: bold;
}

.tooltip {
  background-color: rgba(0, 0, 0, 0.85);
  border-radius: 3px;
  color: #ffffff;
  pointer-events: none;
  position: absolute;
  transform: translate(-50%, 0);
  transition: all 0.1s ease;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>

<canvas id="canvas" height="100"></canvas>

<script id="template-tooltip" type="text/html">
  <div class="tooltip">
    <div class="title">{{title}}</div>
    <div class="table">
      <div class="table-body">{{rows}}</div>
    </div>
  </div>
</script>

<script id="template-tooltip-row" type="text/html">
  <div class="table-row">
    <div class="table-cell title">{{group}}:</div>
    <div class="table-cell align-right">{{value}}</div>
  </div>
</script>

Leave a comment