這是我參與更文挑戰的第 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 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
語句執行如下步驟:
focus
,blur
事件就不傳播,mouseenter
和 mouseleave
事件也不會傳播;on<event>
分配的處理程序,那麼你可使用 return false
來阻止默認行爲。on<event>
分配的處理程序中返回的 return false
。在全部其餘狀況下,return
值都會被忽略。而且,返回 true
沒有意義。addEventListener
,你可使用 return false
,但經常使用的是使用 event
對象中 e.preventDefault()
方法來阻止默認行爲的發生。return false
作着與 event.stopPropagation()
和 event.preventDefault()
相似的工做,但它們之間毫無關聯。