inputの入力値の制御でIME入力とかいろいろ辛かった話

この記事は、[株式会社Y's アドベントカレンダー](https://qiita.com/advent-calendar/2017/ysinc) 3日目の記事になります。

#これまでのあらすじ:santa_tone2:
やんやんは激怒した。必ず、かの、邪智暴虐の入力値をごにょごにょして、全角数字を半角数字に変換して半角数字以外を取り除いて適切な箇所に半角スペースを挿入しなければならぬと決意した。
しかし、やんやんにはIME入力時の挙動がわからぬ。

#ズバリ実装したかったこと:santa_tone2:

- キー入力ごと＆フォーカスアウトで
    - 全角数字を半角に変換
    - 半角数字以外を取り除く
    - 5番目と10番目に半角スペースを挿入する
    - スペースを挿入した際には、キャレットの位置も一つ右に移動させる
 
※キャレット（入力してる時に表示される｜これ）

そこに立ちはだかったのは、IME入力でした:innocent:


#ということで、IMEとは:santa_tone2:
IME（Input Method Editor）は、文字入力をサポートするソフトウェアのことです。
<img width="300" alt="ime.png" src="https://qiita-image-store.s3.amazonaws.com/0/169142/1f7808cc-f193-f03e-bcf7-1f7fd2591941.png">

こんな感じで、キーボードで「yannyann」と入力して「やんやん」と変換してくれるような機能ですね。

focusout時に「邪智暴虐の入力値をごにょごにょして、半角数字以外を取り除いて適切な箇所に半角スペースを挿入」とかであればよかったのですが、その時実装したかったのは、キー入力時だったので、IMEを制御しなければいけないことに・・・

#「1」を入力した時の挙動まとめ:santa_tone2:

[日本語入力時に発生するキーイベントのテスト](http://freefielder.jp/lab/javascript/im.html)を使用させていただき、
以下のブラウザで検証しました

- Chrome: 62.0.3202.94
- Firefox: 57.0.1
- Edge: 40.15063.674.0
- IE11: 11.0.9600.17843
- safari: 11.0.1


##IME-OFF

|            | Chrome | Firefox | Edge | IE11 | safari |
|:----------:|:------:|:-------:|:----:|:----:|:------:|
| keydown    |   49   |    49   |  49  |  49  |   229  |
| keypress   |   49   |    0    |  49  |  49  |    -   |
| keyup      |   49   |    49   |  49  |  49  |   49   |

##IME-ON

|            | Chrome | Firefox | Edge | IE11 | safari |
|:----------:|:------:|:----------------:|:----:|:----:|:------:|
| keydown    |   229  |   Win: - / Mac: 49        |  229 |  229 |   229  |
| keypress   |   -    |        -         |  -   |  -   |    -   |
| keyup      |   49   |        -         |  49  |  49  |   49   |

これを見るだけでも闇が深いですね・・・
さらに変換を確定させるのにEnter押した時とかEnter押さずにフォーカスアウトさせた時とかの場合も想定しなきゃいけない・・・:baby:

#辛かった経緯:santa_tone2:
いろんなところでつまずき結局実装できなかったので、具体的にこういうところで困ったよというのを思い出しながら迷走した順番に書いていきます。
もし解決手段あったら教えてください・・・

##IMEでの入力を考慮しなくて良かったのでkeyupで検知
当初、IMEを考慮してませんでした。
なので、上の表からもわかるように、keyupなら各ブラウザの挙動が同じで都合が良かったので、keyupで検知していました。

##キーの入力速度で呼ばれるイベントの順番が変わる
keyupで制御しようとすると、キー入力速度によって次の文字のkeydownの方が先に呼ばれてしまう。

どういうことかというと、半角で「123」と入力したとすると・・・
###ゆっくり入力した場合
| 呼ばれる順番 | イベントの種類  | キーコード |
|:----------:|:-----------:|:-------:|
| １          | keydown     |    49   |
| 2          | keypress    |    49   |
| 3          | keyup       |    49   |
| 4          | keydown     |    50   |
| 5          | keypress    |    50   |
| 6          | keyup       |    50   |
| 7          | keydown     |    51   |
| 8          | keypress    |    51   |
| 9          | keyup       |    51   |


###素早く入力した場合

| 呼ばれる順番 | イベントの種類  | キーコード |
|:----------:|:-----------:|:-------:|
| １          | keydown     |    ４９   |
| 2          | keypress    |    49   |
| 3          | keydown     |    50   |
| 4          | keypress    |    50   |
| 5          | keydown     |    51   |
| 6          | keypress    |    51   |
| 7          | keyup       |    49   |
| 8          | keyup       |    50   |
| 9          | keyup       |    51   |

こんな感じになります。スペースを挿入する時にキャレットの制御もしなきゃいけないのでイベントの順番が変わるの辛かった気がする。

##IME入力した時も想定しなければいけないことになる
IMEを想定しない状態でも結構面倒だったのですが、なんとかバグを潰すことに成功。

ところが、IME入力も救わなければいけないことになりました。
この時点では、IME入力すると入力内容が点滅する感じになってました。。
ソースコードも書こうと思っていたのですが、あまりに整理できてなくて恥ずかしいので、またちゃんとしてから載せます。。。

##IME-ONの場合は変換確定されてからもろもろの処理を走らせよう
ということで、IME-ONの場合だけ、変換確定前のキー入力時には入力制御の関数はreturnして、Enterキーを押下した際、もしくはフォーカスアウト時に関数を実行させようと考えました。

##IME入力をFirefoxで検知できない
は〜〜〜〜〜〜〜？？？？？？？？？:innocent:
お手上げ侍＼(^O^)／


#今回の辛みポイントまとめ:santa_tone2:
- IME-ON、OFFで呼ばれるイベントが変わる
- ブラウザごとに呼ばれるイベントが変わる
- キー入力速度によってイベントの呼ばれる順番が変わる
- safari・・・半角で・・・？
- Firefoxどうした :sos:

複合的な問題があってバグがバグを生むみたいなことになってましたが、今回ざっくりまとめてみて、まだkeydownの方が可能性あるのかなとぼんやり思いました。

##追記
いまだにちょこちょこ記事いいね付くので、正しい知識の記事を紹介しておきます。
KeyboardEvent.keyCodeでお手上げになった人はぜひ・・・
　↓
■KeyboardEvent.keyCodeとは何か
https://d-toybox.com/studio/lib/what_is_keyCode.html

次は 4日目 @ranmaru_genki さんの記事です。お楽しみに！

#参考サイト:pray:
[jQueryでIME入力確定時にイベントを発行する](https://qiita.com/hrdaya/items/6488d8dd3962cf35c0a0)
[IE系でinputイベントが発火しないのをなんとかしてみる（ついでにIME入力中は発火しないイベントも追加）](https://qiita.com/hrdaya/items/29576a5e1a303c074249)
[JavaScript における 日本語入力 確定 (Enter) イベント](https://garafu.blogspot.jp/2015/09/javascript-ime-enter-event.html)