這可能是一個愚蠢的問題,但我無法弄清楚在 D3 中何時使用 .join 以及何時使用 .append 。
就像在這個塊中一樣,我不知道為什么在這里使用 join 而不是 append 。
elements.selectAll('circle')
.data(d=>d.values)
//.append('circle')
.join('circle')
.attr("class","dots")
.attr('r',2)
.attr('fill',d=>colorScale(d['track']))
.attr("cx", d=>dateScale(d['edate']))
.attr("cy", d=>valueScale(d['record_time']));
有人可以幫我理解嗎?
uj5u.com熱心網友回復:
TL; 博士
selection.append 本身只是將單個子元素附加到它被呼叫的選擇中的每個元素(繼承其父級的資料)。selection.join() 依賴于資料:它執行進入/更新/退出回圈,以便 DOM 中匹配元素的數量與資料陣列項的數量相匹配。
您擁有的代碼表明您想要使用 .enter().append("circle") 而不是僅僅 .append("circle"):這完成了進入/更新/退出回圈的 enter() 部分,即也通過使用 .join() 完成。
您可以使用加入或單獨的進入/退出/更新選擇來實作相同的結果,加入只是一種方便,如檔案中所述:
此方法是顯式通用更新模式的便捷替代方法,可替換 selection.enter、selection.exit、selection.append、selection.remove 和 selection.order。(檔案)
進入/更新/退出
當看到selectAll()接著.data()我們選擇所有匹配的元件,存在每一個,我們從資料陣列將其系結的專案。.data() 的使用回傳所謂的更新選擇:它包含現有元素(如果有),新提供的資料系結到這些現有專案。
但是,如果所選元素的數量與專案的數量不匹配1,則 .data() 會創建進入選擇或退出選擇。如果我們有多余的資料項,那么我們有一個輸入選擇,為我們需要添加的每個專案一個元素,以便具有相同數量的 DOM 元素和資料陣列項。相反,如果我們有多余的 DOM 元素,那么我們有一個退出選擇。
在更新選擇上呼叫 .enter() 回傳輸入選擇。這個選擇包含占位符(“從概念上講,輸入選擇的占位符是指向父元素檔案的指標”),我們可以使用 .append("tagname") 添加我們需要的元素。
相反,在更新選擇上呼叫 .exit() 回傳退出選擇,它通常被 .exit().remove() 洗掉;
這種模式通常看起來像這樣:
let circle = svg.selectAll("circle")
.data([1,2,3,4])
circle.exit().remove();
circle.enter()
.append("circle")
.attr...
circle.attr(...
首先我們選擇所有的圓圈,假設有 2 個圓圈可供選擇。
其次,我們使用 selection.exit() 洗掉多余的元素:但是,由于我們有四個資料項并且只有兩個匹配的 DOM 元素,因此沒有要洗掉的內容,因此選擇是空的,沒有任何內容被洗掉。
第三,我們根據需要添加元素,以確保匹配的DOM元素的數量與資料陣列項的數量相同。由于我們有四個資料項并且只有兩個匹配的 DOM 元素,因此輸入選擇包含兩個占位符(指向父項的指標)。我們將圓圈附加到它們并根據需要設定它們的樣式。
最后,我們使用包含兩個預先存在的圓圈的更新選擇,并根據新資料根據需要設定它們的樣式。
通常我們希望新元素和現有元素的樣式相同,因此我們可以使用合并方法來組合輸入和更新選擇:
let circle = svg.selectAll("circle")
.data([1,2,3,4])
circle.exit().remove();
circle.enter()
.append("circle")
.merge(circle)
.attr(...
這稍微簡化了我們的代碼,因為我們不需要分別為 enter 和 update 重復樣式。
加入
那么 .join() 是從哪里來的呢?其為方便。以最簡單的形式: .join("elementTagName") .join 將上面的代碼替換為:
let circle = svg.selectAll("circle")
.data([1,2,3,4])
.join("circle")
.attr(...
這里的 join 方法洗掉退出選擇并回傳合并的更新選擇和輸入選擇(包含新圓圈),我們現在可以根據需要設定樣式。它本質上是一種速記方法,可以讓您撰寫更簡潔的代碼,但在功能上與第二個代碼塊相同2。
你的代碼
In your code, you have a selection of one or more elements (a/some parent element/s). The bound datum contains a child array, which you wish to use to create child elements. In order to do so you provide to .data() that child data array:
parentElements.selectAll("circle")
.data(d=>d.values)
You can follow that up with .join(): this will do the enter/update/exit cycle for every parent element so that they each have the proper amount of circles and returns a selection of all these circles.
You cannot use just .append() because that would append one circle to every parent element, returning a selection of these circles. This is very unlikely to be the desired result.
Instead, as noted at the top of this answer, you can use .enter().append("circle") so that you are using the pattern correctly.
You only need the enter selection if you create the elements once and never update the data, otherwise, you'll need to use enter/update/exit methods to handle excess elements, excess data items, and updated elements.
Ultimately, the difference between join and enter/update/exit is a matter of code preference, style, succinctness, but otherwise, there is nothing that you can't do with one that you can't do with the other.
1 Assuming the provision of only one parameter to .data() - the 2nd, optional, parameter is a keying function which matches DOM element and data item based on a key. DOM elements without a matching datum are placed in the exit selection, data array items without a matching DOM element are placed in the enter selection, the remainder are placed in the update selection.
2 Assuming that .join() is not provided its second or third parameters, which allow more granular control of the enter/exit/update cycle.
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/352716.html
標籤:d3.js
