開発環境
- vue@2.x
- vuetify@2.x
検証環境
- 機種:Mac mini (M1, 2020)
- ブラウザ:Chrome, Firefox
※safariは挙動が異なるので、動作検証中です。
全角入力の変換完了と同時に送信されるアンチパターン
enterキー押出でフォームを送信したい場合、まず下記のような実装方法が頭に浮かぶのではないでしょうか?
<div id="app">
<input
@keyup.enter.exact="submit"
>
</div>
<script>
new Vue({
el: "#app",
methods: {
submit() {
// 処理
}
}
})
</script>
この手法の場合、半角英数字入力あれば問題なく送信できます。しかし日本語の「かな→漢字変換」が伴うと、変換されたタイミングでフォームも送信されてしまいます。
例えば、「今日はいい天気ですね!」と送信したいのに、「今日は」を変換したタイミングでフォームが送信されてしまうんですね…
この問題に対する解決策を次章で示します。
完成形の動作確認
下記JSFiddleの[Result]タブにて、テキストを入力+enter押出で動作をご確認ください。入力値が送信されたら、input部品の下側に送信したテキスト値が表示されます。
英数字を入力した場合は、1度目のenter押出で送信されます。一方で日本語を入力した場合は、1度目のenter押出で変換処理を行い、2度目のenter押出で送信されます。
ソースコードの解説
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<div id="app">
<input
@keydown.prevent.enter.exact="submit()"
@compositionstart.prevent="compositionstart()"
@compositionend.prevent="compositionend()"
v-model.trim="input_text"
>
<div>
{{ submitted_text }}
</div>
</div>
new Vue({
el: "#app",
data: {
composing: false,
input_text: "",
submitted_text: "",
},
methods: {
compositionstart() {
this.composing = true
},
compositionend() {
this.composing = false
},
submit() {
if(!this.composing) {
this.submitted_text = this.input_text
}
this.composing = false
},
}
})
.exact 修飾子
<input
@keydown.prevent.enter.exact="submit()"
>
input要素のイベントハンドリングの記述に、enter.exactとあります。これはVue.jsの.exact修飾子という仕組みを利用して、システム修飾子の正確な組み合わせを制御しています。
上記のコードベースで掘り下げると、enterキーのみが押されている場合にイベントが発火します。つまり、command + enterやshift + enter時にイベントは発生しません。
composing
composing: false,
全角入力の変換ステータスを制御するデータプロパティです。
初期値はfalseで、次に解説するcompositionstart・compositionendイベント発火のタイミングで値を変更します。
<input
@keydown.prevent.enter.exact="submit()"
>
compositionstart, compositionend
compositionstart() {
this.composing = true
},
compositionend() {
this.composing = false
},
全角入力の変換処理に対応する場合、JavaScriptのcompositionstartイベントと、compositionendイベントを使います。
compositionstartとcompositionendイベントの特徴は以下の通り。
- compositionstart : 変換を伴う全角入力が開始されると発火
- compositionend : 変換完了して全角入力が終了すると発火
全角変換の開始と終了それぞれのイベントを取得し、変換中の場合はcomposing = true。変換が完了したらcomposing = falseです。
submit()
submit() {
if(!this.composing && this.input_text) {
this.submitted_text = this.input_text
}
this.composing = false
},
実際の送信処理は、下記の条件をクリアした場合に実行するようにしています。
- テキスト値が存在する
- 全角変換がfalseである
半角入力の場合は変換処理が発生しないので、enterを1度押せば値は送信されます。
もし不明な点や質問、ご指摘がありましたら、記事下のフォームからコメントをお願いします。
コメント