【輪読会資料】基礎から学ぶVue.js CHAPTER3 イベントとフォーム入力の受け取り 読書メモ
以下の記事は2019/2/14 コワーキングスペース秋葉原Weeybleで行われる輪読会
[秋葉原] 基礎から学ぶVue.js輪読会 ch3 イベントとフォーム入力 (初心者歓迎!)のための読書メモとなります。
以下の書籍の CHAPTER3 イベントとフォーム入力の受け取り のメモです。
- 作者: mio
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2018/05/29
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
CHAPTER3 イベントとフォーム入力の受け取り
ちなみに Weeybleで去年同じ本の輪読会で使ったドキュメント、およびソースコードは以下にありました。
GitHub yasugahira0810/vuejs_chapter3
書籍用のサイトのCHAPTER3記述ページ(サンプルコード有り)
これまでの輪読会資料
CAHPTER 1
【輪読会資料】基礎から学ぶVue.js CHAPTER1 Vue.jsとフレームワークの基礎知識 読書メモ - 作りたいものがありすぎる
CHAPTER 2
【輪読会資料】基礎から学ぶVue.js CHAPTER2 データの登録と更新 - Qiita
SECTION 13 イベントハンドリング
イベントハンドラ
これまでのサンプルのボタンに出てた v-on
の事をここでは解説する
イベントに紐づける処理の内容をこの本では「イベントハンドラ」と呼び
イベントハンドラとイベントを紐づけることを「ハンドル」と呼ぶ
イベントはmousewheel
等IE9では動かないものもあるので注意
<button v-on:click="doRemove(index)">モンスターを削除</button>
@
で記述も可能
<button @click="doRemove(index)">モンスターを削除</button>
new Vue({ el: '#app', data: { }, methods: { doRemove: function (index) { // ボタンクリックでこの処理が走る this.list.splice(index, 1) } } })
click
同様ブラウザが対応していれば以下のイベントも使える
- scroll
- mousewheel
フォーム入力の取得
v-on
ディレクティブで入力内容を確認してからデータに代入することができる
<input v-bind:value="message" v-on:change="handleInput">
new Vue({ el: '#app', data: { message: 'Hello Vue.js', }, methods: { handleInput: function (event) { // 代入前に何か処理を行う… // バリデード処理とかできるのかな? this.message = event.target.value } } })
イベント修飾子
click などのDOMのふるまいを変更する
- .stop event.stopPropagation(); イベント伝播,バブリングを止める
- .prevment event.preventDefault(); 禁止操作の指定 リンク操作、
submit
の処理をキャンセル - .capture キャプチャーモードDOMイベントをハンドルする
- .self
- .native
- .once
- .passive { passive: true } でDOMイベントはハンドルする
クリックイベント マウスボタンを指定できる
- .left
- .right
- .middle
作例
<!-- いずれもhandlerメソッドでマウス右クリックでconsole.logにmouse event のlogを出力する --> <div v-on:click.right="handler">example</div> <!-- こっちは`.prevment`修飾子で右クリックメニューの表示を禁止している --> <div v-on:click.right.prevent="handler">example</div>
new Vue({ el: '#app', methods: { handler: function (comment) { console.log(comment) } } })
Extra DOMイベント伝播,バブリングについて
そもそもJavaScriptのバブリングの概念を知っておく必要あり
DOMイベントのキャプチャ/バブリングを整理する 〜 JSおくのほそ道 #017
その上でイベント修飾子を付与することでバブリングを制御できる。
入れ子のDOMイベントの発生順序は
JSの addEventListener
には第三引数に省略可能でデフォルトはfalseの値がある。
これをuseCapture
という
<div id="outer"> <div id="inner" align="center"></div> </div>
function out(s) {return function() {console.log(s);}} document.getElementById('outer').addEventListener('click', out('outer'), false); // ←コレ document.getElementById('inner').addEventListener('click', out('inner'));
#outer
,.addEventListener
の第三引数を false
または省略した場合、innerのイベントが先に発火する。
逆にture
にすればouterが先に発火する
結果として、これを理解していないでアッチコッチにイベント仕込むと、親要素にイベントが伝播しまくって困った事になるらしい。
そこでVue.jsでは前述のイベント修飾子を使って伝播の制御を行う、という事らしい
では以下のJSを元に書くイベント修飾子の動きを確認してゆく
new Vue({ el: '#app', methods: { handler: function (comment) { console.log(comment) } } })
.stop
event.stopPropagation(); イベント伝播,バブリングを止める
div2クリックでdiv2
のみが出力
<div v-on:click="handler('div1')"> div1 <a href="#top" v-on:click.stop="handler('div2')">div2</a> </div>
.prevent
event.preventDefault(); 禁止操作の指定 リンク操作、submit
の処理をキャンセル
div2クリックでdiv2
,div1
と出力(操作の禁止をするのみなので伝播は通常通り起こるという事?)
<div v-on:click="handler('div1')"> div1 <a href="#top" v-on:click.prevent="handler('div2')">div2</a> </div>
.capture
キャプチャーモードでイベントを発生させる バブリングモードのイベントよりも先に発生する
div3クリックでdiv1
,div3
,div2
の順で出力
<div v-on:click.capture="handler('div1')"> div1 <div v-on:click="handler('div2')"> div2 <div v-on:click="handler('div3')">div3</div> </div> </div>
.self
evant.target
が自分自身の時だけハンドラが呼び出される
<div class="overlay" v-on:click.self="close">div</div>
.native
直接イベントを発火させたい場合に使う 詳細はCAPTER5に
<!-- コンポーネントをクリックするとハンドラが呼び出される --> <my-component v-on:click.native="handler"></my-component> <!-- コンポーネントをクリックしてもハンドラは呼び出されない --> <my-component v-on:click="handler"></my-component>
.passive
event.prevmentDefault()を呼び出さない事を明示的にする
.preventとの併用はNG
モバイル環境でのスクロールカク追記を防ぐ等に使用
キー修飾子
キーボード入力時に呼び出される様になる修飾子,キーコードか、キー指定でもOK
<!-- どちらもEnterキーを表す --> <input v-on:keydown.13="handler"> <input v-on:keydown.enter="handler">
システム修飾子
キーが押されている場合のみハンドラが呼び出される
以下はshiftキーの例
<button v-on:click.shift="doDelete"></button>
その他詳細はVue.js公式ガイド「イベントハンドリング」「システム修飾子キー」を参照のこと
SECTION14 フォーム入力バインディング
フォームの入力や選択値を、データを同期する 「双方向データバインディング」 にはv-model
ディテクティブを使う
v-model
の使い方
テキストフォームをmessage
プロパティとバインディングした例
<div id="app"> <input v-model="message"> <p>{{ message }}</p> </div>
new Vue({ el: '#app', data: { message: 'Hello!' } })
Vue.jsの双方向データディバイディング
入力した文字をデータに反映したい場合は、入力イベントをハンドルして取得したデータをリアクティブデータに代入する必要がある。
this.message = event.taget.value // ここでデータが書き換わる
一連の例に出て来るmessage
を使用した処理は良く行われる
v-mode
ディレクティブはDOMのデータバインディングと要素から取得したデータをリアクティブにするための鉄板構文らしい。
v-modelで受け取りデータの型
基本、入力フォームは文字列型、複数選択フォームは配列型となるが、値にデータバインディングを使用した場合、値の型はバインドされているデータによって変わる。
複数行テキスト
文字列となる。
<textarea v-model="message"></textarea> <pre>{{ message }}</pre>
new Vue({ el: '#app', data: { message: 'Hello!' } })
チェックボックス
単数の場合、は単純に bool
<label> <input type="checkbox" v-model="val"> {{ val }} </label>
new Vue({ el: '#app', data: { val: true } })
複数要素は配列、各要素にvalue
属性を設定
<label><input type="checkbox" v-model="val" value="A"> A</label> <label><input type="checkbox" v-model="val" value="B"> B</label> <label><input type="checkbox" v-model="val" value="C"> C</label> <p>{{ val }}</p>
new Vue({ el: '#app', data: { val: [] } })
AとCの選択では ["A", "C"]
となる
ラジオボタン
デフォルトは文字列
<label><input type="radio" value="a" v-model="val"> A</label> <label><input type="radio" value="b" v-model="val"> B</label> <label><input type="radio" value="c" v-model="val"> C</label> <p>{{ val }}</p>
new Vue({ el: '#app', data: { val: '' } })
セレクトボックス
単一選択プルダウン形式
デフォルト文字列
<select v-model="val"> <option disabled="disabled">選択してください</option> <option value="a">A</option> <option value="b">B</option> <option value="c">C</option> </select>
<p>{{ val }}</p> new Vue({ el: '#app', data: { val: '' } })
複数選択リスト形式
<select v-model="val" multiple> <option value="a">A</option> <option value="b">B</option> <option value="c">C</option> </select> <p>{{ val }}</p>
new Vue({ el: '#app', data: { val: [] } })
AとCの選択では ["A", "C"]
となる
画像ファイル
v-model
は使用できない。リアクティブにするならchange
イベントをハンドルする
<input type="file" v-on:change="handleChange"> <div v-if="preview"><img v-bind:src="preview"></div>
new Vue({ el: '#app', data: { preview: '' }, methods: { handleChange: function (event) { var file = event.target.files[0] if (file && file.type.match(/^image\/(png|jpeg)$/)) { this.preview = window.URL.createObjectURL(file) } } } })
画像選択するとプレビューが出る!カッコイイ!
その他入力タイプ
range
,color
等HTML5の入力タイプも使える
横スライドレンジの数値が出る奴
<input type="range" v-model.number="val">{{ val }}
new Vue({ el: '#app', data: { val: 50 } })
修飾子
v-model
にくっつく奴
修飾子 | 作用 |
---|---|
.lazy | inputの代わりにchangeイベントはハンドルする |
.number | 値を数値に変換する |
.trim | 値の余分なスペースを削除する |
.number
の使用例
テキストフォームに入った値はtype="number"
としても文字列となる。
だが、これで数値として取得することが出来る。
<input type="text" v-model.number="price"> {{ price }}
new Vue({ el: '#app', data: { price: 50 } })
SECTION 15 マウント要素外のイベントと操作
v-on
はDOMのwindow
,body
では使用できない為、それらを扱いたい場合はJS純正のaddEventLisner
メソッドを使う事になる。注意点として、不要になっても自動的に解除されないので、不要になった際はフック(ライフサイクルフックCAPTER1の最後の奴)を使って解除する必要がある。
スクロールイベントの取得
発生頻度の高いイベント等は、タイマーを使用して処理の実行頻度を抑えると良い。
以下はwindow
のスクロールイベントを200ms間隔でwindow.scrollY
プロパティを更新する例
これを応用して、サイドバーを画面に常に固定したり、スクロールすると表示を変化させるメニュー等に使用可能。
<header v-bind:class="{ compact: scrollY > 200 }"> 200pxより下にスクロールしたら .compact を付与する </header>
new Vue({ el: '#app', data: { scrollY: 0, timer: null }, created: function () { // ハンドラを登録 window.addEventListener('scroll', this.handleScroll) }, // CAPTER1最後のライフサイクルダイアグラムの beforeDestroy beforeDestroy: function () { // ハンドラを解除(コンポーネントやSPAの場合忘れずに!) window.removeEventListener('scroll', this.handleScroll) }, methods: { // 違和感のない程度に200ms間隔でscrollデータを更新する例 handleScroll: function () { if (this.timer === null) { this.timer = setTimeout(function () { this.scrollY = window.scrollY clearTimeout(this.timer) this.timer = null }.bind(this), 200) } } } })
bodyに適当な要素を縦長に書いてから開発コンソールのElements
を開いて確認してみる
スクロール前
<header class=""> 200pxより下にスクロールしたら .compact を付与する </header>
スクロール後
<header class="compact"> 200pxより下にスクロールしたら .compact を付与する </header>
当然、JS内の最後の値200
の数値を大きくすると反応はニブくなる。
スムーススクロールの実装
よくある『ページTOP』で滑らかに移動する奴はwindow
オブジェクトを操作している、ライブラリを使えば簡単に実装できる、ここでは「Smooth Scroll」を使った例を示す。
GitHub Smooth Scroll
<script src="https://cdn.jsdelivr.net/npm/smooth-scroll@12.1.5"></script> <div id="app"> <div class="content">...</div> <div v-on:click="scrollTop"> ページ上部へ移動 </div> </div>
// ここでSmoothScrollを変数に入れている var scroll = new SmoothScroll() new Vue({ el: '#app', methods: { scrollTop: function () { // scrollTopのバインドでSmoothScrollのメソッド animateScroll() を呼んでいる // 引数に画面最上部からの位置を指定できる scroll.animateScroll(0) } } })
COLUMN Vue.js以外からのイベントの読み取り
プラグインの実装等で、Vue.js以外のDOM操作ライブラリを使わざるを得ない場合、JSのdispatchEvent
を使ってイベント検知が出来る
以下はjQueryのval
メソッドと絡めた例
<div id="app"> <input id="message" v-on:input="handleInput"> <button data-update="jQuery!">jQueryからの更新</button> </div> <!-- html内でjQueryのCDNを別途読み込むこと --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
$(document).on('click', '[data-update]', function () { $('#message').val($(this).attr('data-update')) // 入力値を更新したらイベントを発生させる $('#message')[0].dispatchEvent(new Event('input')) }) new Vue({ el: '#app', methods: { handleInput: function (event) { console.log(event.target.value) } } })