討論的主要是兩個事件模型:IE事件模型與DOM事件模型
IE內核瀏覽器的事件模型是冒泡型事件(沒有捕獲事件過程),事件句柄的觸發順序是從ChildNode到ParentNode。
<div id="ancestor"> <button id="child"> Open the console and click me </button> </div>
以上的HTML代碼在IE內核下,事件是這樣傳播的:{
1、Button#child;
2、div#ancestor;
3、Body;
4、Document
}
DOM標准的瀏覽器事件是:捕獲事件和冒泡事件。
捕獲事件過程:{
1、Window
2、Document
3、Body
4、Div#ancestor
5、Button#child
}
冒泡事件過程:{
6、Div#ancestor
7、Body
8、Document
9、Window
}
當開發者在一個元素上注冊了事件后,這個事件的響應順序是從window(最頂層)開始一級一級的向下傳播,然后到了該元素后事件捕獲過程結束,事件開如冒泡,一級一級向父層元素冒泡(請注意第6步)。
當然,開發者可以很輕松的決定DOM標准的瀏覽器中的事件需要在哪個傳播過程觸發。
事件的注冊機制:
DOM標准的瀏覽器事件注冊方法是通過addEventListener方法注冊,而IE內核的瀏覽器則是通過attachEvent方法注冊。
這兩個方法的區別:
addEventListener方法帶有三個參數,分別是:eventType、handler、useCapture。
eventType不帶有on字符串;
handler參數是一個事件句柄,這個函數或方法帶有一個事件對象參數;
useCapture參數決定了事件句柄觸發在哪種事件傳播階段,如果useCapture為true則為捕獲階段,反之則為冒泡階段。
繼續看演示:
var ancestorHandler = function (e){ //...... }, childHandler = function (e){ //...... }; document.querySelector('#ancestor').addEventListener('click',ancestorHandler,false);//注意第三個參數 ,注冊了一個在冒泡階段觸發的事件句柄 document.querySelector('#child').addEventListener('click',childHandler,true);//注意第三個參數 ,注冊了一個在捕獲階段的事件句柄
當用戶在這個DIV元素上點擊時,事件的執行順序是childHandler、ancestorHandler。
原因:按鈕的事件是在捕獲階段觸發的,也就是從上到下,而DIV的事件是注冊在冒泡階段,也就是點擊了這個按鈕開始從這個按鈕的位置往上冒泡。
阻止事件的冒泡:
DOM事件對象提供了stopPropagation方法用於阻止事件流。
var ancestorHandler = function (e){ //...... }, childHandler = function (e){ e.stopPropagation(); //...... };
以上代碼在childHandler函數中添加了e.stopPropagation()代碼片段,它將阻止事件流,事件流包括捕獲階段及冒泡階段的事件流。
再修改上面的代碼如下:
var ancestorHandler = function (e){ //...... }, childHandler = function (e){ //...... }; document.querySelector('#ancestor').addEventListener('click',ancestorHandler,true);//注意第三個參數 document.querySelector('#child').addEventListener('click',childHandler,true);//注意第三個參數
以上的代碼產生的結果是:用戶在DIV元素上單擊時,將會依次觸發ancestorHandler、childHandler函數,為什么?因為我們將div#ancestor的事件注冊到捕獲階段了,也就是從上至下。當然了我們還可以阻止childHandler方法的執行。
以上代碼將阻止按鈕的事件觸發。當用戶點擊了DIV的區域,僅僅觸發ancestorHandler函數,因為阻止了事件流。
IE內核的瀏覽器中是如何注冊事件的。IE內核提供了attachEvent方法為元素注冊事件,注意該方法與DOM的addEventListener方法區別很大!該方法帶有兩個參數:
{
eventType 事件類型,請注意這個參數與addEventListener的eventType的區別,它必須帶有on;
handler 事件句柄 ,請注意attachEvent沒有提供事件捕獲階段的參數,IE內核的事件都是發生在冒泡階段!
}
var ancestorHandler = function (e){ //...... }, childHandler = function (e){ //...... }; document.getElementById('ancestor').attachEvent('onclick',ancestorHandler);//注意沒有第三個參數 document.getElementById('child').attachEvent('onclick',childHandler);//注意沒有第三個參數
以上代碼在IE中將為DIV元素和按鈕元素注冊了不同的事件。
另外還有一些注意事項:
1、DOM標准的addEventListener方法執行事件的順序是按照事件注冊的順序執行的。而attachEvent方法則相反–后注冊的事件先觖發,先注冊的事件后觸發。
2、DOM標准的瀏覽器文本節點也會冒泡,而IE內核的瀏覽器文本節點不會冒泡。
3、DOM標准的瀏覽器事件對象與IE內核的瀏覽器事件不同(具體請參閱http://www.quirksmode.org/js/introevents.html)。
4、DOM標准的瀏覽器事件卸載方式與IE內核的事件卸載方式不同。
1 object.removeEventListener(eventType,handler,useCapture);//DOM標准的事件卸載方式
2 object.detachEvent(eventType,handler);//IE內核的事件卸載方式
在DOM標准的事件卸載方式中需要注意的是:事件捕獲的參數。如果你的事件是注冊在捕獲階段,則卸載事件時,必須將其指定為捕獲階段(true),否則無法卸載;如果你的事件注冊在注冊在冒泡階段,則必須將其指定為冒泡階段(false),否則同樣無法卸載!
寫作不易,難免有疏漏和錯誤,還請慷慨指正,不錯請推薦
每天多學一點點 代碼少敲一點點