# Vue.js 學習旅程Mile 10 – Event Handling 事件處理篇-2:Modifiers 修飾符
###### tags: `w3HexSchool` `Vue` `Javascript`
## 修飾符 Modifiers
修飾符依照功能可以分為下面兩類:
* 事件修飾符(Event Modifiers):事件相關的處理或設定。
* 按鍵修飾符(Key Modifiers):鍵盤或是按鈕的事件中觸發的按鍵設定。
## 事件修飾符 (Event Modifiers)
Vue 提供一些常用處理的修飾符,讓我們可以直接設定在屬性上,除了減少程式碼量外,最大的目的是讓方法盡量只是單純處理邏輯,而不用多去擔心相關的 DOM 處理。
* `.stop` : 呼叫 event.stopPropagation(),停止觸發上層 DOM 元素事件
* `.prevent` : 呼叫 event.preventDefault(),避免瀏覽器預設行為
* `.capture` : 不管觸發事件的目標是否是下層, 設定 `capture` 的事件一定會先觸發
* `.self` : 僅觸發自己範圍的事件,不包含子層
### `.stop` 修飾符
`.stop` 修飾符會叫用 `event.stopPropagation()` ,`stopPropagation` 方法會停止事件的冒泡。
在預設的情況下,觸發了下層 DOM 元素的事件後,會往上叫用其他 DOM 元素的事件,可是當加上 `stopPropagation` 後,就只會到目前的事件為止,不會往父層觸發。
Js 預設的事件傳遞方向為向上冒泡(**event bubbling**),也就是從內到外執行。
```htmlmixed=
<div class="outer" @click="alert('outer, none-once, default')">
outer
<div class="middle" target="_blank" @click="alert('middle, none-capture, default')">
middle
<div class="inner" target="_blank" @click.stop="alert('inner, not trigger upper except capture')">
inner, stopPropagation(not trigger upper except capture)
</div>
</div>
</div>
```
當按下 inner 後, middle 跟 outer 的事件會因為 inner 的 click 加入了 `.stop` 修飾符而不被觸發。
### `.prevent` 修飾符
`.prevent` 修飾符會叫用 `event.preventDefault()` ,`preventDefault` 會停止瀏覽器的預設行為。例如: checkbox 的勾選/取消勾選行為、 form 的 submit 送出表單行為等,都會因為 `preventDefault` 而沒有觸發。
```htmlmixed=
<a class="inner" href="https://developer.mozilla.org/" target="_blank"
@click.prevent="alert('inner2, none-passive, default, not open new page')">
inner2, none-passive & preventDefault(not open new page)
</a>
```
原本按下超連結後會直接開啟 href 設定的頁面,可是因為 click 事件有設定 `.prevent` 修飾符,所以不會開啟連結。
### `.capture` 修飾符
不管觸發事件的 DOM 元素是誰,使用 `.capture` 修飾符的事件會優先觸發。
```htmlmixed=
<div class="middle" target="_blank" @click.capture="alert('middle, capture')" @click="alert('middle, none-capture, default')">
middle, capture & none-capture & self
<a class="inner1" href="https://www.mozilla.org" target="_blank" @click="alert('inner1, passive, open new page')">
inner1, passive & preventDefault(which is not allowed)
</a>
</div>
```
當按下 inner1 時,可以看到 alert('middle, capture') 先被觸發,再來是 alert('inner1, passive, open new page') ,最後才是 alert('middle, none-capture, default') ,由此現象可知 `.capture` 會打破由內而外的事件傳遞規則,先行觸發。
關於捕獲與冒泡事件,可參考以下文章:
* [DOM 的事件傳遞機制:捕獲與冒泡](https://blog.techbridge.cc/2017/07/15/javascript-event-propagation/)
* [JavaScript Capture Bubble DOM事件獲取&冒泡](https://iandays.com/2019/03/21/eventpass/)
* [Bubbling and capturing](https://javascript.info/bubbling-and-capturing)
![](https://i.imgur.com/3QSQTur.png)
### `.self` 修飾符
`.self` 修飾符只會觸發自己範圍內的事件,不包含子元素。
```htmlmixed=
<div class="middle" target="_blank" @click.self="alert('middle, self')">
middle, self
<div class="inner4" target="_blank" @click="alert('inner4')">
inner4
</div>
</div>
```
### `.once` 修飾符
`.once` 修飾符使事件只會觸發一次。
```htmlmixed=
<div class="outer" @click.once="alert('outer, once')" @click="alert('outer, none-once, default')">
outer
</div>
```
當點擊第一次的時候兩個事件都會被觸發,可是之後都只有沒有 `.once` 修飾符的事件會被觸發。
### `.passive` 修飾符
`.passive` 修飾符會無視 `event.preventDefault()` 的功能,只要加上此修飾符就一定會觸發瀏覽器的預設行為。
不要把 `.passive` 和 `.prevent` 一起使用,因為 `.prevent` 將會被忽略。
```htmlmixed=
<a class="inner1" href="https://www.mozilla.org" target="_blank" @click.passive.prevent="alert('inner1, passive, open new page')">
inner1, passive & preventDefault(which is not allowed)
</a>
```
由於 `.passive` 會使 `preventDefault` 無效,所以就算在 `.passive` 後面加上 `.prevent` 還是會開啟連結頁面。
### 修飾符的順序
使用修飾符時,順序很重要;相應的程式碼會以同樣的順序產生。因此,用 `v-on:click.prevent.self` 會阻止所有的點擊,而 `v-on:click.self.prevent `只會阻止對元素自身的點擊。
## 按鍵修飾符(Key Modifiers)
監聽鍵盤事件的特定鍵值。
:::warning
[keyCode](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode) 的事件用法已經被廢棄了並可能不會被最新的瀏覽器支持。
:::
### 按鍵值
只要是在 [KeyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values) 上的按鍵名稱都可以用 **kebab-case** 的方式設定在修飾符上。
```htmlmixed=
<input v-on:keyup.page-down="onPageDown">
```
Vue 會在函數中使用 $event.key === 'PageDown' 判斷式決定是否為目標按鈕
### 按鍵碼別名
為了在必要的情況下支持舊瀏覽器,Vue 提供了絕大多數常用的按鍵碼的別名:
* `.enter`
* `.tab`
* `.delete` (Delete 跟 Backspace 按鍵都會觸發)
* `.esc`
* `.space`
* `.up`
* `.down`
* `.left`
* `.right`
### 自定義按鍵修飾符別名
在 `Vue.config.KeyCodes` 物件中設定自定義的別名:
* key : 別名名稱,不可使用 camelCase 設定名稱,但是可以使用雙引號綁定 kebab-case 的別名。
* value : 對應的按鍵編號,可以使用陣列同時綁定多個編號。
```htmlmixed=
<div id="app">
<input @keyup.f1="what='keyup.f1'" @keyup.up="what='keyup.up'" @keyup.insert-mode="what='keyup.insert-mode'" />
<span>You trigger event by <strong>{{what}}</strong></span>
</div>
```
```javascript=
Vue.config.keyCodes = {
f1: 112, // f1
up: [38, 87], // 數字鍵 8 跟 w
// insertMode: 73, // won't work
"insert-mode": 73 // i
};
```
### 系統修飾鍵 (System Modifier Keys)
僅在按下相應按鍵時才觸發滑鼠或鍵盤事件的監聽器
* `.ctrl`
* `.alt`
* `.shift`
* `.meta`
```htmlmixed=
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>
```
#### 與常規按鍵不同之處
使用系統修飾鍵設定修飾符的 `keyup` 事件只有在該系統鍵按住的狀態下才會觸發事件,例如:
```htmlmixed=
<div id="app">
<input"
@keyup.ctrl="what='keyup.ctrl'"
@keyup.17="what='keyup.17'"
/>
<span>You trigger event by <strong>{{what}}</strong></span>
</div>
```
* `@keyup.ctrl` : 要在按住 Ctrl 鍵的狀況下釋放其他按紐才會觸發。
* `@keyup.17` : 只要釋放 Ctrl 按鈕就會觸發。
#### `.exact` 修飾符
設定 `.exact` 修飾符代表一定要完全符合系統修飾鍵的設定才會觸發事件
```htmlmixed=
<!-- 即使 Alt 或 Shift 被一同按下時也會觸發 -->
<button v-on:click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的時候才觸發 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>
<!-- 沒有任何系統修飾符被按下的時候才觸發 -->
<button v-on:click.exact="onClick">A</button>
```
### 滑鼠按鍵修飾符 (Mouse Button Modifiers)
Vue 提供左中右三個滑鼠按鍵的修飾符
* `.left`
* `.right`
* `.middle`
## 參考資料
* [Vue.js - Event Handling](https://vuejs.org/v2/guide/events.html)
* [Vue.js - v-on](https://cn.vuejs.org/v2/api/index.html#v-on)
* [Vue.js Core 30天屠龍記(第17天): `v-on` 的修飾符 Part 1 - 事件修飾符](https://ithelp.ithome.com.tw/articles/10206828)
* [Vue.js Core 30天屠龍記(第18天): `v-on` 的修飾符 Part 2 - 按鍵修飾符](https://ithelp.ithome.com.tw/articles/10207356)
* [Day10 - [Directives] 事件處理(Event Handling)](https://ithelp.ithome.com.tw/articles/10194631)
* [用範例理解 Vue.js #12:Event Handling(v-on)](https://ithelp.ithome.com.tw/articles/10192961)
* [Vue.js: Methods 與事件處理 (Event Handling)](https://cythilya.github.io/2017/04/17/vue-methods-and-event-handling/)
* [MDN-EventTarget.addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters)