一、概述
事件流:事件流描述的是從頁(yè)面中接收事件的順序。
DOM事件流傳播的三個(gè)過(guò)程:
事件捕獲階段 ——》 處于目標(biāo)階段 ——》 事件冒泡階段。
事件冒泡:事件開(kāi)始時(shí)由最具體的元素接收,然后逐級(jí)向上傳播到較為不具體的元素
事件捕獲:不太具體的節(jié)點(diǎn)更早接收事件,而最具體的元素最后接收事件,和事件冒泡相反

支持W3C標(biāo)準(zhǔn)的瀏覽器在添加事件時(shí)用addEventListener(event,fn,useCapture)方法,基中第3個(gè)參數(shù)useCapture是一個(gè)Boolean值,用來(lái)設(shè)置事件是在事件捕獲時(shí)執(zhí)行,還是事件冒泡時(shí)執(zhí)行。而不兼容W3C的瀏覽器(IE)用attachEvent()方法,此方法沒(méi)有相關(guān)設(shè)置,不過(guò)IE的事件模型默認(rèn)是在事件冒泡時(shí)執(zhí)行的,也就是在useCapture等于false的時(shí)候執(zhí)行,所以把在處理事件時(shí)把useCapture設(shè)置為false是比較安全,也實(shí)現(xiàn)兼容瀏覽器的效果。
二、示例
假設(shè)一個(gè)元素div,它有一個(gè)下級(jí)元素p。
<div onclick="alert(1)">
<p onclick="alert(2)">元素</p>
</div>
這兩個(gè)元素都綁定了click事件,如果用戶點(diǎn)擊了p,它在div和p上都觸發(fā)了click事件,那這兩個(gè)事件處理程序哪個(gè)先執(zhí)行呢?事件順序是什么?
事件捕獲
當(dāng)你使用事件捕獲時(shí),父級(jí)元素先觸發(fā),子級(jí)元素后觸發(fā),即div先觸發(fā),p后觸發(fā)。
事件冒泡
當(dāng)你使用事件冒泡時(shí),子級(jí)元素先觸發(fā),父級(jí)元素后觸發(fā),即p先觸發(fā),div后觸發(fā)。
W3C模型
W3C模型是將兩者進(jìn)行中和,在W3C模型中,任何事件發(fā)生時(shí),先從頂層開(kāi)始進(jìn)行事件捕獲,直到事件觸發(fā)到達(dá)了事件源元素。然后,再?gòu)氖录赐线M(jìn)行事件冒泡,直到到達(dá)document。
【注】:當(dāng)兩個(gè)元素有包含關(guān)系并且事件相同時(shí)才會(huì)觸發(fā)冒泡和捕獲機(jī)制
另外:
1)、盡管“DOM2級(jí)事件”,即addEventListener(event,fn,useCapture)方法,標(biāo)準(zhǔn)規(guī)范明確規(guī)定事件捕獲階段不會(huì)涉及事件目標(biāo),但是在IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都會(huì)在捕獲階段觸發(fā)事件對(duì)象上的事件。所以結(jié)果就是有兩次機(jī)會(huì)在目標(biāo)對(duì)象上面操作事件。
2)、然而并非所有的事件都會(huì)經(jīng)過(guò)冒泡階段 。所有的事件都要經(jīng)過(guò)捕獲階段和處于目標(biāo)階段,但是有些事件會(huì)跳過(guò)冒泡階段:如,獲得輸入焦點(diǎn)的focus事件和失去輸入焦點(diǎn)的blur事件。
三、阻止事件流
為何阻止事件流:
如我們點(diǎn)擊p標(biāo)簽時(shí)希望只彈出1,但是點(diǎn)p的時(shí)候頁(yè)會(huì)觸發(fā)div上的點(diǎn)擊事件彈出2,這是我們不想要的效果,所以就需要阻止事件流
阻止冒泡
document.getElementsByTagName("p").addEventListener("click",function(event){
console.log("阻止冒泡");???
event.stopPropagation();
},false);
另外:
event.cancelBubble=true;? IE8以下版本瀏覽器支持的阻止事件冒泡方法
上面的event.stopPropagation();是其他瀏覽器支持的阻止事件冒泡方法
阻止捕獲
DOM3級(jí)新增事件stopImmediatePropagation()方法來(lái)阻止事件捕獲,另外此方法還可以阻止事件冒泡
document.getElementsByTagName("p").addEventListener("click",function(){????
console.log("阻止捕獲");????
event.stopImmediatePropagation();
},true);?
stopImmediatePropagation() 和 stopPropagation()的區(qū)別在哪兒呢?
后者只會(huì)阻止冒泡或者是捕獲。 但是前者除此之外還會(huì)阻止該元素的其他事件發(fā)生,但是后者就不會(huì)阻止其他事件的發(fā)生。
注意:在DOM事件流中,實(shí)際的目標(biāo)在捕獲階段不會(huì)接收到事件,下一個(gè)階段是處于目標(biāo)階段,這時(shí)事件被觸發(fā),最后進(jìn)入事件冒泡階段。我們認(rèn)為處于目標(biāo)階段是事件冒泡階段的一部分。
四、兩種事件流模型
1、所有現(xiàn)代瀏覽器都支持事件冒泡,但在具體實(shí)現(xiàn)中略有差別:
IE5.5及更早版本中事件冒泡會(huì)跳過(guò)<html>元素(從body直接跳到document)。
IE9、Firefox、Chrome、和Safari則將事件一直冒泡到window對(duì)象。
2、IE9、Firefox、Chrome、Opera、和Safari都支持事件捕獲。盡管DOM標(biāo)準(zhǔn)要求事件應(yīng)該從document對(duì)象開(kāi)始傳播,但這些瀏覽器都是從window對(duì)象開(kāi)始捕獲事件的。
3、由于老版本瀏覽器不支持,很少有人使用事件捕獲。建議使用事件冒泡。
五、事件代理
傳統(tǒng)的事件處理中,需要為每個(gè)元素添加事件處理器。js事件代理則是一種簡(jiǎn)單有效的技巧,通過(guò)它可以把事件處理器添加到一個(gè)父級(jí)元素上,從而避免把事件處理器添加到多個(gè)子級(jí)元素上。
target、this、currentTarget的區(qū)別
先訴重點(diǎn)理論:
1. target:觸發(fā)事件的某個(gè)具體對(duì)象,只會(huì)出現(xiàn)在事件流的目標(biāo)階段(誰(shuí)觸發(fā)誰(shuí)命中,所以肯定是目標(biāo)階段)
2. currentTarget:綁定事件的對(duì)象,恒等于this,可能出現(xiàn)在事件流的任意一個(gè)階段中
3. 通常情況下terget和currentTarget是一致的,我們只要使用terget即可,但有一種情況必須區(qū)分這三者的關(guān)系,那就是在父子嵌套的關(guān)系中,父元素綁定了事件,單擊了子元素(根據(jù)事件流,在不阻止事件流的前提下他會(huì)傳遞至父元素,導(dǎo)致父元素的事件處理函數(shù)執(zhí)行),這時(shí)候currentTarget指向的是父元素,因?yàn)樗?b>綁定事件的對(duì)象,而target指向了子元素,因?yàn)樗?b>觸發(fā)事件的那個(gè)具體對(duì)象,如下代碼和截圖所示:
<div id="one">
? <div id="three"></div>
</div>
one.addEventListener('click',function(e){
? ? console.log(e.target);? //three
? ? console.log(e.currentTarget);? //one
},false);

target:獲得觸發(fā)事件的標(biāo)簽,currentTarget:得到綁定事件的標(biāo)簽