この記事は CAMPHOR Advent Calendar 2017 22日目の記事です。
昨日は @ryota-ka による Type-level TypeScript - ryota-ka's blog でした。
CompositionEvent が多くの主要ブラウザでサポートされた2017年冬なら、JavaScriptで日本語入力に対応したちょっとした入力補完機能の実装はシュッとできると思ったのですが、ブラウザによって細かい実装が異なっていてちまちまとしたworkaroundが必要になったのでメモ
検証環境
- Windows10 / Microsoft Edge 41.16299.150
- Windows10 / InternetExplorer11 バージョン: 11.64.16299.0
- macOS Sierra 10.12.6 / Google Chrome バージョン: 63.0.3239.84
- macOS Sierra 10.12.6 / Safari バージョン: 11.0.2
- macOS Sierra 10.12.6 / Opera バージョン: 49.0.2725.64
- macOS Sierra 10.12.6 / Firefox Quantum バージョン: 57.0.1
KeyboardEvent.keyCode
IME によるテキスト編集中は keyodown
イベントによって発火する KeyboardEventkeyCode
は 229
になります。
これを使えば IMEによるテキスト編集中の keydown
イベントにトリガする処理を制御することができる。
editorElem.addEventListener('keydown', function(event) { if (event.keyCode !== 229) { // do something ... } });
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
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
になってしまうようでした。
- MDN の Browser compatibility では(デスクトップの) IE, Safari で未対応と書かれていますが、検証環境の Safari 11.0.2 では
将来的にはこちらを使うと良いのだろうけど、IE, Edge で未対応なのでまだ使えそうにはないですね...
InputEvent.isComposing
InputEvent.isComposing - Web APIs | MDN
text composition system
によるテキスト編集中はtrue
となる readonly な属性- 後述する
compositionstart
とcompositionend
の間で発火したイベントならtrue
- 後述する
- Browser compatibility
- MDN の Browser compatibility では(デスクトップの) IE, Safari, Chrome, Opera で未対応と書かれていますが、検証環境のOpera49、Google Chrome では
InputEvent.isComposing
を取得できた。 - Microsoft Edge 41、IE11 では、
KeyboardEvent.isComposing
はundefined
になってしまうようでした。
- MDN の Browser compatibility では(デスクトップの) IE, Safari, Chrome, Opera で未対応と書かれていますが、検証環境のOpera49、Google Chrome では
こちらも将来的にIMEによるテキスト編集中の InputEvent
かどうかの判断にはこの値を使うと良いのだろうけど、IE, Edge で未対応だとまだ使えそうにないですね。
CompositionEvent
CompositionEvent - Web APIs | MDN
compositionstart
: IMEによるテキスト編集開始時に発火compositionupdate
: IME で編集中のテキストが変更された時に発火compostionend
: IMEによるテキスト編集終了時に発火
- MDN の Browser compatibility によれば Opera は未サポート、Safariは
?
と書かれていますが、検証環境のSafari, Operaではイベントは発火するようでした。- ただし
data
属性は未実装だったりする様子
- ただし
InputEvent
がIMEによるテキスト編集中のものかどうかを調べるにはInputEvent.isComposing
を使う代わりに、CompostionEvent を監視して状態を管理すると良さそう
var isComposing = false; editorElem.addEventListener('input', function(event) { if (!isComposing) { // do something ... } }); editorElem.addEventListener('compostionstart', function(event) { isComposing = true; }); editorElem.addEventListener('compostionstart', function(event) { isComposing = false; });
楽ちん
CompositionEvent/InputEvent の発火順序
詳しくは
- https://www.w3.org/TR/input-events-2/#event-order-during-ime-composition
- https://www.w3.org/TR/uievents/#events-composition-input-events
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 |
確認してみた
またもや雑に実験用スクリプトを使って検証環境で調べてみる
- Firefox: 最初の方でも書いたけどIME編集中はkeydown/keyupイベントが発火しない https://bugzilla.mozilla.org/show_bug.cgi?id=354358
- Safari: テキスト編集終了時
keydown
がcompositionend
の後に発火してる(このときのkeyCode
は 229) - Edge: テキスト編集終了時に
keydown
もkeyup
も発火しない
現状だとブラウザ実装によってイベント発火順序が結構違うみたいですね...あまり使うことはない気がするけど、ブラウザ間のイベント発火順序の違いを吸収するためのworkaroundは Handling IME events in JavaScript – Not Rocket Science に詳しく書かれていて良かった。
参考
- UI Events
- Handling IME events in JavaScript – Not Rocket Science
- DOM3 keyboard, input and composition events (pt2) - Google ドキュメント
まとめ
現状だとブラウザ互換性を考えると結構複雑ですね、2017年になっても日本語入力はやっぱり難しい。