[Chartjs]-Chart.js change legend toggle behaviour

33👍

To reverse how legend label clicking behaves, you can use the legend onClick option to implement the new click logic. Here is an example below that will give you the desired behavior. Note, in this implementation if you click an already hidden label, it will unhide it, and hide all the others.

function(e, legendItem) {
  var index = legendItem.datasetIndex;
  var ci = this.chart;
  var alreadyHidden = (ci.getDatasetMeta(index).hidden === null) ? false : ci.getDatasetMeta(index).hidden;

  ci.data.datasets.forEach(function(e, i) {
    var meta = ci.getDatasetMeta(i);

    if (i !== index) {
      if (!alreadyHidden) {
        meta.hidden = meta.hidden === null ? !meta.hidden : null;
      } else if (meta.hidden === null) {
        meta.hidden = true;
      }
    } else if (i === index) {
      meta.hidden = null;
    }
  });

  ci.update();
};

Here is a working example as well.

If however, you want a more complex logic that will unhide a label that is currently hidden when at least one other label is currently visible, then you can use the below implementation.

function(e, legendItem) {
  var index = legendItem.datasetIndex;
  var ci = this.chart;
  var alreadyHidden = (ci.getDatasetMeta(index).hidden === null) ? false : ci.getDatasetMeta(index).hidden;       
  var anyOthersAlreadyHidden = false;
  var allOthersHidden = true;

  // figure out the current state of the labels
  ci.data.datasets.forEach(function(e, i) {
    var meta = ci.getDatasetMeta(i);

    if (i !== index) {
      if (meta.hidden) {
        anyOthersAlreadyHidden = true;
      } else {
        allOthersHidden = false;
      }
    }
  });

  // if the label we clicked is already hidden 
  // then we now want to unhide (with any others already unhidden)
  if (alreadyHidden) {
    ci.getDatasetMeta(index).hidden = null;
  } else { 
    // otherwise, lets figure out how to toggle visibility based upon the current state
    ci.data.datasets.forEach(function(e, i) {
      var meta = ci.getDatasetMeta(i);

      if (i !== index) {
        // handles logic when we click on visible hidden label and there is currently at least
        // one other label that is visible and at least one other label already hidden
        // (we want to keep those already hidden still hidden)
        if (anyOthersAlreadyHidden && !allOthersHidden) {
          meta.hidden = true;
        } else {
          // toggle visibility
          meta.hidden = meta.hidden === null ? !meta.hidden : null;
        }
      } else {
        meta.hidden = null;
      }
    });
  }

  ci.update();
}

Here is a working example for this alternate implementation as well.

To use this in your specific code, just place it in your chart’s legend config using the onClick property.

var config = { 
  type: 'radar', 
  data: chartata,   
  animationEasing: 'linear',
    options: {       
     legend: {
      fontSize: 10,
      display: true,
      itemWidth: 150,
      position: 'bottom',
      fullWidth: true,
      labels: {
        fontColor: 'rgb(0,0,0)',
        boxWidth: 10,
        padding: 20
      },
      onClick: function(e, legendItem) {
        var index = legendItem.datasetIndex;
        var ci = this.chart;
        var alreadyHidden = (ci.getDatasetMeta(index).hidden === null) ? false : ci.getDatasetMeta(index).hidden;

        ci.data.datasets.forEach(function(e, i) {
          var meta = ci.getDatasetMeta(i);

          if (i !== index) {
            if (!alreadyHidden) {
              meta.hidden = meta.hidden === null ? !meta.hidden : null;
            } else if (meta.hidden === null) {
              meta.hidden = true;
            }
          } else if (i === index) {
            meta.hidden = null;
          }
        });

        ci.update();
      },
    },
     tooltips: {
      enabled: true
    },
    scale: {
      ticks: {
        fontSize: 15,
        beginAtZero: true,
        stepSize: 1,
        max: 5
      }
    }
  },
}, 

It was not clear what behavior you wanted the ‘all’ option to have, but you might be able to use the legend.labels.generateLabels option to trick Chart.js to add an ‘all’ label (you will have to modify the above onClick logic to deal with that thought.

However, I think a better solution would be to implement your own link or button outside of the chart.js canvas that would show/hide all datasets. Check out the Chart.js radar sample page to see how they bind buttons with chart actions.

2👍

Please try this code for pie chart for a single dataset to toggle the behavior of the pie chart of chart.js .

onClick:function(e, legendItem){
                var index = legendItem.index;
                var ci = this.chart;
                var meta = ci.getDatasetMeta(0);
                var CurrentalreadyHidden = (meta.data[index].hidden==null) ? false : (meta.data[index].hidden);
                var allShown=true;
                $.each(meta.data,function(ind0,val0){
                    if(meta.data[ind0].hidden){
                        allShown=false;
                        return false; 
                    }else{
                        allShown=true;
                    }
                });
                if(allShown){
                    $.each(meta.data,function(ind,val){
                        if(meta.data[ind]._index===index){
                            meta.data[ind].hidden=false;
                        }else{
                            meta.data[ind].hidden=true;
                        }
                    });
                }else{
                    if(CurrentalreadyHidden){
                        $.each(meta.data,function(ind,val){
                            if(meta.data[ind]._index===index){
                                meta.data[ind].hidden=false;
                            }else{
                                meta.data[ind].hidden=true;
                            }
                        });
                    }else{
                        $.each(meta.data,function(ind,val){
                            meta.data[ind].hidden=false;
                        }); 
                     }
                 }
                ci.update();

            }

1👍

a shorter code with the legend.onClick solution

// this.chart.options
legend: {
    onClick: (_, item: LegendItem) => {
        if (!this.chart) {
            return;
        }
        const currentHidden: boolean = this.chart.data.datasets[item.datasetIndex].hidden ?? false;
        if (currentHidden) {
            this.chart.data.datasets.forEach(ds => {
                ds.hidden = true;
            });
            this.chart.data.datasets[item.datasetIndex].hidden = false;
        } else {
            this.chart.data.datasets.forEach(ds => {
                ds.hidden = currentHidden == (ds.hidden ?? false);
            });
            this.chart.data.datasets[item.datasetIndex].hidden = false;
        }
        this.chart?.update();
    },
},

Leave a comment