剛接觸 JS 的那個時候,啥也不懂,只想着如何利用 Google、百度到的函數來解決實際的問題,不會想到去一探究竟。javascript
漸漸的,對 JS 的語言的不斷深刻,有機會去了解一些原理性東西。最近在看 JQuery 源碼,感觸不少,總想着用原生的 JS 去實現本身的一個 JQuery 庫。說實在的,JQuery 裏面不少函數和思路,是千百開源工做者長期的貢獻,哪能是短期就能消化的了。html
最近再次碰到 addEventListener
函數(MDN 上關於 addEventListener 的介紹,很詳細),因爲以前並無弄懂第三個參數的含義,要麼默認值,要麼手動設置成 false。此次看了很多文章,完全把事件冒泡和捕獲弄懂。java
事件冒泡與捕獲是 DOM 中事件傳播的兩種方式,好比說對於註冊了相同事件的兩個 DOM 元素(簡單點就是兩個 div,一裏一外),當點擊裏層 div 的時候,這兩個事件誰先執行。程序員
冒泡事件,由裏向外,最裏層的元素先執行,而後冒泡到外層。瀏覽器
捕獲事件,由外向裏,最外層的元素先執行,而後傳遞到內部。dom
在 IE 9 以前是隻支持事件冒泡,IE 9(包括 IE 9) 以後和目前主流的瀏覽器都同時支持兩種事件。編輯器
如何設置,只需修改 addEventListener
的第三個參數,true 爲捕獲,false 爲冒泡,默認爲冒泡。函數
舉個簡單的例子,spa
<div> <span class="out"> <span class="in"></span> </span> </div> <script type="text/javascript"> var dom_out = document.getElementsByClassName('out')[0]; var dom_in = document.getElementsByClassName('in')[0]; dom_out.addEventListener('click',function(){ alert('out'); },false); dom_in.addEventListener('click',function(){ alert('in'); },false); </script>
在上面這個例子中,事件是按照冒泡來執行的,點擊裏層的 in
,會看到先 alert
的順序是先 "in" 後 "out",若是把事件改爲捕獲,alert
的順序又不同了。code
<script type="text/javascript"> var dom_out = document.getElementsByClassName('out')[0]; var dom_in = document.getElementsByClassName('in')[0]; dom_out.addEventListener('click',function(){ alert('out'); },true); dom_in.addEventListener('click',function(){ alert('in'); },true); </script>
上面這個例子是捕獲事件的例子,點擊 in
效果是否是不同呢?
之因此會有冒泡和捕獲事件(像 IE 9 以前的瀏覽器不支持捕獲事件,還真是反程序員),畢竟在實際中處理事情確定有個前後順序,要麼由裏向外,要麼由外向裏,二者都是必須的。
但有時候爲了兼容 IE 9 如下版本的瀏覽器,都會把第三個參數設置成 false 或者默認(默認就是 false)。
如今已經說清楚冒泡和捕獲,那麼若是同時出現冒泡和捕獲會出現什麼結果?
原來瀏覽器處理時間分爲兩個階段,捕獲階段和冒泡階段,
先執行捕獲階段,若是事件是在捕獲階段執行的(true 狀況),則執行;
而後是冒泡階段,若是事件是在冒泡階段執行的(false 狀況),則執行;
來看一看例子就知道了:
<div> <span class="s1">s1 <span class="s2">s2 <span class="s3">s3 </span> </span> </span> </div>
此次咱們設置三個 span,分別是 s1, s2, s3,而後設置 s1,s3 爲冒泡執行,s2 爲捕獲執行:
<script type="text/javascript"> var s1 = document.getElementsByClassName('s1')[0]; var s2 = document.getElementsByClassName('s2')[0]; var s3 = document.getElementsByClassName('s3')[0]; s1.addEventListener('click',function(){ alert('s1'); },false); s2.addEventListener('click',function(){ alert('s2'); },true); s3.addEventListener('click',function(){ alert('s3'); },false); </script>
從運行的效果來看,點擊 s3,依次 alert
s2 => s3 => s1,說明:
捕獲事件和冒泡事件同時存在的,並且捕獲事件先執行,冒泡事件後執行;
若是元素存在事件且事件的執行時間與當前邏輯一致(冒泡或捕獲),則執行。
固然,有時候咱們只想執行最內層或最外層的事件,根據內外層關係來把範圍更廣的事件取消掉(對於新手來講,不取消冒泡,很容易中招的出現 bug)。event.stopPropagation()
(IE 中window.event.cancelBubble = true
)能夠用來取消事件冒泡。
有時候對於瀏覽器的默認事件也須要取消,這時候用到的函數則是 event.preventDefault()
(IE 中window.event.returnValue = false
)。
那麼默認事件取消和中止冒泡有什麼區別呢?個人理解:瀏覽器的默認事件是指瀏覽器本身的事件(這不廢話嗎),好比 a 標籤
的點擊,表單的提交等,取消掉就不會執行啦;冒泡則取消的是由外向裏(捕獲)、由裏向外(冒泡),stop 以後,就不會繼續遍歷了。stackoverflow 上的解答
看下例子,依舊是上面那個例子,不過每一個函數都加了 中止冒泡:
s1.addEventListener('click',function(e){ e.stopPropagation(); alert('s1'); },false); s2.addEventListener('click',function(e){ e.stopPropagation(); alert('s2'); },true); s3.addEventListener('click',function(e){ e.stopPropagation(); alert('s3'); },false);
點擊的結果是:當點擊 s2 或 s3 的時候,都會 alert
s2,點擊 s1,彈出 s1。由於事件被取消的緣故,點擊 s3,執行 s2後就不會在向下執行了。
在看一個 preventDefault
的例子。
<div> <a href="/">點我回主頁</a> </div> <div> <a href="/" class="back">點我不回主頁</a> </div> <script type="text/javascript"> var back = document.getElementsByClassName('back')[0]; back.addEventListener('click', function(e){ e.preventDefault(); }); </script>
第二個連接是否是回不了主頁,由於瀏覽器的默認事件被取消了。
以上全部例子請在非低版本 IE 瀏覽器的環境下瀏覽 O_o
總結就補充兩個兼容 IE 的函數吧:
function stopBubble(e) { //若是提供了事件對象,則這是一個非IE瀏覽器 if ( e && e.stopPropagation ) //所以它支持W3C的stopPropagation()方法 e.stopPropagation(); else //不然,咱們須要使用IE的方式來取消事件冒泡 window.event.cancelBubble = true; } //阻止瀏覽器的默認行爲 function stopDefault( e ) { //阻止默認瀏覽器動做(W3C) if ( e && e.preventDefault ) e.preventDefault(); //IE中阻止函數器默認動做的方式 else window.event.returnValue = false; return false; }
共勉!
stackoverflow 什麼是事件冒泡和捕捉
stackoverflow stopPropagation 和 preventDefault 的區別
MDN addEventListener
javascript阻止事件冒泡和瀏覽器的默認行爲
因爲 SF 編輯器不支持上傳 demo,只能以圖片展現,歡迎來 個人博客 查看 demo。