8

I'm developing a site that uses chart.js (www.chartjs.org).

I have to make a line chart that shows multiple series of data, that users can hide or show by clicking on the respective legend symbol (similar to this http://js.syncfusion.com/demos/web/#!/azure/chart/linechart).

Is there any way to do it with chartjs?

pomarc
  • 2,194
  • 3
  • 23
  • 30
  • Just redraw the chart without that series? – Pointy Jan 22 '15 at 15:54
  • @Pointy I was wondering if there was an easier, self-contained and more automatic way to do it. This way I must dinamically attach an event to the legend items, exclude or include series and redraw. With many charts this goes rapidly towards chaos, imo, but thank you anyway. – pomarc Jan 23 '15 at 17:24
  • Well as far as I can tell, Chart.js in particular doesn't have a lot of sophistication about supporting interactions. – Pointy Jan 23 '15 at 17:59

6 Answers6

14

You can try this
Create store for hidden datasets

window.chartName = new Chart(...

window.chartName.store = new Array();

Then use this function to update chart, must be handled by click on legend item

function updateDataset(legendLi, chart, label) {
      var store = chart.store;
      var exists = false;
      for (var i = 0; i < store.length; i++) {
        if (store[i][0] === label) {
          exists = true;
          chart.datasets.push(store.splice(i, 1)[0][1]);
          legendLi.fadeTo("slow", 1);
        }
      }
      if (!exists) {
        for (var i = 0; i < chart.datasets.length; i++) {
          if (chart.datasets[i].label === label) {
            chart.store.push([label, chart.datasets.splice(i, 1)[0]]);
            legendLi.fadeTo("slow", 0.33);
          }
        }
      }
      chart.update();
    }

Don't forget updated legend template in chart options

legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li class=\"legend-item\" onclick=\"updateDataset($(this), window.chartName, '<%=datasets[i].label%>')\"><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"

Shortly, i added this onclick handler for li component

<li class=\"legend-item\" onclick=\"updateDataset($(this), window.chartName , '<%=datasets[i].label%>')\"><

For example fiddle

Igor
  • 171
  • 1
  • 7
  • I don't know why this isn't more popular. It helped me a lot, thank you! +1 and more if I could – SirDerpington Dec 02 '15 at 08:52
  • Igor, great solution! I have multiple datasets in a specific order. Using the code in your jsfiddle (http://jsfiddle.net/1qjco9Ls/), once you hide something then click to show it again, that dataset is pushed to the end. Any way to have it remember its original place in line once its hidden and then unhidden? – rocky Jan 14 '16 at 21:45
4

This is now available in Charts.js

From the documentation http://www.chartjs.org/docs/#chart-configuration-legend-configuration

Legend Configuration

The legend configuration is passed into the options.legend namespace. The global options for the chart legend is defined in Chart.defaults.global.legend.

onClick function(event, legendItem) {} A callback that is called when a 'click' event is registered on top of a label item

onHover function(event, legendItem) {} A callback that is called when a 'mousemove' event is registered on top of a label item

Community
  • 1
  • 1
benallansmith
  • 816
  • 3
  • 10
  • 26
1

An update to @benallansmith answer, the link to the documentation is now :

https://www.chartjs.org/docs/latest/configuration/legend.html

My version of the example code, to have datasets 2 and 3 hidden while clicking on legend for dataset 1 (datasets 2 and 3 have their legends hidden with the filter function):

public static readonly myChart: Chart.ChartConfiguration = {
    type: 'line',
    data: {
        datasets: [
            {
                label: 'A',
                borderColor: 'red',
                fill: false,
                lineTension: 0
            },
            {
                label: 'B',
                borderColor: 'blue',
                fill: false,
                lineTension: 0,
                pointRadius: 0
            },
            {
                borderColor: 'blue',
                borderDash: [5, 10],
                borderWidth: 1,
                fill: false,
                lineTension: 0,
                pointRadius: 0
            },
            {
                borderColor: 'blue',
                borderDash: [5, 10],
                borderWidth: 1,
                fill: false,
                lineTension: 0,
                pointRadius: 0
            }
       ]
   },
   options: {
       legend: {
           labels: {
               filter: function(legendItem, chartData) {
                    if (legendItem.datasetIndex > 1) {
                        return false;
                    }
                    return true;
                }
            },
            onClick: function(e, legendItem) {
                const index = legendItem.datasetIndex;
                if (index === 1) {
                    const ci = this.chart;
                    [
                        ci.getDatasetMeta(1),
                        ci.getDatasetMeta(2),
                        ci.getDatasetMeta(3)
                    ].forEach(function(meta) {
                        meta.hidden = meta.hidden === null ? !ci.data.datasets[1].hidden : null;
                    });
                    ci.update();
                } else {
                    // Do the original logic
                    Chart.defaults.global.legend.onClick.call(this, e, legendItem);
                }
            }
        },
        responsive: false,
        animation: {
            duration: 0
        },
    }
...
};
Ailete619
  • 194
  • 1
  • 18
1

my version is v2.8.0 I find a work way, try it.

add

legend : {

                }

into option

var donutOptions     = {
                maintainAspectRatio : false,
                responsive : true,
                animation: {
                    animateScale: true,
                    animateRotate: true
                },
                tooltips: {
                    intersect: false,
                    callbacks: {
                        label: function (tooltipItem, data) {
                            ...
                        }
                    },
                },
                plugins: {
                    datalabels: {
                        ...
                        },
                    },
                },
                legend : {

                }

            };

and use this


donutChart.chart.getDatasetMeta(0).data[index].hidden = true;

donutChart.update();
EZone Lai
  • 106
  • 6
0

I've found no way to do this. It's easy to put some onclick behaviour on the legend template, and you can easily change the series stroke and colors alpha channel to 0 so that the area and stroke disappear but I've found no way to do this on points.

I've decided to use google charts for this particular chart, and chart.js whenever I do not need that behavior, hoping that the good creators of chart.js will add it in the future.

pomarc
  • 2,194
  • 3
  • 23
  • 30
-3
onLegendClick: function (e) {
var series = e.target;
                series.isVisible() ? series.hide() : series.show();
            },

dxcharts have a function called onLegendClick, a few others are onSeriesClick and onPointClick. I hope this helps.

Alvin
  • 1