討論的主要是兩個事件模型: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),否則同樣無法卸載!