[Chartjs]-Select All and Unselect Option for chart.js

27πŸ‘

βœ…

If you need to show/hide charts selecting/unselecting all labels here is an example:

var barChartData = {
  labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
  datasets: [{
    label: 'One',
    backgroundColor: 'rgba(206, 0, 23, 1)',
    data: [0, 1, 3, 0, 2, 0, 0, 2, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 2, 1, 0, 1, 2, 1, 1, 0, 0, 0, 2, 2, 0, 3]
  }, {
    label: 'Two',
    backgroundColor: 'rgba(206, 0, 23, 0.75)',
    data: [0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1]
  }, {
    label: 'Three',
    backgroundColor: 'rgba(206, 0, 23, 0.5)',
    data: [0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 2, 0, 0, 0, 1, 0, 0, 0, 0, 1]
  }]

};
var ctx = document.getElementById('canvas').getContext('2d');
var chartInstance = new Chart(ctx, {
  type: 'bar',
  data: barChartData,
  options: {
    title: {
      display: false,
    },
    responsive: true,
    scales: {
      xAxes: [{
        stacked: true,
      }],
      yAxes: [{
        stacked: true
      }]
    }
  }
});

$("#toggle").click(function() {
	 chartInstance.data.datasets.forEach(function(ds) {
    ds.hidden = !ds.hidden;
  });
  chartInstance.update();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
<button id="toggle">show/hide all</button>
<canvas id="canvas" height="500" width="800"></canvas>

Jsfiddle: https://jsfiddle.net/beaver71/00q06vjp/

Credits: see https://github.com/chartjs/Chart.js/issues/3009


Update: for pie chart use β€œmeta”, see: https://jsfiddle.net/beaver71/u0y0919b/

3πŸ‘

The correct answer result in :

chart.data.datasets.forEach((obj, index) => {
   let meta = this.eval_chart.getDatasetMeta(index);
   meta.hidden = !meta.hidden || null;
});
chart.update();

As wrote in the documentation : https://www.chartjs.org/docs/latest/configuration/legend.html#custom-on-click-actions

3πŸ‘

V3 Answer

You can use a custom generateLabels function together with a custom onClick to get it as an extra legendItem like so:

const defaultLegendClickHandler = Chart.defaults.plugins.legend.onClick;
const pieDoughnutLegendClickHandler = Chart.controllers.doughnut.overrides.plugins.legend.onClick;

const options = {
  type: 'line',
  data: {
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [{
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        borderColor: 'pink'
      },
      {
        label: '# of Points',
        data: [7, 11, 5, 8, 3, 7],
        borderColor: 'orange'
      }
    ]
  },
  options: {
    plugins: {
      legend: {
        onClick: (evt, legendItem, legend) => {
          const type = legend.chart.config.type;
          let allLegendItemsState = [];

          if (legendItem.text === 'hide all datasets' || legendItem.text === 'show all datasets') {
            if (typeof legend.hideAll === 'undefined') {
              legend.hideAll = false;
            }

            legend.chart.data.datasets.forEach((dataset, i) => {
              legend.chart.setDatasetVisibility(i, legend.hideAll)
            });

            legend.hideAll = !legend.hideAll;
            legend.chart.update();

            return;
          }

          if (type === 'pie' || type === 'doughnut') {
            pieDoughnutLegendClickHandler(evt, legendItem, legend)
          } else {
            defaultLegendClickHandler(evt, legendItem, legend);
          }

          allLegendItemsState = legend.chart.data.datasets.map((e, i) => (legend.chart.getDatasetMeta(i).hidden));

          if (allLegendItemsState.every(el => !el)) {
            legend.hideAll = false;
            legend.chart.update();
          } else if (allLegendItemsState.every(el => el)) {
            legend.hideAll = true;
            legend.chart.update();
          }
        },
        labels: {
          generateLabels: (chart) => {
            const datasets = chart.data.datasets;
            const {
              labels: {
                usePointStyle,
                pointStyle,
                textAlign,
                color
              }
            } = chart.legend.options;

            const legendItems = chart._getSortedDatasetMetas().map((meta) => {
              const style = meta.controller.getStyle(usePointStyle ? 0 : undefined);

              return {
                text: datasets[meta.index].label,
                fillStyle: style.backgroundColor,
                fontColor: color,
                hidden: !meta.visible,
                lineCap: style.borderCapStyle,
                lineDash: style.borderDash,
                lineDashOffset: style.borderDashOffset,
                lineJoin: style.borderJoinStyle,
                strokeStyle: style.borderColor,
                pointStyle: pointStyle || style.pointStyle,
                rotation: style.rotation,
                textAlign: textAlign || style.textAlign,
                datasetIndex: meta.index
              };
            });

            legendItems.push({
              text: (!chart.legend.hideAll || typeof chart.legend.hideAll === 'undefined') ? 'hide all datasets' : 'show all datasets',
              fontColor: color,
              fillStyle: 'turquoise', // Box color
              strokeStyle: 'turquoise', // LineCollor around box
            });

            return legendItems;
          }
        }
      }
    }
  }
}

const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
  <canvas id="chartJSContainer" width="600" height="400"></canvas>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.js"></script>
</body>

2πŸ‘

For ChartJS 2.9.3, this works as requested by David in the comments:

const chart = ...
chart.data.datasets.forEach(dataset => {
  Object.keys(dataset._meta).forEach(key => {
    const current = !dataset._meta[key].hidden
    dataset._meta[key].hidden = current || null
  })
})
chart.update()

Toggles all with a button, while playing nicely with the individual toggling in the chart legend.

0πŸ‘

For v4 and TypeScript, or if you want to make sure you don’t get an exception because meta can be undefined, I used the following code below. I wanted to make sure it always toggled all to be in the same state (select/unselect all), hence the hidden variable outside the function scope.

My answer is inspired by @ivom’s answer.

let hidden = false
function toggleAllDatasets() {
  if (!chart) {
    return
  }

  hidden = !hidden

  chart.data.datasets.forEach((obj, index) => {
    let meta = chart?.getDatasetMeta(index)
    if (!meta) {
      return
    }
    meta!.hidden = hidden
  });

  chart.update()
}

Leave a comment