# イベントハンドリング

Learn how to handle events in a free Vue School lesson

# イベントの購読

v-on ディレクティブを使うことで、DOM イベントの購読、イベント発火時の JavaScript の実行が可能になります。これは通常 @ に省略することができます。v-on:click="methodName" もしくは @click="methodName" と書いて使用します。

例:

<div id="basic-event">
  <button @click="counter += 1">Add 1</button>
  <p>The button above has been clicked {{ counter }} times.</p>
</div>
1
2
3
4
Vue.createApp({
  data() {
    return {
      counter: 0
    }
  }
}).mount('#basic-event')
1
2
3
4
5
6
7

結果:

See the Pen Event handling: basic by Vue (@Vue) on CodePen.

# メソッドイベントハンドラ

多くのイベントハンドラのロジックはより複雑になっていくので、v-on 属性の値に JavaScript 式を記述し続けるのは現実的ではありません。そのため、v-on は呼び出したいメソッドの名前も受け付けます。

例:

<div id="event-with-method">
  <!-- `greet` は、あらかじめ定義したメソッドの名前 -->
  <button @click="greet">Greet</button>
</div>
1
2
3
4
Vue.createApp({
  data() {
    return {
      name: 'Vue.js'
    }
  },
  methods: {
    greet(event) {
      // メソッド内の `this` は、 Vue インスタンスを参照します
      alert('Hello ' + this.name + '!')
      // `event` は、ネイティブ DOM イベントです
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
}).mount('#event-with-method')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

結果:

See the Pen Event handling: with a method by Vue (@Vue) on CodePen.

# インラインメソッドハンドラ

メソッド名を直接指定する代わりに、インライン JavaScript 式でメソッドを指定することもできます:

<div id="inline-handler">
  <button @click="say('hi')">Say hi</button>
  <button @click="say('what')">Say what</button>
</div>
1
2
3
4
Vue.createApp({
  methods: {
    say(message) {
      alert(message)
    }
  }
}).mount('#inline-handler')
1
2
3
4
5
6
7

結果:

See the Pen Event handling: with an inline handler by Vue (@Vue) on CodePen.

時には、インラインステートメントハンドラでオリジナルの DOM イベントを参照したいこともあるでしょう。特別な $event 変数を使うことでメソッドに DOM イベントを渡すことができます:

<button @click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
1
2
3
// ...
methods: {
  warn(message, event) {
    // ネイティブイベントを参照しています
    if (event) {
      event.preventDefault()
    }
    alert(message)
  }
}
1
2
3
4
5
6
7
8
9
10

# 複数イベントハンドラ

イベントハンドラ内ではカンマで区切ることで、複数のメソッドを設定することができます:

<!-- ボタンをクリックすると、one()とtwo()の両方が実行されます -->
<button @click="one($event), two($event)">
  Submit
</button>
1
2
3
4
// ...
methods: {
  one(event) {
    // one($event)のハンドラーロジック
  },
  two(event) {
    // two($event)のハンドラーロジック
  }
}
1
2
3
4
5
6
7
8
9

# イベント修飾子

イベントハンドラ内での event.preventDefault() または event.stopPropagation() の呼び出しは、様々な場面で共通に必要になります。これらはメソッド内部で簡単に呼び出すことができますが、DOM イベントの込み入った処理をおこなうよりも、純粋なデータロジックだけになっている方がより良いでしょう。

この問題に対応するために、Vue は v-on のために イベント修飾子(event modifiers) を提供しています。修飾子は、ドット(.)で表記されるディレクティブの接尾辞を思い返してください。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
<!-- クリックイベントの伝搬が止まります -->
<a @click.stop="doThis"></a>

<!-- submit イベントによってページがリロードされません -->
<form @submit.prevent="onSubmit"></form>

<!-- 修飾子は繋げることができます -->
<a @click.stop.prevent="doThat"></a>

<!-- 値を指定せず、修飾子だけ利用することもできます -->
<form @submit.prevent></form>

<!-- イベントリスナーを追加するときにキャプチャモードで使います -->
<!-- 言い換えれば、内部要素を対象とするイベントは、その要素によって処理される前にここで処理されます -->
<div @click.capture="doThis">...</div>

<!-- event.target が要素自身のときだけ、ハンドラが呼び出されます -->
<!-- 言い換えると子要素のときは呼び出されません -->
<div @click.self="doThat">...</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

TIP

修飾子を使用するとき、関連するコードが同じ順序で生成されるため注意してください。それゆえ、@click.prevent.self を使用すると全てのクリックイベントを防ぐことはできますが、@click.self.prevent は要素自身におけるクリックイベントを防ぐだけです。

<!-- 最大1回、クリックイベントはトリガされます -->
<a @click.once="doThis"></a>
1
2

他の修飾子とは違って、ネイティブ DOM イベント専用ではありますが、.once 修飾子をコンポーネントイベントでも使用することができます。まだコンポーネントについて読んでいないなら、今は気にする必要はありません。

Vue は addEventListenerpassive オプション (opens new window)に対応する .passive 修飾子も提供しています。

<!-- `onScroll` が `event.preventDefault()` を含んでいたとしても -->
<!-- スクロールイベントのデフォルトの挙動(つまりスクロール)は -->
<!-- イベントの完了を待つことなくただちに発生するようになります -->
<div @scroll.passive="onScroll">...</div>
1
2
3
4

.passive 修飾子は特にモバイルでのパフォーマンスを改善するのに有用です。

TIP

.passive.prevent を一緒に使わないでください。.prevent は無視され、ブラウザにはおそらく警告が表示されます。.passive はイベントのデフォルトの挙動を妨げないことをブラウザに伝達することを思い出してください。

# キー修飾子

キーボードイベントを購読するにあたって、特定のキーのチェックが必要になることがあります。Vue では、v-on または @ に対してキー修飾子を追加することができます:

<!-- `key` が `Enter` のときだけ、`vm.submit()` が呼ばれます  -->
<input @keyup.enter="submit" />
1
2

KeyboardEvent.key (opens new window) で公開されている任意のキー名は、ケバブケースに変換することで修飾子として直接使用できます。

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

上の例では、ハンドラは $event.key'PageDown' に等しい場合だけ呼ばれます。

# キーコード

Vue は最も一般的に使用されるキーコードのエイリアスを提供しています:

  • .enter
  • .tab
  • .delete ("Delete" と "Backspace" キー両方をキャプチャします)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

# システム修飾子キー

次の修飾子を使用すると、対応するキーが押されたときにのみマウスもしくはキーボードのイベントリスナをトリガできます:

  • .ctrl
  • .alt
  • .shift
  • .meta

Note

注意: Macintosh キーボードの場合、meta はコマンドキー(⌘)です。Windows のキーボードでは、meta はウィンドウキー(⊞)です。Sun Microsystems のキーボードでは、メタは実線のダイヤモンド(◆)とマークされています。特定のキーボードでは、特に MIT や Lisp マシンのキーボードと Knight キーボード、space-cadet キーボード、メタのような後継機には "META" と表示されます。 Symbolics のキーボードでは、 “META" または “Meta" というラベルが付いています。

例:

<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
1
2
3
4
5

TIP

修飾子キーは通常のキーとは異なり、keyup イベントで使用する場合、イベント発生時に押されていなければならないことに注意してください。言い換えると、keyup.ctrlctrl を押しながら何かのキーを離したときにのみ、トリガされます。ctrl キーだけを離しても、トリガされません。

# .exact 修飾子

.exact 修飾子はイベントを引き起こすために必要なシステム修飾子の正確な組み合わせを制御します。

<!-- これは Ctrl に加えて Alt や Shift キーが押されていても発行されます -->
<button @click.ctrl="onClick">A</button>

<!-- これは Ctrl キーが押され、他のキーが押されてないときだけ発行されます -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- これは システム修飾子が押されてないときだけ発行されます -->
<button @click.exact="onClick">A</button>
1
2
3
4
5
6
7
8

# マウスボタンの修飾子

  • .left
  • .right
  • .middle

これらの修飾子は、イベントのトリガのハンドリングを、特定のマウスのボタンのみに制限します。

# なぜ HTML にリスナを記述するのですか

これまで説明してきたようなイベント監視のアプローチは、"関心の分離"という古き良きルールを破っているのではないか、と心配されるかもしれません。安心してください。すべての Vue ハンドラ関数と式は、現在の View を扱う ViewModel に厳密に閉じています。それによってメンテナンスが難しくなることはありません。実際、v-on または @ を利用することでいくつかの利点があります。

  1. HTML テンプレートを眺めることで、JS コード内のハンドラ関数を探すことを容易にします

  2. JS 内のイベントリスナーを手作業でアタッチする必要がないので、ViewModel を DOM 依存のない純粋なロジックにできます。これはテスタビリティを向上させます。

  3. ViewModel が消去されるときに、すべてのイベントリスナーは自動で削除されます。手動でそれらの消去をおこなうことを気にする必要はありません。

Deployed on Netlify.
最終更新日: 2021-06-19, 04:36:37 UTC