I'm learning D3 and have come across an issue using the select operator.
Specifically, why does the following code add the <p>
element outside the body, instead of inside it?
var pData1 = d3.select("body").select("p").data([1]).enter().append("p");
I am using a totally blank HTML file with just <head>
and <body>
tags to test.
<html lang="en">
<head>
<title><!-- Insert your title here --></title>
<script type="text/javascript" src="d3.min.js"></script>
</head>
<body>
</body>
</html>
I'm learning D3 and have come across an issue using the select operator.
Specifically, why does the following code add the <p>
element outside the body, instead of inside it?
var pData1 = d3.select("body").select("p").data([1]).enter().append("p");
I am using a totally blank HTML file with just <head>
and <body>
tags to test.
<html lang="en">
<head>
<title><!-- Insert your title here --></title>
<script type="text/javascript" src="d3.min.js"></script>
</head>
<body>
</body>
</html>
Share
Improve this question
edited May 4, 2014 at 18:51
cookie monster
11k4 gold badges33 silver badges45 bronze badges
asked May 4, 2014 at 18:15
Takeshi PattersonTakeshi Patterson
1,2776 gold badges16 silver badges31 bronze badges
2
|
3 Answers
Reset to default 14(This repeats the content from Lars Kotthoff's answer, but I'd spent time creating the demo so I thought I'd still post.)
The problem is that select
, unlike selectAll
, does not re-define the parent element for elements added in the enter()
selection.
d3.select("body").select("p#a")
.data([1])
.enter().append("p").attr("id", "a")
.text("This paragraph is appended to <html> (the document root)
because no selectAll statement reset the parent element.");
d3.selectAll("p#b")
.data([1])
.enter().append("p").attr("id", "b")
.text("This paragraph is appended to <html>
because the selectAll statement is called directly on the root.");
d3.selectAll("body").select("p#c")
.data([1])
.enter().append("p").attr("id", "c")
.text("This paragraph is also appended to <html>
because the last selectAll statement was called directly from the root.");
d3.select("body").selectAll("p#d")
.data([1])
.enter().append("p").attr("id", "d")
.text("This paragraph is appended to <body>
because the selectAll statement is a sub-selection of the body selection.");
d3.selectAll("body").selectAll("p#e")
.data([1])
.enter().append("p").attr("id", "e")
.text("This paragraph is also appended to <body>
because the final selectAll statement is a sub-selection of the body.");
http://fiddle.jshell.net/eLF4H/
It is unusual to use an enter chain after a select
statement (versus selectAll), because usually you are selecting multiple elements if you are going to do a data join. However, if you want to create the element if it doesn't exist or update it if it does, you have two options:
use a selectAll statement followed by the data join
var pdata1 = d3.select("body").selectAll("p#data") //select element if it exists .data([dataObject]); //join to the current data pdata1.enter().append("p").attr("id", "data"); //create element if required pdata1.text(function(d){return d.textValue;}); //set or update the element based on the data
use an if statement to create the element if necessary and use
.datum()
to bind the datavar pdata1 = d3.select("p#data") //select element if it exists if ( pdata1.empty() ) { pdata1 = d3.select("body").append("p").attr("id", "data"); //create element if required } pdata1.datum(dataObject) //note that you don't need to put the data into an array .text(function(d){return d.textValue;}); //set or update the element based on the data
As suggested in the comments, the way to do what you're trying to do is to use .selectAll()
. The difference between .select()
and .selectAll()
is that .select()
returns elements while .selectAll()
returns elements grouped by ancestor. This then affects any subsequent .data()
operations in terms of what the elements are appended to. From the documentation:
Grouping by selectAll also affects subsequent entering placeholder nodes. Thus, to specify the parent node when appending entering nodes, use select followed by selectAll:
d3.select("body").selectAll("div")
there is no p element to select, so I would suggest appending the p element before entering your data:
var pData1 = d3.select("body").append("p")
pData1.data([1])...
do whatever else you need to do after .data, although there is no need to append another p
selectAll('p')
instead? – Felix Kling Commented May 4, 2014 at 18:22d3.select("body").append("p");
– vinit Commented May 4, 2014 at 18:35