Search code examples
javascriptchart.jsstacked-chart

Vertical stack bar displays zeroes / "invisible" values with Chart.js


I'm trying to make a vertical stack bar plot with Chart.js. It works pretty well but for some labels (i.e., for one vertical part of the graph) I don't necessarily have strictly positive values for all the dataset points. I don't see the rectangle for those which is good, but if I hover at the wrong place, I see a 0 which bothers me.

Here is a MRE because my description was probably not very understandable:

var chart = new Chart(ctx, {
   type: 'bar',
   data: {
      labels: ['1', '2'],
      datasets: [{
         label: 'a',
         data: [1, 8],
         backgroundColor: '#22aa99'
      }, {
         label: 'b',
         data: [1, 2],
         backgroundColor: '#109618'
      }, {
         label: 'c',
         data: [0, 1],
         backgroundColor: '#dc3912'
      }, {
         label: 'd',
         data: [4, null],
         backgroundColor: '#3366cc'
      },
      {
         label: 'e',
         data: [4, null],
         backgroundColor: '#9661ca'
      }]
   },
   options: {
      responsive: false,
      legend: {
         position: 'right'
      },
      scales: {
         xAxes: [{
            stacked: true
         }],
         yAxes: [{
            stacked: true
         }]
      }
   }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx" width="700"></canvas>

I would like this not to be possible (hovering on d on the right part even though d is 0 (as well as e)): enter image description here

Using data: [4, null] does not work either, since I have that now instead: enter image description here


Solution

  • You can filter out tooltip items docs for latest chart.js, docs for v2.6.0.

    For version 4 charts, a filter for positive values would look like:

       options: {
          // .... other options
          plugins:{
             tooltip:{
                filter: ({parsed: {y}}) => y > 0
             },
             // ... other plugin options
          }
       }
    

    Snippet demo:

    new Chart("ctx", {
       type: 'bar',
       data: {
          labels: ['1', '2'],
          datasets: [{
             label: 'a',
             data: [0, 8],
             backgroundColor: '#22aa99'
          }, {
             label: 'b',
             data: [1.56, 2],
             backgroundColor: '#109618'
          }, {
             label: 'c',
             data: [0, 1],
             backgroundColor: '#dc3912'
          }, {
             label: 'd',
             data: [4, null],
             backgroundColor: '#3366cc'
          },
             {
                label: 'e',
                data: [4, null],
                backgroundColor: '#9661ca'
             }]
       },
       options: {
          responsive: false,
          scales: {
             x: {
                stacked: true,
                display: false
             },
             y: {
                stacked: true,
                display: false
             }
          },
          plugins:{
             tooltip:{
                mode: 'nearest',
                filter: ({parsed: {y}}) => y > 0
             },
             legend: {
                position: 'right'
             },
          }
       }
    });
    <canvas width="600" id="ctx"></canvas>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

    while for 2.6.0 charts, the filter might be:

       options: {
          // .... other options
          tooltips:{
             filter: ({index, datasetIndex}, {datasets}) => datasets[datasetIndex].data[index] > 0
          }
       }
    

    Snippet demo:

    new Chart("ctx", {
       type: 'bar',
       data: {
          labels: ['1', '2'],
          datasets: [{
             label: 'a',
             data: [0, 8],
             backgroundColor: '#22aa99'
          }, {
             label: 'b',
             data: [1.56, 2],
             backgroundColor: '#109618'
          }, {
             label: 'c',
             data: [0, 1],
             backgroundColor: '#dc3912'
          }, {
             label: 'd',
             data: [4, null],
             backgroundColor: '#3366cc'
          },
          {
             label: 'e',
             data: [4, null],
             backgroundColor: '#9661ca'
          }]
       },
       options: {
          responsive: false,
          legend: {
             position: 'right'
          },
          tooltips:{
             mode: 'nearest',
             filter: ({index, datasetIndex}, {datasets}) => datasets[datasetIndex].data[index] > 0
          },
          scales: {
             xAxes: [{
                stacked: true,
                display: false
             }],
             yAxes: [{
                stacked: true,
                display: false
             }]
          }
       }
    });
    <canvas width="600" id="ctx"></canvas>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>

    As a side note, the issue with nulls was addressed in the very next version of the one you used, 2.7.0, and if you used version 2.7.2 or later, it was very likely you didn't ever observe the issue with zeros, since from that version bar positions were no longer rounded to integer pixel values, so a zero-height bar positioned at an integer y coordinate was a rare occurrence; and if its position was not integer, it would never match the mouse coordinates which are still integer. Still, the possibility exists and the safe solution is to filter them out.