事件傳遞機制
- 捕獲: CAPTURING_PHASE
- DOM 的事件在傳播時,會先從根節點開始往下傳遞到target,這邊你如果加上事件的話,就會處於CAPTURING_PHASE,捕獲階段。
- 元素本身:AT_TARGET
- target就是你所點擊的那個目標,這時候在target身上所加的eventListenr會是AT_TARGET這一個 Phase。
- 冒泡:BUBBLING_PHASE
- 最後,事件再往上從子節點一路逆向傳回去根節點,這時候就叫做BUBBLING_PHASE,也是冒泡階段。
- 注意:掌握原則
- 先捕獲,再冒泡
- 當事件傳到 target 本身,沒有分捕獲跟冒泡
設定於哪個階段做監聽
- 在
.addEventListener()
加上第三個參數true
:捕獲false
:冒泡- 若不寫第三個參數,則預設為 false
取消事件傳遞
e.stopPropagation()
- 加在哪個階段上,事件的傳遞就斷在哪裡,不會再把事件傳遞給「下一個節點」
- 加在「捕獲階段」上
- outer「觸發」捕獲事件,不再傳遞給下一個節點
- 加在「捕獲階段」上
- 加在「冒泡階段」上
- btn「觸發」冒泡事件,不再傳遞給下一個節點
停止後續 v.s 停止預設
e.stopPropagation
停止的是後續事件e.stopImmediatePropagation()
讓其他同一層級的 listener 也不要被執行e.preventDefault()
可以停止瀏覽器的預設行為- 如果我想要阻止頁面上所有的 click 事件( 包括
<a>
預設的動作 ),可以在 window 物件監聽捕獲階段,來阻止底下的所有元素
window.addEventListener('click', function (e) {
e.preventDefault(); // 停止預設功能
e.stopPropagation(); // 停止後續傳遞
}, true) // 指定為從捕獲階段開始監聽
// 底下的事件傳遞全都被阻止了
function addEvent(className) {
document.querySelector(className)
.addEventListener('click', function (e) {
console.log(className, '捕獲', e.eventPhase);
}, true)
document.querySelector(className)
.addEventListener('click', function (e) {
console.log(className, '冒泡', e.eventPhase);
}, false)
};
新手易混肴問題
利用迴圈幫全部按鈕加上事件監聽,希望利用迴圈的 i 值,在按鈕被點選的時候加上編號 i。
const btnGroup = document.querySelectorAll('.btn');
for (var i = 1; i <= btnGroup.length; i += 1) {
btnGroup[i].addEventListener('click', function (e) {
console.log(i);
})
}
但是實際上當 click 事件觸發時,迴圈已經跑完了(i 已經跑到 btnGroup.length),所以編號會通通是最後一號。
解決方式:
利用屬性 attribute 來紀錄每一圈新增按鈕時候的 i 值。這樣迴圈跑完後雖然 i 值已經跑到 btnGroup.length了, 每個按鈕依然有自己的 attribute 可以抓
<button class="btn" data-value="1">1</button>
<button class="btn" data-value="2">2</button>
<script>
const btnGroup = document.querySelectorAll('.btn');
for (let i = 0; i < btnGroup.length; i += 1) {
btnGroup[i].addEventListener('click', function (e) {
console.log(e.target.getAttribute('data-value'));
})
}
</script>
事件代理 Event delegation
- 利用事件傳遞的機制,把 button 的 eventListener 綁定在上層的 .outer 元素上,就叫做 事件代理
<div class="outer">
<button class="btn_add" >add</button>
<button class="btn" data-value="1">1</button>
<button class="btn" data-value="2">2</button>
</div>
<script>
document.querySelector('.outer').addEventListener('click', function (e) {
console.log(e.target.getAttribute('data-value'));
})
</script>