# 2-6 、 瀏覽器事件傳遞機制
###### tags: `FE102` `HTML+CSS` `2020九月第一周` `進度筆記` `Lidemy心得` 09/04
***
## 用圖層來舉例說明
存成一個 .html 檔案:
```
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>瀏覽器上執行 JS</title>
<style>
.outer {
width: 500px;
height: 200px;
background: orange;
}
.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>
</html>
```
- 開啟 devtool ,點橘色圖層只會觸發 outer 。

- 但點下綠色會同時觸發 inner 和 outer 。

- 按下 click me 會發現同時觸發 outer 、 inner 和 btn 三者。

- 會發現 script 的觸發對應了 div 標籤的層層疊疊順序。
***
參考資料:
[[第七週] DOM - 事件傳遞機制:捕獲與冒泡、事件代理| Yakim shu](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwiShIPRoeDrAhVCyYsBHT2MAg4QFjACegQIBBAB&url=https%3A%2F%2Fyakimhsu.com%2Fproject%2Fproject_w7_eventListener.html&usg=AOvVaw3dfU2Fh5B9-Ygf08pJGSmY)
[DOM 的事件傳遞機制:捕獲與冒泡 - TechBridge 技術共筆部落格](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwiShIPRoeDrAhVCyYsBHT2MAg4QFjAAegQIAxAB&url=https%3A%2F%2Fblog.techbridge.cc%2F2017%2F07%2F15%2Fjavascript-event-propagation%2F&usg=AOvVaw1kp_TvQvTR2GQsVv7Yjk4y)
[[JavaScript] Javascript 中的 DOM 事件傳遞機制:捕獲與冒泡 (capturing and bubbling) - itsems](https://medium.com/itsems-frontend/javascript-event-bubbling-capturing-794cd2d01e61)
---
# 2-7 、 事件傳遞機制 part.2
###### tags: `FE102` `HTML+CSS` `2020九月第一周` `進度筆記` `Lidemy心得` 09/04
***
## 捕獲和冒泡
1. 捕獲階段: `Capturing_Phase`
- 事件會由最上面的 Windows 傳到下 , [Event dispatch and DOM event flow -w3](https://www.w3.org/TR/DOM-Level-3-Events/#event-flow) 。
2. 目標階段: `Target_Phase`
- 鎖定目標發生。
3. 冒泡階段: `Bubbling_Phase`
- 將從目標發生的那一層事件,一路傳回到 Windows 。
- 先放的 EventListener 先發生,但一樣先捕獲再冒泡。
***
## 不同階段的 EventListener
以 2-6 的 .html 修改:
```
<script>
addEvent('.outer')
addEvent('.inner')
addEvent('.btn')
function addEvent(className) {
document.querySelector(className)
.addEventListener('click', function() {
console.log(className, '捕獲')
}, true)
document.querySelector(className)
.addEventListener('click', function() {
console.log(className, '冒泡')
}, false)
}
</script>
</body>
</html>
```
- ==第一個參數是事件名稱,如上面的 click。==
- ==第二個參數是 call back function , 即 ``function()`` 。==
- ==第三個參數可以是一個布林變數。==
- 如果布林值是 __'false'__ 就會在冒泡階段監聽; __'true'__ 的話就會放在捕獲階段上監聽。
- 如果原本都沒有傳達,預設就是 false 。

點擊綠色圖案,用 dev tool 查看 __即先捕獲再冒泡。__
---
## 用了 preventDefault 後都不會有效果
以 2-6 的 .html 修改:
```
<body>
<div class='outer'>
<div class='inner'>
<a href='/test'>click</a>
<button class='btn'>click me</button>
</div>
</div>
<script>
addEvent('.outer')
addEvent('.inner')
addEvent('.btn')
window.addEventListener('click', function(e) {
e.preventDefault()
}, true)
function addEvent(className) {
document.querySelector(className)
.addEventListener('click', function() {
console.log(className, '冒泡')
}, false)
}
</script>
</body>
```
- `e.preventDefault()` 只要被 call 出來就不會讓 click 、 link 和表單送出完全不會有效果。
---
參考資料:
2-6 的參考資料再看一次~
---
# 2-8 、 停止往上回傳
###### tags: `FE102` `HTML+CSS` `2020九月第一周` `進度筆記` `Lidemy心得` 09/04
- 停止冒泡或是捕獲的方法 , call function 。
***
## ``stopPropagation()``
以 2-7 的 script 修改 .html 檔案:
```
<script>
addEvent('.outer')
addEvent('.inner')
function addEvent(className) {
document.querySelector(className)
.addEventListener('click', function() {
console.log(className, '冒泡')
})
}
document.querySelector('.btn')
.addEventListener('click', function(e) {
e.stopPropagation()
console.log('.btn 冒泡')
})
</script>
```

- 按下 click 只會觸發按鈕本身自己的事件。
- 如果在 window 層直接加入 `e.stopPropagation()` , 就永遠不會傳遞下去。
- 在一個按鈕上可以加入不只一個的 EventListener , 如:
```
document.querySelector('.btn')
.addEventListener('click', function(e) {
e.stopPropagation()
console.log('.btn click 1')
})
document.querySelector('.btn')
.addEventListener('click', function(e) {
e.stopPropagation()
console.log('.btn click 2')
})
```
- 如果只想觸發一個事件就可以用 ``e.stopImmediatePropagation()`` , 立刻阻止其他所有的 EventListener 傳遞。
---
參考資料:
[Event.stopPropagation() - Web APIs | MDN](https://developer.mozilla.org/zh-TW/docs/Web/API/Event/stopPropagation)
[Event.stopImmediatePropagation() - MDN Web Docs - Mozilla](https://developer.mozilla.org/zh-TW/docs/Web/API/Event/stopImmediatePropagation)
---
# 2-9 、 案發現場有先後順序
###### tags: `FE102` `HTML+CSS` `2020九月第一周` `進度筆記` `Lidemy心得` 09/04
==__事件機制問題別搞混。__==
## querySelector 只會回傳第一個元素
以 .html 為例,有兩個按鈕:
```
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>瀏覽器上執行 JS</title>
<style>
</style>
</head>
<body>
<div class='outer'>
<button class='btn'>One</button>
<button class='btn'>Two</button>
</div>
<script>
document.querySelector('.btn')
.addEventListener('click', function() {
alert(1)
})
</script>
</body>
</html>
```
- document.querySelector() 只會回傳第一個元素。
- 即只幫第一個元素加了 EventListener 。
---
## ``document.querySelectorAll()``
- ``document.querySelectorAll()`` 可以選到所有元素,但他底下沒有 ``.addEventListener`` 這個方法。
- 雖然不是陣列但可以當陣列來用。
---
## 作用域相關問題
```
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>瀏覽器上執行 JS</title>
<style>
</style>
</head>
<body>
<div class='outer'>
<button class='btn'>One</button>
<button class='btn'>Two</button>
<button class='btn'>Three</button>
<button class='btn'>Four</button>
<button class='btn'>Five</button>
<button class='btn'>Six</button>
</div>
<script>
const elements = document.querySelectorAll('.btn')
for(var i =0; i<elements.length; i++) {
elements[i].addEventListener('click', function() {
alert(i+1)
})
}
</script>
</body>
</html>
```
- function 是在點擊按鈕時觸發的,點擊的那刻 i 的值會是 `elements.length` 。
- 迴圈跑完後是 6 。
- 會以 data-value 作為自訂的屬性:
---
## 作用域相關 - 自動加入按鈕
```
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>瀏覽器上執行 JS</title>
<style>
</style>
</head>
<body>
<div class='outer'>
<button class='add-btn'>add</button>
<button class='btn' data-value='1'>1</button>
<button class='btn' data-value='2'>2</button>
</div>
<script>
let num = 3
const elements = document.querySelectorAll('.btn')
for(var i =0; i<elements.length; i++) {
elements[i].addEventListener('click', function(e) {
alert(e.target.getAttribute('data-value'))
})
}
document.querySelector('.add-btn').addEventListener('click', function() {
const btn = document.createElement('button')
btn.setAttribute('data-value', num)
btn.innerText = num
num++
document.querySelector('.outer').appendChild(btn)
})
</script>
</body>
</html>
```
- 一直增加按鈕。

- 但點 1 和 2 以外的按鈕不會產生視窗,宣告的 elements 只幫 1 和 2 有加入 EventListener 。
- 寫的迴圈不會自動幫你加入 EventListener 。
---
參考資料:
[Document.querySelectorAll() - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll)
[JavaScript 基礎知識-querySelectorAll - iT 邦幫忙::一起幫忙 ...](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwj2rLW4gOHrAhW0JaYKHdUFCV8QFjABegQIAxAB&url=https%3A%2F%2Fithelp.ithome.com.tw%2Farticles%2F10211614&usg=AOvVaw29kQegw_SvG768wialbo_h)
[各瀏覽器中querySelector和querySelectorAll的實現差異分析 ...](https://codertw.com/%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC/293373/)
---
# 2-10 、 案件委託
###### tags: `FE102` `HTML+CSS` `2020九月第一周` `進度筆記` `Lidemy心得` 09/04
## 直接在 class 的最上層加事件監聽
eventDelegation , 有點像是請人幫忙訂所有人便當的概念:
```
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>瀏覽器上執行 JS</title>
<style>
</style>
</head>
<body>
<div class='outer'>
<button class='add-btn'>add</button>
<button class='btn' data-value='1'>1</button>
<button class='btn' data-value='2'>2</button>
</div>
<script>
let num = 3
document.querySelector('.add-btn').addEventListener('click',
function() {
const btn = document.createElement('button')
btn.setAttribute('data-value', num)
btn.classList.add('btn')
btn.innerText = num
num++
document.querySelector('.outer').appendChild(btn)
})
document.querySelector('.outer').addEventListener('click',
function(e) {
if (e.target.classList.contains('btn')) {
alert(e.target.getAttribute('data-value'))
}
})
</script>
</body>
</html>
```
- 這樣蠻有效率,很常用,不用浪費資源 function 在監聽每個事件。
- 可以處理動態新增的情形,如上面增加按鈕。
- 透過冒泡機制往上傳,就算底下新增東西,還是可以接到事件。
---
參考資料:
[Event Delegation — 事件委派介紹與觸發委派的回呼函數| by ...](https://medium.com/@realdennis/event-delegation-%E4%BA%8B%E4%BB%B6%E5%A7%94%E6%B4%BE%E4%BB%8B%E7%B4%B9-%E8%88%87-%E8%A7%B8%E7%99%BC%E5%A7%94%E6%B4%BE%E7%9A%84%E5%9B%9E%E5%91%BC%E5%87%BD%E6%95%B8-2990921a5ba2)
[Event Delegation 事件委派| Summer。桑莫。夏天](https://cythilya.github.io/2015/07/08/javascript-event-delegation/)
[為什麼有時你應該優先考慮event delegate 而不是 ... - iT 邦幫忙](https://ithelp.ithome.com.tw/articles/10120565)