keydownとkeyupの違いに気をつけて!IME入力時に順番が違う!


はじめに

フロントエンド開発していると、日本語入力確定時のEnterキーが、確定時のEnterキー打鍵と判別がつかず更新されてしまう事象に悩むことは誰しも遭遇したことあることかと思います。


↑↑例)調査という文字を打って、漢字変換を確定Enterを押したらそのまま更新になっちゃう

そんな時は、compositionイベントを利用すれば良いのですが、不可解な事象に少しハマってしまい数時間溶けてしまいました。

同じ事象でハマる人が減りますように

compositionイベントの使い方

一般的には以下のように、datacomposingのような変数を用意し、compositionstart, compositionendでそれぞれcomposingを更新することで、状態のチェックができると思います。

vue.js
<template>
  ...
  <input
    v-model="name"
    type="text"
    @compositionstart="composing = true"
    @compositionend="composing = false"
    @keyup.enter="onChangeName"
  >
  ...
</template>

<script>
export default {
  data() {
    return {
      composing: false // IME入力中かどうか
    }
  },
  methods: {
    onChangeName () {
      if(this.composing) return
      // 更新処理を書く
    }
  }
}
<script>
...

(普段はTS入れてますが、わかりやすいように省略)

そして上記のようにmethodsthis.composingを判定して後続処理を止めてたりします。これで日本語入力確定時に更新処理を走らないようにしたりします。

しかし問題は起きた

上記のようにやっていて、問題なくcomposing値も更新され問題なかったのですが、おや?日本語入力確定時にも更新処理が走ってしまうぞ?!という状況に遭遇したのが今回のケースです。

試しにconsoleを仕込んでデベロッパーツールを見てみると、「composing値が先に終わって、更新処理時の判定時には使えていない!?」状態となっていたのでした。

vue.js
// 省略

<script>
export default {
  data() {
    return {
      composing: false
    }
  },
  watch: {
    composing(val) {
      console.info(val, 'watch') // ←追加!
    }
  }
  methods: {
    onChangeName () {
      console.info(val, 'methods') // ←追加!
      if(this.composing) return
      // 更新処理を書く
    }
  }
}
<script>
...

もう発狂ものです。

どうしたらよいのか?

結論から行くと、メソッドを呼び出しているkeyupイベントをkeydownイベントに変更してください。

vue.js
<template>
  ...
  <input
    v-model="name"
    type="text"
    @compositionstart="composing = true"
    @compositionend="composing = false"
    @keydown.enter="onChangeName" // ←keyupからkeydownに変更
  >
  ...
</template>
// 省略

そうすると見事に、methodsでの判定箇所でcomposingfalseになる箇所が後になってくれてます

なんでkeydownで回避できるの?

keydownイベントは文字通り、キーが押された時に発火し、keyupイベントはキーを押して話した時に発火します。また今回これで事象を回避できたことから、keyupイベントはcompositionendと同時に呼び出すとcompositionendが優先されることがわかりました。また、keydownを利用することで明示的にcompositionendイベントよりも先に発火させることができるものだと思います。

さいごに

keyCodeプロパティやkeyPressイベントがDeprecatedになっていくので、日本語IME入力時にはcompositionイベントを利用していくことになると思いますが、こういうケースもあるのでご注意ください〜

ちょくちょく詰まったネタは記事にしていきたいと思います!