javascript
JavaScript事件的捕获阶段(Capture phase)
最近項(xiàng)目中遇到一個(gè)實(shí)際問(wèn)題,自己搞了半天沒(méi)解決,最后在StackOverflow上求助,不到3分鐘就得到了解決方案,而且答案意外的很簡(jiǎn)單——加一個(gè)參數(shù)。
場(chǎng)景是這樣的:有一個(gè)表格(事實(shí)上這樣的表格在這個(gè)系統(tǒng)很多),每一行(row)是顧客的信息(名字,訂單號(hào),付款金額...),要求點(diǎn)擊表格行(tr元素)打開(kāi)訂單詳情,點(diǎn)擊顧客名字(包裹在一個(gè)a元素里,a在td里)時(shí)打開(kāi)顧客信息頁(yè)面,不打開(kāi)訂單詳情。這不簡(jiǎn)單直接么,jQuery把a(bǔ)元素和tr元素的事件都委托給tbody或者table不就可以了么?
$('tbody').on('click', 'tr', function() {alert('order detail...')}).on('click', 'a', function(e) {e.stopPropagation()alert('contact detail')})如果只是在當(dāng)前單個(gè)頁(yè)面的代碼里寫(xiě)這段,沒(méi)問(wèn)題。但因?yàn)轫?xiàng)目很多頁(yè)面都有這樣包含"<a>顧客名字</a>"的地方,于是我干脆在一個(gè)每個(gè)頁(yè)面都會(huì)引用的js文件中加了一段把這樣的a元素(元素類(lèi)名取為“gotocontact”)的handler委托給document元素的代碼,如下(這里假設(shè)a元素是葉子元素,即不會(huì)有子元素):
// 因?yàn)榇藭r(shí)還沒(méi)引入jQuery,所以用了原生方法 document.addEventListener('click', function(e) {var el = e.srcElement || e.target;if (el && el.classList && el.classList.contains('gotocontact')) {e.stopPropagation();alert('going to contact...')} })然后在那個(gè)頁(yè)面的代碼里把tr元素的handler委托給tbody(id為“l(fā)ist”):
$('#list').on('click', 'tr', function() {alert('order detail...')})結(jié)果這樣是有問(wèn)題的,當(dāng)點(diǎn)擊a元素時(shí)兩個(gè)handler都會(huì)觸發(fā),不符合預(yù)期。
stackoverflow上的答案是在addEventListener參數(shù)里添加第三個(gè)參數(shù):
document.addEventListener('click', function(e) {var el = e.srcElement || e.target;if (el && el.classList && el.classList.contains('gotocontact')) {e.stopPropagation();alert('going to contact...')} }, true) // ^^^^ add third parameter?問(wèn)題解決。一個(gè)參數(shù)就解決了?!
知道addEventListener有這個(gè)參數(shù)選項(xiàng)(MDN),但是根本不知道起什么作用。
然后開(kāi)始搜索,看到了這篇文章,算是講得比較通俗易懂的:
W3C標(biāo)準(zhǔn)中DOM事件有3個(gè)階段:capture phase(捕獲階段), target phase(目標(biāo)階段), bubble phase(冒泡階段)。而常用的on<event> 和?addEventListener(event, handler) (注意沒(méi)有第三個(gè)參數(shù))只會(huì)作用于后兩個(gè)階段。如下圖(來(lái)自W3C):
把W3C對(duì)這張圖的說(shuō)明簡(jiǎn)單翻譯下搬過(guò)來(lái):
事件生成后,事件的傳播路徑(propagation path)先被確定下來(lái),路徑是一個(gè)有序列表,列表中最后一個(gè)元素是目標(biāo)元素,往前依次是目標(biāo)元素的父元素、祖先元素,一直到Window對(duì)象。接下來(lái)事件開(kāi)始傳播(propagate):
1.捕獲階段。事件對(duì)象從Window對(duì)象開(kāi)始沿傳播路徑向下,依次經(jīng)過(guò)各元素傳播至目標(biāo)元素的父元素;
2.目標(biāo)階段。事件對(duì)象到達(dá)目標(biāo)元素;
3.冒泡階段。事件對(duì)象從目標(biāo)元素的父元素開(kāi)始沿傳播路徑向上,依次經(jīng)過(guò)各元素傳播至Window對(duì)象。
?
照著圖拿本例來(lái)看。沒(méi)加第三個(gè)參數(shù)時(shí),兩個(gè)handler都只在冒泡階段起作用(jQuery的on方法就是addEventListener不加第三個(gè)參數(shù)的包裝)。當(dāng)我點(diǎn)擊a.gotocontact時(shí),事件對(duì)象從Window到達(dá)a標(biāo)簽元素這段時(shí)間內(nèi)都沒(méi)有觸發(fā)handler。事件對(duì)象到達(dá)tbody#list時(shí),觸發(fā)其handler,alert('order detail...'),而此處沒(méi)有stopPropagation(也不能有,否則事件傳播不到document,document的handler就不會(huì)觸發(fā))。之后到達(dá)document,觸發(fā)handler,alert('going to contact'),而此處的stopPropagation也只是不讓事件傳到最后的Window對(duì)象,事件基本走完了傳播路徑的一個(gè)往返。
加上第三個(gè)參數(shù)時(shí),addEventListener添加的handler會(huì)在捕獲階段起作用。事件傳播到document元素時(shí)觸發(fā)handler,這里的stopPropagation停止事件進(jìn)一步傳播。事件剛走了一步就被停止傳播,到不了tbody#list,達(dá)到預(yù)期效果。
?
?
?參考:
1.?Bubbling and capturing
2. UI Events
3. Event | MDN
轉(zhuǎn)載于:https://www.cnblogs.com/Monkey-D-Pixel/p/10070331.html
總結(jié)
以上是生活随笔為你收集整理的JavaScript事件的捕获阶段(Capture phase)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 西方:东风21D打击航母是鸡肋
- 下一篇: 辽宁舰360°转弯我国航母还能上岸机动