IME 入力 - ブラウザ間の違い

IME 入力のブラウザ間での挙動の違いを調査したのでその結果をまとめます。

:::note
**[IME（Input Method Editor）](https://developer.mozilla.org/ja/docs/Glossary/Input_method_editor)**
IME はテキスト入力をサポートするソフトウェアです。
かな入力時に（漢字などの）変換候補を表示する機能などを提供しています。
:::

IME 入力は、日本語話者なら必ず使っているであろう機能であり、アジア圏のユーザを対象にしている Web アプリケーション内で、テキスト入力を制御したい場合は、IME 入力を制御する JavaScript コードを書く必要があるかもしれません。

JavaScript で IME 入力を制御する際には、以下のような点に注意が必要です。

- ブラウザ間で、IME 入力に関係する Event の発生の順番が異なる場合があります。
- 同じブラウザでもバージョンにより Event の発生の順番が異なる場合があります。
- 同じブラウザで同じバージョンでも、入力エリアが `textarea` か `input` か `contenteditable` かで Event のプロパティの値に違いがある場合がある。

IME 入力を制御するコードを書く前に、ブラウザ間での IME 入力の挙動の違いを理解することは重要です。

# 調査対象

さまざまな OS、ブラウザ、入力エリア、Event を対象に IME 入力時の挙動を比較しました。調査対象を以下にまとめています。

## OS

MacOS および Windows の環境での調査を行っています。

- macOS Ventura 13.0.1 (MacBook Air - Apple M1 2020)
- macOS Sonoma 14.1.2(MacBook Air - Apple M1 2020)
- Windows 10 (DynaBook)

MacOS は 2 つのバージョンでの調査を行っていますが、`inputEvent.isComposing`　プロパティの値の違い以外同じであったため、基本的に 2 つの結果を分けずにまとめて書いています。

## ブラウザ

以下が、調査対象のブラウザとそのバージョンです。

- `Microsoft Edge` (mac / win)
  - macOS
    - version: 115.0.1901.203
    - version: 120.0.2210.61
  - windows
    - version: 95.0.1020.40
    - version: 120.0.2210.61
- `Google Chrome` (mac / win)
  - macOS
    - version: 116.0.5845.140
    - version: 120.0.6099.71
  - windows
    - version: 101.0.4951.54
    - version: 120.0.6099.71
- `Mozilla Firefox` (mac)
  - macOS
    - version: 120.0.1
- `Safari` (mac)
  - macOS
    - version: 16.1
    - version: 17.1.2

`Internet Explorer` はサポートが既に終了しているので調査の対象としていません。

## 入力エリア

入力エリアが、以下の３つの場合に分けて調査しました。

- `textarea` タグ (`<textarea>`)
- `input` タグ (`<input>`)
- `contenteditable` 属性が `true` の要素

`Document.designMode`はあまり一般的な機能ではないと思ったので、調査の対象から外しました。
本記事では `input` と表記していたら、`input` イベントを指します。`<input>` は `input` タグと表記します。

## Event

今回調査した Event は以下の 7 つです。

- [`beforeinput`](https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/beforeinput_event): 要素の値が変更されようとしているときに発生
- [`input`](https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/input_event): 要素の値 (value) が変更されたときに発生
- [`keydown`](https://developer.mozilla.org/ja/docs/Web/API/Element/keydown_event): キーが押されたときに発生
- [`keyup`](https://developer.mozilla.org/ja/docs/Web/API/Element/keyup_event): キーが離されたときに発生
- [`compositionstart`](https://developer.mozilla.org/ja/docs/Web/API/Element/compositionstart_event): IME などのテキスト変換システムが新しい変換セッションを開始した時に発生
- [`compositionupdate`](https://developer.mozilla.org/ja/docs/Web/API/Element/compositionupdate_event): IME などのテキスト変換システムによって制御されているテキスト変換セッションに新しい文字が入力されたときに発生
- [`compositionend`](https://developer.mozilla.org/ja/docs/Web/API/Element/compositionend_event): IME などのテキスト編集システムが現在の編集セッションを完了またはキャンセルした時に発生

`keypress` は非推奨であるため、`change` は、IME 入力時のリアルタイム性に欠けるため、調査の対象としていません。

参考： - [Element: keypress イベント - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/Element/keypress_event) - [IME（全角）入力における js イベント現状調査 - Qiita](https://qiita.com/darai0512/items/fac4f166c23bf2075deb#change%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88)

# 調査用コード

以下が調査に使用したコードです。

```html:index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
  </head>

  <body>
    <div>
      <input type="text" id="input">
      <br>
      <textarea id="textarea"></textarea>
      <br>
      <div id="contenteditable" contenteditable="true"><p><br></p></div>
    </div>

    <script>
      const input = document.querySelector('#input');
      addEvent(input);

      const textarea = document.querySelector('#textarea');
      addEvent(textarea);

      const contenteditable = document.querySelector('#contenteditable');
      addEvent(contenteditable);

      function addEvent(editor){
        if(
          !(editor instanceof HTMLTextAreaElement) &&
          !(editor instanceof HTMLInputElement) &&
          !(editor instanceof HTMLDivElement)
        ) return;

        // InputEvent
        editor.addEventListener("beforeinput", function(e) {
          console.log(`type: ${e.type}, data: ${e.data}, isComposing: ${e.isComposing}`);
        });

        editor.addEventListener("input", function(e) {
          console.log(`type: ${e.type}, data: ${e.data}, isComposing: ${e.isComposing}`);
        });

        // KeyboardEvent
        editor.addEventListener("keydown", function(e) {
          console.log(`type: ${e.type}, key: ${e.key}, isComposing: ${e.isComposing}`);
        });

        editor.addEventListener("keyup", function(e) {
          console.log(`type: ${e.type}, key: ${e.key}, isComposing: ${e.isComposing}`);
        });

        // CompositionEvent
        editor.addEventListener("compositionstart", function(e) {
          console.log(`type: ${e.type}, data: ${e.data}`);
        });

        editor.addEventListener("compositionupdate", function(e) {
          console.log(`type: ${e.type}, data: ${e.data}`);
        });

        editor.addEventListener("compositionend", function(e) {
          console.log(`type: ${e.type}, data: ${e.data}`);
        });
      }

    </script>
  </body>
</html>
```

- `contenteditable` の要素は、[Quill](https://quilljs.com/) や [Lexical](https://playground.lexical.dev/) などの WYSIWYG ライブラリの初期状態を参考に `<div id="contenteditable" contenteditable="true"><p><br></p></div>` としています。
- `addEvent` 関数で以下のプロパティをコンソールに表示します。
  - **[InputEvent](https://developer.mozilla.org/ja/docs/Web/API/InputEvent)** (`beforeinput`, `input`)
    - [type](https://developer.mozilla.org/ja/docs/Web/API/Event/type): イベントの種別を表す文字列
    - [data](https://developer.mozilla.org/ja/docs/Web/API/InputEvent/data): 挿入された文字
    - [isComposing](https://developer.mozilla.org/ja/docs/Web/API/InputEvent/isComposing): `compositionstart` と `compositionend` の間に発生したかを示す値
  - **[KeyboardEvent](https://developer.mozilla.org/ja/docs/Web/API/KeyboardEvent)** (`keydown`, `keyup`)
    - [type](https://developer.mozilla.org/ja/docs/Web/API/Event/type): イベントの種別を表す文字列
    - [key](https://developer.mozilla.org/ja/docs/Web/API/KeyboardEvent/key): ユーザーが押したキーの値
    - [isComposing](https://developer.mozilla.org/ja/docs/Web/API/KeyboardEvent/isComposing): `compositionstart` と `compositionend` の間に発生したかを示す値
  - **[CompositionEvent](https://developer.mozilla.org/ja/docs/Web/API/CompositionEvent)** (`compositionstart`, `compositionupdate`, `compositionend`)
    - [type](https://developer.mozilla.org/ja/docs/Web/API/Event/type): イベントの種別を表す文字列
    - [data](https://developer.mozilla.org/ja/docs/Web/API/CompositionEvent/data): イベントを発生させた入力システムによって生成された文字

# 調査結果

## 要点

すでに文章が長くなり始めているので先に重要な点を簡単にまとめます。

1. 最新のブラウザでは、KeyboardEvent を除くと、`compositionupdate` -> `beforeinput` -> `input` の順で Event が発生している。
1. IME 終了時の Event の発生の順番は、多くパターンがあり、InputEvent や KeyboardEvent が発生せずに `compositionend` しか発生しないパターンもある。
1. `KeyboardEvent` は IME 入力（を含むテキスト入力処理）の制御に向いていない。
   > テキストの入力を処理したい場合は、代わりに input イベントを使用してください。ユーザーが他の種類のテキスト入力、例えば、タブレット端末やタブレット機器による手書き入力システムなどを使用している場合、キーボードイベントが発生することはありません。
   > [KeyboardEvent - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/KeyboardEvent)
2. `input` のプロパティの値は、contenteditable 要素では扱いづらい。
3. バージョン 16.4 以降の Safari では `inputEvent.isComposing` がサポートされており、IME 入力中かの判断が容易になった。
4. `Microsoft Edge` は バージョン 79 から `Google Chrome` と同じ Chromium ベースに移行しため、`Microsoft Edge` と `Google Chrome` は同じような挙動をする。

## 非 IME （ローマ字入力）

IME 入力の Event を調査する前に、まずローマ字入力に焦点を当てます。

- ローマ字入力ではブラウザ間やバージョン間で差異がなく、以下の順番で Event が発生する。
  1. **`keydown`**
  2. **`beforeinput`**
  3. **`input`**
  4. **`keyup`**
- ただし、長押ししている間は`keyup` が発生しない。
  1. **`keydown`**
  2. **`beforeinput`**
  3. **`input`**
  4. **`keydown`**: 長押ししている間、keydown が発生し続ける。
  5. **`keyup`**: キーが離されるとはじめて発生する。
- 以下の場合、`input` が発生しない。
  - 削除する対象がないのに、`Backspace` や `Delete`、`Control + D`
  - input タグ内で `Enter` もしくは `Shift + Enter`
- 以下の場合、Safari は `beforeinput` も発生しない（Chrome と Edge、Firefox は発生する）
  - 削除する対象がないのに、`Backspace` や `Delete`、`Control + D`
  - input タグ内で `Enter` もしくは `Shift + Enter`
- **`isComposing`** は `InputEvent`、 `KeyboardEvent` いずれも **`false`**
- `InputEvent` の **`data`** は入力値（基本的に `beforeinput` と `input` で同じ）
- `Backspace` や `Backspace + fn`、`Control + D`、`Enter`、`Shift + Enter` の場合は、`InputEvent` の `data` の値はどれも `null`

`InputEvent`の **`inputType`** プロパティを使用することで、`null`の識別ができます。

以下は、`inputType` の値の、input タグ、textarea ダグ、contenteditable 要素での違いをまとめたものです。
※ `Backspace`、 `Delete`、`Control + D` は削除する対象があることを前提としています。（削除する対象がない場合は上記で示したように Event 自体が発生しないことがあります。）
※ 特に分けて書いていない限り、`beforeinput` と `input` の `inputType` の値は同じです。表内では、beforeinput イベントは　**BI**、input イベントは **I** と表記しています。
|KeyboardEvent.key|　 input タグ|textarea ダグ|contenteditable|
|:--|:--|:-|:-|
|**Backspace**|deleteContentBackward|deleteContentBackward|deleteContentBackward|
|**Delete** (※1)|deleteContentForward|deleteContentForward|deleteContentForward|
|**Control + d**|deleteContentForward|deleteContentForward|deleteContentForward|
|**Enter** (Edge, Chrome, Firefox)|**BI**: insertLineBreak <br>**I**: 発生しない|insertLineBreak|**insertParagraph**(※2)|
|**Enter** (Safari)|**BI**: 発生しない<br>**I**: 発生しない|insertLineBreak|**insertParagraph**|
|**Shift + Enter** (Edge, Chrome, Firefox)|**BI**: insertLineBreak <br>**I**: 発生しない|insertLineBreak|insertLineBreak(※2)|
|**Shift + Enter** (Safari)|**BI**: 発生しない<br>**I**: 発生しない|insertLineBreak|**insertParagraph**|

※1 Mac のキーボード場合は `Backspace + fn`、Dynabook のキーボードの場合は `DEL`
※2 Edge と Chrome では、`input` イベントの inputType が `insertText` になる場合がある。(文字入力後、間隔を空けずに Enter (もしくは Shift + Enter) を押すと insertText になる。その後、続けて Enter (もしくは Shift + Enter) を押す場合も insertText になる。）

### ローマ字入力まとめ

以上のように、ローマ字入力に関しても、ブラウザ間での違いが見受けられます。
テキスト入力時はブラウザ間でイベントの発生順に違いはないですが、`Enter` や`Shift + Enter`のキー入力時やテキスト削除時では、イベントが発生しなかったり、発生した場合でもプロパティの値が異なることがあります。

ローマ字入力の制御をクロスブラウザ対応する場合、**input タグ**や **textarea ダグ**では `input` イベントと `beforeinput` イベントの両方が使えそうですが、**contenteditable** 内では `input` イベントの inputType は信用ができず、`beforeinput`イベントで制御するのが適切なようです（Safari では contenteditable 内で `Enter`と`Shift + Enter`の違いを識別する場合は KeyboardEvent イベントの利用も必要）。

## IME

本題の IME 入力に焦点を当てます。

### ローマ字入力との違い

- IME 入力では **CompositionEvent** (`compositionstart`, `compositionupdate`, `compositionend`) が発生します。
- compositionstart と compositionend の間に発生した KeyboardEvent (keydown, keyup) と InputEvent (beforeinput, input) イベントの **isComposing** プロパティは `true`が返されます。（[バージョンが 16.4 よりも前の Safari の場合](https://developer.mozilla.org/ja/docs/Web/API/InputEvent/isComposing#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%BC%E3%81%AE%E4%BA%92%E6%8F%9B%E6%80%A7)は **InputEvent の isComposing プロパティは`undefined`**）
- ローマ字入力と同様に keydown 長押ししている間、keydown が発生し続けますが、ローマ字入力は 1 つ目の Event のみが入力エリアに反映される一方、IME 入力では 2 つ目以降の Event も入力エリアに反映されます（例えば、a キーを押し続けると、`ああああああああああ....`となる）。
- InputEvent の data プロパティの値は、ローマ字入力の場合は入力文字(a, 1, k など)と一致しますが、IME 入力は IME 入力中のすべての文字(か, １００, 下記など)を含んでいます。

### IME 入力の Event の発生の順番

IME 入力の Event の発生の順番を一度に理解するのは難しいので 3 つのフェーズに分けて説明します。

1. IME 入力開始時
2. IME 入力中
3. IME 入力終了時

また、ローマ字入力の際と同様に `keyup` はキーが離されるとはじめて発生するので、キーを押し続けていると、次の文字の keydown イベントの方が先に発生します。このため、本調査は、他のキーを押す前に現在押しているキーを離しているという前提を基本としています。


表内では、イベントはそれぞれ以下のように省略して表記しています。
- keydown: **KD**
- keyup: **KU** 　
- compositionstart: **CS**
- compositionupdate: **CU**
- compositionend: **CE**
- input: **I**
- beforeinput: **BI**

#### IME 入力開始時
かな入力ができる状態で、キーボードの文字を入力すると IME 入力が開始します。

**Windows**
まずは、Windows の Edge と Chrome を見ていきます。

IME 入力で `k` を押すと以下のような結果になります。
()内は、InputEvent.data もしくは KeyboardEvent.key または CompositionEvent.dataの値を表しています。
| Edge <br>(ver.95) | Edge <br>(ver.120)| Chrome <br>(ver.101) | Chrome <br>(ver.120) |
| ---------------- | ----------------- | -------------------- | ---------------------- |
|KD(Process)|  KD(Process)  |KD(Process)|  KD(Process)  |
|CS() |  CS()   |CS() |  CS()   |
|BI(k)|**CU**(k)|BI(k)|**CU**(k)|
|CU(k)|**BI**(k)|CU(k)|**BI**(k)|
| I(k)|   I(k)  | I(k)|   I(k)  |
| KU (Process)| KU (Process)| KU (Process)| KU (Process)|
| KU (k)| KU (k)| KU (k)| KU (k)|

- Edge(ver.95) と Chrome(ver.101) はイベントの順番が同じです。また、Edge(ver.120) と Chrome(ver.120) のイベントの順番も同じです。
- ブラウザの種類やバージョンに関係なく、`keydown` が最初に発生します。**data プロパティの値は `Process`** で、**isComposing プロパティの値は `false`** です。
- `compositionstart` 以降の KeyboardEvent (keydown, keyup) と InputEvent (beforeinput, input) イベントの **isComposing プロパティは `true`** が返されます。
- Edge と Chrome の新旧バージョンでそれぞれ `beforeinput` と `compositionupdate` の発生の順番が入れ替わっています。
- `keyup` は2回呼ばれますが、それぞれの key プロパティは `Process` と `入力したキー` です。

**MacOS**
| Edge (ver.115)<br>Chrome (ver.116)| Edge (ver.120)<br>Chrome (ver.120)| Firefox (ver.120)| Safari (ver.17.1.2)|
| --------| ----- | -------- | --------- | 
| KD(k)| KD(k)　| KD(Process)        |  CS()  |
| CS()　　　　　　　| CS()　　　　　　　　　| CS(`<empty string>`)|  CU(k)|
| BI(k)| **CU(k)**| CU(k)|  BI(k)|
| CU(k)| **BI(k)**| BI(k)|  I(k)|
| I(k)| I(k)| I (k)| KD(k)|
| KU(k)| KU(k)| KU(k)| KU(k)|

- Windows との比較 (Edge と Chrome)
    - Windows の Edge や Chrome と異なり、`keydown` では、data プロパティの値は`入力したキー`です。
    - Windows の Edge や Chrome と同じように、Edge と Chrome の新旧バージョンでそれぞれ `beforeinput` と `compositionupdate` の発生の順番が入れ替わっています。
    - Mac の Edge と Chrome は `keyup` は 1 回しか呼ばれません。data プロパティの値は`入力したキー`です。
- Firefox
    - Edge や Chrome の新バージョンと同じ順序で Event が発生しますが、`keydown` イベントでは、data プロパティの値は`Process`です。
    - `compositionupdate` の data プロパティの値は空文字でなく、`<empty string>`です。
- Safari
    - KeyboardEvent を除けば、Edge や Chrome の新バージョン、Firefox と同じ順序で Event が発生しますが、`keydown` が `input` の後に回されています。
    - [バージョンが 16.4 よりも前の Safari の場合](https://developer.mozilla.org/ja/docs/Web/API/InputEvent/isComposing#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%BC%E3%81%AE%E4%BA%92%E6%8F%9B%E6%80%A7)は **InputEvent の isComposing プロパティの値は `undefined`** になります。

#### IME 入力中
**IME 入力中**つまり「IME 入力開始後で、IME 入力が確定するまでの間」の Event の発生の順番を見ていきます。

具体的には以下の 3 つのケースが想定されます。
1. 次の文字が入力される（例: あ -> あい）
2. 予測された単語の選択（例: あい -> 愛）
3. 文字を削除（例: あい -> あ）
※ 文字を全て削除するとIME入力が終了してしまう（例: あ -> ）

どのケースも以下の順番で Event が発生します。
`k` キーを押し、IME 入力開始した後、 `a` キーを押した際の結果です。
| `Windows`<br>Edge (ver.120)<br>Chrome (ver.120)|`MacOS`<br>Edge (ver.120)<br>Chrome (ver.120)| Firefox (ver.120)| Safari (ver.17.1.2)|
| ----------- | --- | --------- | --------- | 
|KD(Process)|KD(a)　|KD(Process)|CU (か)|
|CU(か)      |CU(か)|CU(か)    |BI(か)|
|BI(か)      |BI(か)|BI(か)    |I(か)|
|I(か)       |I(か)| I(か)     |KD(a)|
|KU(Process)|KU(a)|KU(a)     |KU(a)|
|KU(a)      |      |          ||

- 旧バージョンの Edge と Chrome は `beforeinput` と `compositionupdate` の発生の順番が入れ替わっているだけなので表にはまとめていません。
- バージョンが 16.4 よりも前の Safari の場合は **InputEvent の isComposing プロパティの値は `undefined`** になります。
- KeyboardEvent を除けば、すべてのブラウザで `compositionupdate` -> `beforeinput` -> `input` の順番で Event が発生します。


#### IME 終了時
**IME 終了時**つまり「IME 入力が確定する時」の Event の発生の順番を見ていきます。

具体的には以下の 4 つのケースが想定されます。
1. Enter キーをクリックして IME 入力を確定
2. IME 入力中に入力中の文字を全て削除（例: あ -> ）
3. IME 入力中に画面をクリック
4. 予測された単語の選択中に他のキーをクリック


**Enter キーをクリックして IME 入力を確定**
以下は、`k` キーを押し、IME 入力開始した後、さらに `a` キーを押し、IME によって`か`に変換された状態で Enter キーをクリックした場合の結果です。
| `Windows`<br>Edge (ver.120)<br>Chrome (ver.120)|`MacOS`<br>Edge (ver.120)<br>Chrome (ver.120)| Firefox (ver.120)| Safari (ver.17.1.2)|
| ----------- | ----- | --------- | --------- | 
|KD(Process)|KD(Enter)　|KD(Process)|BI (null)|
|CU(か)      |CU(か)   |BI(か)     |I (null))|
|BI(か)      |BI(か)  |CE(か)　　　　　　　　  |BI(か)|
|I(か)       |I(か)   | I(か)      |I(か)|
|CE(か)　　　　　　　　　　　　|CE(か)   |KU (Enter) |CE(か)|
|KU(Process)|KU(Enter)|           |KD(Enter)|
|           |         |           |KU(Enter)|

:::note
**Enter キーの関連情報**
Windows では、予測された単語の選択中に Enter キーを 1 度クリックするとIMEが確定します。
しかし、macOS では Enter キーを 2 度クリックする必要がある場合があります。その場合、1 度目のクリックでは以下の順で 2 つの Event が発生します。
||key|isComposing|
|--|--|--|
|KD |Enter|true|
|KU |Enter|true|
:::

**IME 入力中の文字を全て削除**
以下は、`k` キーを押し、IME 入力開始した後、さらに `a` キーを押し、IME によって`か`に変換された状態で `Backspace` キーをクリックした場合の結果です。

| `Windows`<br>Edge (ver.120)<br>Chrome (ver.120)|`MacOS`<br>Edge (ver.120)<br>Chrome (ver.120)| Firefox (ver.120)| Safari (ver.17.1.2)|
| ---------- | ----------- | --------- | --------- | 
|KD(Process)　|KD(Backspace)|KD(Process)|BI (null)|
|CU()       |CU()         |CU()      |I(null)|
|BI()     　　 |BI()         |BI()      |CE()|
|I(null) 【※1】|I(null) 【※1】  |CE()       |KD(Backspace)|
|CE()       |CE()        |I(か)      |KU(Backspace)|
|KU(Process)|KU(Backspace)|KU(Backspace)|     |
|KU(Backspace)|           |             |     |

:::note
※1 Edge と Chrome で contenteditable 要素を場合は、注意が必要です。
* 1 つのキーだけ(例: k, あ, １)  をクリック後、IME 入力中の 文字を全て消す
    -> `input` の `data` プロパティの値は `null`になる。
* 2 つのキー以上(例: か, １２) をクリック後、IME 入力中の 文字を全て消す
    -> `input` の `data` プロパティの値は`削除直前の文字`になる。
    
これは、`Backspace` 以外に `Escape` を使い、IME 入力中の 文字を全て削除した場合も同じです。
:::

:::note
Windows 用の　Edge (ver.120)　と Chrome (ver.120) で、DEl(Delete)キー使用してIME 入力中の文字を全て削除すると、Backspace キーの場合と同じ順番で Event が発生する。
    　最後の、keyup の key の値は、`Backspace` ではなく `Delete` です。
:::

以下は、`k` キーを押し、IME 入力開始した後、さらに `a` キーを押し、IME によって`か`に変換された状態で `Escape` キーをクリックした場合の結果です。
| `Windows`<br>Edge (ver.120)<br>Chrome (ver.120)|`MacOS`<br>Edge (ver.120)<br>Chrome (ver.120)| Firefox (ver.120)| Safari (ver.17.1.2)|
| ----------- | ----------- | --------- | --------- | 
|KD(Process)　|KD(Escape)|KD(Process)|CU()|
|CU()        |CU()        |CU()     |BI()|
|BI()|BI()        |BI()     |I()|
|I(null) 【※1】|I(null) 【※1】|I()      |BI(null)|
|CE()        |CE()   |CU()      |I(null)|
|KU(Process)　　|KU(Escape) |BI()      |CE()  |
|KU(Escape)　　　　|           |CE()       |KD(Escape)|
|            |           |I()       |KU(Escape)|
|            |           |KU(Escape)|        |

:::note
※1 Edge と Chrome で contenteditable 要素を場合は、注意が必要です。
* 1 つのキーだけ(例: k, あ, １)  をクリック後、IME 入力中の 文字を全て消す
    -> `input` の `data` プロパティの値は `null`になる。
* 2 つのキー以上(例: か, １２) をクリック後、IME 入力中の 文字を全て消す
    -> `input` の `data` プロパティの値は`削除直前の文字`になる。
    
これは、`Escape` 以外に `Backspace` を使い、IME 入力中の 文字を全て削除した場合も同じです。
:::

:::note
Escape キーの関連情報
- 予測された単語の選択中の場合に Escape キーを 1 度クリックすると、選択前の文字に戻ります。（例: 川 -> かわ）
    Windows の場合は、2 度クリックする必要がある場合がある。
    
- 予測された単語の選択中でない場合、IME 入力中の文字は全て消えます。
:::

**IME 入力中に画面をクリック**

以下は、`k` キーを押し、IME 入力開始した後、さらに `a` キーを押し、IME によって`か`に変換された状態で`画面をクリック`した場合の結果です。クリックした場所によって結果が変わる場合があります。

 + Web ページ上の要素をクリック

| `Windows`<br>Edge (ver.120)<br>Chrome (ver.120)|`MacOS`<br>Edge (ver.120)<br>Chrome (ver.120)| Firefox (ver.120)| Safari (ver.17.1.2)|
| ------ | --- | ----- | --- | 
|CE(か)  |CE(か)|BI(か) |CE()|
|BI(null)|     |CE(か)　|    |
|I(null)|     |I(か)  |    |
|BI(か)　　|     |       |    |
|I(か)　　 |    　　|       |    |

+ Web ページ外の画面をクリック 
 
| `Windows`<br>Edge (ver.120)<br>Chrome (ver.120)|`MacOS`<br>Edge (ver.120)<br>Chrome (ver.120)| Firefox (ver.120)| Safari (ver.17.1.2)|
| -------- | --- | ----- | --- | 
|CE(か)    |CE(か)|BI(か) |BI(null)|
|BI(null) |     |CE (か)|I(null)|
|I(null)　  |     |I(か)  |BI(か)|
|         |      |       |I(か)|
|         |      |       |CE()|


**予測された単語の選択中に他のキーをクリック**
以下は、`k` キーを押し、IME 入力開始した後、さらに `a` キーを押し、IME によって`か`に変換された状態で`画面をクリック`した場合の結果です。クリックした場所によって結果が変わる場合があります。

| `Windows`<br>Edge (ver.120)<br>Chrome (ver.120)|`MacOS`<br>Edge (ver.120)<br>Chrome (ver.120)| Firefox (ver.120)| Safari (ver.17.1.2)|
| -------- | --- | ----- | --- | 
|KD(Process)|KD (他の key)|KD(Process)【※1】|BI(null)|
|CU (下記) |CU (下記)     |BI(下記)|I(null)|
|BI(下記) 　 |BI(下記)     |CE(下記)|BI(下記)|
|I(下記)　　　　|I(下記)        |I(下記)|I(下記)|
|CE(下記) |CE(下記)       |(->CS()->)|CE(下記)|
|(->CS()->) |(->CS()->)      |       |(->CS()->)|


:::note
※1  クリックしても`keydown` が発生させないキーがあります。
Firefox で　`英数(Eisu)` キーや`かな(KanjiMode)` キーを押しても `keydown` は発生しません。
:::


# 終わりに

最新のブラウザでは、Event の順番がブラウザ間で統一されつつあり、また、InputEvent の isComposing プロパティが主要なすべてのブラウザでサポートされ、IME 入力の制御が古いバージョンと比べて容易になっています。

ただし、IME 入力の確定の方法によって、IME 入力終了時に発生する Event の数や順序が異なる点は依然として残っており、IME 入力の制御が完全には容易になっていません。