話很少說,先附上W3C連接:www.w3.org/TR/DOM-Leve…
javascript
如今,咱們就能夠知道,DOM事件流的三個階段是先捕獲階段,而後是目標階段,最後纔是冒泡階段。咱們時常面試所說的先捕獲後冒泡也由此而來。事件代理就是利用事件冒泡或事件捕獲的機制把一系列的內層元素事件綁定到外層元素。 html
實際操做中,咱們能夠經過 element.addEventListener() 設置一個元素的事件模型爲冒泡事件或者捕獲事件。
先來看一下 addEventListener 函數的語法:java
element.addEventListener(type, listener, useCapture)
複製代碼
<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時,執行結果以下:
面試
<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時,運行結果以下:
函數
咱們將上述的代碼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
咱們將上圖中c元素的事件註冊調換一下順序,就能夠知道目標階段執行事件的順序了,以下圖:
spa
咱們知道了事件冒泡和事件捕獲的原理,那麼對於事件委託就比較容易理解了。
重複一遍,**事件代理就是利用事件冒泡或事件捕獲的機制把一系列的內層元素事件綁定到外層元素。**至於爲何一般咱們說事件代理是利用事件冒泡的機制來實現的,只是你們習覺得常而已。代理
<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時,執行結果以下:
以上的東西總結起來就是有如下幾點:
一直不理解事件流的模型爲何是先捕獲後冒泡?直到看到第一張圖的那個Window,才明白爲何是先捕獲後冒泡了。由於Window對象是直接面向用戶的,那麼用戶觸發一個事件,如點擊事件,確定是用window對象開始的,因此天然就是先捕獲後冒泡啦。