Select All and Unselect Option for chart.js
Asked Answered
O

5

13

I'm Working on Chart.js, wanted to implement Select All and Unselect All option for labels. I'm trying to find it out but couldn't get anything such.

Please do help me out if anything such feature can be implemented.

Ostrich answered 13/3, 2018 at 6:49 Comment(0)
G
29

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/

Guajardo answered 13/3, 2018 at 9:6 Comment(6)
Beaver, it's not working for pie chart. I guess chartInstance.data.datasets in pie is giving different data attributes. any idea for pie chart ?Ostrich
For pie chart you have to use "meta", see my fiddle: jsfiddle.net/beaver71/u0y0919bGuajardo
Beaver, can you please help me here, right now first if I click some individual label, then show/hide all event doesn't apply to that particular clicked label. Is there any crack where we can apply show/hide all to all the labels by default no matter if he click individual or show/hide allOstrich
If you check the code carefully you see that the logic is basically ds.hidden = !ds.hidden;. So it inverts ds.hidden value... change this logic as you prefer...Guajardo
yes you are right, but after clicking individual labels and then show/hide button, the values is changing to true / false but not reflecting in UI. Not getting how to handle this.Ostrich
Beaver, I had edited your code and added logic to apply show/hide event to all the labels. The functionality is working fine but issue is that once I click on show/hide button, then individual lables works only after double click instead of single click for first time. here is fiddle link : jsfiddle.net/bhushand/31mbcnfq/6 can you pls help me out how can I fix this issue. I want labels to work for first click instead of double clickOstrich
L
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

Livvy answered 26/11, 2020 at 10:32 Comment(1)
This is the correct answer, although for TypeScript you need to change it a bit, since meta is possibly undefined. So you risk getting exceptions if you run the code as is.Woorali
B
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>
Boles answered 27/10, 2021 at 1:31 Comment(0)
K
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.

Kerekes answered 25/5, 2020 at 10:51 Comment(2)
Thanks! behaviour of individual toggling in the chart legend + my custom hide/show button was driving me crazyNonjuror
I'm using the react-chartjs-2 library. I wanted a select all / unselect all feature. This was helpful. For my line chart, if toggle {dataset._meta[0].hidden = true} else {dataset._meta[0].hidden = null}. I was originally using false instead of null but null ended up being the key to avoid the double click issue mentioned above.Ensnare
W
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()
}
Woorali answered 22/6, 2023 at 14:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.