JavaScript 事件冒泡、事件捕獲和事件委托

1.理解事件流

一言以蔽之,事件捕獲是從外層元素到目標元素的過程,事件冒泡是從目標元素到外層元素的過程。如圖:

event flow

html:

<div id="wrapper">
    <button id="event">事件處理程序</button>
</div>

javascript:

var wrapper = document.getElementById('wrapper');
var event = document.getElementById('event');

wrapper.addEventListener("click", function(e){
    console.log('捕獲階段執行父元素wrapper的事件處理程序');
}, true);
wrapper.addEventListener("click", function(e){
    console.log('冒泡階段執行父元素wrapper的事件處理程序');
}, false);
event.addEventListener("click", function(e){
    console.log('捕獲階段執行子元素event的事件處理程序');
}, true);
event.addEventListener("click", function(e){
    console.log('冒泡階段執行子元素event的事件處理程序');
}, false);

正如前面所說,這段代碼的輸出是:

捕獲階段執行父元素wrapper的事件處理程序
捕獲階段執行子元素event的事件處理程序
冒泡階段執行子元素event的事件處理程序
冒泡階段執行父元素wrapper的事件處理程序

Demo

2.阻止冒泡

應該在那個階段執行元素的事件處理程序呢?

多數情況下,我們希望在觸發一個元素的事件處理程序時,不影響它的父元素。比如:點擊button,并不希望父元素的click事件處理程序被觸發。

解決方法是:在冒泡階段執行事件處理程序,然后阻止冒泡。

wrapper.addEventListener("click", function(e){
    console.log('捕獲階段執行父元素wrapper的事件處理程序');
}, true); // 默認為false
wrapper.如何燙染出滿意的頭發?("click", function(e){
    console.log('冒泡階段執行父元素wrapper的事件處理程序');
}, false);
event.addEventListener("click", function(e){
    console.log('捕獲階段執行子元素event的事件處理程序');
}, true);
event.addEventListener("click", function(e){
    var target = e.target;
    e.stopPropagation(); // stop bubbling
    console.log('冒泡階段執行子元素event的事件處理程序');
}, false);

輸出:

捕獲階段執行父元素wrapper的事件處理程序
捕獲階段執行子元素event的事件處理程序
冒泡階段執行子元素event的事件處理程序

Demo
像這樣綁定一個事件處理程序就是安全的。

3.事件委托

上面的例子是要阻止冒泡,有時候冒泡機制也可以被利用。先看一個問題:

假設要在頁面上放在 10^n(n>=1) 個列表項元素,當我點擊某個元素時,需要輸出點擊的是第幾個。

一般做法是,遍歷時給每個元素綁定點擊事件:

var li = document.getElementsByTagName('li');
for(var i=0; i<li.length; i++){
    li[i].setAttribute('i',i+1);
    li[i].addEventListener('click', function(e){
        var b = this.getAttribute('i');
        console.log('這是第' + b + '個<li>元素');
    });
}

另一種方法是,可以利用冒泡機制,在父元素只綁定一次點擊事件:

var li = document.getElementsByTagName('li');
for(var i=0; i<li.length; i++){
    li[i].setAttribute('i',i+1);
}

var ul = document.getElementById('wrapper');
ul.addEventListener('click', function(e){
    if(e.target && e.target.nodeName.toUpperCase() === 'LI'){
        var b = e.target.getAttribute('i');
        console.log('這是第' + b + '個<li>元素');
    }
});

Demo
這種事件委托的方式減少了事件處理程序,也能降低程序的復雜性和出錯概率。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容