Vueのバブリング止めてくれたりするイベント修飾子まとめ(@click.stopとか)

JavaScriptは便利な機能をたくさん用意してくれてます。

それをVueはイベント修飾子という形で色々使いやすく用意してくれています。

今回はそんなイベント修飾子の解説です。

公式は[こちら](https://jp.vuejs.org/v2/guide/events.html#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E4%BF%AE%E9%A3%BE%E5%AD%90)。

# **イベント修飾子は何がある？**

Vueは全部で6つイベント修飾子を用意してくれています。

- `.stop`
- `.prevent`
- `.capture`
- `.self`
- `.once`
- `.passive`

一つずつ解説していきます。

### **.stop**

これをつけるとバブリングを止めてくれます。

バブリングとは、子要素で発火したイベントが親要素でも効いちゃうってやつです。

具体的にバブリングを止めないとどうなるかというと、
子要素と親要素が両方clickイベントでの起こるメソッドを構えていた場合、
子要素でclickイベントが起こると、子要素のclickイベントでのメソッドの実行と同時に親要素のclickイベントのメソッドも実行されちゃう、という事象が起こります。

基本的にJavaScriptはイベントが発火されると、このバブリングの動きをして子から親へとイベントを伝播しちゃいます。
その伝播に対して「この先には伝播させない」と止めてくれるイベント修飾子が.stopになります。

ちなみにjsで用意されている[event.stopPropagation()](https://developer.mozilla.org/ja/docs/Web/API/Event/stopPropagation)をラッピングしています。

### **.prevent**

こちらもよく使われるやつです。これはformのsubmitイベントに対して使うやつです。

form要素に送信先が指定されていない場合のデフォルトの動きをキャンセルしてくれます。
送信先が指定されていないデフォルトの動きとは、自身のURLに対して送信を行うというものです。

要は、送信先のURL指定がなければリロードしちゃうよって話ですね。
これをキャンセルしてくれます。

.preventをつけるとリロードせずに値を送信しますよーって感じです。

jsで言うところの[event.preventDefault()](https://developer.mozilla.org/ja/docs/Web/API/Event/preventDefault)をラッピングしてます。

### **.capture**

これはちょっとマニアックなやつです。
イベントの伝播をキャプチャフェーズで行うようにしてくれます。

まずは前提知識がいるのでマニアックな話を挟みます。
ざっくりとしか解説しないので、細かいところは各自調べていただけたらと思います。

まずJavaScriptはイベントを感知して色々行ってくれますが、このイベント実行にはいくつかフェーズと呼ばれるものがあります。

**キャプチャフェーズ**、**ターゲットフェーズ**、**バブリングフェーズ**の3つです。

一番簡単なのはターゲットフェーズです。

これは、イベントが起こった場所を特定するっていうものですね。
このタグのこのイベントだな、となります。

次にバブリングフェーズです。

これは.stopの解説でも出てきましたが、イベントが発火するとターゲットフェーズで発火した場所が特定されます。
そしてそのイベントが起こった要素からどんどん親要素にイベントが伝播していきます。
上に上がっていくのがバブリングフェーズと覚えてください。(子:イベント発火箇所→親→さらに親→ルート)

そして最後が、キャプチャフェーズです。

これはバブリングフェーズとは反対に、ターゲットフェーズで特定した要素に向かって上から順番にイベントが伝播していきます。(ルート→親→子→孫:イベント発火箇所)

jsでは、ターゲットフェーズからバブリングフェーズにいくのが基本的な動きなんですが、
これをキャプチャフェーズの動きで実行させるというのが.captureです。
あんまり使う機会はないかもしれません。

以下の図がわかりやすいです。拾ってきました。

!![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3681767/7aa243e0-b746-3164-c98b-a4905acb9c8a.png)

[W3C](http://www.w3.org/TR/DOM-Level-3-Events/)のドキュメントに載っていた図をお借りしました

実験してみたければ、Vueの動く環境で親要素と子要素両方にclickイベントを待たせておいて`.stop`と組み合わせれば、親のメソッドだけ動くのがわかると思います。

```sample.vue
<!-- 細かいのは端折ってます -->

<div>
  <div id="myParent" v-on:click.capture.stop="onClickParent">
    親要素
    <div id="myElement" v-on:click.capture.stop="onClickChild">
      子要素
    </div>
  </div>
</div>

<script>
~~~

methods: {
    onClickParent() {
      *console*.log('親要素のclickイベントが発火');
    },
    onClickChild() {
      *console*.log('子要素のclickイベントが発火');
    },
}
~~~
<script>

// クリックすると、「親要素のclickイベントが発火」とだけ表示される。
// .captureだけ消した状態でクリックすると、「子要素のclickイベントが発火」とだけ表示される。
```

ちなみに.captureはjsでいうと

[event.addEventListener()](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener)で第3引数のuseCaptureにtrueを指定した時と同様の動作です。

### **.self**

これは.stopと似ています。

イベントの伝播を制御してくれて、イベントが発生した要素でのみイベントハンドラーを実行します。

.stopとの違いは.stopは伝播を止めると言うのに近いので、2つ下の要素から伝播してきたものを自分のタイミングで止める、みたいな使い方もできちゃいますが.selfは自身しか実行しないので.stopよりも目的が限定的です。

jsでいうと、`event.target`が自分自身の場合のみハンドラが呼び出されるという感じですね。

### **.once**

一回だけ実行するよってやつです。一回実行すると構えているイベントでは使えなくなります。

これは余談になりますが、
よくハマる例として、ボタンの連打対策とかに.onceを使うというのがすぐに思いついたりするんですが、
バリデーションなどボタン押下するための条件を盛り込んでいると、バリデーションで引っかかると使えなくなるバグの原因になったりしますので注意です。

jsでいうと、[event.addEventListener()](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener)の第3引数のoptionsに`{ once: true }`を指定した時と同じ動きです。

### **.passive**

スクロールイベントをスムーズにしてくれるやつです。

イベントの完了を待たずにスクロールしてくれるみたいですね。

注意点としては、.preventのイベント修飾子と同時に使うのは公式で非推奨なのでやめましょう。

`.prevent`が無視されて警告が出ちゃうそうです。

これもjsでは[event.addEventListener()](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener)の第3引数のpassiveに`{ passive: true }`を指定した時の動きですね。

# **イベント修飾子の使い方**

いつものv-onと同じ感覚で使います。

```vue
<v-btn @click.stop="methodA"></v-btn>
```

あとは、イベント修飾子はつなげて書くこともできます

```vue
<a @click.stop.prevent="methodB"></a>
```

イベント修飾子のみのパターンもあります。

```vue
<form v-on:submit.prevent></form>
```

# **おわり**

使う機会がとてもたくさん、というわけではないですが、
絶対に知っておくべき便利なイベント修飾子のご紹介でした。

Vueが用意してくれてるのは知っていても、jsのどの機能なのかまで明記されているサイトは少なかったので今回盛り込んでおります。

よかったら参考にしてください

# **参考**

・[Vue公式](https://jp.vuejs.org/v2/guide/events.html#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E4%BF%AE%E9%A3%BE%E5%AD%90)

・[https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener)

・[https://developer.mozilla.org/ja/docs/Web/API/Event](https://developer.mozilla.org/ja/docs/Web/API/Event)

・[https://qiita.com/yokoto/items/27c56ebc4b818167ef9e](https://qiita.com/yokoto/items/27c56ebc4b818167ef9e)

・[https://johobase.com/vue-js-once-modifier/](https://johobase.com/vue-js-once-modifier/)

・[https://johobase.com/vue-js-capture-modifier/](https://johobase.com/vue-js-capture-modifier/)

・[https://johobase.com/javascript-event-propagation/](https://johobase.com/javascript-event-propagation/)

・[https://qiita.com/hosomichi/items/49500fea5fdf43f59c58](https://qiita.com/hosomichi/items/49500fea5fdf43f59c58)

・[https://rbell.jp/blog/nuoysymac/](https://rbell.jp/blog/nuoysymac/)