When using d3.js why must I call selectAll after select when appending new elements to an entry selection?
Asked Answered
C

1

6

Suppose we have a svg element with no child nodes:

<svg id="bargraph" width="400" height="90" ></svg>

Suppose we also have an array of data:

var data = [10,20,30,40,50];

This code properly append new rect elements to the svg element.

 d3.select("#bargraph")
 .selectAll("rect")
 .data(data)
 .enter()
 .append("rect")
 .attr("height", 10)
 .attr("width", function(d) { return d; })
 .attr("x", 0)
 .attr("y", function (d, i) { return i * 20; })
 .attr("fill", "blue");

The code below does not append new rect elements to the svg element. Can you tell me why?

 d3.selectAll("#bargraph rect")
 .data(data)
 .enter()
 .append("rect")
 .attr("height", 10)
 .attr("width", function(d) { return d; })
 .attr("x", 0)
 .attr("y", function (d, i) { return i * 20; })
 .attr("fill", "blue");
Chuipek answered 10/3, 2018 at 19:6 Comment(0)
N
4

Whenever you append elements, D3 appends them to a selected parent element(s). You aren't selecting a parent element, instead you are only selecting elements that have a specified parent.

In your example, you are selecting all rect that have a parent with id bargraph, while you can update those nodes successfully with:

d3.selectAll("#bargraph rect").data(data).attr(...)

But, using the following won't append items to #bargraph (as you state):

d3.selectAll("#bargraph rect").data(data).enter()

Here, D3 will scan the entire document, the parent element, (d3.selectAll()) and return each matching element that matches the selector criteria. The enter selection will then create a placeholder node for each element in the data array that does not exist. These nodes are created in relation to the parent element selection:

Conceptually, the enter selection’s placeholders are pointers to the parent element (documentation)

Just as we may select rectangles and enter circles, there is nothing linking the selector and the type/placement of the element we wish to enter.

In the example below, I select (and update) the SVG circle using d3.selectAll("svg circle"), but I enter a div - and we can see that the div is appended to the element of the parent selection, which in the case of d3.selectAll() is the document itself, not the SVG (and not the body), despite my selector:

var svg = d3.select("body").append("svg");

svg.append("circle")
  .attr("cx",40)
  .attr("cy",40)
  .attr("r",20)
  .attr("fill","orange");

var circles = d3.selectAll("svg circle")
  .data(["steelblue","yellow"])
  .attr("fill",function(d) { return d; });

circles.enter().append("div")
  .html(function(d) { return d; });
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

Ok, but why is D3 like that? Ultimately, I can't answer why, because this is the result a decision made by people other than myself. I'd hazard some of the reasoning may be along these lines:

If selectors such as (#bargraph rect) determined where elements would be entered, then there could be certain challenges depending on the selector:

  • What if you didn't want to enter elements to the element with id bargraph - perhaps you want to enter elements elsewhere (but still needed to select only specific children of the element with id bargraph).
  • If you selected a class, or every div/p/etc, which parent element would the newly entered elements be appended to?
  • If selecting a class of element, you probably want to append siblings, rather than children to that class.

Granted the last two are about class rather than ID, but behavior should be similar regardless of the selector string (different behavior for IDs, classes, and elements in a selector string would likely be confusing). The chosen approach was probably chosen as the the cleanest and clearest for the programmer by those deciding.

Nightstick answered 11/3, 2018 at 0:2 Comment(4)
There are very few interesting questions/answers here, like this one, which after 35 views had no upvote. Strange.Sclerous
Ah, it's how it goes (as I'm sure you've experienced), though I have got lucky lately with a few questions that are a bit more interesting than the usual issues.Nightstick
Well, I'm losing my interest in SO... All the questions seem to be the same, only very basic stuff.Sclerous
Well, you've certainly put in an extraordinary amount of time so far. Seems slower lately though, perhaps that is best - so that I can work more on some side projects.Nightstick

© 2022 - 2024 — McMap. All rights reserved.