關於Selection 與 Data綁定
主要參考文章 How Selections Work,非常清楚地用文字搭配圖表展示D3 Selection運作與Data綁定的機制;以下內容節錄並意譯(非逐字翻譯)部分文章。
A Subclass of Array
你可能會認為Selection是一個包含DOM 元素的陣列,但這是錯的;Selection是Array的子類,除了保留Array原有的特性外(如 forEach,Map,Filter),還多加上了操作被選定元素的方法,例如attr
、style
。
然而,D3有提供原生Array更方便的替代方式,例selection.each,或是複寫原本的函式,如selection.filter或selection.sort
另外之所以說Selection並不是單純字面上的Array,主要是因為Selection是Array of Array of elements
Selection包含group
陣列,而group陣列中又放置element
var selection = d3.select("body")
(selection) <--> (group) <--> (body)
如果要存取body,也可以透過selection[0][0]
這種原生Array的存取方式喔;
同樣的selectAll
也是包含一個group陣列
var selection = d3.selectAll("h2");
(selection) <--> (group) <--> (h2)
|-> (h2)
|-> (h2)
d3.select和d3.selectAll都確切包含一個group
,如果想要包含多個group就要用多個selectAll合併,原本就有group中的elements會組成新的group,每個group都會有parentNode
d3.selectAll("tr").selectAll("td");
//第一個selectAll,此時parent為document
(selection) <--> (group) <--> (tr)
|-> (tr)
|-> (tr)
//第二個selectAll,此時parent為tr
(selection) <--> (group) <--> (td)
|-> (td)
|-> (td)
|->(group) <--> (td)
|-> (td)
|-> (td)
Non-Grouping Operations
從上述得知,selectAll和select差別在於selectAll可以創造新的group,而select保留當前的group;而且在select中,舊有group中的每一個element正好對應到新group的一個元素(1 to 1的概念吧,原文為
The select method differs because there is exactly one element in the new selection for each element in the old selection),所以select可以直接將data從parent直接傳給child,而selectAll必須使用 data-jion。
另外,append和insert
是從select延伸而來,所以也可以直接傳遞data。
d3.selectAll("section");
(selection) <--> (group) <--> (section)
|-> (section)
|-> (section)
//此時parentNode還是document喔
d3.selectAll("section").append("p");
(selection) <--> (group) <--> (p)
|-> (p)
|-> (p)
Null Elements
在select時難免會遇到在group中找不到element的時候,此時D3會塞入null,而null其實不太會影響運作,在style()和 attr()時會自動跳過
//假設只有一個section中包含aside
d3.selectAll("section").select("aside");
(selection) <--> (group) <--> (null)
|-> (null)
|-> (aside)
Bound to Data
有點訝異的是 Data並不屬於selection的一部分,反而是屬於element本身,D3將資料放在element底下的__data__
屬性當中(初始為undefined,且通常不會直接操作該屬性);所以不管selection如何,屬element的datum並不會受到影響,也可以隨時被取得。
要將datum綁定到element上有三種方式
- 綁定到一群元素上:selection.data
- 直接賦值到特定元素上:selection.datum
- 從parent直接繼承:append, insert, 或是 select
//bad
document.body.__data__ = 42;
//good
d3.select("body").datum(42);
//h1的__data__一樣是42
d3.select("body").datum(42).append("h1");
What is data
在D3中的Data包含多種型態,Array、JSON Object Array、2D Array等都是。
有趣的是,在D3中 selection.data定義的事一個資料的群組而非屬於每個元素(defines data per-group rather than per-element),所以說在 selection的元素群組 <--> selection.data資料群組必須有個連結的管道,也就是pairing key
。
預設D3會依照陣列排序生成Key,然後依照陣列順序將Data綁定到DOM元素上,也可以透過自定義方式決定配對。
D3提供的三種配對模式為
Update - 資料有相對應的元素
Enter - 資料沒有相對應的元素
Exit - 資料有相對應的元素的刪除,保留資料沒有對應的元素
這部分可以看網站內的動畫比較好理解。
總結
一開始接觸D3,總覺得selection、group和data綁定好像很簡單,但如果沒有弄仔細就容易出現未知的錯誤(圖表就是不出來)
花點時間了解基本的內容對於操作D3就更加容易上手。