事件捕獲瀏覽器
由網景最早提出,事件會從最外層開始發生,直到最具體的元素,也就是說假如父元素與子元素都綁定有點擊事件,又互相重疊,那麼先出發的會是父元素的事件,而後再傳遞到子元素。函數
事件冒泡spa
由微軟提出,事件會從最內從的元素開始發生,再向外傳播,正好與事件捕獲相反。代理
這兩個概念都是爲了解決頁面中事件流的發生順序,w3c採起了折中的辦法,制定了統一的標準:先捕獲再冒泡。code
addEventListen(event, function, useCapture)
添加事件的第三個參數默認值爲false,即默認使用事件冒泡,若爲true則使用事件捕獲的機制,如下爲示例:對象
當咱們使用默認事件註冊機制的時候,點擊child元素時,會前後輸出child,container,這就是事件冒泡機制:事件
container.addEventListener('click', () => console.log('container')) child.addEventListener('click', () => console.log('child'))
將第三個參數設爲true,點擊child元素時,輸出順序就會變爲container、child:ip
container.addEventListener('click', () => console.log('container'), true) child.addEventListener('click', () => console.log('child'), true)
倘若你但願點擊child只打印child,不觸發container的事件,咱們就須要用到stopPropagation
,它阻止捕獲和冒泡階段中當前事件的進一步傳播:get
container.addEventListener('click', () => console.log('container')) child.addEventListener('click', (e) => { e.stopPropagation(); console.log('child'); })
若你想要點擊child只打印container,就須要進行捕獲事件,並在container事件觸發時進行阻止捕獲:it
container.addEventListener('click', (e) => { e.stopPropagation(); console.log('container') }, true) child.addEventListener('click', () => console.log('child'), true)
與stopPropagation,還有一種事件方式叫作preventDefault,它的做用不是用於阻止冒泡,而是阻止瀏覽器默認行爲,如a標籤跳轉,表單提交等。
事件委託,又稱爲事件代理,通俗來說是將元素的事件函數處理交由其餘對象處理。它容許您避免向特定節點添加事件偵聽器,咱們這裏所談論的事件委託,與冒泡捕獲流程相關,所以事件委託在此場景指的是子對象的處理事件交由父對象處理。
當你須要爲一個動態的列表元素添加click事件時,若直接對列表項綁定事件,可能會重複註冊不少個事件監聽器。爲了解決上述問題,咱們能夠利用事件委託的思想,在父級註冊一個事件監聽器,統一進行子元素的事件處理。
爲了更好的說明,在這裏舉一個示例:
<ul class="list"> <li class="item" data-id="1">1</li> <li class="item" data-id="2">2</li> <li class="item" data-id="3">3</li> <li class="item" data-id="4">4</li> <li class="item" data-id="5">5</li> </ul>
爲了給每個li
綁定上事件,若採用一般的方法,可能咱們須要這樣寫代碼:
document.querySelectorAll('.item').forEach(item => { item.onclick = e => handleClick(e.target.dataset.id) })
可是列表數據可能會動態的變化,這樣咱們就避免不了動態的去註冊事件,數量還可能不少,那有沒有什麼一勞永逸的方法呢?固然,讓咱們使用事件委託的方法,也就是將點擊事件綁定到ul
元素上:
document.querySelector('.list').onclick = e => { if (e.target.matches('li.item')) { handleClick(e.target.dataset.id) } }
值得注意的是,event.target
表明事件起源目標的引用,若要尋找當前註冊事件對象的引用,請用event.currentTarget
。