d3.js adding legend to multiline series chart

12,375

Here is a fixed & refactored version of your code.

    var legend = svg.selectAll('g')
        .data(cities)
        .enter()
      .append('g')
        .attr('class', 'legend');

    legend.append('rect')
        .attr('x', width - 20)
        .attr('y', function(d, i){ return i *  20;})
        .attr('width', 10)
        .attr('height', 10)
        .style('fill', function(d) { 
          return color(d.name);
        });

    legend.append('text')
        .attr('x', width - 8)
        .attr('y', function(d, i){ return (i *  20) + 9;})
        .text(function(d){ return d.name; });

You need to use enter(), but enter() and exit() methods cannot be used with datum(). Quoting from the d3 wiki

selection.datum([value])

Gets or sets the bound data for each selected element. Unlike the selection.data method, this method does not compute a join (and thus does not compute enter and exit selections).

Share:
12,375
CQM
Author by

CQM

Updated on July 19, 2022

Comments

  • CQM
    CQM almost 2 years

    How would one add a legend to the multiline series chart? I tried but am not getting any legend to display.

    The block here:

    http://bl.ocks.org/3884955

    has a flaw when the various series converge to the same point, like zero. All the labels will be overlayed on each other. Instead of going for these labels, a traditional legend would be useful.

    I tried adding this

    var legend = svg.append("g")
    .attr("class", "legend")
    .attr("height", 100)
    .attr("width", 100)
    .attr('transform', 'translate(-20,50)');    
    
    legend.selectAll('rect')
      .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
      .append("rect")
      .attr("x", width)
      .attr("y", function(d, i){ return i *  20;})
      .attr("width", 10)
      .attr("height", 10)
      .style("fill", function(d) { 
        return color.domain(d3.keys(d[0]).filter(function(key) { return key !== "day"; }));
      });
    
    legend.selectAll('text')
      .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
      .append("text")
      .attr("x", width)
      .attr("y", function(d, i){ return i *  20 + 9;})
      .text(function(d) {
      return d.name;
      });
    

    to the end of the code, the key names (d.name) match how my data is formatted, but it does not display. At one point it showed all black boxes to the right of the graph so that means I am close but I am missing something important

    any insight appreciated

  • CQM
    CQM over 11 years
    hm, I find some discrepancies with that logic, in the example graph city variable in some places start off with .append("text") then .datum(...) with no enter() after that, but just attribute modifying. Also, when I did add enter() my console said object has no enter() and gave an error
  • Alex_B
    Alex_B over 11 years
    I could be wrong but i think the confusion comes from the fact (if i am reading this correctly) .datum() adds/changes the value to an existing element in your selection. In your code where you state "legend.selectAll('rect')" none exist and to create the initial join you would need to use ".data(data).enter()" to create new "rect" elements.
  • Alex_B
    Alex_B over 11 years
    here's a good answer to the question about .data and .datum stackoverflow.com/questions/13181194/…
  • theUser
    theUser about 10 years
    I addded a dot and your code worked for me. var legend = svg.selectAll('.g')