⌨️

<input>要素の日本語入力・カーソル挙動と Android での onKeyDown の制御

2024/12/19に公開

Applibot Advent Calendar 2024」 19日目の記事になります。

はじめに

<input> は一見シンプルですが、日本語入力 (IME) やモバイル環境、特にAndroid では独特の挙動があり、開発者を悩ませることがあります
実際に私もかなり苦しみました😢
この記事では、これらの課題と解決策を、実際のコード例とともに解説します

対象読者:

  • Web 開発経験者 (HTML, CSS, JavaScript, TypeScript などの基礎知識がある方)
  • 日本語入力周りの処理で困っている方
  • <input> 要素のカーソル位置制御を理解したい方
  • Android での入力関連の問題を解決したい方

日本語入力の制御

日本語入力で発火する CompositionEvent

以下のようなキーボードで入力を行った時に input で発火するイベントが CompositionEvent です。

CompositionEvent には、以下の3つの種類があります。

入力開始時に発火する onCompositionStart

compositionstart イベントは、 IME などのテキスト変換システムが新しい変換セッションを開始した時に発生します。

例えば、このイベントはユーザーがピン音 IME を使用して漢字の入力を開始した後に発生します。

入力値変更時に発火する onCompositionUpdate

compositionupdate イベントは、 IME などのテキスト変換システムによって制御されているテキスト変換セッションに新しい文字が入力されたときに発生します。

例えば、このイベントは、ユーザーがピン音 IME を使用して漢字の入力をしている最中に発生します。

入力値確定時に発火する onCompositionEnd

compositionend イベントは、 IME などのテキスト編集システムが現在の編集セッションを完了またはキャンセルした時に発生します。

例えば、このイベントは、ユーザーが ピン音 IME を使用して漢字の入力を完了した後に発生します。

CompositionEvent を利用する上での注意点:

単純に値を入力するだけであれば特に問題はないです
しかし値のフォーマットを行うような場合には、変換確定前の文字列は未確定であることによって onChange イベントがうまく動かない場合があります

また、onChange イベントでのフォーマットによってカーソル位置が末尾に移動してしまう可能性もあるので、カーソル位置の制御も考える必要がある場合もあります

カーソル位置の制御

<input> 要素のカーソル位置は、selectionStartselectionEnd プロパティで取得でき、 input.setSelectionRange で設定できます

const start = inputElement.selectionStart;
const end = inputElement.selectionEnd;
console.log(`カーソル位置/選択範囲: ${start} から ${end}`);

// カーソルを位置 5 に移動
inputElement.setSelectionRange(5, 5);

// 位置 3 から 6 までを選択
inputElement.setSelectionRange(3, 6);

カーソル位置の取得

input.selectionStart

selectionStart は HTMLInputElement インターフェイスのプロパティで、選択テキストの先頭インデックスを表す数値です。何も選択されていない場合は、 <input> 要素内のテキスト入力カーソル(キャレット)の位置を返します。

input.selectionEnd

selectionEnd は HTMLInputElement インターフェイスのプロパティで、選択テキストの末尾のインデックスを表す数値です。選択がない場合、これは現在のテキスト入力カーソル位置の直後の文字のオフセットを返します。

カーソル位置の設定

input.setSelectionRange

HTMLInputElement.setSelectionRange() メソッドは、 <input> または <textarea> 要素の中で現在のテキストの選択範囲の開始位置と終了位置を設定します。

Android 特有の挙動と対策

Android では、イベントの挙動が他の環境と異なり onKeyDownevent.keyUnidentified になります

This is a known bug in Chrome on Android. See this other question and the official bug report. I wish I had better news for you, but it looks like you can't rely on keyboard triggers in web apps on Android if you need to capture the individual key values.

引用元: https://stackoverflow.com/questions/59584061/why-is-unidentified-returned-on-keyboard-input-on-mobile

これによって onKeyDown で特定のキーが押下された時に特定の処理をすることができなくなります。

例えば、削除キーが押下された時に特定の関数を走らせたくてもできない と言う問題があります。

対策

削除キー(Backspace, Delete)を押下した時の onChangeevent.nativeEvent.datanull になることを利用して特定の関数を実行する

まとめ

<input> 要素での日本語入力処理は、CompositionEvent、カーソル位置制御、Android への対応など、いくつか注意点があります
本記事で紹介したテクニックを活用することで、より快適なユーザー体験を提供できるWeb アプリケーションを開発できます

Discussion