開発環境

  • 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時にイベントは発生しません。

.exact 修飾子

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度押せば値は送信されます。

もし不明な点や質問、ご指摘がありましたら、記事下のフォームからコメントをお願いします。

カテゴリー: Vue.js