概要
JavaScriptでキーボード押下時などに日本語IME入力中かを判定する方法のまとめです。
日本語入力周りのイベントは、2022年現在主だったブラウザでも挙動に差異があり面倒です。
IME(全角)入力におけるjsイベント現状調査
こちらの方の記事とだいぶ内容が被っていますが、私の調査結果と微妙に異なるところもあり、改めて自分なりにまとめています。
調査対象ブラウザ
- Chrome 99 (Mac)
- Firefox 98 (Mac)
- Safari 15 (Mac)
- Chrome 99 (Windows)
- Firefox 98(Windows)
- Edge 99(Windows)
※ Edgeはバージョン79からChromiumエンジンに変わっており、それ以前は挙動が全く異なると思われるので注意
日本語入力時に発生するイベントの流れ
まず、日本語入力時に発生するイベントとその発生順序を確認しました。
OSによる差はなかったので、ブラウザ種別だけで分けて記載しています。
日本語IMEが有効な状態で1文字目を入力
Chrome / Edge / Firefoxはkeydownから始まりますが、Safariはkeydownで終わるという真逆の挙動になります。
| # | Chrome/Edge | Firefox | Safari |
|---|---|---|---|
| 1 | keydown | keydown | compositionstart |
| 2 | compositionstart | compositionstart | compositionupdate |
| 3 | beforeinput | compositionupdate | beforeinput |
| 4 | compositionupdate | beforeinput | input |
| 5 | input | input | keydown |
2文字目を入力
こちらもkeydownの位置がSafariとそれ以外で真逆です。
| # | Chrome/Edge | Firefox | Safari |
|---|---|---|---|
| 1 | keydown | keydown | compositionupdate |
| 2 | beforeinput | compositionupdate | beforeinput |
| 3 | compositionupdate | beforeinput | input |
| 4 | input | input | keydown |
EnterでIMEの変換を確定
Safariは2回inputが発生します。
後述しますが、この2回は1回目のinputTypeがdeleteCompositionText、2回目のinputTypeがinsertFromCompositionになっており、一度テキストが消えて再挿入されるようなイベントの流れになっています。
| # | Chrome/Edge | Firefox | Safari |
|---|---|---|---|
| 1 | keydown | keydown | beforeinput |
| 2 | beforeinput | beforeinput | input |
| 3 | compositionupdate | compositionend | beforeinput |
| 4 | input | input | input |
| 5 | compositionend | compositionend | |
| 6 | keydown |
マウスクリックでIMEを終了
IME変換中に画面などをクリックするとIMEが終了しますが、その際Firefoxだけinputが発生します。
| # | Chrome/Edge | Firefox | Safari |
|---|---|---|---|
| 1 | compositionend | beforeinput | compositionend |
| 2 | compositionend | ||
| 3 | input |
keydownイベントで日本語IME入力中か判定する
keydownイベントはSafari以外では文字入力の一番最初に発生するイベントなので、この時点で判定に使えるのは、keydownイベントで取得できるKeyboardEventオブジェクトだけになります。
具体的にはKeyboardEventの以下のプロパティを日本語IME入力の判定に使うことができます。
KeyboardEvent.isComposing
IMEなどテキスト変換システムが動作中であるかを示すboolean変数です。
この値で判定できれば良いのですが、この値はSafari以外ではIME入力の一文字目でfalseになります。
KeyboardEvent.key
IME入力中は値がProcessになるという仕様があるのですが、MacのChromeとSafariはその仕様に対応していませんでした。
KeyboardEvent.key
IME and composition keys
"Process" [3]: The Process key. Instructs the IME to process the conversion.
[3] The Process key currently returns "Unidentified" in Firefox and Internet Explorer. Google Chrome returns the value of the key as if IME were not in use.
上記のMDNの説明だとFirefoxはUnidentifiedを返すと書いていますが、実際に確認したらProcess になっていたので、最近対応されたのかもしれません。
KeyboardEvent.keyCode
IME入力中は値が229になります。
確認した全てのブラウザで共通でしたが、このプロパティはdeprecatedなのでそのうちなくなる可能性が高いです。
keydownイベントでの日本語IME判定例
isComposingとkeyはブラウザによって値が異なるため、現状、deprecatedのkeyCodeを判定に使わざるをえません。
しかし、keyCodeが削除されても動くように、isComposingとkeyも使って以下のように判定するのが良いんじゃないでしょうか。
window.addEventListener('keydown', e => {
if (e.isComposing || e.key === 'Process' || e.keyCode === 229) {
// IME入力中
} else {
// IME入力中でない
}
});
beforeinputとinputイベントでIME入力中か判定する
beforeinputとinputイベントではInputEventオブジェクトが取得でき、InputEventの以下のプロパティが日本語入力の判定に使えます。
InputEvent.isComposing
この値がtrueならIME入力中と判断できますが、このプロパティはまだSafariにサポートされていません。
InputEvent.inputType
Safari以外のブラウザでは、日本語入力中はどのタイミングでも(変換確定時も)insertCompositionTextになりました。
しかし、Safariのみ変換確定時の値が異なり、以下のようになります。
SafariのinputTypeの値
| # | イベント | inputTypeの値 |
|---|---|---|
| 文字入力 | beforeinut | insertCompositionText |
| input | insertCompositionText | |
| Enterで変換確定 | beforeinut | deleteCompositionText |
| input | deleteCompositionText | |
| beforeinut | insertFromComposition | |
| input | insertFromComposition |
Safariも合わせると日本語入力中はinsertCompositionText、deleteCompositionText、insertFromCompositionの3つのinputTypeが発生しましたが、こちらの資料を見ると、deleteByCompositionという値もあるようです。
remove a part of the DOM in order to recompose this part using IME
との説明があります。
いまいちどういう状況で発生するものなのかわかりませんが、using IMEとあるのでこの値もIME入力中とみなすことにします。
beforeinputとinputイベントでの日本語IME判定例
window.addEventListener('input', e => {
const imeTypes = ['insertCompositionText', 'deleteCompositionText', 'insertFromComposition', 'deleteByComposition'];
if (imeTypes.includes(e.inputType)) {
// IME入力中
} else {
// IME入力中でない
}
});
compositionstartとcompositionendを使った判定
beforeinput は調べた全てのパターンで compositionstart と compositionendの間に発生するので、compositionstart と compositionendでフラグ管理してIME入力中かどうかを判定することもできます。
var isComposing = false;
window.addEventListener('compositionstart', e => {
isComposing = true;
});
window.addEventListener('compositionend', e => {
isComposing = false;
});
window.addEventListener('beforeinput', e => {
if (isComposing) {
// IME入力中
} else {
// IME入力中でない
}
});
ただし、inputイベントについては、Firefoxのみ変換確定時compositionendの後に発生するので同じ方法を使えません。