使用數(shù)據(jù)文件:tweets.json
{
"tweets": [
{
"user": "Al",
"content": "I really love seafood.",
"timestamp": " Mon Dec 23 2013 21:30 GMT-0800 (PST)",
"retweets": ["Raj", "Pris", "Roy"],
"favorites": ["Sam"]
}, {
"user": "Al",
"content": "I take that back, this doesn't taste so good.",
"timestamp": "Mon Dec 23 2013 21:55 GMT-0800 (PST)",
"retweets": ["Roy"],
"favorites": []
}, {
"user": "Al",
"content": "From now on, I'm only eating cheese sandwiches.",
"timestamp": "Mon Dec 23 2013 22:22 GMT-0800 (PST)",
"retweets": [],
"favorites": ["Roy", "Sam"]
}, {
"user": "Roy",
"content": "Great workout!",
"timestamp": " Mon Dec 23 2013 7:20 GMT-0800 (PST)",
"retweets": [],
"favorites": []
}, {
"user": "Roy",
"content": "Spectacular oatmeal!",
"timestamp": " Mon Dec 23 2013 7:23 GMT-0800 (PST)",
"retweets": [],
"favorites ": []},
{
"user": "Roy",
"content": "Amazing traffic!",
"timestamp": " Mon Dec 23 2013 7:47 GMT-0800 (PST)",
"retweets": [],
"favorites": []
},
{
"user": "Roy",
"content": "Just got a ticket for texting and driving!",
"timestamp": " Mon Dec 23 2013 8:05 GMT-0800 (PST)",
"retweets": [],
"favorites": ["Sam", "Sally", "Pris"]
},
{
"user": "Pris",
"content": "Going to have some boiled eggs.",
"timestamp": " Mon Dec 23 2013 18:23 GMT-0800 (PST)",
"retweets": [],
"favorites": ["Sally"]
},
{
"user": "Pris",
"content": "Maybe practice some gymnastics.",
"timestamp": " Mon Dec 23 2013 19:47 GMT-0800 (PST)",
"retweets": [],
"favorites": ["Sally"]
},
{
"user": "Sam",
"content": "@Roy Let's get lunch",
"timestamp": " Mon Dec 23 2013 11:05 GMT-0800 (PST)",
"retweets": ["Pris"],
"favorites": ["Sally", "Pris"]
}
]
}
首先先導入文件,并將文件數(shù)據(jù)存入變量Data_
中朦前。(但是因為導入方法是異步方法介杆,它發(fā)送文件請求后就會返回,執(zhí)行后面的邏輯韭寸。所以如果后續(xù)邏輯緊接著就是用Data_
春哨,可能面臨此時數(shù)據(jù)未返回未賦予Data_
從而其值為空的情況,在這里不考慮)恩伺。
var Data_;
d3.json('tweets.json',function(d){
Data_=d;
});
然后赴背,在建立列表,當然此時html
頁面的body
元素中是完全空白的:
d3.select("body").append("ul");
d3.select("ul").selectAll("li").data(Data_.tweets,function(d,i){
return d.user;
}).enter().append("li").text(d=>d.user);
這里有幾個要點
首先選擇了
body
元素,然后創(chuàng)建(追加一個)ul
元素凰荚。在此基礎(chǔ)上選擇所有li
元素燃观,顯然此時沒有l(wèi)i元素,所以.selectAll("li")
會返回一個空集便瑟。-
在空集基礎(chǔ)之上綁定數(shù)據(jù)缆毁,暫且先不管
data()
。因為此時沒有DOM元素(li
)可以用來綁定數(shù)據(jù)胳徽,造成情況就是待綁定的數(shù)據(jù)多余了,這些多余的數(shù)據(jù)分別被封裝為EnterNode
類型對象爽彤,推入data
方法所返回的對象的_enter
屬性之中养盗,因此后續(xù)使用enter()
方法時會處理這些多余的數(shù)據(jù)。可以看見适篙,data方法返回的Selection對象中有_enter往核、_exit、_groups嚷节、_parents四個屬性聂儒,當綁定數(shù)據(jù)(調(diào)用data方法)后又多余數(shù)據(jù),這些數(shù)據(jù)會被封裝為EnterNode類型對象硫痰,推入_enter中衩婚,數(shù)據(jù)被放在_data_屬性之中。當然可以想到效斑,如果綁定數(shù)據(jù)之后非春,DOM元素反而多余了,那么這些元素就會被推入_exit屬性之中缓屠,后面會看到 調(diào)用
enter()
之后奇昙,后面的方法鏈會被循環(huán)調(diào)用。此時由于_enter
屬性中有10個EnterNode
對象敌完,則方法鏈會被調(diào)用10次储耐,每一次都會傳入EnterNode
對象的__data__
屬性作為數(shù)據(jù),在語句.enter().append("li").text(d=>d.user);
滨溉,會插入一個li
元素(append
方法是上圖中的_parent屬性調(diào)用的什湘,可見后面有一個[ul],它保存的就是父元素晦攒,在父元素上追加一個li
子元素)禽炬,一旦有這樣一個li
元素生成,那么li
元素就會自動創(chuàng)建一個__data__
屬性勤家,它的值和EnterNode
對象的__data__
是一致的腹尖,可以通過document.getElementsByTag("li")[i]._data_
來查看。
text用來設(shè)置元素文本,它的參數(shù)是一個函數(shù)热幔,這個d當然就是__data__
乐设。
另外要注意, 如果忽略了append
绎巨,直接寫成.enter().text(d=>d.user);
則不會有任何效果近尚,因為沒有實際添加元素,自然也不會有數(shù)據(jù)被實際綁定场勤。-
回到
data
方法:data(Data_.tweets,function(d,i){ return d.user; })
首先注意戈锻,寫成
Data_.tweets
而不能寫成Data_
,第一個參數(shù)是指定被綁定的數(shù)據(jù)集和媳,它一定要是一個數(shù)組類型或類數(shù)組類型格遭。在上面的json文件可知,引用Data_
后留瞳,里面只有一個tweets
對象拒迅,這顯然不合適,需要使用Data_.tweets
她倘,它代表數(shù)據(jù)數(shù)組璧微。
另外,第二個參數(shù)是指定主鍵硬梁,如果忽略前硫,那么就按照數(shù)據(jù)數(shù)組的數(shù)字下標來充當主鍵。我們使用一個函數(shù)的返回值來充當主鍵荧止,它是這么工作的:- 首先开瞭,計算selection中選中的DOM元素(
selectAll("li")
)的主鍵,如果有多個DOM結(jié)點元素的主鍵值相同罩息,則多余的DOM結(jié)點會被推入_exit_
中嗤详,供之后的exit()
方法連調(diào)用。 - 然后瓷炮,按照給定的Key函數(shù)葱色,基于數(shù)據(jù)集(
Data_.tweets
)計算出每一個數(shù)據(jù)項的主鍵值。并與DOM元素的主鍵進行匹配娘香。如果這個主鍵匹配成功苍狰,則該元素可以被用來后續(xù)操作;如果匹配不成功(或主鍵值重復)烘绽,則說明該數(shù)據(jù)是多余的淋昭,那么它會被封裝為EnterNode
并被推入_enter
中,供enter()
之后的方法鏈調(diào)用
由于第一次使用安接,會創(chuàng)建li元素并且為其綁定
user
作為主鍵值翔忽。注意,主鍵值(也就是函數(shù)返回值)必須是數(shù)字或者字符串,所以歇式,如果想把一個對象作為主鍵驶悟,需要返回JSON.stringift(obj)
或其他定義方法把它變成字符串,否則就會調(diào)用Object.prototype.toString().call
材失,這樣就會得出[object Object]
痕鳍,則顯然會造成主鍵重復 - 首先开瞭,計算selection中選中的DOM元素(
我們來看一個例子。上述代碼運行后龙巨,會立刻在html頁面中創(chuàng)建一個由10個列表項的列表笼呆,主鍵是每一個tweet
的user
,但是顯然主鍵是有重復的≈急穑現(xiàn)在诗赌,我們再次運行代碼
d3.select("ul").selectAll("li").data(Data_.tweets,function(d,i){
return d.user;
});
其返回結(jié)果
已知,前三個列表項的主鍵都是'Al'(用戶名)昼榛,所以只有第一個被推入
_groups
中供后續(xù)操作境肾,后面兩個li的主鍵與第一個重復剔难,它們就被推入了_exit
中胆屿,供exit()
調(diào)用后操作,同理偶宫,因為重復非迹,這兩個li所對應(yīng)的_data_
屬性數(shù)據(jù)也被封裝并推入_enter_
之中。