5.

Vue.js @click / v-on:click 完全ガイド(修飾子・$event・Composition API・$emit)

編集
この記事の要点
  • Vue.js のクリックイベントは @click="method"v-on:click の省略形)
  • 引数や $event を渡したいときは @click="method($event, id)"
  • 修飾子で挙動を変更: .stop / .prevent / .once / .self / .capture
  • Composition API では const handle = () => { ... } を template で @click="handle"
  • 子コンポーネントから親へは $emit("click", payload)(Vue3: defineEmits

基本: @click と v-on:click

Vue.js でクリックイベントを扱う最も基本的な書き方は v-on:click ディレクティブで、@ で省略できます。

<template>
  <!-- 省略形 (推奨) -->
  <button @click="increment">+1</button>

  <!-- 正式形 -->
  <button v-on:click="increment">+1</button>

  <!-- インライン式 -->
  <button @click="count++">+1</button>

  <!-- 引数を渡す -->
  <button @click="addItem('apple')">Apple</button>

  <!-- $event とともに渡す -->
  <button @click="addItem('apple', $event)">Apple</button>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);

const increment = () => { count.value++; };
const addItem = (name, event) => {
  console.log(name, event.target);
};
</script>

$event: ネイティブイベントオブジェクト

メソッドの引数に $event を渡すと、DOM の MouseEvent をそのまま受け取れます。

<template>
  <button @click="onClick($event)">クリック</button>
  <button @click="onClick">クリック (自動)</button>
</template>

<script setup>
const onClick = (e) => {
  console.log(e.target);          // クリックされた要素
  console.log(e.clientX, e.clientY); // 座標
  console.log(e.shiftKey, e.ctrlKey); // 修飾キー
  e.preventDefault();             // デフォルト動作キャンセル
  e.stopPropagation();            // バブリング停止
};
</script>

引数を書かずに @click="onClick" としても、第一引数に自動的に MouseEvent が渡されます。

イベント修飾子(Modifiers)

頻出のイベント処理を 修飾子で 1 行に書けます。e.preventDefault() 等を JS 側に書かずに済みます。

修飾子動作等価な JS
.stopイベント伝播を止めるe.stopPropagation()
.preventデフォルト動作をキャンセルe.preventDefault()
.captureキャプチャフェーズで処理addEventListener の第 3 引数 true
.self自身が target のときだけ発火if (e.target === e.currentTarget)
.once一度だけ発火addEventListener の {once:true}
.passiveパッシブリスナー(スクロール最適化)addEventListener の {passive:true}
<!-- バブリング停止 -->
<div @click="onOuter">
  <button @click.stop="onInner">押しても onOuter は呼ばれない</button>
</div>

<!-- ページ遷移を止めて Ajax 送信 -->
<form @submit.prevent="onSubmit">
  <button>送信</button>
</form>

<!-- 一度だけ -->
<button @click.once="onceOnly">一度だけ反応</button>

<!-- 修飾子は連結可 -->
<a @click.stop.prevent="onLink" href="/foo">独自処理</a>

<!-- 自身がクリックされたときだけ -->
<div @click.self="onBackdrop" class="modal-backdrop">
  <div class="modal-content">子要素クリックは無視される</div>
</div>

マウスボタン・キー修飾子

<!-- 左クリックのみ -->
<button @click.left="onLeft">左クリック</button>

<!-- 右クリックのみ -->
<button @click.right="onRight">右クリック</button>

<!-- 中ボタンクリック -->
<button @click.middle="onMiddle">中ボタン</button>

<!-- Ctrl + クリック -->
<button @click.ctrl="onCtrlClick">Ctrl+Click</button>

<!-- Shift + クリック(他のキーは無視) -->
<button @click.shift.exact="onShiftOnly">Shift+Click のみ</button>

Composition API でのクリック処理

<script setup>
import { ref, computed } from 'vue';

const count = ref(0);
const items = ref([]);

// シンプルな関数
const increment = () => count.value++;

// 引数あり
const remove = (id) => {
  items.value = items.value.filter(i => i.id !== id);
};

// async も使える
const submit = async () => {
  const res = await fetch('/api/save', { method: 'POST' });
  if (!res.ok) console.error('失敗');
};
</script>

<template>
  <button @click="increment">{{ count }}</button>

  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.name }}
      <button @click="remove(item.id)">削除</button>
    </li>
  </ul>

  <button @click="submit">保存</button>
</template>

子コンポーネントから親へ: $emit

子コンポーネントでクリックが起きたとき、親に通知するには $emit(Composition API では defineEmits)を使います。

<!-- 子: MyButton.vue -->
<script setup>
const emit = defineEmits(['click', 'longpress']);

const onNativeClick = (e) => {
  emit('click', { x: e.clientX, y: e.clientY });
};
</script>

<template>
  <button @click="onNativeClick"><slot /></button>
</template>

<!-- 親: Parent.vue -->
<script setup>
const handleChildClick = (payload) => {
  console.log('子からの payload:', payload);
};
</script>

<template>
  <MyButton @click="handleChildClick">押す</MyButton>
</template>

v-on のオブジェクト構文(複数イベント)

<template>
  <!-- 複数のイベントをまとめて -->
  <input v-on="{
    click: onClick,
    focus: onFocus,
    blur: onBlur,
    keydown: onKey,
  }" />
</template>

React onClick との比較

項目Vue.jsReact
記法@click="fn"onClick={fn}
引数渡し@click="fn(id)"onClick={() => fn(id)}
イベント取得$event または第一引数第一引数 SyntheticEvent
preventDefault@click.prevente.preventDefault() 手動
stopPropagation@click.stope.stopPropagation() 手動
カスタムイベント$emit + defineEmitsprops として渡す

よくあるトラブル

  • @click="fn()" と書いてレンダリング時に即実行されてしまう → 引数が無いなら @click="fn" に。引数があるなら @click="fn(arg)" のまま OK(テンプレ式は遅延評価される)
  • v-for 内でクリック先が間違う:key が無い / 重複している。一意キーを付ける
  • 子コンポーネントの @click が効かない → ルート要素が単一でない場合、attrs 継承されない。defineEmits(['click']) + @click="$emit('click', $event)" で明示
  • ボタン連打で多重送信.once 修飾子、または disabled 制御

FAQ

Q: Vue 2 と Vue 3 で書き方は変わる?
A: 基本構文は同じ(@click、修飾子、$emit)。Vue 3 は Composition API(<script setup>)と defineEmits が追加されたぶん、より明示的になりました。

Q: 動的なイベント名は使える?
A: 使えます。@[eventName]="handler" のように動的引数を指定できます。Vue 2.6+ / Vue 3。

Q: addEventListener との違いは?
A: @click は内部で addEventListener を呼んでいます。コンポーネントが unmount されるとリスナーも自動解除されるので、手動 cleanup 不要。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. インストール(ファイルのダウンロード)
  2. npmを使用したプロジェクトの作成
  3. for 繰り返し処理
  4. ifの条件分岐とtemplateを用いたグループ化
  5. クリック時のイベント処理(on:click)
  6. modelとdata フォーム入力値とDOMへの即時反映
  7. computed(算出プロパティ)と使い方とdataとの違い
  8. ライフサイクルフック(created / mounted / updated / destroyedの使い方)
  9. $nextTickの使い方(ライフサイクルフック)
  10. メソッドの定義方法
  11. エラー一覧
  12. ルーティング設定
  13. aリンクの貼り方と動的URLの作成
  14. Mixinを利用した共通処理の記述方法
  15. v-bindによるデータ連携
  16. ヘッダー/フッターの共通コンポーネント
  17. ナビゲーションの現在ページをハイライトする方法
  18. 画面サイズの取得方法

最近更新/作成されたページ