Why does this D3 code add a <p> element outside the body, and not inside it?

I am learning D3 and am encountering a problem with the select statement.

In particular, why does the following code add a <p> element outside the body, and not inside it?

var pData1 = d3.select("body").select("p").data([1]).enter().append("p");

I use an absolutely empty HTML file with <head> and <body> tags for testing.

 <html lang="en"> <head> <title><!-- Insert your title here --></title> <script type="text/javascript" src="d3.min.js"></script> </head> <body> </body> </html> 
+7
javascript jquery
source share
3 answers

(This repeats the content from Lars Cottoff’s answer, but I took the time to create a demo, so I thought I would post it anyway.)

The problem is that select , unlike selectAll , does not override the parent element for elements added to 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 the input chain after the select statement (compared to selectAll), because usually you select multiple elements if you intend to collect data. However , if you want to create an element if it does not exist or update it if it does , you have two options:

  • use selectAll statement followed by data aggregation

     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 the if statement to create the element, if necessary, and use .datum() to bind the data

     var 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 
+11
source share

As pointed out in the comments, the way to do what you are trying to do is to use .selectAll() . The difference between .select() and .selectAll() is that .select() returns elements, and .selectAll() returns elements grouped by ancestor. This then affects any subsequent .data() operations in terms of adding elements. From the documentation :

Grouping by selectAll also affects subsequent input of placeholder nodes. Thus, to specify the parent node when adding inbound nodes, use select and then selectAll:

d3.select ("body"). SelectAll ("DIV")

+3
source share

There is no p element to select, so I would suggest adding the p element before entering your data:

 var pData1 = d3.select("body").append("p") pData1.data([1])... 

do whatever you need to do after .data, although there is no need to add another p

+2
source share

All Articles