[英]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 文檔中寫的一個例子。
使用selectAll(null)
是保證“輸入”選擇始終對應於數據數組中的元素,數據中的每個元素都包含一個元素。
為了回答您的問題,我們必須簡要解釋一下 D3.js 中什么是“輸入”選擇。 您可能知道,D3 的主要特性之一是將數據綁定到 DOM 元素的能力。
在 D3.js 中,當將數據綁定到 DOM 元素時,可能出現三種情況:
在情況 #3 中,所有沒有對應 DOM 元素的數據點都屬於“輸入”選擇。
因此,在 D3.js 中,“輸入”選擇是在將元素連接到數據之后包含不匹配任何 DOM 元素的所有數據的選擇。 如果我們在“輸入”選擇中使用附加函數,D3 將創建新元素,為我們綁定該數據。
這是一個維恩圖,解釋了有關數據點數量/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)
不會匹配任何現有的 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.