一、由來
為什么要有事件捕獲和事件冒泡?
在主觀意義上,我點擊哪個元素,就只有哪個元素執(zhí)行事件,這是合理的。
但是呢,在DOM樹中,元素和元素的容器是一體的,就像人的四肢和人是一起的一樣。所以觸發(fā)了元素的事件,容器也勢必是觸發(fā)了事件的,就像你觸摸了某個人的手臂,勢必表示觸摸了這個人一樣。
為了表示這種“一體”的關(guān)系,于是有了事件捕獲和事件冒泡。
二、概念
那么什么是捕獲、什么是冒泡?
為了實現(xiàn)一體關(guān)系,有兩種觸發(fā)事件的順序。一種是由上至下,一種是由下至上,它們分別是捕獲和冒泡。
為了更好地理解,下面使用現(xiàn)實例子做類比。
冒泡的例子:
人的手臂被針刺到,會感覺到疼痛。
在這個例子中,針直接作用到人的手臂,手臂對被刺這個事件進(jìn)行響應(yīng),發(fā)送神經(jīng)信號。然后,大腦接收這個信號,產(chǎn)生痛覺。這樣從低級神經(jīng)到高級神經(jīng)的傳遞順序叫冒泡。也就是:
底層元素先對相應(yīng)事件做出響應(yīng),然后再到上層元素做出響應(yīng),依次傳遞,這樣的事件流叫冒泡。
然而,對于捕獲,并不符合這種“一體”關(guān)系,反而像是為了實現(xiàn)這種一體關(guān)系而建立的機制。這里使用的是層層詢問的方式。
一個按鈕被點擊了,根元素就知道了有元素被點擊了,但是不知道直接被點擊的是誰,但是自己肯定被點擊。先觸發(fā)自己的點擊事件,詢問下層元素,下層元素也先觸發(fā)點擊事件,再將詢問向下傳遞,直到目標(biāo)元素。
其中,不管捕獲還是冒泡,(直接被作用元素)目標(biāo)元素響應(yīng)事件的過程叫目標(biāo)過程。
三、圖解

四、執(zhí)行順序
現(xiàn)代瀏覽器都采用先捕獲后冒泡的順序,而在目標(biāo)階段,如果對同樣的觸發(fā)源,有多個響應(yīng)事件的話,按事件添加的先后順序執(zhí)行。
<div id="parent">
parent
<div id="child">child</div>
</div>
var parent = document.getElementById('parent');
var child = document.getElementById('child');
// parent的捕獲響應(yīng)
parent.addEventListener('click', function(){
console.log('parent: catch click');
}, true);
// parent的冒泡響應(yīng)
parent.addEventListener('click', function(){
console.log('parent: bubble click');
}, false);
// child的捕獲響應(yīng)
child.addEventListener('click', function(){
console.log('child: catch click');
}, true);
// child的冒泡響應(yīng)
child.addEventListener('click', function(){
console.log('child: bubble click');
}, false);
// child的on綁定
child.onclick = function(){
console.log('child: on click');
};
結(jié)果:
parent: catch click
child: catch click
child: bubble click
child: on click
parent: bubble click
然后,經(jīng)過將child的on綁定、捕獲、冒泡響應(yīng)事件調(diào)換順序,都可以發(fā)現(xiàn)在目標(biāo)過程并不符合先捕獲后冒泡的約束,而是誰先綁定,誰先觸發(fā)。當(dāng)然,這也是因為它是一個單獨的過程,獨立于捕獲和冒泡過程的原因,因為壓根沒關(guān)系嘛。
五、事件委托
其實事件委托就是,不在目標(biāo)階段對目標(biāo)事件進(jìn)行相應(yīng),改為放到捕獲和冒泡階段。而由于捕獲和冒泡階段發(fā)生在目標(biāo)元素的上層元素(目標(biāo)階段的捕獲和冒泡響應(yīng)事件個人認(rèn)為不應(yīng)該劃分到捕獲和冒泡階段比較好),就相當(dāng)于把事件響應(yīng)交給了上層元素,這就是事件委托,就這么簡單。
<ul id="ul">
<li>a</li>
<li>b</li>
</ul>
var ul = document.getElementById('ul');
ul.addEventListner('click', function(){
console.log('li被點擊');
}, false);
像上面這個例子,ul是被li撐開的,所以,點擊ul勢必會點擊到li。這樣,把li的響應(yīng)操作給ul是可以考慮的,這就是事件委托。
提示:由于將多個li元素監(jiān)聽事件改為只有一個ul元素監(jiān)聽事件,一定程度上提高了性能。