從零開(kāi)始學(xué)習(xí)javascript項(xiàng)目(2)——事件的監(jiān)聽(tīng)、捕獲和冒泡

從零開(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)行處理。

這樣就很清楚了。我再貼一張官方圖片:

事件觸發(fā)過(guò)程

如果你看不到上面的圖片,那么高清無(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)單的交互功能就算完成了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 以下文章為轉(zhuǎn)載,對(duì)理解JavaScript中的事件處理機(jī)制很有幫助,淺顯易懂,特分享于此。 什么是事件? 事件(E...
    jxyjxy閱讀 3,175評(píng)論 1 10
  • (續(xù)jQuery基礎(chǔ)(1)) 第5章 DOM節(jié)點(diǎn)的復(fù)制與替換 (1)DOM拷貝clone() 克隆節(jié)點(diǎn)是DOM的常...
    凜0_0閱讀 1,527評(píng)論 0 8
  • 事件是一種異步編程的實(shí)現(xiàn)方式,本質(zhì)上是程序各個(gè)組成部分之間的通信。DOM支持大量的事件,本節(jié)介紹DOM的事件編程。...
    周花花啊閱讀 686評(píng)論 0 3
  • 現(xiàn)在的孩子獨(dú)生子女居多,所以家長(zhǎng)對(duì)孩子寵愛(ài)有加。真象對(duì)待小皇帝一般。含在嘴里怕化了,捧在手里怕掉了。隨著時(shí)間的推移...
    光明自在行閱讀 616評(píng)論 2 6
  • 正常如你我的普通人,也許想的最多的就是怎么能多拿些工資;或者工作上的一堆破事讓自己牢騷滿腹,天天抱怨個(gè)沒(méi)完沒(méi)了...
    靜小酥閱讀 406評(píng)論 0 0

友情鏈接更多精彩內(nèi)容