IMEまわりのキー入力制御ではDeprecatedなAPIを使わなくちゃいけない、悲しいね。というお話です。
背景
入力フォームでEnterキー押下時に、フォームの内容を確定したいことがあります。
素朴に実装すると次のようなソースコードです。
<input id="input">
<span id="message">
<script>
input.addEventListener('keydown', (e) => {
  if(e.key === 'Enter') {
    message.innerText = '確定'
  }
})
</script>
keydownイベントを監視し、イベントのkeyがEnterであれば確定とします。
問題
このコードには問題があります。
Firefox(68.0.1)とWindowsのGoogle Chromee(75.0.3770.142)では期待通りに動作します。
MacのGoogle Chrome(75.0.3770.142)とSafari(12.1.2)では、日本語を入力するとき、期待通りに動作しません。
変換確定のEnterを、フォームの確定として扱ってしまいます。
https://codepen.io/ledsun/pen/bXggJN に動作サンプルがあるので試してみてください。
原因
前述の2ブラウザでは、IMEの確定時にkeydownイベントのkeyプロパティがEnterになります。
対策
必要なのはIME確定時のEnterとそれ以外のEnterを見分ける方法です。
次の作戦はどちらか一方を採用すればよいです。
両方やる必要はありません。
作戦1 keyプロパティの代わりにkeyCodeプロパティを使う
keydownイベントのKeyboardEvent.keyCodeプロパティは、
If an Input Method Editor is processing key input and the event is keydown, return 229.
とあるように、IME確定時のEnterは229に、それ以外のEnterのときは13になります。
keyCodeプロパティの値は、IME確定時のEnterとそれ以外のEnterで変ります。
これを使うと次のようなソースコードになります。
<input id="input">
<span id="message">
<script>
input.addEventListener('keydown', (e) => {
  if(e.keyCode === 13) {
    message.innerText = '確定'
  }
})
</script>
気になる点は、keyCodeプロパティがDeprecatedなところです1。
作戦2 keydownイベントの代わりにkeypressイベントを使う
keypressイベントは、
a key that produces a character value is pressed down
とあるように、文字入力がある場合のみ発火します。このため、IME確定時には発火しません。
これを使うと次のようなソースコードになります。
<input id="input">
<span id="message">
<script>
input.addEventListener('keypress', (e) => {
  if(e.key === 'Enter') {
    message.innerText = '確定'
  }
})
</script>
やはり、気になる点は、keypressイベントがDeprecatedなところです2。
その他の補足(恨み言)
keyプロパティ
FirefoxとWindowsのGoogle Chromeは、IMEの確定時にkeydownイベントのkeyプロパティがProcessです。MacのGoogle ChromeとSafariも同じように、keyプロパティがProcessになってくれればいいのに😩
isComposingプロパティ
isComposingというプロパティがあります。
the event is fired after compositionstart and before compositionend
compositionstartからcompositionendの間、つまりIMEの候補が表示されてから確定されるまでの間trueになります。
すごく良さそうです。
Safariでは、IME確定時のEnter入力のkeydownイベントはcompositionendの後に発火します。
つまり、IME確定時のEnter入力のKeyboardEventのisComposingはfalseです😩
参考資料
- IME(全角)入力におけるjsイベント現状調査 - Qiita
- jQueryでテキストボックスの変更を監視/検知する - Qiita
- javascriptの日本語入力ハンドリングについてのメモ - Qiita
- [Vue.js] 日本語変換の確定でkeyup.enterが発火してしまうのを防ぐ - Qiita
- inputの入力値の制御でIME入力とかいろいろ辛かった話 - Qiita
- jQueryでIME入力確定時にイベントを発行する - Qiita
- HTMLElement: beforeinput event - Web APIs | MDN
- 
KeyboardEvent.keyCodeとは何か - WebStudioによれば、keyCodeプロパティがDeprecatedなのは、ブラウザごとにキーマッピングの扱いが違い、どのキーが押されたのか特定するのが困難なためのようです。もしそうであれば、Enterキーの判定に使う分には問題がないかもしれません。 ↩ 
- 
MDNでは、keypressイベントの代わりに beforeinputまたはkeydownイベントを使うように書かれています。しかし、beforeinputはEnterキーの押下では発火しません。keydownイベントでは、当初の問題のようにkeyプロパティでEnterキー押下がIME確定のためのものかどうか判定できません。 ↩
