先來個範例
<head>
<style>
.outer {
width: 500px;
height: 200px;
background: red;
}
.inner {
width: 300px;
height: 100px;
background: green;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner">
<button class="btn">click me</button>
</div>
</div>
<script>
addEvent(".outer")
addEvent(".inner")
addEvent(".btn")
function addEvent(className) {
document.querySelector(className)
.addEventListener('click', function () {
console.log(className)
})
}
</script>
</body>
當點下最小的 btn ,三個事件都會被觸發,點綠色,綠和紅都會被觸發。
事件傳遞機制詳解:捕獲與冒泡
好文推薦 : DOM 的事件傳遞機制:捕獲與冒泡
.addEventListener(1,2,3)
- 第一個參數是事件名稱
- 第二個參數是 callback function
- 第三個參數是 boolean 變數
如果 boolean 變數是 false ,會放在冒泡階段上,如果是 true ,會放在捕獲階段上
事件傳遞順序 :
- 先捕獲,再冒泡
- 當事件傳到 target 本身,沒有分捕獲跟冒泡
當我們點擊 box 的事件時,click 會先傳給 window,一直往下傳,同一個事件再傳回來
不管有沒有加事件監聽,事件都會是這樣傳
那加一個事件監聽
document.querySelector('.box')
.addEventListener('click', function () {
console.log('box click')
})
當傳傳傳,往上傳時,listener 就可以聽到那個事件
加兩個事件監聽,當我按下 box 的 click 兩個事件都會監聽到。
log box click
再來 log inner click
document.querySelector('.box')
.addEventListener('click', function (e) {
console.log('box click')
})
document.querySelector('.inner')
.addEventListener('click', function (e) {
console.log('inner click')
})
如果在 box 監聽事件上加 e.stopPropagation()
那就只會 log box click
上面的 log inner click
就不會顯現出來
document.querySelector('.box')
.addEventListener('click', function (e) {
e.stopPropagation()
console.log('box click')
})
document.querySelector('.inner')
.addEventListener('click', function (e) {
console.log('inner click')
})
改變監聽事件的位置,加入 .addEventListener
的第三個參數,false 會放在冒泡階段上,true 會放在捕獲階段上,下面執行完會先 log inner click
,再 log box click
document.querySelector('.box')
.addEventListener('click', function (e) {
e.stopPropagation()
console.log('box click')
}, false)
document.querySelector('.inner')
.addEventListener('click', function (e) {
console.log('inner click')
}, true)
如果在 windows 上加
e.stopPropagation()
那不管在哪邊加 Listener 都不會被執行到,事件都不會被往下傳。上面有說當事件傳到 target 本身,沒有分捕獲跟冒泡,所以是按照程式寫的順序。因此下面會先 log
box 冒泡
接著才是box 捕獲
document.querySelector('.box')
.addEventListener('click', function (e) {
e.stopPropagation()
console.log('box 冒泡')
}, false)
document.querySelector('.box')
.addEventListener('click', function (e) {
e.stopPropagation()
console.log('box 捕獲')
}, true)
在同一個地方是可以放兩個 Listener
如果其中一個放 e.stopPropagation()
兩個事件都還是被執行,因為他們在同一層,如果只要一個被執行,那就要下 e.stopImmediatePropagation()
,會阻止任何的 EventListener
e.stopPropagation
和 e.preventDefault
的差別
e.stopPropagation
: 取消事件繼續往下傳遞
e.preventDefault
: 取消瀏覽器的預設行為,和傳遞事件完全無關
取消瀏覽器的預設行為例如阻止超連結,參考這篇
表單提交(submit)時使用preventDefault可能產生的問題