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?
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.
# 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.
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/
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' append
ed nodes are attached.
This is why select
produced the observed behavior, and selectAll
remedied the problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With