Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3: data, enter, append pattern adds data to outer block

I'm using the D3 javascript library to render some basic web charts. I want to add three <path> elements to an <svg> block, but D3 is adding the elements to the end of the <html> block instead. Here's the complete html source:

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="d3.v2.js"></script>
<script>
var chartData = [ 1, 2, 3 ];
d3.select("html").select("body").append("svg")
  .data(chartData, function(d) { console.log("data d:", d); return d; })
  .enter()
    .append("path")
      .attr("d", function(d) { return d; });
</script>
</body>

Chrome's developer console shows the resulting html to be:

<html><head><meta charset="utf-8">
<style type="text/css"></style></head><body>
<script src="d3.v2.js"></script>
<script>
var chartData = [ 1, 2, 3 ];
d3.select("html").select("body").append("svg")
  .data(chartData, function(d) { console.log("data d:", d); return d; })
  .enter()
    .append("path")
      .attr("d", function(d) { return d; });
</script><svg></svg>
</body><path d="1"></path><path d="2"></path><path d="3"></path></html>

The <svg> block was created, but for some reason, the <path> blocks are outside of it. How can I correct this error, and put them inside the <svg> where they belong?

like image 938
Toby Lael Avatar asked Dec 01 '12 23:12

Toby Lael


People also ask

What does d3 append do?

d3 append() append() - Appends a new element with the specified name as the last child of each element in the current selection, returning a new selection containing the appended elements.

What is selection in d3?

# d3.select(selector) · Source. Selects the first element that matches the specified selector string. If no elements match the selector, returns an empty selection. If multiple elements match the selector, only the first matching element (in document order) will be selected.


2 Answers

First off, you should have a high-level variable that only refers to the svg. Secondly, you should specify the height and width of the SVG when creating it. You don't need to specify a select of html, so:

var width = 400,
    height = 300;
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

Now you can use the svg variable to append paths. It is recommended that you group the paths together (hence the "g" appended first). And you should also do a selectAll for paths, because you want to bind the data you are adding to the path elements. So:

svg.append("g").selectAll("path")
    .data(chartData)
  .enter().append("path")
    .attr("d", function(d) { return d; });

I recommend the D3 tutorials here: http://alignedleft.com/tutorials/d3/

like image 104
mccannf Avatar answered Sep 30 '22 12:09

mccannf


For future visitors having a similar issue:

It appears the difference of behavior in this case (and similar others) between select and selectAll hinges on the fact that selectAll creates a new group.

D3's selection object is an array of arrays with some additional special properties. Each array corresponds to a group of objects. One property of the group is the parentNode, which is the node from which the initial selectAll was executed.

When the outermost selection is a select, the root is implicitly the document's root node--in this case, html.

Since selectAll creates a new group within the selection for each match (or entry), each group is assigned as its parent the node from which it derives. With select, since no new group is created, the original parentNode for each group is preserved, even if it no longer accurately represents the parent of the nodes within the group.

The parentNode also appears (empirically) to be the node to which enter selections' appended nodes are attached.

This is why select produced the observed behavior, and selectAll remedied the problem.

like image 21
Ben Mosher Avatar answered Sep 30 '22 12:09

Ben Mosher