javascript事件冒泡詳解和捕獲、阻止方法
一、事件的發(fā)生順序
這個問題的起源非常簡單,假設你在一個元素中又嵌套了另一個元素
-----------------------------------
| element1 |
| ------------------------- |
| |element2 | |
| ------------------------- |
| |
-----------------------------------
:并且兩者都有一個onClick事件處理函數(shù)(event handler)。如果用戶單擊元素2,則元素1和元素2的單擊事件都會被觸發(fā)。但是哪一個事件先被觸發(fā)?哪一個事件處理函數(shù)會被首先執(zhí)行?換句話說,事件的發(fā)生順序到底如何?
二、兩種模型
不出所料,在那些“不堪回首”(瀏覽器大戰(zhàn))的日子里,Netscape和微軟有兩種截然不同的處理方法:
Netscape主張元素1的事件首先發(fā)生,這種事件發(fā)生順序被稱為捕獲型
微軟則保持元素2具有優(yōu)先權,這種事件順序被稱為冒泡型
這兩種事件順序是截然相反的。Explorer瀏覽器只支持冒泡事件,Mozilla,Opera7和Konqueror兩者都支持。而更古老的opera和iCab兩者都不支持
三、捕獲型事件
當你使用捕獲型事件時
| |
---------------| |-----------------
| element1 | | |
| -----------| |----------- |
| |element2 \ / | |
| ------------------------- |
| Event CAPTURING |
-----------------------------------
:元素1的事件處理函數(shù)首先被觸發(fā),元素2的事件處理函數(shù)最后被觸發(fā)
四、冒泡型事件
當你使用冒泡型事件時
/ \
---------------| |-----------------
| element1 | | |
| -----------| |----------- |
| |element2 | | | |
| ------------------------- |
| Event BUBBLING |
-----------------------------------
:元素2 的處理函數(shù)首先被觸發(fā),元素1其次
五、W3C 模型
W3c明智的在這場爭斗中選擇了一個擇中的方案。任何發(fā)生在w3c事件模型中的事件,首是進入捕獲階段,直到達到目標元素,再進入冒泡階段
| | / \
-----------------| |--| |-----------------
| element1 | | | | |
| -------------| |--| |----------- |
| |element2 \ / | | | |
| -------------------------------- |
| W3C event model |
------------------------------------------
為一個web開發(fā)者,你可以選擇是在捕獲階段還是冒泡階段綁定事件處理函數(shù),這是通過addEventListener()方法實現(xiàn)的,如果這個函數(shù)的最后一個參數(shù)是true,則在捕獲階段綁定函數(shù),反之false,在冒泡階段綁定函數(shù)。
假設你要做
element1.addEventListener('click',doSomething2,true)
element2.addEventListener('click',doSomething,false)
如果用戶單擊元素2,則接下來會發(fā)生:
(事件在這里就像一個觀光客,由外至內(nèi)游覽,逐漸接近被觸發(fā)的主要元素,然后又反向離開)
1.單擊事件首先進入捕獲階段開始(逐漸接近元素2的方向)。查看元素2的祖先元素中是否有在捕獲階段有onclick處理函數(shù)的
2.發(fā)現(xiàn)元素1有一個,于是doSomething2被執(zhí)行
3.事件檢查到目標自己(元素2),捕獲階段沒有發(fā)現(xiàn)更多的處理函數(shù)了。事件開始進入冒泡階段,想當然執(zhí)行doSomething(),這個綁定于元素2冒泡階段的函數(shù)。
4.事件向遠離元素2的方向,查看是否有任何祖先元素在冒泡階段綁定了一個處理函數(shù)。沒有這樣的情況,所以什么也沒有發(fā)生
相反的情況是:
element1.addEventListener('click',doSomething2,false)
element2.addEventListener('click',doSomething,false)
現(xiàn)在如果用戶點擊元素2會發(fā)生:
1.單擊事件進入捕獲階段。查看元素2的祖先元素中是否有在捕獲階段有onclick處理函數(shù)的,結果一無所獲
2.事件檢查到目標自己。事件開始進入冒泡階段,并且執(zhí)行綁定于元素2冒泡階段的函數(shù)。doSomething()
3.事件開始遠離目標,檢查元素2的祖先元素中是否有在冒泡階段綁定了處理函數(shù)的
4.發(fā)現(xiàn)了一個,于是元素1的doSomething2()被執(zhí)行
六、兼容性和傳統(tǒng)模式
在支持w3c dom(文檔對象模型) 的瀏覽器中,傳統(tǒng)的事件綁定方法是
默認被視為在綁定于冒泡階段
七、使用冒泡型事件
很少的開發(fā)人員會有意識的去使用冒泡型事件或者捕獲型事件。在他們今天制作的網(wǎng)頁中,沒有必要讓一個事件因為冒泡而被好幾個函數(shù)處理。但是有時用戶通常會很疑惑,因為在他們只點擊了一次鼠標之后出現(xiàn)了許多種情況(多個函數(shù)被執(zhí)行,因為冒泡)。而大多數(shù)情況下你還是希望你的處理函數(shù)相互獨立的。當用戶點擊了某一個元素,發(fā)生什么,點擊另一個元素,又對應發(fā)生些什么,相互獨立,而不因為冒泡連鎖。
八、一直在發(fā)生
首先你要明白的是事件捕獲或者冒泡一直在發(fā)生。如果你給整個頁面文檔的定義一個通用onclick處理函數(shù)
document.onclick = doSomething;
if (document.captureEvents) document.captureEvents(Event.CLICK);
在頁面上單擊任何元素的單擊事件,最終會冒泡至頁面最高文檔層,因此觸發(fā)那個通用的處理函數(shù),除非之前一個處理函數(shù)明確的指出終止冒泡,這樣才冒泡才不會傳播到整個文檔層面
對上面代碼第二句的補充:
>>>先說IE
object.setCapture() 當一個object的被 setCapture 后,他的方法將會被繼承到整個文檔進行捕獲。
當不需要把方法繼承到整個文檔捕獲時,要用 object.releaseCapture()
>>>others
Mozilla 也有類似的功能,方法稍微不同
window.captureEvents(Event.eventType)
window.releaseEvents(Event.eventType)
>>>example
//若加上下面這句話,則方法會被繼承到document(或者window,不同瀏覽器不同)來捕獲
obj.captureEvents(Event.click); //FF
obj.setCapture() //IE
九、用法
因為任何事件傳播終止于頁面文檔(這個最高層),這使默認的事件處理函數(shù)變得可能,假設你有這樣一個頁面
------------------------------------
| document |
| --------------- ------------ |
| | element1 | | element2 | |
| --------------- ------------ |
| |
------------------------------------
element1.onclick = doSomething;
element2.onclick = doSomething;
document.onclick = defaultFunction;
現(xiàn)在如果用戶單擊元素1或者元素2,doSomething()將被執(zhí)行。如果你愿意的話,如果你不想讓事件冒泡至執(zhí)行defaultFunction(),你可以在這里阻止事件冒泡向上傳播,。但是如果用戶點擊頁面上的其他部位,defaultFunction()還是會被執(zhí)行。這樣的效果或許有時能用的上。
設置頁面——使處理函數(shù)有范圍較大的觸發(fā)面積,在“拖拽效果”腳本中是必須的。一般來說在某一個元素層上發(fā)生 mousedown事件意味著選擇了這個元素,并且使它能夠響應mousemove事件。雖然mousedown通常綁定于這個元素層上以避免瀏覽器bug,但是其他兩者的事件函數(shù)的范圍必須是整個頁面(?)
記住瀏覽器學的第一法則(First Law of Browserology)是:一切皆有可能(anything can happen),并且是在你起碼有點準備的時候。所以有可能發(fā)生的是,用戶拖拽時,大幅度在頁面上移動他的鼠標,腳本卻不能在大幅度中做出反應,以至于鼠標也就不再停留在元素層上了
1.如果onmouseover處理函數(shù)綁定在元素層上,這個元素層不會再對鼠標的移動有任何反應,這會讓用戶覺得奇怪
2.如果onmouseup處理函數(shù)綁定在元素層上,事件也不能被觸發(fā),后果是,用戶想放下這個元素層后,元素層持續(xù)對鼠標移動做出反應。這會引起(用戶)更多的迷惑(?)
所以在這個例子中,事件冒泡非常的有用,因為將你的處理函數(shù)放在頁面層能保證他們一直能被執(zhí)行
十、把它給關了(阻止事件冒泡)
但是一般情況下,你會想關了所有的冒泡和捕獲以保證函數(shù)之間不會打擾到對方。除此之外,如果你的文檔結構相當?shù)膹碗s(許多table之間相互嵌套或者諸如此類),你也會為了節(jié)省系統(tǒng)資源,而關閉冒泡。此時瀏覽器不得不檢查目標元素的每一個祖先,看是否它有一個處理函數(shù)。即使一個都沒有找到,剛剛的搜索同樣花費不少時間
在微軟的模型中,你必須設置事件的cancelBubble的屬性為true
在w3c模型中你必須調(diào)用事件的stopPropagation()方法
這會阻止所有冒泡向外傳播。而作為跨瀏覽器解決方案應該這么作:
function doSomething(e)
{
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
在支持cancelBubble屬性的瀏覽器中設置cancelBubble無傷大雅。瀏覽器會聳一聳肩然后創(chuàng)造一個這個屬性。當然這也并不能真正的取消冒泡,但至少能保證這條命令是安全正確的
十一、currentTarget
像我們之前看到的一樣,一個事件用target或者是srcElement屬性用來表示事件究竟發(fā)生在哪個目標元素上(即用戶最初點擊的元素)。在我們的例子中是元素2,因為我們單擊了它。
非常重要的是,要明白在捕獲或者冒泡階段的目標元素是不變的,它始終與元素2相關聯(lián)。
但是假設我們綁定了以下函數(shù)
element2.onclick = doSomething;
如果用戶單擊元素2, doSomething()會被執(zhí)行兩次。但是你怎么知道哪個html元素正在響應這個事件?target/srcElement也沒有給出線索,但人們總是更傾向于元素2,因為它是引起事件的原因(因為用戶點擊的是它)。
為了解決這個問題,w3c 增加了currentTarget這個屬性,它就指向正在處理事件的元素:這恰是我們需要的。很不幸的是微軟模型中并沒有相似的屬性
你也可以使用”this”關鍵字。在上面的例子中,它相當于正在處理事件的html元素,就像currentTarget。
十二、微軟模型的問題
但是當你使用微軟事件綁定模型時,this關鍵字并不相當于HTML元素。聯(lián)想缺少類似currentTarget屬性的微軟模型(?)——按上面的代碼操作的話,你這么做便意味著:
element2.attachEvent('onclick',doSomething)
你無法確切知道哪一個HTML元素正在負責處理事件,這是微軟事件綁定模型最嚴重的問題,對我來說,這也是我從不使用它的原因,哪怕是在開發(fā)僅供Windows下的IE的應用程序
我希望能夠盡快增加currentTarget類似的屬性——或者遵循標準?web開發(fā)者們需要這些信息
后記:
因為沒有實戰(zhàn)過javascript,所以這篇文章有些地方我并不是很理解,只能硬生的翻譯出來,比如談拖拽效果的那一段,如果大家有什么補充和疑問可以留言給我,謝謝支持啦!
PS:這里再為大家推薦一款關于JS事件的在線查詢工具,歸納總結了JS常用的事件類型與函數(shù)功能:
javascript事件與功能說明大全:
相關文章
解決javascript:window.close()在chrome,Firefox下失效的問題
本篇文章是對javascript:window.close()在chrome,Firefox下失效問題的解決方法進行了分析介紹。需要的朋友參考下2013-05-05javascript實現(xiàn)多欄閉合展開式廣告位菜單效果實例
這篇文章主要介紹了javascript實現(xiàn)多欄閉合展開式廣告位菜單效果,可實現(xiàn)類似手風琴切換展示效果的功能,具有一定參考借鑒價值,需要的朋友可以參考下2015-08-08js實現(xiàn)div模擬模態(tài)對話框展現(xiàn)URL內(nèi)容
這篇文章主要介紹了js實現(xiàn)div模擬模態(tài)對話框展現(xiàn)URL內(nèi)容的功能,涉及javascript動態(tài)操作頁面元素樣式與ajax調(diào)用的相關技巧,需要的朋友可以參考下2016-05-05用js將long型數(shù)據(jù)轉換成date型或datetime型的實例
下面小編就為大家?guī)硪黄胘s將long型數(shù)據(jù)轉換成date型或datetime型的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07