0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue3のイベント修飾子 `.prevent` と `.stop` を触ってみたメモ

0
Posted at

Vue3でボタンやリンクを触っていると、クリックしたときの「本来の動き」と「親要素へのイベントの伝わり方」が気になってきました。今回はイベント修飾子の .prevent.stop を試したメモです。

画像解析結果の一覧や詳細画面を作るときも、カード全体をクリック可能にしつつ、中のボタンだけ別の動きにしたい場面がありそうなので、ここで少し整理しておきます。

まだ途中段階なので、勘違いなどあれば教えていただけるとうれしいです。

今日やったこと

クリック時の標準動作を止める .prevent と、親要素へのクリック伝播を止める .stop を確認しました。

Vueでは、v-on または省略形の @ を使ってDOMイベントを受け取れます。@click は「クリックされたときに処理を実行する」という指定で、.prevent.stop はその後ろに付けるイベント修飾子です。Vue.js公式ドキュメントでも、.prevent.stopv-on のイベント修飾子として案内されています。 vueframework

リンクをクリックすると、通常はhrefに指定したページへ移動します。preventDefault() は、このようなブラウザ本来の標準動作を止めるメソッドです。

image.avif > 「クリックしただけで別ページへ移動すると、動きを確認する前に画面が変わってしまうね。」

Vueでは、$event を使うと発生したDOMイベントをテンプレートから受け取れます。次の例では、ボタンのクリック時にpreventDefault()を呼び出しています。

<template>
  <!-- buttonには通常、ページ移動などの標準動作はないが、イベント本体を受け取る書き方を確認する例 -->
  <button @click="$event.preventDefault()">Button</button>

  <!-- リンクをクリックしても href のURLへ移動しないようにする -->
  <a href="https://www.vuejs.org" @click="$event.preventDefault()">
    Vue.js
  </a>
</template>

ただし、Vueではこの用途のために .prevent が用意されています。@click.prevent と書くと、Vue側でevent.preventDefault()を呼ぶ形になるため、リンクの画面遷移を止めたい意図が読み取りやすいです。Vueの修飾子とネイティブのイベント処理の対応例でも、.preventevent.preventDefault() に対応しています。 vueframework

<template>
  <!-- クリックしてもリンク先へ移動せず、画面をそのまま維持する -->
  <a href="https://www.vuejs.org" @click.prevent>
    Vue.js
  </a>
</template>

@click.prevent="" と空の式を明示しても動く形はありますが、処理を書かないなら@click.preventのほうが短く、今の自分には目的が分かりやすく見えました。

つまずいたところ

.prevent.stop はどちらもクリックに付けられるものの、止めている対象が違うところで少し混乱しました。

.prevent は画面遷移を止める

最初は「クリックイベントそのものを止めるもの」なのかと思いましたが、.prevent が止めるのはイベントに関連する標準動作でした。リンクならページ遷移、フォームなら送信時の再読み込みなどが対象になりそうです。

image.avif > 「イベントを止めるのではなく、ブラウザがいつもやる動きを止める、と考えると少し分かれそう。」

例えば、画像解析結果のカードをクリックしたときに詳細を表示する前に確認ダイアログを出したい場合、リンクとして作った画面遷移をいったん止めてから処理を考える場面があるかもしれません。まだその実装は試していませんが、.prevent はこうした場面につながりそうです。

.stop は親へ伝わるイベントを止める

もう一つ混乱したのが、ボタンをクリックしたのに親のdivに書いた処理まで実行されることでした。クリックイベントは子要素から親要素へ伝わるため、ボタンを押すと外側のdiv@clickも反応します。

次のコードでは、ボタンを押すと親のdivにあるcount++も実行されるため、画面のカウントが増えます。

<script setup>
import { ref } from 'vue'

// count は画面に表示する数値。
// 値が変わると、テンプレート内の {{ count }} も自動で更新される。
const count = ref(0)
</script>

<template>
  <!-- ボタンを押したあと、親要素の処理で増えた数値を確認する -->
  <p>{{ count }}</p>

  <!-- div全体のクリックで count を1増やす -->
  <div @click="count++">
    <!-- クリックは親の div にも伝わるため、ここを押しても count が増える -->
    <button>Button</button>
  </div>
</template>

一方で、ボタン側に.stopを付けると、クリックイベントが親要素へ伝わらなくなります。つまり、ボタンをクリックしても外側のdivにあるcount++は実行されません。

image.avif > 「親のクリックまで反応するのは不具合ではなくて、イベントが伝わっている結果なんだね。」
<script setup>
import { ref } from 'vue'

// 初期表示するカウント。
// ref を使うことで、クリック後の値の変化を画面に反映できる。
const count = ref(0)
</script>

<template>
  <!-- 親要素がクリックされた回数を表示する -->
  <p>{{ count }}</p>

  <!-- divをクリックすると count が1増える -->
  <div @click="count++">
    <!-- .stop により、ボタンのクリックを親の div へ伝えない。
         そのため、このボタンを押しても count は増えない。 -->
    <button @click.stop="count = 30">Button</button>
  </div>
</template>

この例では、ボタンを押すとcount30になりますが、親のcount++は実行されないため、31にはなりません。.stopevent.stopPropagation() に対応する修飾子です。 vueframework

カード全体をクリックすると詳細を開き、カード内の「削除」や「編集」ボタンは別の操作だけをしたい場合に使えそうです。画像解析結果の一覧でも、カードクリックとカード内ボタンの役割を分けるときに必要になりそうだと感じました。

今の理解(メモ)

まだふわっとしているが、.prevent は標準動作、.stop はイベントの伝播を止めるためのものだと整理しています。

  • @click は、クリックされたときに処理を実行するためのVueの書き方です
  • $event は、テンプレート内でクリックイベントなどのDOMイベントを受け取るために使える値です
  • .prevent は、リンクの移動やフォーム送信など、ブラウザが元々行う動作を止めたいときに使えそうです
  • .stop は、子要素をクリックしたときに親要素のクリック処理まで実行されないようにしたいときに使えそうです
  • ref(0) のように作った値は、テンプレート内では.valueを付けずに{{ count }}と表示でき、クリック処理ではcount++count = 30のように書けるように見えています
image.avif > 「名前は似ているけれど、止める相手が違う。まずはリンクと親子要素で覚えるとよさそう。」

今の理解では、.prevent は「リンクを押しても画面を移動させない」、.stop は「内側のボタンを押しても外側のカードをクリックしたことにしない」と考えると区別しやすいです。Vueにはこれ以外にも.self.once.capture.passiveなどのイベント修飾子があるため、必要になったタイミングで少しずつ確認したいです。 ja.vuejs

次にやりたいこと

小さな画面を作りながら、修飾子が必要になる場面を確かめたいです。

  • 画像や作品のカード全体をクリックすると詳細表示に進む画面を作る。カード全体のクリック処理がどのように書けるか確認したいです
  • カード内に「お気に入り」や「削除」のようなボタンを置き、.stopでカードのクリックと分ける。ボタンを押しても詳細画面へ進まない状態を試したいです
  • 検索フォームを作り、送信時に.preventを付ける。画面の再読み込みをせず、入力した検索語で一覧を絞り込む流れを試したいです
  • .self も確認する。子要素をクリックした場合には動かず、要素自身をクリックした場合だけ処理する違いを見てみたいです

今日の整理

今回のイベント修飾子を、後から見返せるように今の理解でまとめます。

用語 役割 利用イメージ 次に確認したいこと
@click クリック時に処理を実行する ボタンでカウントを増やす、カードを開く メソッドに処理を分ける書き方
$event 発生したDOMイベントを受け取る preventDefault()などを直接呼ぶ イベントから取得できる情報
.prevent ブラウザの標準動作を止める リンク遷移やフォーム送信を止める フォーム検索での利用
.stop 親要素へのイベント伝播を止める カード内ボタンとカードクリックを分ける 複数のクリック処理を置く場合
ref 画面と連動する値を持つ カウント、検索語、一覧データ 配列やオブジェクトの扱い

おわりに

今回は.prevent.stopを触って、クリックに関する処理も「何を止めるのか」で役割が分かれることが少し見えてきました。まだなんとなく動く段階ですが、親子要素を置いたサンプルで見ると、イベントが伝わる流れを確認しやすかったです。

フロント側を覚えていけば、画像解析結果の一覧を見やすく表示したり、カード内に操作ボタンを置いたり、検索フォームで表示内容を絞り込んだりする画面にも近づけそうです。まずは小さな画面で、イベントがどこまで伝わるのかを確認していきたいです。

学習中の内容なので、勘違いや理解不足もあると思います。もし気になる点があれば教えてもらえるとうれしいです。

 注意事項
 本ブログに掲載している内容は、私個人の見解であり、
 所属する組織の立場や戦略、意見を代表するものではありません。
 あくまでエンジニアとしての経験や考えを発信していますのでご了承ください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?