dygraphs icon indicating copy to clipboard operation
dygraphs copied to clipboard

checkboxes in legend to control visibility

Open danvk opened this issue 9 years ago • 5 comments

From [email protected] on April 25, 2012 15:41:14

It would be nice to have an option to show checkboxes in the legend, using which a user can switch the visibility of curves on/off.

Original issue: http://code.google.com/p/dygraphs/issues/detail?id=326

danvk avatar Oct 20 '14 22:10 danvk

From [email protected] on April 30, 2012 16:25:26

This is a fairly common request/addition. It should be implemented in a plugin.

Status: Accepted
Labels: -Type-Defect Type-Enhancement

danvk avatar Oct 20 '14 22:10 danvk

From [email protected] on December 04, 2012 04:36:22

Labels: Plugins Component-UI

danvk avatar Oct 20 '14 22:10 danvk

From [email protected] on March 01, 2013 11:02:31

Do you mean plugins/legend.js or some other kind of plugin?

danvk avatar Oct 20 '14 22:10 danvk

From [email protected] on June 14, 2013 02:18:24

It would be handy, to have curves visibility option integrated into legend.

danvk avatar Oct 20 '14 22:10 danvk

The good news is that it's possible to get this working fairly well without changes to dygraphs itself using the excellent legendFormatter feature... and I've shared what I came up with below in case it helps others (or is useful for implementing this as a full dygraphs plugin or option).

However there are a couple of limitations in dygraphs that make this a little harder than it could be (would be great if these could be fixed):

  1. There's no way to look up a Dygraph object from event handlers since there's no id in the graphDiv generated by Dygraph (or any way to get the Dygraph instance from the graphDiv once looked up). To save people having to stash these into a custom data structure to permit lookup by id (when they have many or a dynamic number of charts), would be great to have the Dygraph constructor do something similar to what I've put into the legendFormatter, i.e. ensure a graphDiv.id is set and provide a graphDiv.dygraph attribute.
  2. Dygraph.prototype.setColors_ removes the color setting from any series with visibility=false, making it difficult to show the line color in the legend after visibility has been toggled. Think it would make more sense to keep coloring based on index of series and unaffected by which are currently visible, i.e. remove the isVisible check in that function.

Anyway, JavaScript code to add clickable legends is here:

function legendFormatter(data) {
	var dygraph = data.dygraph;
	var html = "";
	var showvalues = data.x != null; // false if there's no selected value currently
	
	// Need a way to lookup the JavaScript dygraph object later from the onclick listener 
	// (using just a javascript string), so assign a unique id to the div and add a data attribute to it
	// (would be great if the dygraphs constructor did this automatically, could be useful for other use cases also)
	if (!dygraph.graphDiv.id) {
		var i = 1;
		while (document.getElementById("__dygraph"+i)) 
			i++;
		dygraph.graphDiv.id = "__dygraph"+i;
	}
	if (!dygraph.graphDiv.dygraph) { dygraph.graphDiv.dygraph = data.dygraph; }
	
	var seriesIndex = 0;
	data.series.forEach(function(series) 
	{
		html += "<label><input type='checkbox' onclick=\\"document.getElementById('"+dygraph.graphDiv.id+"').dygraph.setVisibility("+seriesIndex+", ";
		// nb: we have to use dygraph.visibility() here as series.isVisible has incorrect value for points where y value is undefined
		if (dygraph.visibility()[seriesIndex]) { 
			html += "false);\\" checked>";
		} else {
			html += "true);\\" >"; 
		}
		
		var labeledData = series.labelHTML;
		
		// workaround for the bug where Dygraph.prototype.setColors_ un-sets color for any series where visibility=false; 
		// this workaround gives correct color if configured using options{colors:[...]} and falls back to transparent if not
		series.dashHTML = series.dashHTML.replace("color: undefined;", "color: "+(dygraph.getOption('colors')[seriesIndex] || "rgba(255,255,255,0.0)")+";");
		
		if (showvalues && series != undefined && series.y != undefined) { labeledData += ': ' + series.yHTML; }
		if (series.isHighlighted) { labeledData = '<b>' + labeledData + '</b>'; }
		html += series.dashHTML + " " + labeledData + "</label><br>\\n";
		seriesIndex += 1;
	});
	// Display x value at the end, after all the series (to avoid making them jump up/down when there's no selection)
	if (showvalues) {
		html += this.getLabels()[0] + ': '+data.xHTML;
	}

	return html;
}

var g = new Dygraph(parentdiv, [ ... ], {
	"labels": ["time", "series 1", "series 2"], 
	"colors": ["red", "teal"], // at present, dash color can be shown only in legend only if explicitly specified like this (not when automatic)
	"labelsSeparateLines": true, 
	"legend": "always", // keep legend visible so it's always possible to un-hide series
	"legendFormatter":legendFormatter // override formatting of legend to add checkboxes
	});

ben-spiller avatar Dec 26 '19 18:12 ben-spiller