当使用 d3.js 时,为什么我必须在 select 之后调用 selectAll 将新元素附加到条目 selection?

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

假设我们有一个没有子节点的 svg 元素:

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

假设我们还有一个数组 data:

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

此代码正确地将新的 rect 元素附加到 svg 元素。

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

下面的代码 不会 将新的 rect 元素附加到 svg 元素。你能告诉我为什么吗?

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

每当您附加元素时,D3 都会将它们附加到 selected parent 元素。您不是 selecting 一个 parent 元素,而只是 selecting 具有指定 parent.

的元素

在您的示例中,您正在 select 访问具有 parent 和 ID bargraph 的所有 rect,而您可以使用以下命令成功更新这些节点:

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

但是,使用以下内容不会将项目附加到 #bargraph(如您所述):

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

在这里,D3 将扫描整个文档,parent 元素,(d3.selectAll()) 和 return 每个匹配 selector 条件的匹配元素。然后输入 selection 将为数据数组中不存在的每个元素创建一个占位符节点。这些节点是相对于 parent 元素创建的 selection:

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

就像我们可以 select 矩形和输入圆圈一样,没有任何链接 selector 和我们希望输入的元素的 type/placement。

在下面的示例中,我使用 d3.selectAll("svg circle") select(并更新)SVG 圆圈,但我输入了 div - 我们可以看到 div 附加到 parent selection 的元素,在 d3.selectAll() 的情况下是文档本身,而不是 SVG(而不是正文),尽管我 select或:

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>


好的,但为什么 D3 是这样的?归根结底,我无法回答为什么,因为这是我以外的人做出的决定的结果。我敢说一些推理可能是沿着这些思路进行的:

如果 select 或 (#bargraph rect) 确定元素的输入位置,则可能存在某些挑战,具体取决于 select 或:

  • 如果您不想将元​​素输入到 ID 为 bargraph 的元素中怎么办 - 也许您想在其他地方输入元素(但仍然需要 select 仅特定 children id 为 bargraph).
  • 的元素
  • 如果您 select 编辑了一个 class 或每个 div/p/etc,新输入的元素将附加到哪个 parent 元素?
  • 如果 selecting 一个 class 元素,您可能想要添加兄弟姐妹,而不是 children 到那个 class。

虽然最后两个是关于 class 而不是 ID,但是无论 select 或字符串如何,行为应该是相似的(ID、classes 和元素的不同行为在 selector 字符串中可能会造成混淆)。所选择的方法可能被那些决定的程序员选择为最干净和最清晰的方法。