d3基础入门一-选集、数据绑定等核心概念
引入D3
D3下载,本文下载时的版本为6.5.0
mkdir d3.6.5.0 unzip --help unzip d3.zip -d d3.6.5.0/ ls d3.6.5.0/ API.md CHANGES.md d3.js d3.min.js LICENSE README.md
$ ls 01_empty.html static
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>import d3</title> <script type="text/javascript" src="static/d3.6.5.0/d3.js"></script> </head> <body> <script type="text/javascript"> alert(1); </script> </body> </html>
python3
python -m http.server 8801 &
python2
python -m SimpleHTTPServer 8801 &
http://127.0.0.1:8801/01_empty.html
D3的选集
JS控制台中输入
d3.select("body")
返回的是一个JS对象,除了原型prototype外,主要有两个数组,
_groups 自己相关及其子元素相关信息
_parents 父元素相关,body的父元素就是html了
这个空页面,所以body中没有元素,下面添加多个元素,
<p>11</p>
<p>12</p>
<p>13</p>
在JS控制台上输入
selectAll与select的不同是,_groups数组中的每个元素都是一个NodeList
D3数据绑定
<body> <p>11</p> <p>12</p> <p>13</p> <script type="text/javascript"> let dataset = [1,2,3,4,5] d3.selectAll("p") .data(dataset); </script> </body>
data() 将五个数据绑定到三个元素
可以看到元素还是三个,但每个元素上多了一个__data__属性,它的值对应的是数组中的元素
<script type="text/javascript"> let dataset = [1,2,3,4,5] d3.selectAll("p") .data(dataset) .enter(); </script>
创建一个选集,就是初始化两个数组
function Selection(groups, parents) { this._groups = groups; this._parents = parents; }
然后使用原型构建一系列方法,开头这6个方法非常重要,弄清理它们的作用有助于深入了解D3
Selection.prototype = selection.prototype = { constructor: Selection, select: selection_select, selectAll: selection_selectAll, filter: selection_filter, data: selection_data, enter: selection_enter, exit: selection_exit,
enter()方法是创建一个新的选集并返回这个选集,重点就是其传入的参数:this._enter,
var sparse = function(update) { return new Array(update.length); }; var selection_enter = function() { return new Selection(this._enter || this._groups.map(sparse), this._parents); };
this._enter这个属性是在data()方法中完成初始化的,data()这个方法稍微复杂,请直接看最后四行
var selection_data = function(value, key) { if (!value) { data = new Array(this.size()), j = -1; this.each(function(d) { data[++j] = d; }); return data; } var bind = key ? bindKey : bindIndex, parents = this._parents, groups = this._groups; if (typeof value !== "function") value = constant$1(value); for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { var parent = parents[j], group = groups[j], groupLength = group.length, data = value.call(parent, parent && parent.__data__, j, parents), dataLength = data.length, enterGroup = enter[j] = new Array(dataLength), updateGroup = update[j] = new Array(dataLength), exitGroup = exit[j] = new Array(groupLength); bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); // Now connect the enter nodes to their following update node, such that // appendChild can insert the materialized enter node before this node, // rather than at the end of the parent node. for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { if (previous = enterGroup[i0]) { if (i0 >= i1) i1 = i0 + 1; while (!(next = updateGroup[i1]) && ++i1 < dataLength); previous._next = next || null; } } } update = new Selection(update, parents); update._enter = enter; update._exit = exit; return update; };
在data()方法中完成了两个重要属性的初始化_enter,_exit,并返回一个新的选集
enter是按数据集的长度初始化的,而exit则是按html已有元素的长度初始化,之后再进行数据与html元素的绑定
dataLength = data.length, enterGroup = enter[j] = new Array(dataLength), updateGroup = update[j] = new Array(dataLength), exitGroup = exit[j] = new Array(groupLength);
enter()
<script type="text/javascript"> var dataset = [1,2,3,4,5] var enter = d3.selectAll("p") .data(dataset) .enter(); var jsonStr = JSON.stringify(enter); alert(jsonStr); </script>
从输出上可以看到enter是一个选集,它比一个空的选集包含的内容更少
{"_groups":
[[null,null,null,
{"ownerDocument":{
"location":{
"href":"file:///opt/wks/d3/d3-book-master/d3_demo/03_dataset.html",
"origin":"null",
"protocol":"file:","host":"","hostname":"","port":"",
"pathname":"/opt/wks/d3/d3-book-master/d3_demo/03_dataset.html","search":"","hash":""}},
"namespaceURI":"http://www.w3.org/1999/xhtml","_next":null,"_parent":{},
"__data__":4},
{"ownerDocument":{"location":{"href":"file:///opt/wks/d3/d3-book-master/d3_demo/03_dataset.html","origin":"null","protocol":"file:","host":"","hostname":"","port":"","pathname":"/opt/wks/d3/d3-book-master/d3_demo/03_dataset.html","search":"","hash":""}},"namespaceURI":"http://www.w3.org/1999/xhtml","_next":null,"_parent":{},
"__data__":5}]],
"_parents":[{}]}
enter选集_groups 总共有5个元素,前三为空,后两个为绑定的数据元素的对象
前面说将五个数据绑定到三个存在的元素上,后面两个数据元素,D3为其绑定一个“待初始化的对象”上。
enter选集 = 数据集合 - DOM集合 ,代表数据集合中没有绑定DOM的、未可视化的选集对象。
现在我们将集合分为三类:
enter() : 数据集合中没有绑定DOM的、未可视化的选集对象。
data() : 数据集合中绑定DOM的、可视化的选集对象。
exit() : DOM集合中没有关联数据的选集对象,或者是数据退出的DOM对象选集。
其他选集:
merge() : enter() 是未可视化的数据选集,那么初始化完enter()的选集后,想要一个全集时,就可以使用merge(),得到一个新旧合并的D3选集。
merge(bars) merge的参数是一个绑定的数据的选集,merge会返回当时最新的图形和数据绑定的选集。
//Select… var bars = svg.selectAll("rect") .data(dataset, keyFunc); //Bind data with custom key function
如何确定数据
data(dataset) 按数组dataset的索引确定数组,通常数组的中元素是简单类型
data(dataset,key) 按指定的方法key确定数组中的元素,通常数组中的元素是JS对象