Event Handling - イベント処理と modifier

ロードマップ: Vue.js学習ロードマップ

公式ドキュメント: https://vuejs.org/guide/essentials/event-handling.html

What(何についてか)

Vue の v-on(省略形 @)を使って DOM イベントを受け取り、状態更新や関数呼び出しを行うための仕組みを扱う。 この章では、inline handler と method handler の使い分け、DOM Event の自動受け渡し、$event の明示的な受け渡し、event modifier・key modifier・mouse button modifier の役割を整理する。

Why(なぜ必要か)

Vue の UI はリアクティブな状態に応じて更新されるが、状態変化の入口はユーザー操作である。 クリック、フォーム送信、キーボード入力、スクロールなどのイベントを適切に受け取り、DOM 都合の制御と業務ロジックを分離することで、テンプレートと処理の見通しを良くできる。

How(どう動くか)

1. v-on / @ でイベントを監視する

<button @click="count++">Add 1</button>

@click は click イベント発生時に右辺の式や関数を実行する。 Vue 独自のイベントというより、DOM イベントをテンプレートから宣言的に扱いやすくしたものとして理解する。

2. handler の書き方は inline と method の2系統がある

Inline handler

<script setup>
import { ref } from 'vue'
 
const count = ref(0)
</script>
 
<template>
  <button @click="count++">Add 1</button>
  <p>Count is: {{ count }}</p>
</template>

短い式をその場で実行する。 単純なカウンタ更新やフラグ切り替えのように、テンプレート上で意味が完結する処理に向く。

Method handler

<script setup>
import { ref } from 'vue'
 
const name = ref('Vue.js')
 
function greet(event) {
  alert(`Hello ${name.value}!`)
  if (event) {
    alert(event.target.tagName)
  }
}
</script>
 
<template>
  <button @click="greet">Greet</button>
</template>

複雑な処理は関数へ分離する。 @click="greet" のように関数参照を渡すと、Vue は native DOM Event を第1引数として自動で渡す。

3. greetgreet() は別物である

  • @click="greet" は method handler
  • @click="greet()" は inline handler

Vue は foofoo.bar のような識別子・プロパティ参照を method handler とみなし、foo()count++ のような式は inline handler とみなす。

そのため、@click="greet()" では event は自動注入されない。 必要であれば $event を明示する。

<button @click="greet($event)">Greet</button>
<button @click="greet('hello', $event)">Greet</button>

4. $event で元の DOM Event を明示的に受け取れる

<button @click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

inline handler で独自引数と event を併用したいときに使う。 実務では「削除対象の id と event を両方渡す」などの場面で有効である。

5. event modifier で DOM イベント制御をテンプレートへ寄せる

<form @submit.prevent="onSubmit"></form>
<a @click.stop="doThis"></a>
<div @click.self="closeModal">...</div>

handler 内で preventDefault()stopPropagation() を都度書かず、テンプレート側に制御意図を明示できる。

主な event modifier

Modifier役割実務例
.stop親要素への伝播を止めるカード内の削除ボタン
.preventブラウザ標準動作を止めるフォーム送信, リンク遷移抑止
.selfevent.target がその要素自身のときだけ実行するモーダル背景クリックで閉じる
.capturecapture phase で先に拾う特殊なイベント順制御
.once一度だけ発火する初回のみの導線
.passiveprevent しない前提でブラウザ最適化を有効にするscroll / touch 系

modifier の順序は意味を持つ

@click.prevent.self="doThat"@click.self.prevent="doThat" は同じではない。 Vue は modifier を書いた順に対応コードを組み立てるため、条件判定より前に preventDefault() を実行するか、後に実行するかが変わる。

概念的には次の違いになる。

// @click.prevent.self="doThat"
event.preventDefault()
if (event.target !== event.currentTarget) return
doThat()
// @click.self.prevent="doThat"
if (event.target !== event.currentTarget) return
event.preventDefault()
doThat()

前者は子要素クリックでも default action を止める可能性があり、後者はその要素自身がクリックされた場合にだけ default action を止める。

.passive.prevent は併用しない

.passive は「この listener は preventDefault() しない」とブラウザへ伝えるため、.prevent と思想が衝突する。 スクロール系イベントの性能改善では .passive 単体で使う。

6. key modifier で特定のキーだけ反応させる

<input @keyup.enter="submit" />
<input @keyup.page-down="onPageDown" />

Vue は KeyboardEvent.key に応じた key modifier を提供する。 よく使う alias として .enter, .tab, .delete, .esc, .space, .up, .down, .left, .right がある。

System modifier keys

<input @keyup.alt.enter="clear" />
<div @click.ctrl="doSomething">Do something</div>

.ctrl, .alt, .shift, .meta を組み合わせて補助キー付き操作を定義できる。 .exact を使うと、余計な補助キーなしの厳密な組み合わせだけに限定できる。

<button @click.ctrl.exact="onCtrlClick">A</button>
<button @click.exact="onClick">A</button>

IME を考慮する場合は handler 側で補う

日本語入力のように IME 変換中の Enter と送信確定の Enter を区別したい場合、@keyup.enter@keydown.enter だけでは不十分なことがある。 その場合は event.isComposing や composition 系イベントを使って判定する。

function onEnter(event: KeyboardEvent) {
  if (event.isComposing) return
  submit()
}

7. mouse button modifier でボタン種別を限定する

  • .left
  • .right
  • .middle

これらは物理的な左右ボタンではなく、論理的な main / secondary / auxiliary 操作に対応する。 右クリック独自メニューでは contextmenu.prevent を組み合わせる設計も多い。

<div @contextmenu.prevent="openMenu">
  Right click me
</div>

イベント処理の全体フロー

flowchart LR
  A["ユーザー操作"] --> B["DOM Event 発生"]
  B --> C["Vue template の @event が受け取る"]
  C --> D["modifier で制御"]
  D --> E["handler 実行"]
  E --> F["状態更新"]
  F --> G["UI 再描画"]

実務での書き分け指針

単純な状態更新

<button @click="count++">Add 1</button>

複雑な処理

<button @click="save">Save</button>

業務引数を渡したい

<button @click="removeItem(id)">Delete</button>

業務引数と event を両方使いたい

<button @click="removeItem(id, $event)">Delete</button>

フォーム送信でページリロードを止めたい

<form @submit.prevent="onSubmit">

モーダル背景クリックだけで閉じたい

<div @click.self="closeModal">

Options API → Composition API 差分(補足)

項目Options API(旧)Composition API(新)
handler の定義場所methods に配置する<script setup> で通常の関数として定義する
状態参照this.count, this.name を使うref / reactive を直接参照する
this の依存this に現在インスタンスが束縛されるthis を使わず、変数と関数を直接扱う
イベント処理の書き味構造は似るが methods 中心setup 由来の関数と状態をそのままテンプレートへ公開する

Key Concepts

用語説明
v-on / @DOM イベントを監視するディレクティブ
inline handlerテンプレート上で式を直接実行する handler
method handler関数参照を渡して呼び出す handler
$eventinline handler 内で参照できる元の DOM Event
event modifier伝播や default action などのイベント制御を宣言する記法
key modifier特定キーや補助キーに限定する記法
.exact補助キーの厳密一致を要求する modifier
mouse button modifier主ボタン / 副ボタン / 補助ボタンに応じて分岐する記法
event.isComposingIME 変換中かどうかを判定する DOM Event のプロパティ