本篇大纲:Joining Data、绑定 DOM 元素跟资料的方法、data 跟 datum 的比较、何时该用 data / datum
上一章讲解完 D3 的 Selection,今天要来看资料绑定
的部分啦!大家请把零食、饮料准备好,因为这篇的概念太重要了,所以将会是非常长篇幅的文章!等不及的人可以按照大纲去找自己想看的段落~话不多说,我们赶快开始吧!
我们先来看到官方文件提供的 API 们
绑定资料的这些 API 是 D3.js 很重要、也非常方便的功能,它能让透过资料的增减去新增或删除 DOM 元素,这样我们就只要专注在资料的变化上就好,是个非常方便的功能~
要特别注意的是,这些绑定资料的 API 归类於 selection 分类之下,这是因为我们要先用 d3.select 的方法选定DOM节点後,才能将资料绑定到 d3.select 回传的 selection 实体上,并对资料跟元素的配对与增减进行对应处理
。
上面说的看起来可能很难懂,但说白了其实就是如果你不先用 d3.selection 选定节点,就不能用.data()去绑定资料!所以你看所有的程序码,这两个方法都是一起出现的,没有单独使用 d3.data() 的情况
d3.select('div').data()
另外,我个人认为确定哪个方法是归类在哪边蛮重要的,因为 D3 实在有太多 API
有些名字一模一样
例如 ⇒ Scale 中的 Continuous Scales 跟 Ordinal Scales 中都有 .domain( )、.range( )的方法
有些却是只有它独有
例如 ⇒ Scale 中只有 Continuous Scales 有 .invert( ) 的方法,因此其他 scale 无法使用.invert()
如果 API 使用错误,就会发现图片出不来、console一直报错,除错半天却不明白到底哪里有问题。更常遇到的情况是,你看了某篇文章的图表不错,作者也有分享程序码,於是你想直接拿来用并多加一些功能上去,但却发现加上新功能後一直出错,查半天找不出问题。所以如果有不太确定的 API,建议直接先去官方文件查查,减少自己困在程序码的时间~
话说远了赶快拉回来~我们先前看到D3.js提供了五个绑定资料的方法,我觉得这些方法可以分成两类:
绑定DOM元素跟资料的方法
增减资料数量与DOM元素不匹配的方法
接着就来讲讲我为何会这样分类,以及这些 API 该怎麽使用吧!
D3 提供了两个把资料跟元素绑定的方法,分别是
这两个方法会把资料阵列跟 DOM 元素们绑定在一起,并返还一个更新後与资料绑定的 selection 物件,之後就能针对这个 selection 物件去决定要用 enter/exit 去增加或删减 DOM 元素。
要注意的是,这两个方法只会绑定数量一样的DOM元素与资料阵列。一旦资料比较多、DOM 元素比较少,亦或是反过来的情况,多余的资料或 DOM 元素就会落入 enter 或是 exit 的区块,我们便能使用增减资料数量与 DOM 元素不匹配的方法( enter/exit )的方法去增加或删除相对应的 DOM 元素。
selection.data
我们首先来看到官方文件对於 selection.data( ) 的阐述~
当资料阵列跟 DOM 元素绑定时,会按照 index 的顺序将资料一一绑定到DOM元素上。这时单一个 data 就会被存在匹配的单一个DOM元素 _data_ 这个属性中,并达成让元素与 DOM「黏」在一起的效果
const bindData = d3.select('p')
.data(['绑定资料'])
console.log(bindData)
console.log(bindData['_groups'][0][0]['__data__'])
此时你会看到一个大物件,展开後里面长这样
我们的 _data_ 就藏在 _groups 里面。展开 _groups 之後,再点开 0 这个阵列,并且一路滑到最下面,就能找到 _data_ 了
const multiData = ['绑', '定', '资', '料']
const bindMultiData = d3.selectAll('.multiData')
.data(multiData)
console.log(bindMultiData)
console.log(bindMultiData['_groups'][0])
这样一来,把 bindMultiData 印出来时,就会看到资料会按照 index 一个个被绑到相对应的node节点上
const multiData = ["绑", "定", "资", "料"];
const bindUnmatchData = d3.select(".unmatchData").data(multiData);
console.log("bindUnmatchData", bindUnmatchData);
这种情况很常见,因为我们的资料不一定会跟 DOM 元素数量一样;或是当资料有变动时,也会让两者的匹配度产生改变,这时就要用到增减资料数量与DOM元素不匹配的方法来处理
。但这个晚点再说,我们先来看到绑定资料的另一个方法- selection.datum( )
selection.datum( )
一样先看到官方文件对这个 API 的解释
官方文件上说的是,datum( ) 跟 data( )的不同在於,datum 无法合并资料、不能改变资料的顺序,也不能去增减绑定的DOM API。这到底是什麽意思呢? 我们直接看范例比较清楚
现在我们的画面上只有一个 calss="join" 的 DOM 元素,但 joinData 资料包含两个物件。因此当我们使用 selection.data( )
将资料跟DOM 元素绑定时,因为selection.data 会将资料按照index的顺序一一绑定到DOM元素上,但由於DOM元素不够,所以实际上只能够绑定到第一个物件 {name:'jin'},剩下的资料则会被归类的 enter 内
// html
<div class="join"></div>
// JS
const joinData = [{name:'jin'}, {name:'JIN'}]
const jData = d3.select('.join').data(joinData)
console.log('jData',jData)
如果你想将这两个元素一起绑定到这个DOM的话,可以使用合并资料的方式
// html
<div class="join"></div>
// JS
const joinData = [{name:'jin'}, {name:'JIN'}]
const jData = d3.select('.join').data([joinData])
console.log('jData',jData)
这样一来就能看到这个 DOM 元素把整包资料都绑进来了。
但如果我们改用 selection.datum( )
这个方法的话,它并不会按照阵列的顺序去绑定 DOM 元素,而是直接把整包物件都绑定到 DOM 上面。既然每次都是整包资料绑定元素,这样一来自然就没有资料跟元素数量不匹配的问题,也就没有 join、enter、exit 这些增减 DOM 元素的方法可以使用
// html
<div class="join"></div>
// JS
const joinDatum = [{name:'jin'}, {name:'JIN'}]
const jDatum = d3.select('.join').datum(joinDatum)
console.log('joinDatum',joinDatum)
如果还想了解更多,可以直接看这篇作者亲自在Stackoverflow上的回答
如果你看完上面的解说後,仍然搞不懂这两个方法差在哪里的话⋯⋯⋯⋯别担心!下面就是简单的统整:
.data( )
将传入的资料阵列,按照索引值一个一个绑定到DOM元素上.datum( )
将传入的资料阵列,整包绑订到 一个DOM 元素上举例而言,假如我们的DOM上面有五个 < rect >
<svg>
<rect />
<rect />
<rect />
<rect />
<rect />
</svg>
要绑定的资料则是 [10, 20, 30, 40],分别用 data( ) 跟 datum( ) 去绑定
const data = [10, 20, 30, 40];
// data()
const d3Data = d3.select(".chartContainer").selectAll("rect").data(data);
// datum()
const d3Datum = d3.select(".chartContainer").selectAll("rect").datum(data);
console.log("d3Data", d3Data);
console.log("d3Datum", d3Datum);
接着再观察印出来的console,会看到 data( ) 只绑订了四个 < rect >,datum( ) 却绑定了五个 < rect >:
点开每个DOM元素,找到绑定的 _data_ 资料,会发现
整理比较
DOM 元素 | 使用 data 绑定得到的 _data_ | 使用 datum 绑定得到的 _data_ |
---|---|---|
第一个 < rect > | 10 | ['10', '20', '30', '40'] |
第二个 < rect > | 20 | ['10', '20', '30', '40'] |
第三个 < rect > | 30 | ['10', '20', '30', '40'] |
第四个 < rect > | 40 | ['10', '20', '30', '40'] |
第五个 < rect > | 没绑到资料,归到exit selection 内 | ['10', '20', '30', '40'] |
了解这两者的差异後,大家心里一定会出现一个疑惑:那到底什麽时候要用data( )、什麽时候要用datum( )呢?
基本上就是:
适合使用 datum
适合使用 data()
以上就是绑定DOM元素跟资料的方法
!
本来想一口气把接下来的增减资料数量与DOM元素不匹配的方法
也写完,但发现如果继续写下去,可能就要变成万言书了(登愣)~~为了方便读者阅读与查找,最後决定还是拆成上下两集吧!今天就先讲完 data( ) 跟 datum( ) 这两个绑定资料的方法与差异,明天我们再来看看 enter( )、exit( )、join( ) 这几个 API 又是干嘛的~
最後,一样附上本章的程序码与图表 Github 、 Github Page,这次基本上都是看 console.log 出来的内容,所以看到画面一片白不要太震惊哦~
<<: 意图下载微微软家的新OS,嚐鲜不成载到加好加满的谜包
这里我们先介绍 gulp-clean-css(压缩css) 与 gulp-uglify(压缩js) ...
今天我们接续 RDS Lab 实作。 创建第一台 RDS instance 按下左边列表的 Dat...
前言 这篇演讲适合所有人听,特别是当你觉得「为什麽我明明在做对的事情,但大家都不接受我的意见」时,...
这边我是打API爬的,所以先写了序列化: class IgCommentsSerializer(se...
写在前面 placeholder placeholder placeholder placehold...