score:14

Accepted answer

(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 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
    

score:2

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

score:3

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")


Related Query