HTML 事件(三) 事件流與事件委托


    本篇主要介紹HTML DOM中的事件流和事件委托。

其他事件文章

1. HTML 事件(一) 事件的介紹

2. HTML 事件(二) 事件的注冊與注銷

3. HTML 事件(三) 事件流與事件委托

4. HTML 事件(四) 模擬事件操作

 

目錄

1. 事件流

  1.1 何為事件流

  1.2 事件流的三個階段

  1.3 addEventListener()注冊事件流的階段

  1.4 阻止事件流的傳播

2. 事件委托

  2.1 何為事件委托

  2.2 ul、li場景示例

  2.3 JQuery的事件委托

    2.3.1 delegate()

    2.3.2 on()

 

1. 事件流 (Event Flow)

1.1 何為事件流

簡單來說HTML的元素會出現嵌套的關系,比如:一個Div嵌套了一個Button按鈕,當2個元素都注冊了click點擊事件。點擊里面的Button按鈕時,Div的click事件也會觸發。

那么問題來了,既然都會觸發,得有個觸發的順序吧?是按照Div → Button這樣的順序觸發,還是按照Button → Div的順序觸發?

在之前,兩大瀏覽器廠商網景和微軟都有各自的觸發順序:

網景的瀏覽器采用捕獲方式:按照Div → Button這樣的順序觸發.

而微軟的瀏覽器采用冒泡方式:按照Button → Div的順序觸發。

在2級DOM事件規范制定時干脆合二為一:事件流同時包含了這2個階段。

 

1.2 事件流的三個階段

1.2.1 三個階段

2級DOM事件規范制定了事件流的三個階段:捕獲階段目標階段冒泡階段

捕獲階段(Capture Phase):事件從最外層的window對象到目標節點的父節點依次觸發的階段。(從外到內)

目標階段(Target Phase):事件在目標節點上觸發時的階段。

冒泡階段(Bubbing Phase):事件從目標節點的父節點到最外層的window對象依次觸發的階段。(從內到外)

1.2.2 示例圖

 

 

1.3 addEventListener()注冊事件流的階段

元素對象通過addEventListener()注冊事件時,此方法的的第三個參數可設置本次注冊是捕獲階段還是冒泡階段

1.3.1 addEventListener()方法說明

語法:EventTarget.addEventListener(eventName, eventHandler [, useCapture] )

參數

①eventName {string} :所要注冊的事件名稱,不區分大小寫。此名稱不需要像注冊事件屬性那樣在前綴加上"on"。如注冊鼠標點擊事件,寫為click。

②eventHandler {function | function Object} :函數或者函數對象。事件觸發時所需要執行的函數;當使用函數對象多次注冊同一事件時,只當注冊一遍。

③useCapture {boolean} 可選 :是否處於捕獲階段,默認為false。

  |-true:當前注冊的事件為捕獲階段。

  |-false:當前注冊的事件不為捕獲階段,為冒泡階段。

 

1.3.2 示例

示例使用到Event事件對象的部分屬性:

屬性 readonly Object currentTarget :只讀,獲取正在處理此事件的對象。

屬性 readonly int eventPhase :只讀,表示事件的處理階段:0表示沒有正在處理,1表示捕獲階段,2表示目標階段,3表示冒泡階段。

屬性 readonly Object target :只讀,獲取觸發此事件的對象。

function clickHandle(e){
    console.log("事件階段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
} 

window.addEventListener('click',clickHandle,false);
window.addEventListener('click',clickHandle,true);
document.addEventListener('click',clickHandle,false);
document.addEventListener('click',clickHandle,true);
document.documentElement.addEventListener('click',clickHandle,false);
document.documentElement.addEventListener('click',clickHandle,true);
document.body.addEventListener('click',clickHandle,false);
document.body.addEventListener('click',clickHandle,true);
document.getElementById('div').addEventListener('click',clickHandle,false);
document.getElementById('div').addEventListener('click',clickHandle,true);
document.getElementById('btn').addEventListener('click',clickHandle,false);
document.getElementById('btn').addEventListener('click',clickHandle,true);

1.4 阻止事件流的傳播

Event 事件對象的stopPropagation()、stopImmediatePropagation()方法可阻止事件流的后續傳播。

stopImmediatePropagation()方法除了阻止事件流傳播還會阻止當前事件在此元素的后續事件處理程序。

事件流的三個階段調用這2個方法,會有不同的阻止傳播方式:

1.4.1 在捕獲階段調用

說明:在捕獲階段調用stopPropagation()方法時,此元素后續的事件流都會阻止,包括捕獲階段、目標階段、冒泡階段

示例:在1.3.2示例代碼中的body元素捕獲階段調用此方法

document.body.addEventListener('click',function(e){
    console.log("事件階段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
    e.stopPropagation();
},true);

結果:事件流在body的捕獲階段就截至了,后續的階段都沒有執行

 

1.4.2 在目標階段調用

說明:在目標段調用stopPropagation()方法時,捕獲階段和目標階段會執行完畢,冒泡階段不會被執行

示例:在1.3.2示例代碼中的button按鈕元素目標階段調用此方法

document.getElementById('btn').addEventListener('click',function(e){
    console.log("捕獲階段注冊:事件階段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
    e.stopPropagation();
},false);
document.getElementById('btn').addEventListener('click',function(e){
    console.log("冒泡階段注冊:事件階段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
    e.stopPropagation();
},true);

結果:捕獲階段和目標階段執行完畢,冒泡階段未被執行。

1.4.3 在冒泡階段調用

說明:在冒泡段調用stopPropagation()方法時,捕獲階段和目標階段會執行完畢,元素后續的冒泡階段不會被執行

示例:在1.3.2示例代碼中的body冒泡階段調用此方法

document.body.addEventListener('click',function(e){
    console.log("事件階段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
    e.stopPropagation();
},false);

結果:捕獲階段和目標階段執行完畢,body后續的冒泡階段未被執行

2. 事件委托(Event Delegate)

2.1 何為事件委托

HTML元素含有嵌套關系,並且事件流含有冒泡階段。子元素的觸發事件會冒泡到父元素的相同事件上。

一般情況只需給子元素注冊特定的事件處理程序即可,但當子元素過多或頻繁的進行增減操作怎么辦?

比如一個ul包含了幾十個li元素,對每個li元素進行單獨的事件注冊會影響性能。而現只要在父元素注冊事件監聽器,等待li事件觸發后的冒泡階段即可。

簡單來說事件委托就是父元素監聽子元素的冒泡事件

 

2.2 ul、li場景示例

Div容器包含了多個li子元素,在Div容器注冊事件委托。

HTML代碼

<div id="div">
    <ul id="ul" >
        <li data-key="北京">北京</li>
        <li data-key="上海">上海</li>
        <li data-key="杭州">杭州</li>
    </ul>
</div>

 

JS代碼

document.getElementById('div').addEventListener('click',function(e){
    var value=e.target.attributes['data-key'].value; // 獲取目標階段元素的'data-key'屬性的值
    console.log(value); 
});

  

2.3 JQuery的事件委托

在JQuery中,父元素可調用delegate()、on()作為事件委托使用。

2.3.1 delegate()

語法:$('父元素').delegate( selector [, eventType] [, eventData], handler )

參數

①selector {string} :子元素的選擇器、

②eventType {eventType} 可選 :觸發的事件類型。如:click。

③eventData {object} 可選 :觸發事件時event.data指向的值。

④handler {function} :事件注冊的處理程序。

示例

$('#div').delegate('li', 'click', function() {
    var v = $(this).data('key');
    console.log(v);
});

 

2.3.2 on()

說明:JQuery1.7版本開始時,推薦on()代替delegate()方法。

語法:$('元素').on( events [, selector ] [, data ], handler )

參數

①events {string} :一個或多個事件名稱。

②selector {string} 可選 :子元素選擇器。若無此值,表示元素注冊本身的事件。若含有此值,表示只有子元素的事件觸發,才會觸發注冊的事件。

③data {object} 可選 :觸發事件時的event.data指向的值。

④handler {function} :事件注冊的處理程序。

示例

$('#div').on('click','li',function(e) {
    var v = $(this).data('key');
    console.log(v);
});

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM