簡體   English   中英

選擇 null:D3 中 selectAll(null) 背后的原因是什么?

[英]Selecting null: what is the reason behind selectAll(null) in D3?

我已經看到一些 D3 代碼帶有這樣的模式來附加元素:

var circles = svg.selectAll(null)
    .data(data)
    .enter()
    .append("circle");

我真的不明白這個片段。 為什么選擇null

我理解 D3 的方式,如果一個是附加圓圈,它應該是:

var circles = svg.selectAll("circle")
    .data(data)
    .enter()
    .append("circle");

同樣,如果要附加 HTML 段落,它應該是:

var circles = svg.selectAll("p")
    .data(data)
    .enter()
    .append("p");

類也是如此:如果一個類附加元素foo ,它應該是selectAll(".foo")

但是, selectAll(null)確實有效! 元素被追加。

那么,那個null什么意思? 我在這里缺少什么?


注意:這是一個自我回答的問題,試圖針對許多以前的問題涉及但 API 未解釋的主題提供“規范”問答 下面的大部分答案來自我在已滅絕的StackOverflow 文檔中寫的一個例子。

tl;博士

使用selectAll(null)是保證“輸入”選擇始終對應於數據數組中的元素,數據中的每個元素都包含一個元素。


“進入”選擇

為了回答您的問題,我們必須簡要解釋一下 D3.js 中什么是“輸入”選擇。 您可能知道,D3 的主要特性之一是將數據綁定到 DOM 元素的能力。

在 D3.js 中,當將數據綁定到 DOM 元素時,可能出現三種情況:

  1. 元素個數和數據點個數相同;
  2. 元素多於數據點;
  3. 數據點多於元素;

在情況 #3 中,所有沒有對應 DOM 元素的數據點都屬於“輸入”選擇。

因此,在 D3.js 中,“輸入”選擇是在將元素連接到數據之后包含不匹配任何 DOM 元素的所有數據的選擇。 如果我們在“輸入”選擇中使用附加函數,D3 將創建新元素,為我們綁定該數據。

這是一個維恩圖,解釋了有關數據點數量/DOM 元素數量的可能情況:

在此處輸入圖片說明

將數據綁定到已經存在的 DOM 元素

讓我們打破你提議的附加圈子的片段。

這個...

var circles = svg.selectAll("circle")
    .data(data)

... 將數據綁定到包含所有圓圈的選擇。 在 D3 術語中,這就是“更新”選擇。

那么,這...

.enter()
.append("circle");

... 表示“輸入”選擇,為每個與所選元素不匹配的數據點創建一個圓圈。

Sure, when there is no element (or a given class) in the selection, using that element (or that class) in the selectAll method will work as intended. 因此,在您的代碼段中,如果svg選擇中沒有<circle>元素,則可以使用selectAll("circle")為數據數組中的每個數據點附加一個圓圈。

這是一個簡單的例子。 <body>沒有<p> ,我們的“輸入”選擇將包含數據數組中的所有元素:

 var body = d3.select("body"); var data = ["red", "blue", "green"]; var p = body.selectAll("p") .data(data) .enter() .append("p") .text(d=> "I am a " + d + " paragraph!") .style("color", String)
 <script src="https://d3js.org/d3.v4.min.js"></script>

但是如果我們在該頁面中已經有一個段落會發生什么? 我們來看一下:

 var body = d3.select("body"); var data = ["red", "blue", "green"]; var p = body.selectAll("p") .data(data) .enter() .append("p") .text(d=> "I am a " + d + " paragraph!") .style("color", String)
 <script src="https://d3js.org/d3.v4.min.js"></script> <p>Look Ma, I'm a paragraph!</p>

結果很明顯:紅色段落消失了! 它在哪里?

第一個數據元素“red”綁定到已經存在的段落。 然后,只創建了兩個段落(我們的“輸入”選擇),藍色的和綠色的。

這是因為,當我們使用selectAll("p") ,我們選擇了<p>元素! 並且該頁面中已經有一個<p>元素。

選擇空

但是,如果我們使用selectAll(null) ,則不會選擇任何內容! 那個頁面中已經有一個段落並不重要,我們的“輸入”選擇將始終包含數據數組中的所有元素。

讓我們看看它的工作原理:

 var body = d3.select("body"); var data = ["red", "blue", "green"]; var p = body.selectAll(null) .data(data) .enter() .append("p") .text(d=> "I am a " + d + " paragraph!") .style("color", String)
 <script src="https://d3js.org/d3.v4.min.js"></script> <p>Look Ma, I'm a paragraph!</p>

這就是選擇 null 的目的:我們保證所選元素和數據數組之間沒有匹配項

選擇空值和性能

因為我們沒有選擇任何東西,所以selectAll(null)是迄今為止添加新元素的最快方法:我們不必遍歷 DOM 搜索任何東西。

這是一個比較,使用jsPerf

https://jsperf.com/selecting-null/1

在這個非常簡單的場景中, selectAll(null)明顯更快。 在充滿DOM元素的真實頁面中,差異可能更大。

何時不使用 selectAll(null)

正如我們剛剛解釋的, selectAll(null)不會匹配任何現有的 DOM 元素。 對於始終附加數據數組中的所有元素的快速代碼來說,這是一個很好的模式。

但是,如果您計划更新您的元素,也就是說,如果您計划進行“更新”(和“退出”)選擇,請不要使用selectAll(null) 在這種情況下,選擇您計划更新的元素(或類)。

因此,如果您想根據不斷變化的數據數組更新圓圈,您可以執行以下操作:

//this is the "update" selection
var circles = svg.selectAll("circle")
    .data(data);

//this is the "enter" selection
circles.enter()
    .append("circle")
    .attr("foo", ...

//this is the "exit" selection
circles.exit().remove();

//updating the elements
circles.attr("foo", ...

在這種情況下,如果您使用selectAll(null) ,圓圈將不斷附加到選擇中,堆積起來,並且不會刪除或更新圓圈。


PS:作為歷史的好奇, selectAll(null)模式的創建可以追溯到 Mike Bostock 等人的這些評論: https : //github.com/d3/d3-selection/issues/79

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM