JavaScript 事件傳播機制

這是我參與更文挑戰的第 6 天,活動詳情查看:更文挑戰javascript

JavaScript 採用異步事件驅動編程模型,與 HTML 的交互是經過事件實現的。html

當咱們執行了特定的事件時,如在一個按鈕上監聽了 click 點擊事件,當你按下按鈕時,它將觸發你給定的事件句柄(又稱事件處理函數,其中執行一些 JS 語句)。但這個觸發過程是怎樣的呢?下面咱們來經過一些問題來看看今天要講的事件的傳播機制。java

你能夠在這👉測試效果,配合下文進行閱讀。編程

什麼是事件流?

事件流是在網頁上接收事件的順序。當您單擊嵌套在其餘各類元素中的元素時,在您單擊目標元素後,它會先觸發目標元素上的 click 事件,在一層層往上觸發事件,最終到達最頂層的 window 對象,這是瀏覽器默認的事件冒泡行爲(IE 9+)。事件流有兩種方式:瀏覽器

  • 從外到內(事件捕獲)
  • 從內到外(事件冒泡)

簡單示例:如下問題都用該結構markdown

<ul id="emoji">
  <li id="smile">😀</li>
  <li>😁</li>
  <li>😂</li>
  <li>🤣</li>
  <li>😃</li>
  <li>😄</li>
</ul>

<script> emoji.onclick = function (e) { console.log(e.target.innerHTML) } </script>
複製代碼

嘗試一下使用上面示例,你會發現,單擊每一個 <li> 標籤,你會獲得其中的每一個 emoji異步

能獲得這樣的結果是由於,它使用了事件冒泡一個最重要的原理:事件委託(event delegation),後面會介紹到。函數

IE 中的事件流

這裏要介紹一下 IE 6-8 中的事件流,IE 6-8 中的事件流和標準的 DOM 事件流有(獨)所(領)不(風)同(騷),IE 的事件流只支持事件冒泡,不支持事件捕獲。oop

IE 9 以前,它也不支持 addEventListener,也就不能使用其第三個參數來表示是冒泡仍是捕獲,它提供了非標準的事件偵聽器 attachEvent() 方法。post

在這個模型中,事件對象使用 event.srcElement 屬性,而不是 event.target,但二者的效果相同。

emoji.attachEvent('onclick', function (e) {
  var target = e.target || e.srcElement
  alert(target.innerHTML)
})
複製代碼

什麼是事件冒泡?

事件冒泡是事件傳播的一種,其中事件首先在最內部的目標元素上觸發,而後在同一嵌套層次結構中依次在目標元素的祖先(父代)上觸發,直到到達最外層的 DOM 元素。

emoji.onclick = function () {
  alert(11)
}

smile.onclick = function () {
  alert(22)
}

// 11
// 22
複製代碼

什麼是事件捕獲?

事件捕獲也是事件傳播的一種,其中事件首先被最外面的元素捕獲,而後在同一嵌套層次結構中依次觸發目標元素的後代(子元素),直到到達最裏面的 DOM 元素。

當一個事件發生之後,它會在不一樣的 DOM 節點之間傳播(propagation)。DOM 事件標準描述了事件傳播的 3 個階段:

  • 捕獲階段(capture phase):事件從 Window 對象向下傳遞到目標節點。

  • 目標階段(target phase):事件到達目標元素。

  • 冒泡階段(bubbling phase):事件從元素上開始冒泡。

在捕獲階段中,事件從祖先元素向下傳播到目標元素。當事件達到目標元素後,冒泡纔開始。

同時,這三個階段的傳播模型,會使得一個事件在多個節點上觸發。

由於瀏覽器默認是從事件冒泡開始,咱們看不到事件捕獲,因此想要測試事件捕獲咱們須要使用到 addEventListener 方法,addEventListener 用於添加事件句柄、註冊監聽器,參數三還能夠指定在捕獲階段仍是冒泡階段觸發事件:

  • false(默認值)—— 在冒泡階段開始觸發事件;
  • true —— 在捕獲階段開始觸發事件。
smile.addEventListener('click', function(e) {
  console.log(e.target.innerHTML)
})

emoji.addEventListener('click', function(e) {
  alert('Emoji List')
}, true)

document.addEventListener('click', function () {
  alert('document')
})

window.addEventListener('click', function () {
  alert('window')
})

// 點擊 smile 時,將依次輸出 'Emoji List' -> 😀 -> 'document' -> 'window'
// 因爲將 list 設置爲捕獲階段開始,因此先觸發,在觸發 😀,而後在依次往上觸發。
複製代碼

事件委託

事件委託,也稱爲事件代理,是指將本要添加在自身的事件,添加到別人身上。經過冒泡的原理,將事件添加到父級,觸發執行效果。

emoji.addEventListener('click', function(e) {
  console.log(e.target.innerHTML)
})
複製代碼

好處

  • 節省內存佔用,減小事件註冊
  • 新增子對象時,無需再對其進行事件綁定

阻止事件傳播

使用 event.stopPropagation() 方法用於阻止捕獲和冒泡階段中當前事件在 DOM 中的進一步傳播。

function handler(e) { 
  e.stopPropagation()
}
複製代碼

取消默認事件

使用 event.preventDefault() 方法取消瀏覽器對當前事件的默認行爲,例如:

  • 表單元素中使用,它將阻止其提交
  • 錨元素中使用,它將阻止其導航
  • 上下文菜單中使用,它將阻止其顯示或隱藏
function handler(e) { 
  e.preventDefault()
}
複製代碼

您還能夠在事件對象中使用 event.defaultPrevented 查看是否使用了 event.preventDefault() 方法。

它返回一個布爾值用來代表是否在特定元素中調用了event.preventDefault()

function handler(e) {
  e.preventDefault()
  console.log(e.defaultPrevented) // true or false
}
複製代碼

return false 用法涉及哪些步驟

事件處理程序中的 return false 語句執行如下步驟:

  • 首先,它中止了瀏覽器的默認操做或行爲。
  • 它防止事件傳播 DOM
  • 中止回調執行,並在調用時當即返回。

理解事件傳播須要注意

  • 事件自己在傳播,而不是綁定在事件上的方法會傳播;
  • 並不是全部的事件都會傳播,像 focusblur 事件就不傳播,mouseentermouseleave 事件也不會傳播;
  • 您應該知道哪些事件有默認行爲,並在須要時阻止它。
  • 若是是使用 on<event> 分配的處理程序,那麼你可使用 return false 來阻止默認行爲。
  • 事件處理程序返回的值一般會被忽略。惟一的例外是從使用 on<event> 分配的處理程序中返回的 return false。在全部其餘狀況下,return 值都會被忽略。而且,返回 true 沒有意義。
  • 若是是使用 addEventListener,你可使用 return false,但經常使用的是使用 event 對象中 e.preventDefault() 方法來阻止默認行爲的發生。
  • return false 作着與 event.stopPropagation()event.preventDefault() 相似的工做,但它們之間毫無關聯。
相關文章
相關標籤/搜索