HTML5テクニカルノート
Vue.js + Vuex入門 08: フォーカスをコントロールする
- ID: FN1911002
- Technique: HTML5 / ECMAScript 2015
- Library: Vue.js 2.6.10
単一ファイルコンポーネントにVuexのStoreを加えてつくるTodoMVCアプリケーションのチュートリアルシリーズ「Vue.js + Vuex入門」の第8回は、いよいよアプリケーションを仕上げます。操作していて気になるのは、フォーカスの扱いです。不具合をふたつ直します。
01 ダブルクリックで編集しているテキストからフォーカスを外したら編集ボックスを閉じる
ひとつ目は、ダブルクリックで始めた項目の編集が、[enter]キーか[esc]キーを押さないかぎり終わらないことです。項目を編集中にしたまま、新たな項目を追加するフィールドにテキストが入力できてしまうのです(図001)。
図001編集を終えないまま新たな項目が入力できる
編集ボックスコンポーネントsrc/components/TodoEdit.vue
のテキスト入力フィールド(<input type="text">
要素)からフォーカスを外したら、編集が終わるようにしなければなりません。イベントはblur
です。つぎのように、編集キャンセルのリスナー関数(doneEdit()
)を与えました。これで、ダブルクリックで書き替えを始めた入力フィールドから、フォーカスを外すとテキストが編集されないまま終了します。
src/components/TodoEdit.vue<template> <input id="edit" class="edit" type="text" @blur="cancelEdit"> </template>
02 編集ボックスにフォーカスを与える準備
まだひとつ不具合が残っています。項目をダブルクリックしただけで、編集ボックスには触れずに別の場所をクリックした場合は、blur
イベントが起こらないので編集は終わりません。追加項目も入力できてしまいます。それは、項目をダブルクリックしただけでは、テキスト入力フィールドにフォーカスが入らないからです。それなら、ダブルクリックしたとき、そのコンポーネントの要素にフォーカスを移せばよいでしょうか。問題は、ダブルクリックするコンポーネント(src/components/TodoItem.vue
)と編集するコンポーネント(src/components/TodoEdit.vue
)が別で、切り替わることです。
そこで、クラスバインディングしたときの手を使いましょう(「Vue.js + Vuex入門 07」01「項目のダブルクリックで編集ボックスに切り替える」)。コンポーネントsrc/components/TodoList.vue
は、つぎのように編集中の項目のオブジェクト(todo
)を算出プロパティ(editedTodo
)にもち、ふたつが一致する項目にクラス(editing
)をバインディングしていました。
src/components/TodoList.vue<template> <section class="main" v-show="todos.length" v-cloak> <ul class="todo-list"> <li v-for="todo in filteredTodos" :class="{completed: todo.completed, editing: todo === editedTodo}" > </li> </ul> </section> </template> <script> export default { computed: { ...mapState([ 'todos', 'editedTodo' ]), }, }; </script>
フォーカスの操作をするのは、編集ボックスのコンポーネント(src/components/TodoEdit.vue)です。みずからの項目のオブジェクト(todo
)はすでにprops
オプションにもっていますので、編集中の項目(editedTodo
)を算出プロパティに加えてください。あとは、ふたつが一致したときフォーカスを与えればよいでしょう。
src/components/TodoEdit.vueimport {mapState} from 'vuex'; export default { props: { todo: Object }, computed: { ...mapState([ 'editedTodo' ]), }, };
03 カスタムディレクティブを使う
そのとき用いるのが「カスタムディレクティブ」です。カスタムディレクティブには、v-
につづけて任意の名前を定めます。以下のように、コンポーネントsrc/components/TodoEdit.vue
のテンプレートのテキスト入力フィールド(<input type="text">
要素)にディレクティブ(todo-focus
)を加えました。与えた式の値はdirectives
オプションに定めるディレクティブと同名のメソッドの第2引数(binding
)からvalue
プロパティで取り出せます。第1引数(element
)がディレクティブの与えられた要素です。
ディレクティブ名にハイフン(-
)を含めたので、メソッド名の識別子としては定められません。そのため、文字列のキーを使いました。メソッドが呼び出されるのは、ディレクティブがはじめに要素に関連づけられたときと、そのあとは更新される都度その直前です(「関数による省略記法」参照)。
src/components/TodoEdit.vue<template> <input id="edit" class="edit" type="text" v-todo-focus="todo == editedTodo" > </template> <script> export default { directives: { 'todo-focus'(element, binding) { if (binding.value) { element.focus(); } } }, }; </script>
項目をダブルクリックすると、編集ボックスのテキスト入力フィールドにフォーカスが当たるようになりました。そして、フォーカスを外せば編集は終了です。手直しした編集ボックスのコンポーネントsrc/components/TodoEdit.vue
の中身は、つぎのコード001のとおりです。でき上がったTodoMVCアプリケーションのすべてのモジュールについては、CodeSandboxに公開した以下のサンプル001をご参照ください。
コード001■テキスト入力フィールドのフォーカスをコントロールする
src/components/TodoEdit.vue
<template>
<input id="edit" class="edit" type="text"
v-todo-focus="todo == editedTodo"
:value="todo.title"
@input="onInput"
@keypress.enter="doneEdit"
@keyup.esc="cancelEdit"
@blur="cancelEdit">
</template>
<script>
import {mapState} from 'vuex';
export default {
name: 'TodoEdit',
directives: {
['todo-focus'](element, binding) {
if (binding.value) {
element.focus();
}
}
},
props: {
todo: Object
},
data() {
return {
editedTitle: null
};
},
computed: {
...mapState([
'editedTodo'
]),
},
methods: {
onInput(event) {
this.editedTitle = event.target.value;
},
doneEdit(event) {
this.editedTitle = event.target.value;
this.$store.commit('doneEdit', this.editedTitle);
},
cancelEdit(event) {
event.target.value = this.todo.title;
this.$store.commit('cancelEdit');
}
}
};
</script>
サンプル001■vue-vuex-todo-mvc-08
Vue.js + Vuex入門
- Vue.js + Vuex入門 01: Storeを使う
- Vue.js + Vuex入門 02: クラスバインディングと項目の削除
- Vue.js + Vuex入門 03: データの変化に応じた処理とローカルへの保存
- Vue.js + Vuex入門 04: リスト表示する項目を選び出して切り替える
- Vue.js + Vuex入門 05: チェックをまとめてオン/オフする
- Vue.js + Vuex入門 06: チェックした項目をまとめて削除する
- Vue.js + Vuex入門 07: 項目のテキストをダブルクリックで再編集する
- Vue.js + Vuex入門 08: フォーカスをコントロールする
作成者: 野中文雄
作成日: 2019年11月12日
Copyright © 2001-2019 Fumio Nonaka. All rights reserved.