chart.jslegend

Changing legend behavior from strikethrough to color change


As I've found strikethrough to be very annoying as it obscures the text so it's sometimes difficult to tell what the legend label is when hidden, I wanted to change the behavior and so that, instead of strikethrough, the label just changes to a gray color.

I'm using this for the onclick function:

legend:{
   onClick:function(e,legendItem,legend){
      toggleDatasetVisibility(e,legendItem,legend);
   }
}

For the function, I've tried this:

function toggleDatasetVisibility(e, legendItem, legend) {
    const index = legendItem.datasetIndex;
    const ci = mychart;
    const mydataset = ci.data.datasets[index];
    const isVisible = ci.isDatasetVisible(index);
    ci.setDatasetVisibility(index, !isVisible);

    if (ci.isDatasetVisible(index)) {
        legendItem.fontColor = 'red';
        legendItem.hidden = false;
    } else {
        legendItem.fontColor = 'gray';
        legendItem.hidden = false;
    }

    const legendItems = legend.legendItems;
    legendItems[index].textDecoration = 'none';
}

But, the behavior I get now is that the label does change color when I click on it and the mouse cursor is still over the label, but once I move the mouse away, the dataset hides and the label goes back to strikethrough.

Any help that can be provided would be greatly appreciated.


Solution

  • You haven't specified the version of Chart.js you are using, so I assumed the latest, but with that your code gets some errors, which are easily corrected, and then nothing happens when one clicks on the legend color boxes. In any case, the solution should be the same, even wih a slightly older version.

    Changing the legendItem sent as an argument to onClick handler is not the way to change the appearance of the legend item. Nor is the legend.legendItems[index], which is actually the same object.

    At the time of the execution of onClick, the legendItem is only for reading information.

    To make any of the changes in toggleDatasetVisibility appear on the chart, one has to call ci.update(); or functions like ci.hide() or ci.show() that call update themselves.

    However, calls to update result in calls to generateLabels, (source code plugin.legend.js#L675) and there the legend items are recomputed and those recomputed values are used to display the legend label.

    The solution then is to implement any alterations to the labelItems in the function options.plugins.legend.labels.generateLabels, taking into account the state of the label (i.e., hidden):

    const _defaultGenerateLabels = Chart.defaults.plugins.legend.labels.generateLabels;
    function generateLabels(...args){
       const labelItems =  _defaultGenerateLabels.apply(this, args);
       labelItems.forEach(labelItem => {
          if(labelItem.hidden){
             labelItem.hidden = false;
             labelItem.fontColor = '#aaa';
             labelItem.fillStyle = '#ddd';
             labelItem.strokeStyle = '#ccc';
          }
          else{
             labelItem.fontColor = 'red';
          }
       });
       return labelItems;
    }
    

    the lines labelItem.fillStyle = '#ddd'; and labelItem.strokeStyle = '#ccc'; set the adjacent color box to gray too. For this application, there's no need to implement a handler for legend onClick.

    Demo snippet:

    const _defaultGenerateLabels = Chart.defaults.plugins.legend.labels.generateLabels;
    function generateLabels(...args){
       const labelItems =  _defaultGenerateLabels.apply(this, args);
       labelItems.forEach(labelItem => {
          if(labelItem.hidden ){
             labelItem.hidden = false;
             labelItem.fontColor = '#aaa';
             labelItem.fillStyle = '#ddd';
             labelItem.strokeStyle = '#ccc';
          }
          else{
             //labelItem.fontColor = 'red';
             // better set "normal" color in options.plugins.legend.label.color
          }
       })
       return labelItems
    }
    
    const data = {
       labels: [
          'January', 'February', 'March', 'April', 'May', 'June'
       ],
       datasets: [
          {
             label: 'Dataset 1',
             data: Array.from({length: 6}, () => Math.random()*10 - 5),
          },
          {
             label: 'Dataset 2',
             data: Array.from({length: 6}, () => Math.random()*10 - 5),
          },
       ]
    };
    
    const config = {
       type: 'line',
       data: data,
       options: {
          responsive: true,
          plugins: {
             legend: {
                position: 'top',
                labels: {
                   //color: 'red',
                   generateLabels
                }
             }
          }
       },
    };
    
    new Chart("myChart", config);
    <div style="height: 200px">
       <canvas id="myChart"></canvas>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>