1

I am creating a legend (var legend) for my d3js chart. The data is bound to the parent 'g' element, specifically a string (label) that I need to get at in the child 'text' element.

QUESTION: How to assign the parent data from 'g' element to the child text element?

Code:

var legend = svg.selectAll(".legend")
        .data(color.domain().slice().reverse()) // color is array of strings with length = 6
            .enter().append("g")
            .attr("class", "legend")
            .attr("transform", function(d, i) { return "translate(-20," + i * 20 + ")"; });


        legend.data(headers).enter().append("text") // headers is array of strings with length = 6
              .attr("x", width - 24)
              .attr("y", 9)
              .attr("dy", ".35em")
              .style("text-anchor", "end")
              .text(function(d) { return this.parentElement.__data__; }); // not working code here

Thanks! Full code: https://github.com/DeBraid/www.cacheflow.ca/blob/master/styles/js/d3kickchart.js

DeBraid
  • 8,751
  • 5
  • 32
  • 43

2 Answers2

1

You're working much harder than you need to doing all this manual bookkeeping. Tying together different representations of the same data is one of D3's strong suits. Generally you want all your records in a single array as denormalized as possible, rather than trying to manually use indexes and multiple lists to correlate information.

On line 21 where you create the data records, simply add an additional property to hold the range label, and use these ranges as the domain of the color axis. (You might also look into d3's nest operator...)

Your legend can then be something as simple as this (jsfiddle):

var categories = ["foo", "bar", "baz", "qux", "zoo", "yaw"];

var color = d3.scale.ordinal()
    .domain(categories)
    .range(["#98ABC5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c"]);

d3.select("#legend")
    .selectAll("div")
    .data(categories)
    .enter()
    .append("div")
    .style("background-color", color)
    .text(function(d) { return d });
couchand
  • 2,639
  • 1
  • 21
  • 27
  • Great explanation and concise solution. Obviously my code needed some refactoring and this post addresses that, thanks! – DeBraid Feb 20 '14 at 22:25
0

EDIT: The marked correct answer was the way to go. Below is the poor solution I hacked together.

Ended up with a pretty whacky bit of code here, but it works! HT Lars for suggesting adding .select() which was missing before.

    legend.selectAll("text .legend")
        .data([headers.slice().reverse()]) // data takes an array (headers), wrapped in [], order reversed
        .enter().append("text")
          .attr("x", width - 24)
          .attr("y", 9)
          .attr("dy", ".35em")
          .style("text-anchor", "end")
          .text(function(d,i,j) { return d[j];  }); // ** key ** 

Where 'j' is the index of the group containing the current element. Hence, using this.parentNode.data or the like is not needed, since we can access parent 'g' with the above. Not 100% sure on the mechanics of this, but it works! Feel free to elaborate on this methodology if you have cleaner way.

DeBraid
  • 8,751
  • 5
  • 32
  • 43