事件流描述的是從頁面中接受事件的順序。然而ie和netscape分別提出了徹底相反的的概念:事件冒泡和事件捕獲。下面就說說這兩種事件流:html
事件冒泡,就是說時間開始時由具體的元素接受,而後逐級向上傳播到較爲不具體的節點。看看下面的圖就比較清楚了:瀏覽器
好比說在圖中的<div>元素中添加一個click事件,那麼這個<div>元素就是咱們的單擊事件,而後click事件就會沿DOM樹向上傳播,在每一級節點上都會發生,直到傳播到document對象。看看下面的實例:函數
在理解實例事前先說說實例用到的核心操做:addEventListener(),這是DOM2事件定義的方法,用於處理添加指定事件處理程序的操做,有添加的方法固然也就會有刪除事件處理程序的方法removeEventListener()。全部的DOM節點中包含了這兩個方法,它們都接受3個參數:要處理的事件名、做爲事件處理程序的函數和一個布爾值。若是布爾值參數是false,表示在冒泡階段調用事件處理程序;若是是true,表示在捕獲階段調用事件處理程序(稍後會說到事件捕獲),布爾值默認參數是false。flex
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> *{ margin: 0; padding: 0; } html, body { height: 100%; } body{ display: flex; justify-content: center; align-items: center; } .box{ width: 200px; height: 200px; background-color: green; display: flex; align-items: center; justify-content: center; } .inner{ width: 100px; height: 100px; background-color: blue; align-self: flex-end; } .box3{ width: 50px; height: 50px; background-color: red; align-self: flex-end; } </style> </head> <body> <div class="box"> 外部盒 <div class="inner">inner <div class="box3">box3 </div> </div> </div> <script> var oBox = document.querySelector('.box'); var oInner = document.querySelector('.inner'); oBox.addEventListener('click',function(e){ console.log('點擊了外部盒子'); },false); oInner.addEventListener('click',function(e){ console.log('點擊了inner'); },false); var box3 = document.querySelector('.box3'); box3.addEventListener('click',function(e){ console.log('點擊了box3'); },false); </script> </body> </html>
瀏覽器效果:ui
如今就說說實例中觸發事件的執行順序,挺有意思的,容易被繞進去:spa
點擊綠色區域code
輸出的值也就是一個了。htm
點擊藍色區域對象
輸出的值有兩個,這個爲何呢,緣由很簡單,藍色區域是在綠色區域內,藍色區域觸發click事件,在冒泡階段調用事件處理程序,點擊藍色區域,click事件沿DOM樹向上傳播,由類屬性爲inner的元素的向上傳播到類屬性爲box的元素,再依次向上傳播,故輸出的值爲兩個。注意其輸出的順序:先輸出自身元素觸發事件中的值,而後再是父元素事件中的值。由內向外,這也就是事件冒泡的核心內容了。事件
點擊紅色區域
輸出的值有三個,緣由就再也不解釋了,緣由和上面的同樣,同時也要注意下它們的輸出順序。經過這個實例對事件冒泡有了解了,接下來咱們來深刻下:
在咱們實際開發中,是很不肯意事件在DOM層次中的傳播,很影響體驗效果,如上實例,觸發紅色區域的元素,我只想輸出一個值,然而恰恰輸出了三個值,是否是很煩啊???這個仍是有解決方案的,stopPropagation()方法用於當即中止事件在DOM層次中的傳播,即取消進一步的事件冒泡或捕獲。看下面的例子(html代碼與上面實例同樣):
紅色區域中止傳播
<script> var oBox = document.querySelector('.box'); var oInner = document.querySelector('.inner'); oBox.addEventListener('click',function(event){ console.log('點擊了外部盒子'); },false); oInner.addEventListener('click',function(event){ console.log('點擊了inner'); },false); var box3 = document.querySelector('.box3'); box3.addEventListener('click',function(event){ event.stopPropagation(); console.log('點擊了box3'); },false); </script>
先說說上面的代碼中涉及到的一個局部變量event,相信不少人對這個變量都不是很理解,我剛接觸的時候也不是很理解這個變量,爲何會有這個變量,這個變量幹嗎用的,怎麼還能夠調用方法和屬性,好神奇啊!!!如今我就來講說這個局部變量,這個變量就是事件對象,在觸發DOM上的某個事件時,會產生一個事件對象event,這個對象中包含了全部與事件有關的信息,包括了致使事件的元素、事件的類型以及其它與特定事件相關的信息。例如,鼠標操做致使的事件對象中,會包含鼠標位置的信息。
點擊紅色區域:
在紅色區域的click事件中加了stopPropagation(),這就當即中止了事件在DOM層次中的傳播,也就是取消進一步的事件冒泡,輸出的值也就只有一個了。
分別點擊藍色區域、綠色區域:
從輸出能夠看出,這兩部分沒有加stopPropagation(),事件依舊在DOM層次中的傳播,同時注意它們的輸出順序。
2.藍色區域中止傳播
<script> var oBox = document.querySelector('.box'); var oInner = document.querySelector('.inner'); oBox.addEventListener('click',function(event){ console.log('點擊了外部盒子'); },false); oInner.addEventListener('click',function(event){ event.stopPropagation(); console.log('點擊了inner'); },false); var box3 = document.querySelector('.box3'); box3.addEventListener('click',function(event){ console.log('點擊了box3'); },false); </script>
點擊藍色區域
成功取消了事件冒泡,輸出的值是一個即自己觸發輸出,而不是兩個值。
點擊紅色區域
這個輸出了兩個值,不是三個值,或許會感到很好奇,我取消的是藍色區域的事件捕獲,爲何click紅色區域只輸出兩個,其實正由於你在藍色區域內調用了stopPropagation()方法,其事件就會被中止冒泡,即便你點擊的是紅色區域,事件冒泡最終停留在藍色區域,傳播不到綠色區域,即輸出的值爲兩個。
點擊綠色區域
由於綠色區域是三個顏色區域最外層區域,其子元素的取消事件冒泡對本身沒有影響,再者其父元素沒有作任何事件監聽,故輸出的值僅僅是其自身事件觸發獲得。
3.綠色區域中止傳播
<script> var oBox = document.querySelector('.box'); var oInner = document.querySelector('.inner'); oBox.addEventListener('click',function(event){ event.stopPropagation(); console.log('點擊了外部盒子'); },false); oInner.addEventListener('click',function(event){ console.log('點擊了inner'); },false); var box3 = document.querySelector('.box3'); box3.addEventListener('click',function(event){ console.log('點擊了box3'); },false); </script>
分別點擊綠色區域,藍色區域,紅色區域:
獲得的效果和沒有作中止事件在DOM層中的傳播是同樣的,由於綠色區域是三個顏色區域最外層區域,其作取消事件冒泡,其子元素的事件冒泡對此沒有影響,再者其父元素沒有作任何事件監聽,故輸出的值和沒有作中止事件在DOM層中的傳播。
事件捕獲是不太具體的節點應該更早接受到事件,而具體的節點應該最後接受到事件。看看下面的圖:
如圖中,div元素增長一個click事件,在事件捕獲中,document對象首先接受到click事件,而後事件沿DOM樹依次向下,一直傳播到事件的實際目標,即div元素。看看下面的實例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> *{ margin: 0; padding: 0; } html, body { height: 100%; } body{ display: flex; justify-content: center; align-items: center; } .box{ width: 200px; height: 200px; background-color: green; display: flex; align-items: center; justify-content: center; } .inner{ width: 100px; height: 100px; background-color: blue; align-self: flex-end; } .box3{ width: 50px; height: 50px; background-color: red; align-self: flex-end; } </style> </head> <body> <div class="box"> 外部盒 <div class="inner">inner <div class="box3">box3 </div> </div> </div> <script> var oBox = document.querySelector('.box'); var oInner = document.querySelector('.inner'); oBox.addEventListener('click',function(event){ console.log('點擊了外部盒子'); },true); oInner.addEventListener('click',function(event){ console.log('點擊了inner'); },true); var box3 = document.querySelector('.box3'); box3.addEventListener('click',function(event){ console.log('點擊了box3'); },true); </script> </body> </html>
這份代碼與冒泡事件的代碼大體相同,有變化的就是添加事件處理程序的操做addEventListener(),其中的第三個參數改成了true,表示在捕獲階段調用事件處理程序。
分別點擊綠色區域、藍色區域、紅色區域時執行順序:
輸出的緣由相信你們都很清楚了,在這就再也不解釋了,須要注意的是控制檯值得輸出順序,和冒泡事件輸出順序相反的。
接下來再講講捕獲事件中中止事件在DOM層次中的傳播,取消進一步的事件捕獲,這和冒泡事件的很不同,主要緣由在於它們的傳播順序恰好相反,你們須要認真的思考,不要搞混淆了,如今就講講其中的一種比較容易出錯的,其它的你們能夠自行思考,完整代碼已經貼出來了,你們看看下面的實例:
<script> var oBox = document.querySelector('.box'); var oInner = document.querySelector('.inner'); oBox.addEventListener('click',function(event){ console.log('點擊了外部盒子'); },true); oInner.addEventListener('click',function(event){ event.stopPropagation(); console.log('點擊了inner'); },true); var box3 = document.querySelector('.box3'); box3.addEventListener('click',function(event){ console.log('點擊了box3'); },true); </script>
藍色區域的click事件中添加了stopPropagation()方法,取消進一步的事件捕獲,分別點擊不一樣區域,會產生什麼效果呢???你們往下看:
點擊綠色區域
這個緣由就再也不解釋了,你們都懂!!!
點擊藍色區域
輸出的值有兩個,這個你們容易搞錯,在藍色區域的click事件中添加了stopPropagation()方法,取消了事件捕獲,不該該只出現「點擊了inner」的嗎,怎麼還出現了「點擊了外部盒子」呢???這就又可能和事件冒泡搞混淆了,事件捕獲是由不太具體的節點應該更早接受到事件,而具體的節點應該最後接受到事件。簡單點來講是由外向內傳播。藍色區域的click事件中添加了stopPropagation()方法,只是該節點不會再向下傳播,該節點的父元素仍是會傳播到該節點的,因此輸出的值會有兩個,而不是一個。
點擊紅色區域
輸出的值有兩個,這個的緣由和上面的差很少,思想是同樣的,藍色區域的click事件中添加了stopPropagation()方法,該節點不會再向下傳播,點擊紅色區域,捕獲事件只能作到藍色區域就中止了。
「DOM2級事件」規定的事件流包括三個階段:事件捕獲階段、處於目標階段、事件冒泡階段。首先發生的是事件捕獲階段,爲截獲事件提供了機會。而後是實際的目標接收事件。最後一個階段是冒泡階段,能夠在這個階段對事件作出響應。
在DOM事件流中,實際的目標如div元素在捕獲階段不會接受到事件,這也就意味在捕獲階段,事件從document到html元素再到body後就中止了。下一階段是處於目標階段,因而事件在div上發生,並在事件處理中被當作冒泡階段的一部分,而後,冒泡階段發生,事件有傳播會文檔。
<script> var oBox = document.querySelector('.box'); var oInner = document.querySelector('.inner'); oBox.addEventListener('click',function(event){ console.log('點擊了外部盒子'); },false); oInner.addEventListener('click',function(event){ console.log('點擊了inner'); },true); var box3 = document.querySelector('.box3'); box3.addEventListener('click',function(event){ console.log('點擊了box3'); },true); </script>
分別點擊綠色區域、藍色區域、紅色區域
具體緣由就在過多的去解釋了,這個就留給你們思考,清楚的的理解事件流機制就能很快的得出答案。
何處調用事件處理程序,什麼時候在冒泡階段調用事件處理程序,什麼時候在捕獲階段調用事件處理程序。
終於寫完了!