【Vue.js】イベントハンドリングをサンプルを作りながら理解する

**※ 2020/04/13 drag, drop系を追記しました**
**※ 2020/07/22 formの内容を修正しました**

# はじめに

フロントエンド自体が初心者なので正直この辺りについては全然理解が追い付いてないです。

今回は簡単なサンプルを作りながら自分なり理解していこうと思います。

# メソッドハンドラ

Vue.jsでは「v-on」ディレクティブを用いることで要素にイベントリスナをバインドします。

```html
<button v-on:click="hoge">Click me!</button>
```

省略形として`@`を用いる場合もあります。

```html
<button @click="hoge">Click me!</button>
```

# イベント一覧

※すべてではないです 。

## click, dblclick

クリック、ダブルクリック時に発火するイベントです。

<p class="codepen" data-height="219" data-theme-id="light" data-default-tab="html,result" data-user="b1san1" data-slug-hash="QJKKqL" style="height: 219px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="QJKKqL">
  <span>See the Pen <a href="https://codepen.io/b1san1/pen/QJKKqL">
  QJKKqL</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## keyup, keydown

キーアップ、キーダウン時に発火するイベントです。

### キー修飾子

キー修飾子を指定することで任意のキーのみでイベントを発火させることができます。

キーコードで指定も可能ですが、以下のように用意されているものもあります。

- enter
- tab
- delete
- esc
- space
- meta
- up
- down
- left
- right

<p class="codepen" data-height="310" data-theme-id="light" data-default-tab="html,result" data-user="b1san1" data-slug-hash="NERRwZ" style="height: 310px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="NERRwZ">
  <span>See the Pen <a href="https://codepen.io/b1san1/pen/NERRwZ">
  NERRwZ</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## focus, blur

focusはフォーカスされたとき、blurはフォーカスが外れたときにイベントが発火します。

<p class="codepen" data-height="236" data-theme-id="light" data-default-tab="html,result" data-user="b1san1" data-slug-hash="vQXXrz" style="height: 236px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="vQXXrz">
  <span>See the Pen <a href="https://codepen.io/b1san1/pen/vQXXrz">
  vQXXrz</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## change

フォーカスされてから外れるまでの間に値が変化している場合にイベントが発火します。

<p data-height="265" data-theme-id="0" data-slug-hash="VVKmMO" data-default-tab="js,result" data-user="b1san1" data-pen-title="VVKmMO" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/VVKmMO/">VVKmMO</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## mouseover, mouseout

mouseoverはマウスカーソルを当てたとき、mouseoutはマウスカーソルが外れたときにイベントが発火します。

<p data-height="265" data-theme-id="0" data-slug-hash="jQMMdW" data-default-tab="js,result" data-user="b1san1" data-pen-title="jQMMdW" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/jQMMdW/">jQMMdW</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
## mouseenter, mouseleave

mouseover, mouseoutと同じくマウスカーソルを当てたとき、外れたときにイベントが発火しますが、**子要素にイベントを伝搬させない**という違いがあります。

サンプルを動かしてもらうと両者のイベントの発火数の違いがわかると思います。

mouseover, mouseoutは、子要素であるpタグやdivタグ、さらにその子要素のpタグにカーソルを当てたり外した場合でも親側のイベントが発火しています。

一方、mouseenter, mouseleaveは、自身の要素のみにおいてイベントが発火しています。

<p data-height="265" data-theme-id="0" data-slug-hash="YRGpzd" data-default-tab="html,result" data-user="b1san1" data-pen-title="YRGpzd" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/YRGpzd/">YRGpzd</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## mouseup, mousedown

mousedownはマウスのボタンを押したとき、mouseupはマウスのボタンを離したときに発火するイベントです。

マウスのすべてのボタンに反応します。

### キー修飾子

キーの場合と同じでイベント発火を任意のボタンに絞ることができます。

- left

- right

- middle

<p data-height="265" data-theme-id="0" data-slug-hash="aQmBWj" data-default-tab="html,result" data-user="b1san1" data-pen-title="aQmBWj" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/aQmBWj/">aQmBWj</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## scroll

スクロール時に発火するイベントです。

<p data-height="265" data-theme-id="0" data-slug-hash="wQzgLE" data-default-tab="css,result" data-user="b1san1" data-pen-title="wQzgLE" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/wQzgLE/">wQzgLE</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## submit

submitされたときに発火するイベントです。form内のinput要素でEnterキーを押してもイベントが発火します。

submitを使用する際は必ず**prevent修飾子を指定する**ようにしてください。prevent修飾子については後述しますが、デフォルトのsubmitの動作を止める必要があります。

<p class="codepen" data-height="312" data-theme-id="dark" data-default-tab="html,result" data-user="b1san1" data-slug-hash="zMKoaJ" style="height: 312px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="zMKoaJ">
  <span>See the Pen <a href="https://codepen.io/b1san1/pen/zMKoaJ">
  zMKoaJ</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## drag, dragstart, dragend

対象要素をドラッグ可能（draggable）にした場合に、ドラッグ開始（dragstart）、ドラッグ中（drag）、ドラッグ終了（dragend）に該当するイベントになります。

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="html,result" data-user="b1san1" data-slug-hash="JjYjgdy" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="drag">
  <span>See the Pen <a href="https://codepen.io/b1san1/pen/JjYjgdy">
  drag</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## dragenter, dragleave

こちらはドラッグされる側のイベントになります。ドラッグ中にカーソルが当たった時（dragenter）と外れたとき（dragleave）のイベントになります。サンプルの黒い要素に適当なファイルをドラッグしてみてください。

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="html,result" data-user="b1san1" data-slug-hash="QWjbqQp" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="enter,leave">
  <span>See the Pen <a href="https://codepen.io/b1san1/pen/QWjbqQp">
  enter,leave</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## dragover, drop

こちらもドラッグされる側のイベントになります。dragoverは対象要素の中でドラッグしている間発火され続けます。dropは対象要素の中でドラッグをやめたときに発火されます。

サンプルでは対象要素にドラッグ＆ドロップされたファイル名の一覧を表示するようにしています。

注意点として、このようなドラッグ＆ドロップを実装する場合は、**dragoverでpreventDefault()を設定する必要があります。**そうしなければデフォルトの処理としてブラウザでファイルのプレビューが表示されてしまいます。

また**dropではstopPropagation()を設定します。**これをしなければ親要素でもdropイベントが発火してしまい、エラーが表示されてしまいます。

preventDefault()やstopPropagation()については、下記のイベント修飾子を参照してください。

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="html,result" data-user="b1san1" data-slug-hash="VwvLMXO" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="drop">
  <span>See the Pen <a href="https://codepen.io/b1san1/pen/VwvLMXO">
  drop</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# イベント修飾子

イベントを制御するために次のような修飾子が用意されています。

## stop

イベントの伝搬を防ぎます。(stopPropagation()）

例では子要素（背景緑）側にstop修飾子を設定することで親要素（背景黒）のclickイベントの伝搬を止めています。

<p data-height="265" data-theme-id="0" data-slug-hash="dQpNOG" data-default-tab="html,result" data-user="b1san1" data-pen-title="dQpNOG" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/dQpNOG/">dQpNOG</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## prevent

要素のデフォルトアクションを抑止します。（preventDefault()）

例ではaタグのhref要素にalert()を仕込んでいますが、preventを設定しているほうでは発火していないことがわかります。

<p data-height="265" data-theme-id="0" data-slug-hash="qQaRxb" data-default-tab="html,result" data-user="b1san1" data-pen-title="qQaRxb" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/qQaRxb/">qQaRxb</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## capture

addEventListener() optionsのcaptureに該当します。

例を動かすとわかる通り子要素（背景緑）をクリックした場合のイベント発生順序が異なります。

<p data-height="265" data-theme-id="0" data-slug-hash="RqGKJg" data-default-tab="html,result" data-user="b1san1" data-pen-title="RqGKJg" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/RqGKJg/">RqGKJg</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## self

イベントが設定された要素自身にアクションした場合のみイベントが発火します。

例の「self設定なし」の場合は、すべての要素でクリックイベントが発火します。
「self設定あり」の場合は、`.child`の要素をクリックしてもイベントは発火せず、`.parent`の要素自身をクリックした場合のみ発火します。



<p data-height="265" data-theme-id="0" data-slug-hash="jQMydd" data-default-tab="html,result" data-user="b1san1" data-pen-title="jQMydd" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/jQMydd/">jQMydd</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
## once

addEventListener() optionsのonceに該当します。

イベントの発火を一回に制限します。

<p data-height="265" data-theme-id="0" data-slug-hash="WYGRBL" data-default-tab="js,result" data-user="b1san1" data-pen-title="WYGRBL" class="codepen">See the Pen <a href="https://codepen.io/b1san1/pen/WYGRBL/">WYGRBL</a> by b1san (<a href="https://codepen.io/b1san1">@b1san1</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## passive

addEventListener() optionsのpassiveに該当します。

preventDefault()の処理に依存せず処理を実行できるため、Scroll Jankの解決策として利用されます。

# おわりに

フロントエンドのイベント理解するの難しい( ;∀;)