從零開(kāi)始學(xué)習(xí)javascript項(xiàng)目(2)
任務(wù)描述
- 讀取頁(yè)面中id為source的列表,提取城市以及對(duì)應(yīng)的空氣質(zhì)量
- 把數(shù)據(jù)排序以后,在resort列表中按照順序顯示出來(lái)
<ul id="source">
<li>北京空氣質(zhì)量:<b>90</b></li>
<li>上??諝赓|(zhì)量:<b>70</b></li>
<li>天津空氣質(zhì)量:<b>80</b></li>
<li>廣州空氣質(zhì)量:<b>50</b></li>
<li>深圳空氣質(zhì)量:<b>40</b></li>
<li>福州空氣質(zhì)量:<b>32</b></li>
<li>成都空氣質(zhì)量:<b>90</b></li>
</ul>
<ul id="resort">
<!--
<li>第一名:北京空氣質(zhì)量:<b>90</b></li>
<li>第二名:北京空氣質(zhì)量:<b>90</b></li>
<li>第三名:北京空氣質(zhì)量:<b>90</b></li>
-->
</ul>
<button id="sort-btn">排序</button>
任務(wù)規(guī)劃
為了完成這個(gè)任務(wù),我們需要分成三個(gè)部分來(lái)完成這個(gè)動(dòng)作
- 按下button的時(shí)候觸發(fā)動(dòng)作
- 抓取頁(yè)面上的列表到一個(gè)數(shù)組里面
- 把排序后的數(shù)組通過(guò)操作DOM使得新數(shù)組append到新的列表里面
綁定按鈕動(dòng)作與事件監(jiān)聽(tīng)
和之前提到的一樣,對(duì)于按鈕這個(gè)事件的觸發(fā),需要一個(gè)監(jiān)聽(tīng)函數(shù)。首先使用抓取按鈕的DOM
var sort_btn = document.getElementById('sort-btn');
再對(duì)這個(gè)元素進(jìn)行監(jiān)聽(tīng)動(dòng)作。
關(guān)于事件監(jiān)聽(tīng),詳細(xì)點(diǎn)這里。如果不想看那么多,可以看下面的精簡(jiǎn)版。
簡(jiǎn)單的來(lái)說(shuō)js的事件監(jiān)聽(tīng)有三種方法
- element.addEventListener(type, listener[, useCapture]); // IE6~8不支持
- element.attachEvent(’on’ + type, listener); // IE6~10,IE11不支持
- element[’on’ + type] = function(){} // 所有瀏覽器
舉個(gè)栗子:
function cb() { console.log(1); }
element.addEventListener('click', cb, false);
element.attachEvent('onclick', cb);
element.onclick = cb;
type :事件類型
listener :事件觸發(fā)后的回調(diào)函數(shù)
useCapture :是否使用捕獲,如果值為true, useCapture 表示用戶希望發(fā)起捕獲。 在發(fā)起捕獲之后, 只要Dom子樹(shù)下發(fā)生了該事件類型,都會(huì)先被該事件監(jiān)聽(tīng)器捕獲,然后再被派發(fā)到Dom子樹(shù)中的事件監(jiān)聽(tīng)器中。并且向上冒泡的事件不會(huì)觸發(fā)那些發(fā)起捕獲的事件監(jiān)聽(tīng)器。 useCapture 默認(rèn)值為false 。
addEventListener是W3C工作組在DOM Level 2開(kāi)始引入的一個(gè)注冊(cè)事件監(jiān)聽(tīng)器的方法;而在此之前,傳統(tǒng)的事件監(jiān)聽(tīng)方法是通過(guò)element[’on’ + type]的方式來(lái)注冊(cè)的。它們兩之間的主要區(qū)別是,element[’on’ + type]的方式無(wú)法使用事件捕獲,并且element[’on’ + type]不支持對(duì)同一個(gè)元素的同一個(gè)事件注冊(cè)多個(gè)事件監(jiān)聽(tīng)器。如下面的例子所示,元素被點(diǎn)擊后只會(huì)輸出1,而不會(huì)輸出0和1.
element.onclick = function(){ console.log(0); }
element.onclick = function(){ console.log(1); }
看完我的解釋,是不是更加不明白了?沒(méi)關(guān)系,趕緊點(diǎn)擊這里。 回過(guò)頭好好看一下好了。
淺談事件的捕獲和冒泡
對(duì)于所有中國(guó)人來(lái)說(shuō),有一個(gè)四字魔咒是永遠(yuǎn)繞不開(kāi)的。只要有人對(duì)你說(shuō)出這四個(gè)字,你就能中邪般地買票去最坑爹的景點(diǎn)、玩命爬上最艱險(xiǎn)的山峰、吃下最難吃的餐館飯菜…這四個(gè)字就是———來(lái)都來(lái)了。
所以,既然看到這了,我就帶你們?nèi)チ私庖稽c(diǎn)更深入的知識(shí)吧,畢竟來(lái)都來(lái)了 。
事件
javascript使用的是異步事件模型,如果你寫過(guò)verilog,那么對(duì)這個(gè)概念應(yīng)該會(huì)比較熟悉,而且javascript中的異步事件是基于觸發(fā)器的,也就是說(shuō)你不必考慮異步時(shí)鐘域里面的數(shù)據(jù)傳輸而產(chǎn)生的亞穩(wěn)態(tài)和計(jì)算數(shù)據(jù)傳輸?shù)膸?。異步事件的在于只要使用時(shí)間處理函數(shù)注冊(cè)一個(gè)回調(diào)函數(shù),一旦事件觸發(fā),就會(huì)立刻執(zhí)行回調(diào)函數(shù)。
DOM事件流的階段
所謂的事件流,就是事件在處理事件傳播過(guò)程中的順序,根據(jù)W3C模型的定義,這個(gè)傳播過(guò)程分別是捕獲階段,目標(biāo)階段,冒泡階段。事件階段存在的意義子啊與 當(dāng)我們?cè)谝粋€(gè)元素里面潛逃另外一個(gè)元素,并且這兩者都綁定了一個(gè)onClink事件,就像下面這樣
+-----------------+
| event1 |
| +-----------+ |
| | event2 | |
| +-----------+ |
| |
+-----------------+
(效果來(lái)自這里不是閑的蛋疼不要嘗試,不過(guò)畫點(diǎn)簡(jiǎn)單的東西還是很給力的)。當(dāng)點(diǎn)擊事件發(fā)生的時(shí)候,哪一個(gè)先被觸發(fā),執(zhí)行的順序是什么?W3C模型采用的是一種先捕獲再冒泡的的方式,大概就像下面這樣的。
/ \\
+----------------| |--| |----------------+
| element1 | | | | |
| +------------| |--| |----------+ |
| |element2 \\ / | | | |
| +------------------------------+ |
| W3C event model |
+----------------------------------------+
? 我們以一個(gè)例子來(lái)說(shuō)明這種流程
<div id = "s1"> s1
<div id = "s2">s2</div>
</div>
<script>
s1.addEventListener("click",function(evt){
console.log("s1捕獲模式");
},true);
s2.addEventListener("click",function(evt){
console.log("s2捕獲模式");
},true);
s1.addEventListener("click",function(evt){
console.log("s1冒泡模式");
},false);
s2.addEventListener("click",function(evt){
console.log("s2冒泡模式");
},false);
</script>
結(jié)果如下
s1捕獲模式
s2捕獲模式
s2冒泡模式
s1冒泡模式
我們可以通過(guò)這個(gè)例子看到,事件傳播的過(guò)程根據(jù)addEventListener方法設(shè)置的第三個(gè)參數(shù)確定捕獲的模式。在捕獲階段,事件到達(dá)事件目標(biāo)之前,事件對(duì)象必須從windows經(jīng)過(guò)目標(biāo)的祖先節(jié)點(diǎn)傳播到時(shí)間目標(biāo),在這個(gè)階段注冊(cè)的事件監(jiān)聽(tīng)器在到達(dá)目標(biāo)之前必須先處理事件。在目標(biāo)階段,事件對(duì)象到達(dá)事件目標(biāo),該階段的事件監(jiān)聽(tīng)器就會(huì)對(duì)其進(jìn)行處理。最后就是冒泡階段,事件對(duì)象以一個(gè)與捕獲階段相反的方向經(jīng)過(guò)祖節(jié)點(diǎn)傳播到window。在這個(gè)階段注冊(cè)的事件監(jiān)聽(tīng)器會(huì)對(duì)相應(yīng)的冒泡時(shí)間進(jìn)行處理。
這樣就很清楚了。我再貼一張官方圖片:

如果你看不到上面的圖片,那么高清無(wú)碼大圖在這里 。當(dāng)然,我們也可以使用stopPropagation這個(gè)函數(shù)來(lái)停止事件的傳播,這里就不展開(kāi)了。如果你有興趣的話,可以研究一下關(guān)于IE對(duì)于事件捕獲的操作方法,通過(guò)監(jiān)聽(tīng)父節(jié)點(diǎn)而不是監(jiān)聽(tīng)父節(jié)點(diǎn)下面的每一個(gè)子節(jié)點(diǎn)來(lái)節(jié)約瀏覽器的資源,通過(guò)關(guān)閉冒泡和捕獲使函數(shù)的執(zhí)行互不干擾而節(jié)約瀏覽器的資源等等。
回歸主線
好了,說(shuō)了這么多。我們?cè)谧约旱捻?xiàng)目里面需要用到的大概就是這么一句話
var sort_btn = document.getElementById('sort-btn');
sort_btn.addEventListener('click',function(){btnHandle()},false);
抓取列表元素到數(shù)組
這就是簡(jiǎn)單的操作DOM的內(nèi)容了,我們的目標(biāo)是把城市和數(shù)字存到一個(gè)個(gè)的鍵值對(duì)中。
//函數(shù)應(yīng)該這么寫
function getData(){
var data = document.getElementById('source').getElementByTagName('li');
var a = [];
var city;
var num;
for(var i=0; i<data.length; i++){
city = data[i].innerHTML.substring(0,data[i].innerHTML.indexOf('空'));
num = data[i].getElementByTagName('b')[0].innerHTML;
a.push([city,num]);
}
return a;
}
- innerHTML不僅可以修改HTML元素還可以把元素內(nèi)容返回出來(lái)。
- substring用于提取字符串中介于兩個(gè)指定下標(biāo)之間的字符。其內(nèi)容是從 start 處到 stop-1 處的所有字符
- indexOf() 方法可返回某個(gè)指定的字符串值在字符串中首次出現(xiàn)的位置
對(duì)列表元素進(jìn)行排序
這一部分也很簡(jiǎn)單,教科書一般的排序方式。
function sortAqiData(data){
data.sort(function(a,b){
return (a[1]-b[1]);
})
}
通過(guò)操作DOM使排序后的數(shù)據(jù)顯示出來(lái)
function render(data){
var resort = document.getElementById('resort');
resort.innerHTML = '';
for(var i=0; i< data.length; i++){
var node = document.creatElement('li');
var html = '第'+(i+1)+'名:'+data[i][0]+'空氣質(zhì)量:<b>'+data[i][1]+'</b>';
node.innerHTML = html;
resort.appendChild(node);
}
}
這樣,一個(gè)簡(jiǎn)單的交互功能就算完成了。