d3.js selection機制——示例詳解

前言

d3中經過selection來操做元素,但selection並不能簡單地被認爲是「一個裝了元素的數組」。本文學習自d3做者Mike Bostock的文章How Selections Work,同時添加了個人代碼實踐,代碼均會放在CodePen上給你們自行調試

但願能起到理解selection的做用,不足或錯誤之處還請指出

javascript

selection是數組的子類

那什麼是selection呢?
selection並非一個DOM數組,它是數組的子類
與數組不一樣點有以下:html

  1. 提供了操做被選中DOM元素的方法
  2. selection數組的元素是group

接下來的兩個小標題將解釋這兩點java

提供操做被選中DOM元素的方法

selection提供了操做被選中DOM元素的方法,好比selection.attr、selection.style等等,同時也繼承了數組的一些方法,好比array.forEach、array.map。固然,咱們通常不會使用這些原生方法,而是使用d3提供的更加方便的方法,好比selection.eachgit

元素爲group的數組

selection是一個元素爲group的數組,每個group是一個DOM數組
用圖表能夠這樣表示,橢圓表示數組元素,中括號表示數組:
github


只有 selection.selectAll可以得到有多個group的selection。接下來咱們以 圖表 + 代碼 + 控制檯打印來講明group,再說明group有什麼做用

一個group的selection

<h1></h1>
<h1></h1>
<h1></h1>
<h1></h1>
<script> const selection = d3.selectAll('h1'); <script> 複製代碼


對應控制檯咱們能夠看見只有一個group

若是是數組

d3.select('h1')
複製代碼

則會選擇一個h1標籤
bash


CodePen打開

多個group的selection

上文咱們提到過只有selection.selectAll可以得到有多個group的selectionapp

<h1>
  <p></p>
  <p></p>
</h1>

<h1>
  <p></p>
  <p></p>
</h1>

<script>
  const h1Selection = d3.selectAll('h1');
  const groupSelection = h1Selection.selectAll('p');
<script>
複製代碼


對應控制檯,咱們能夠看見有多個group

每個舊元素(此例中爲h1)會變成一個group,group中包含舊元素下匹配的元素(此例中爲p);同時每個group會有對應的 父元素,此例中則爲h1
另外,d3.select與d3.selectAll的父元素均爲html根元素
CodePen打開

group有什麼做用

  1. 操做元素時,將相同層次結構的DOM元素以組劃分,方便多組DOM元素的操做
  2. 數據綁定時,以group劃分,方便多層次結構的數據綁定

這裏咱們先舉操做元素的例子,數據綁定的例子在後文

group將相同層次結構的DOM元素以組劃分,在進行多組DOM元素操做時更加方便,所以,selction.attr與selection.style所調用函數的第二個參數是在group範圍的下標,而不是selection範圍的下標函數

groupSelection.attr('some-attr', (data, index)=>{
  console.log('元素在group範圍內的下標:' + index);
})
複製代碼


CodePen打開


至此,咱們能夠看出selection並不能簡單地認爲是一個DOM元素數組,而是數組的子類,數組元素爲group,同時提供了操做被選中DOM元素的方法

接下來咱們進一步探索selection的相關操做學習

不會生成group的操做

只有selection.selectAll方法會生成含多個group的selection
由於select方法只會選中每一個group一個元素,因此select方法會保存已有的group,同時傳遞已綁定的數據

const exampleData = [1, 2];
const h1Selection = d3.selectAll('h1');
const groupSelection = h1Selection.selectAll('p');
console.log(groupSelection);

groupSelection.data(exampleData)

const spanSelection = groupSelection.select('span');
console.log(spanSelection);
console.log(spanSelection.data());
複製代碼


CodePen打開
經過控制檯打印,咱們能夠看見select方法 保存了原先存在的group,同時傳遞了數據

同時,因爲selection.append和selection.insert方法是在select之上進行封裝的,因此他們也有保存group和傳遞數據的特性,這就很是方便了咱們使子元素根據父元素的數據進行相對應的構建,例如:

咱們把數據綁定在已有的div上,在div中插入p,就能夠直接使用對應的數據進行操做了

數據綁定與selection

那麼數據綁定是「綁」到哪裏的呢?d3把數據綁定到DOM元素的__data__屬性上而非selection的屬性上。

const data = [10,11,12];
const selection = d3.selectAll('div').data(data);
console.log(selection);
const DOMList = document.querySelectorAll('div');
console.log(DOMList);
複製代碼

CodePen打開
咱們能夠看到DOM元素中即有對應綁定的數據


若是被selection選中的DOM元素沒有數據,則會返回undefined

<div></div>
<div></div>
<div></div>
<div></div>
  ......
const data = [10,11,12];
const selection = d3.selectAll('div').data(data);
  ......
console.log(d3.selectAll('div').data());
複製代碼


CodePen打開
所以數據是一直綁定在DOM元素上的,selection變量是能夠銷燬的,咱們徹底能夠從新選擇DOM元素,從新獲取先前綁定的數據並進行操做

同時咱們列舉下綁定數據的三種方法:

  • selection.data
  • sekectuib.datum
  • 經過append、insert、select從父節點繼承

selection.data與group

selection.data是以group爲單位進行數據綁定的,而不是以每個元素爲單位
咱們來看看多個group的狀況

以上圖爲例,當有多個group時,selection.data(argument1)傳入的argument1爲函數相比數組變量更爲合適,同時這個函數的參數也會有對應的group下標
咱們仍是以代碼爲例更容易理解

<div>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
</div>
<!-- ...... -->
<!-- ......一共有三個相似的div組 -->

const dataArray = ['a', 'b', 'c', 'd'];
const groupSelection = d3.selectAll('div').selectAll('p');
console.log(groupSelection);
groupSelection.data((parentData, groupIndex)=>{
  //console.log(parentData); //由於父元素無data綁定,因此返回undefined
  console.log('groupIndex:' + groupIndex);
  return dataArray;
});
console.log(groupSelection.data());
複製代碼

CodePen打開
此處groupSelection.data()傳入的爲函數,此函數的第一個參數表示parentData,第二個參數就表示上文提到的groupIndex,由於有三個group,此例中會執行三次數據綁定,咱們也能夠在控制檯上看見按組劃分的groupIndex


每次函數 return dataArray則對應綁定的數據

此例僅爲每一個group都返回一樣的一維數據,旨在說明的是groupIndex

這裏讓咱們回想上文提到的group的另外一個做用:數據綁定時,以group劃分,方便多層次結構的數據綁定

當我趕上多層次數據對應多組group的時候,咱們就能夠減輕層級DOM及層級data的綁定
這裏舉一個簡單的應用:

const hierarchicalData = [
    ['a', 'b', 'c', 'd'],
    ['e', 'f', 'g', 'h'],
    ['i', 'j', 'k', 'l']
];
    .....
groupSelection.data((parentData, groupIndex)=>{
    return hierarchicalData[groupIndex];
});
複製代碼

這樣,就能夠把對應的多層次數據分別綁定到group之中

噹噹selection只有一個group時咱們傳值就沒必要傳函數了,直接傳數組便可,通常只有在多個group的狀況下才須要向selection.data()傳函數

數據綁定匹配元素

當咱們把數據綁定到元素上時,是經過pairing keys(不知道咋翻譯)匹配的,key是一個不可重複的字符串,用於爲數據綁定進行匹配

最簡單的key是取元素對應下標,這裏我直接截取Mike Bostock文中內容


第0個元素匹配data[0],第1個元素匹配data[1]。。。
若是數據和元素的順序沒變的話,index爲key是很方便的,當順序改變時,index就沒法匹配到原先的元素或是數據了,這時咱們須要第二個參數key function來指定key

咱們再舉一個例子,假設某組元素綁定了一組數據,元素順序改變,咱們但願 保持原有元素並綁定數據,而不是銷燬重建

咱們繼續以代碼說明

const oldData = [
    { name: "A", value: "I am A" },
    { name: "B", value: "I am B" },
    { name: "C", value: "I am C" },
    { name: "D", value: "I am D" },
    { name: "E", value: "I am E" },
];
const selection = d3.selectAll('div').data(oldData);

......

//模擬元素從新排序,此行代碼後元素會倒序
const sortedSelection = selection.sort((a, b)=>{
  return -1;
});

//新數據
const newData = [
    { name: "B", value: "I am B!!!!!" },
    { name: "A", value: "I am A!!!!!" },
    { name: "C", value: "I am C!!!!!" },
    { name: "D", value: "I am D!!!!!" },
    { name: "E", value: "I am E!!!!!" },
];
//匹配綁定
sortedSelection.data(newData, (d)=>{
  return d.name;
});
複製代碼

CodePen打開
咱們在綁定數據後對DOM元素進行重排,再次綁定新的數據時根據d.name匹配,能夠發現元素順序仍爲重排後的順序,而並未從新建立

結語

在學習How Selections Work以後,我對selection及group有了更深的理解,group數據傳遞key parser在數據綁定、多層級數據上着實提供了極大的便利,此文能夠說是我的的學習總結,若有不足或錯誤之處還請大佬指出

參考資料

How Selections Work
d3.js API

相關文章
相關標籤/搜索