DOM事件流(event flow )存在三個階段:事件捕獲階段、處於目標階段、事件冒泡階段。
事件捕獲(event capturing):通俗的理解就是,當鼠標點擊或者觸發dom事件時,瀏覽器會從根節點開始由外到內進行事件傳播,即點擊了子元素,如果父元素通過事件捕獲方式注冊了對應的事件的話,會先觸發父元素綁定的事件。
事件冒泡(dubbed bubbling):與事件捕獲恰恰相反,事件冒泡順序是由內到外進行事件傳播,直到根節點。
無論是事件捕獲還是事件冒泡,它們都有一個共同的行為,就是事件傳播,它就像一跟引線,只有通過引線才能將綁在引線上的鞭炮(事件監聽器)引爆,試想一下,如果引線不導火了,那鞭炮就只有一響了!!!
dom標准事件流的觸發的先后順序為:先捕獲再冒泡,即當觸發dom事件時,會先進行事件捕獲,捕獲到事件源之后通過事件傳播進行事件冒泡。不同的瀏覽器對此有着不同的實現,IE10及以下不支持捕獲型事件,所以就少了一個事件捕獲階段,IE11、Chrome 、Firefox、Safari等瀏覽器則同時存在。
說到事件冒泡與捕獲就不得不提一下兩個用於事件綁定的方法addEventListener、attachEvent。當然還有其它的事件綁定的方式這里不做介紹。
addEventListener(event, listener, useCapture)
·參數定義:event---(事件名稱,如click,不帶on),listener---事件監聽函數,useCapture---是否采用事件捕獲進行事件捕捉,
默認為false,即采用事件冒泡方式
addEventListener在 IE11、Chrome 、Firefox、Safari等瀏覽器都得到支持。
attachEvent(event,listener)
·參數定義:event---(事件名稱,如onclick,帶on),listener---事件監聽函數。
attachEvent主要用於IE瀏覽器,並且僅在IE10及以下才支持,IE11已經廢了這個方法了(微軟還是挺識趣的,慢慢向標准靠攏)。
說了一籮筐定義,下面就用上面這兩個方法通過栗子來解釋一下事件捕獲與事件冒泡的具體表現行為差異。
事件冒泡
栗1:
<html lang="zh-cn"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>js事件機制</title> <style> #parent{ width: 200px; height:200px; text-align: center; line-height: 3; background: green; } #child{ width: 100px; height: 100px; margin: 0 auto; background: orange; } </style> </head> <body> <div id="parent"> 父元素 <div id="child"> 子元素 </div> </div> <script type="text/javascript"> var parent = document.getElementById("parent"); var child = document.getElementById("child"); document.body.addEventListener("click",function(e){ console.log("click-body"); },false); parent.addEventListener("click",function(e){ console.log("click-parent"); },false); child.addEventListener("click",function(e){ console.log("click-child"); },false); </script> </body> </html>
通過"addEventListener"方法,采用事件冒泡方式給dom元素注冊click事件,點擊子元素會發生什么呢?如果你對事件冒泡有一定了解的話那你肯定知道上面的代碼會輸出的順序,沒錯,如下圖所示:
事件觸發順序是由內到外的,這就是事件冒泡,雖然只點擊子元素,但是它的父元素也會觸發相應的事件,其實這是合理的,因為子元素在父元素里面,點擊子元素也就相當於變相的點擊了父元素,這樣理解對吧?
這里有同學可能要問了,如果點擊子元素不想觸發父元素的事件怎么辦?肯定可以的,那就是停止事件傳播---event.stopPropagation();
修改栗1的代碼,在子元素的監聽函數中加入停止事件傳播的操作,栗2
child.addEventListener("click",function(e){ console.log("click-child"); e.stopPropagation(); },false);
在點擊子元素的時候就只彈出了子元素那條信息,父元素的事件沒有觸發,因為事件已經停止傳播了,冒泡階段也就停止了。
事件冒泡差不多就講述完了,別急,捕獲還沒說呢!
事件捕獲
栗3,修改栗子1中的代碼,給parent元素注冊一個捕獲事件,如下
var parent = document.getElementById("parent"); var child = document.getElementById("child"); document.body.addEventListener("click",function(e){ console.log("click-body"); },false); parent.addEventListener("click",function(e){ console.log("click-parent---事件傳播"); },false);
//新增事件捕獲事件代碼 parent.addEventListener("click",function(e){ console.log("click-parent--事件捕獲"); },true); child.addEventListener("click",function(e){ console.log("click-child"); },false);
如果你看明白了我前面說的那些,你就知道這個栗子的輸出順序了。
父元素通過事件捕獲的方式注冊了click事件,所以在事件捕獲階段就會觸發,然后到了目標階段,即事件源,之后進行事件傳播,parent同時也用冒泡方式注冊了click事件,所以這里會觸發冒泡事件,最后到根節點。這就是整個事件流程。
上面介紹了事件冒泡、事件捕獲、事件傳播,下面講一下如果通過以上三個知識點進行事件委托
委托在JQuery中已經得到了實現,即通過$(selector).on(event,childSelector,data,function,map)實現委托,一般用於動態生成的元素,當然JQuery也是通過原聲的js去實現的,下面舉一個簡單的栗子,通過js實現通過parent元素給child元素注冊click事件
var parent = document.getElementById("parent"); var child = document.getElementById("child"); parent.onclick = function(e){ if(e.target.id == "child"){ console.log("您點擊了child元素") } }
雖然沒有直接只child元素注冊click事件,可是點擊child元素時卻彈出了提示信息。
到這里是不是對js的事件機制有一定的了解了呢?感覺有幫助的話就看看下面的小黃臉,你懂得哦!
如有錯誤,歡迎指正
如有問題,歡迎提問
作者:木法傳