事件捕獲、事件冒泡以及事件代理

DOM事件流

話很少說,先附上W3C連接:www.w3.org/TR/DOM-Leve…
javascript

image.png

上圖是W3C標準的DOM事件流模型圖,從圖中能夠看出,元素事件響應在DOM樹中是從頂層的Window開始「流向」目標元素(),而後又從目標元素「流向」頂層的Window。

一般,咱們將這種事件流向分爲三個階段: 捕獲階段,目標階段,冒泡階段

  • 捕獲階段是指事件響應從最外層的Window開始,逐級向內層前進,直到具體事件目標元素。在捕獲階段,不會處理響應元素註冊的冒泡事件。
  • 目標階段指觸發事件的最底層的元素,如上圖中的。
  • 冒泡階段與捕獲階段相反,事件的響應是從最底層開始一層一層往外傳遞到最外層的Window。


如今,咱們就能夠知道,DOM事件流的三個階段是先捕獲階段,而後是目標階段,最後纔是冒泡階段。咱們時常面試所說的先捕獲後冒泡也由此而來。事件代理就是利用事件冒泡或事件捕獲的機制把一系列的內層元素事件綁定到外層元素。 html

事件冒泡和事件捕獲

實際操做中,咱們能夠經過 element.addEventListener() 設置一個元素的事件模型爲冒泡事件或者捕獲事件。
先來看一下 addEventListener 函數的語法:java

element.addEventListener(type, listener, useCapture)
複製代碼
  • type
    監聽事件類型的字符串
  • listener
    事件監聽回調函數,即事件觸發後要處理的函數
  • useCapture
    默認值false,表示事件冒泡;設爲true時,表示事件捕獲

事件冒泡舉例

<div id="a" style="width: 100%; height: 300px;background-color: antiquewhite;">
	a
	<div id="b" style="width: 100%; height: 200px;background-color: burlywood;">
		b
		<div id="c" style="width: 100%; height: 100px;background-color: cornflowerblue;">
			c
		</div>
	</div>
</div>
<script> var a = document.getElementById('a') var b = document.getElementById('b') var c = document.getElementById('c') //註冊冒泡事件監聽器 a.addEventListener('click', () => {console.log("冒泡a")}) b.addEventListener('click', () => {console.log('冒泡b')}) c.addEventListener('click', () => {console.log("冒泡c")}) </script>
複製代碼

當咱們點擊c時,執行結果以下:
面試

image.png

冒泡事件的執行順序爲:c -> b -> a

事件捕獲舉例

<div id="a" style="width: 100%; height: 300px;background-color: antiquewhite;">
	a
	<div id="b" style="width: 100%; height: 200px;background-color: burlywood;">
		b
		<div id="c" style="width: 100%; height: 100px;background-color: cornflowerblue;">
			c
		</div>
	</div>
</div>
<script> var a = document.getElementById('a') var b = document.getElementById('b') var c = document.getElementById('c') //註冊捕獲事件監聽器 a.addEventListener('click', () => {console.log("捕獲a")}, true) b.addEventListener('click', () => {console.log('捕獲b')}, true) c.addEventListener('click', () => {console.log("捕獲c")}, true) </script>
複製代碼

當咱們點擊c時,運行結果以下:
函數

image.png

捕獲事件的執行順序爲:a -> b -> c

事件捕獲VS事件冒泡

咱們將上述的代碼a,b,c三個元素都註冊捕獲和冒泡事件,並以元素c做爲觸發事件的主體,即事件流中的目標階段。ui

<div id="a" style="width: 100%; height: 300px;background-color: antiquewhite;">
	a
	<div id="b" style="width: 100%; height: 200px;background-color: burlywood;">
		b
		<div id="c" style="width: 100%; height: 100px;background-color: cornflowerblue;">
			c
		</div>
	</div>
</div>
<script> var a = document.getElementById('a') var b = document.getElementById('b') var c = document.getElementById('c') a.addEventListener('click', () => {console.log("冒泡a")}) b.addEventListener('click', () => {console.log('冒泡b')}) c.addEventListener('click', () => {console.log("冒泡c")}) a.addEventListener('click', () => {console.log("捕獲a")}, true) b.addEventListener('click', () => {console.log('捕獲b')}, true) c.addEventListener('click', () => {console.log("捕獲c")}, true) </script>
複製代碼

a,b,c三個元素都是先註冊冒泡事件再註冊捕獲事件,當咱們點擊c時,執行結果又是如何?請看下圖。
url

image.png

從執行結果能夠看到,a,b兩個元素的事件響應都是先捕獲後冒泡的,但對於觸發事件的目標元素c,事件的響應並無遵循先捕獲後冒泡的規則,這是爲何?由於 目標元素是事件觸發主體處於事件流中的目標階段,處理事件的順序是根據註冊順序來執行的

咱們將上圖中c元素的事件註冊調換一下順序,就能夠知道目標階段執行事件的順序了,以下圖:
spa

image.png

對於非目標元素,若是咱們要先執行冒泡事件再執行捕獲事件,咱們能夠在註冊監聽器時經過暫緩執行捕獲事件,等冒泡事件執行完以後,在執行捕獲事件。

事件代理(事件委託)

咱們知道了事件冒泡和事件捕獲的原理,那麼對於事件委託就比較容易理解了。
重複一遍,**事件代理就是利用事件冒泡或事件捕獲的機制把一系列的內層元素事件綁定到外層元素。**至於爲何一般咱們說事件代理是利用事件冒泡的機制來實現的,只是你們習覺得常而已。代理

<ul id="item-list">
	<li>item1</li>
	<li>item2</li>
	<li>item3</li>
	<li>item4</li>
</ul>
複製代碼

對於上述的列表元素,咱們但願將用戶點擊了哪一個item打印出來,一般咱們能夠給每一個item註冊點擊事件監聽器,可是須要對每一個元素進行事件監聽器的註冊;可是經過事件代理,咱們能夠將多個事件監聽器減小爲一個,這樣就減小代碼的重複編寫了。
利用事件冒泡或事件捕獲實現事件代理:code

var items = document.getElementById('item-list');
//事件捕獲實現事件代理
items.addEventListener('click', (e) => {console.log('捕獲:click ',e.target.innerHTML)}, true);
//事件冒泡實現事件代理
items.addEventListener('click', (e) => {console.log('冒泡:click ',e.target.innerHTML)}, false);
複製代碼

當點擊列表中的item時,執行結果以下:

image.png


所以, 事件代理既能夠經過事件冒泡來實現,也能夠經過事件捕獲來實現

總結

以上的東西總結起來就是有如下幾點:

  • DOM事件流有3個階段:捕獲階段,目標階段,冒泡階段;三個階段的順序爲:捕獲階段——目標階段——冒泡階段;
  • 對於非目標階段的元素,事件響應執行順序遵循先捕獲後冒泡的原則;經過暫緩執行捕獲事件,能夠達到先冒泡後捕獲的效果;
  • 對於目標元素,事件響應執行順序根據的事件的執行順序執行;
  • 事件捕獲是從頂層的Window逐層向內執行,事件冒泡則相反;
  • 事件委託(事件代理)是根據事件冒泡或事件捕獲的機制來實現的。

題外話

一直不理解事件流的模型爲何是先捕獲後冒泡?直到看到第一張圖的那個Window,才明白爲何是先捕獲後冒泡了。由於Window對象是直接面向用戶的,那麼用戶觸發一個事件,如點擊事件,確定是用window對象開始的,因此天然就是先捕獲後冒泡啦。

相關文章
相關標籤/搜索