あっくんブログ Written by Akihiro Tsuji

Vue3のイベントを掘り下げる

Vue.js プログラミング

こんにちは、あっくんです。先日、兵庫県たつの市にいってきました。自然がいっぱいですごい空気のきれいなところで気持ちいいです。月に一度ほど、たつの市に行ってのんびりしています。キャンプをしたことないですが、キャンプ飯を知り合いの家のお庭でしました。チキンラーメンをキャンプ用品のガスバーナーとなべでお湯を沸かして作りました。シンプルに日を使うことで原始的な感じで、癒やされました。今回はVue3のイベントの修飾子についてやっていきましょう。 

イベントの修飾子

イベント関係への処理の割り当てはv-onを使って設定を行います。イベントは発生したら、実行するだけという単純なもではありません。イベントは、他のオブジェクトに伝搬するのか。あるイベントの処理を停止したり再開したりすることもあります。

イベントの修飾子

イベントの修飾子とは、v-onのイベント後に「
修飾子」をつけることができます。

v-on:イベント名.修飾子 = "○○"

修飾子の例、

.stop
イベントの伝搬をそこで停止します。

.prevent
イベントのデフォルトのアクションを停止します。

.capture
イベントをキャプチャーします。

.self
自身がイベントの発生源であるときだけ発火します。

.once
連続してイベントが発生するとき1度だけ発火します。

.passive
scroll(スクロールイベント)でイベントが停止されてもスクロール処理は動くようにします。

イベントの伝搬を考える

イベントが発生した場所に複数のタグが重なったりすると、1番上にあるタグから次々と下のタグにイベントが伝えられます。

Image from Gyazo

↑イベント(click)が発生すると、下のタグへと伝わっていきます。

イベントの流れ

event_appをViteで作成して、やっていきましょう。

開発するディレクトリでターミナルに↓のように記述してevent_appプロジェクトを作って、npmをインストールします。

npm init vite-app event_app

cd event_app
npm install

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <pre v-on:click="clear">{{ message }}</pre> <hr /> <div id="out" class="out" v-on:click="a_event"> A <div id="mid" class="mid" v-on:click="b_event"> B <div id="in" class="in" v-on:click="c_event">C</div> </div> </div> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Event", message: "イベントの伝播について。\n", }; }, methods: { a_event(event) { this.message += "A-Event [" + event.target.id + " → " + event.currentTarget.id + "]\n"; }, b_event(event) { this.message += "B-Event [" + event.target.id + " → " + event.currentTarget.id + "]\n"; }, c_event(event) { this.message += "C-Event [" + event.target.id + " → " + event.currentTarget.id + "]\n"; }, clear() { this.message = "イベントの伝播について。\n"; }, }, }; </script> <style> pre { font-size: 16px; line-height: 1.25; } div.out { padding: 0px; background-color: #eee; width: 300px; height: 200px; } div.mid { padding: 0; background-color: #ddd; width: 200px; height: 170px; } div.in { padding: 0; background-color: #ccc; width: 100px; height: 140px; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑Cのグレー部分をクリックすると、どのようにイベントが伝播されていくかメッセージに表示されます。

C-Eventは一番上の3つ重なった部分です。
C-Eventが実行されると 
[in → in]
[in → mid]
[in → out]
とB,Aへとイベントが伝播されます。

stopしてみる

<div id="mid" class="mid" v-on:click.stop="b_event">
B

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <pre v-on:click="clear">{{ message }}</pre> <hr /> <div id="out" class="out" v-on:click="a_event"> A <div id="mid" class="mid" v-on:click.stop="b_event"> B <div id="in" class="in" v-on:click="c_event">C</div> </div> </div> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Event", message: "イベントの伝播について。\n", }; }, methods: { a_event(event) { this.message += "A-Event [" + event.target.id + " → " + event.currentTarget.id + "]\n"; }, b_event(event) { this.message += "B-Event [" + event.target.id + " → " + event.currentTarget.id + "]\n"; }, c_event(event) { this.message += "C-Event [" + event.target.id + " → " + event.currentTarget.id + "]\n"; }, clear() { this.message = "イベントの伝播について。\n"; }, }, }; </script> <style> pre { font-size: 16px; line-height: 1.25; } div.out { padding: 0px; background-color: #eee; width: 300px; height: 200px; } div.mid { padding: 0; background-color: #ddd; width: 200px; height: 170px; } div.in { padding: 0; background-color: #ccc; width: 100px; height: 140px; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑一番上のCをクリックすると、C(in),B(mid)とイベントが送られ、BでイベントがストップしているためA(out)には遅れません。

selfしてみる

<div id="mid" class="mid" v-on:click.self="b_event">
B

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <pre v-on:click="clear">{{ message }}</pre> <hr /> <div id="out" class="out" v-on:click="a_event"> A <div id="mid" class="mid" v-on:click.self="b_event"> B <div id="in" class="in" v-on:click="c_event">C</div> </div> </div> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Event", message: "イベントの伝播について。\n", }; }, methods: { a_event(event) { this.message += "A-Event [" + event.target.id + " → " + event.currentTarget.id + "]\n"; }, b_event(event) { this.message += "B-Event [" + event.target.id + " → " + event.currentTarget.id + "]\n"; }, c_event(event) { this.message += "C-Event [" + event.target.id + " → " + event.currentTarget.id + "]\n"; }, clear() { this.message = "イベントの伝播について。\n"; }, }, }; </script> <style> pre { font-size: 16px; line-height: 1.25; } div.out { padding: 0px; background-color: #eee; width: 300px; height: 200px; } div.mid { padding: 0; background-color: #ddd; width: 200px; height: 170px; } div.in { padding: 0; background-color: #ccc; width: 100px; height: 140px; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑一番上のCをクリックすると、selfの修飾子でBは自身でイベントが発生していないため、C(in)からA(out)におくられ、B(mid)のイベントは発生しません。

キーイベントについて

入力フィールド関係では、キー操作に関係するイベントが用意されています。

keypress
キーをタイプしたときに発生します。

keydown
キーを押し下げた瞬間に発生します。

keyup
キーを離した瞬間に発生します。

HelloWorld.vueを修正してキーイベントサンプルを作りましょう。

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <pre v-on:click="clear">{{ message }}</pre> <hr /> <div><input type="text" v-on:keydown="type" class="form-control" /></div> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Event", message: "", }; }, methods: { type(event) { this.message += event.key + ""; if (event.key == "Escape") { this.message = ""; } event.target.value = ""; }, clear() { this.message = ""; }, }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑入力フィールドでキーをタイプすると、押したキーがメッセージn追加されていきます。

押したキーの特殊なキーも表示されます。Escキーを押すとメッセージはクリアされます。

キーイベントの修飾子について

特殊なキーイベントを設定するものが中心です。

enter
Enter/Retrunキーのイベント

tab
Tabキーのイベント

delete
Delete/Backspaceキーのイベント

esc
Esc(エスケープ)キーのイベント

space
スペースバーのイベント

up,down,left,right
上下左右キーのイベント

キーごとにイベントを設定する

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <pre>{{ message }}</pre> <hr /> <div> <input type="text" class="form-control" v-on:keypress="type" v-on:keydown.delete="clear" v-on:keydown.space="space" v-on:keydown.enter="enter" /> </div> </div> </template> <script> export default { name: "HelloWorld", data() { return { title: "Event", message: "", } }, methods: { type(event) { if (event.key == "Enter") { return; } this.message += event.key + " "; event.target.value = ""; }, clear() { this.message = ""; }, space() { this.message += "_ "; }, enter(event) { let res = this.message.split("").join(""); this.message = res.split("_").join(""); event.target.value = ""; }, }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑キータイプするとスペースをあけて文字が表示されていきます。Enter/Returnキーを押すとスペースが消え、ひとつづきの文になります。deleteキーを押すと表示がクリアされます。

複数のキーイベントを使う。

type
Enterキー以外のとき、タイプされたキーの文字にスペースを付けてmessageを返します。

clear
messageを消去します。

space
アンダーバーをスペースの代わりにmessageを追加します。

enter
messageのスペースをすべて取り除き、アンダーバーをスペースに変換します。

機能キーの組み合わせ

キーの中でも特殊な動きをするのが、ctrl,shift,alt,といったキーです。

shift
Shiftキーのイベント

control
Ctrlnキーのイベント

alt
Altキーのイベント

meta
Windowsキーのイベント

exact
指定された以外の機能キーが押されていないことを示す。

押された機能キーを表示

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <pre>{{ message }}</pre> <hr /> <div class="area" v-on:click="click" v-on:click.exact="exact" v-on:click.shift="shift" v-on:click.ctrl="ctrl" v-on:click.alt="alt" > click here! </div> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Event", message: "", }; }, methods: { click() { this.message = "click "; }, exact() { this.message += "**no any key**"; }, shift() { this.message += "[shift]"; }, ctrl() { this.message += "[ctrl]"; }, alt() { this.message += "[alt]"; }, }, }; </script> <style> .area { width: 300px; height: 100px; background-color: #fff; padding: 10px; font-size: 20pt; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑白いエリアを機能キーを押した状態でクリックすると、押されたキーを表示します。

ボタンの修飾子

イベントが発生したマウスボタンを特定するための修飾子もあります。3つのボタンそれぞれに修飾子が用意されていますが、何も付けていなければ、通常は左ボタンをクリックしたときにイベントが発生します。

left
左ボタン

right
右ボタン

middle
中央ボタン

exact
指定ボタン以外がおされていない

クリックしたボタンを表示する

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <pre>{{ message }}</pre> <hr /> <div class="area" v-on:click="click" v-on:click.left.prevent="left" v-on:click.middle.prevent="middle" v-on:click.right.prevent="right" > click here! </div> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Event", message: "", }; }, methods: { left() { this.message = "[left button]"; }, right() { this.message = "[right button]"; }, middle() { this.message = "[middle button]"; }, }, }; </script> <style> .area { width: 300px; height: 100px; background-color: #fff; padding: 10px; font-size: 20pt; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑白いエリアをクリックすると、押したマウスボタンを表示します。

preventについて

preventを指定すると、そこでイベントを消費し、背後にイベントが伝えられなくなります。イベントがつたわらないため、メニューのポップアップもされなくなります。

ここまで読んでいただいてありがとうございます。
今回はイベントに対してさまざまな修飾子があって、イベントをコントロールできるということがわかりました。
掌田津耶乃さんのVue.js3超入門で勉強をしています。語りかけるような文章ですごいわかりやすいです。本当におすすめです。