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確定のためのものかどうか判定できません。 ↩