この記事は CAMPHOR Advent Calendar 2017 22日目の記事です。
昨日は @ryota-ka による Type-level TypeScript - ryota-ka's blog でした。
CompositionEvent が多くの主要ブラウザでサポートされた2017年冬なら、JavaScriptで日本語入力に対応したちょっとした入力補完機能の実装はシュッとできると思ったのですが、ブラウザによって細かい実装が異なっていてちまちまとしたworkaroundが必要になったのでメモ
検証環境
KeyboardEvent.keyCode
IME によるテキスト編集中は keyodown
イベントによって発火する KeyboardEventkeyCode
は 229
になります。
これを使えば IMEによるテキスト編集中の keydown
イベントにトリガする処理を制御することができる。
editorElem.addEventListener('keydown', function(event) {
if (event.keyCode !== 229) {
}
});
https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode
If an Input Method Editor is processing key input and the event is keydown, return 229.
GeckoではIMEによるテキスト編集中はkeydown/keyupイベントが発火しないみたいですね
https://bugzilla.mozilla.org/show_bug.cgi?id=354358
deprecated
KeyboardEvent.keyCode
は deprecated なようです。W3Cでも keyCode
属性が記載されていたのは Legacy Key Attributes
の欄ですね、keyCode
はプラットフォームや実装依存の値なので、排除していきたい流れなのかも。
KeyboardEvent.keyCode - Web APIs | MDN
KeyboardEvent.isComposing
KeyboardEvent.isComposing - Web APIs | MDN
- IMEのような text composition system によるテキスト編集中は
true
となるreadonlyな属性
- 後述する
compositionstart
と compositionend
の間で発火したイベントなら true
- Browser compatibility
- MDN の Browser compatibility では(デスクトップの) IE, Safari で未対応と書かれていますが、検証環境の Safari 11.0.2 では
KeyboardEvent.isComposing
の値を取得することができました。
- 検証環境の Microsoft Edge 41、IE11 では、
KeyboardEvent.isComposing
は undefined
になってしまうようでした。
将来的にはこちらを使うと良いのだろうけど、IE, Edge で未対応なのでまだ使えそうにはないですね...
InputEvent.isComposing
InputEvent.isComposing - Web APIs | MDN
text composition system
によるテキスト編集中は true
となる readonly な属性
- 後述する
compositionstart
と compositionend
の間で発火したイベントなら true
- Browser compatibility
こちらも将来的にIMEによるテキスト編集中の InputEvent
かどうかの判断にはこの値を使うと良いのだろうけど、IE, Edge で未対応だとまだ使えそうにないですね。
CompositionEvent
CompositionEvent - Web APIs | MDN
compositionstart
: IMEによるテキスト編集開始時に発火
compositionupdate
: IME で編集中のテキストが変更された時に発火
compostionend
: IMEによるテキスト編集終了時に発火
- MDN の Browser compatibility によれば Opera は未サポート、Safariは
?
と書かれていますが、検証環境のSafari, Operaではイベントは発火するようでした。
InputEvent
がIMEによるテキスト編集中のものかどうかを調べるには InputEvent.isComposing
を使う代わりに、CompostionEvent を監視して状態を管理すると良さそう
var isComposing = false;
editorElem.addEventListener('input', function(event) {
if (!isComposing) {
}
});
editorElem.addEventListener('compostionstart', function(event) {
isComposing = true;
});
editorElem.addEventListener('compostionstart', function(event) {
isComposing = false;
});
楽ちん
CompositionEvent/InputEvent の発火順序
詳しくは
IMEによるテキスト編集開始/編集中
event |
note |
compositionstart |
編集開始時 |
beforeinput |
|
compositionupdate |
|
|
ここでDOMが更新される |
input |
|
テキスト編集終了時
event |
note |
compositionend |
|
beforeinput |
|
|
ここでDOMが更新される |
input |
|
このようなイベント発火順序となるのでIMEによるテキスト編集中の InputEvent
は必ず compositionstart
と compositionend
の間に発火する、はず。
確認してみた
実験用スクリプトを書いて検証環境のブラウザで確認してみた
- IE11, Edge41: テキスト編集終了時の
input
イベントが発火してないように見える?
- Google Chrome, Safari, Opera: テキスト編集終了時の
input
イベントが compositionend
の前に発火してる?
うーむ僕のスクリプトの書き方が悪いだけなのかな?詳しい人教えてください
CompositionEvent/KeyboardEvent の発火順序
詳しくは https://www.w3.org/TR/uievents/#events-composition-key-events
IMEによるテキスト編集開始/編集中
event |
note |
keydown |
|
compositionstart |
編集開始時 |
compositionupdate |
|
keyup |
|
テキスト編集終了時
event |
note |
keydown |
|
compositionend |
|
input |
|
keyup |
|
確認してみた
またもや雑に実験用スクリプトを使って検証環境で調べてみる
現状だとブラウザ実装によってイベント発火順序が結構違うみたいですね...あまり使うことはない気がするけど、ブラウザ間のイベント発火順序の違いを吸収するためのworkaroundは Handling IME events in JavaScript – Not Rocket Science に詳しく書かれていて良かった。
参考
まとめ
現状だとブラウザ互換性を考えると結構複雑ですね、2017年になっても日本語入力はやっぱり難しい。