簡單聊聊瀏覽器JS事件觸發機制

原理

事件捕獲瀏覽器

由網景最早提出,事件會從最外層開始發生,直到最具體的元素,也就是說假如父元素與子元素都綁定有點擊事件,又互相重疊,那麼先出發的會是父元素的事件,而後再傳遞到子元素。函數

事件冒泡spa

由微軟提出,事件會從最內從的元素開始發生,再向外傳播,正好與事件捕獲相反。代理

這兩個概念都是爲了解決頁面中事件流的發生順序,w3c採起了折中的辦法,制定了統一的標準:先捕獲再冒泡。code

addEventListener

addEventListen(event, function, useCapture)添加事件的第三個參數默認值爲false,即默認使用事件冒泡,若爲true則使用事件捕獲的機制,如下爲示例:對象

clipboard.png

當咱們使用默認事件註冊機制的時候,點擊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

相關文章
相關標籤/搜索