ですが IME で変換している状態では 文字数制限を超えて入力できます
確定時に超えた部分は捨てられる挙動です
これができないと日本語入力だと不便です
例えば 2 文字入力できるところに「入力」と入力したいとします
ひらがなで 「にゅうりょく」 と入力してから変換するわけですが IME の変換中も 2 文字しか入らないと 「にゅ」 までしか入力できません
ローマ字入力だと 「nyu」 ですからこの時点でもオーバーしています
React などで input を制御するときは入力文字や文字数を制御して最初から入力できないようにすることがありますが そのときに maxlength の動きのように IME で変換中は入力不可の文字も一時的に入力できるようにしたいです
IME の状態の変化は compositionstart と compositionend イベントで取得できます
一応 input イベントの isComposing プロパティでもわかりますが これだとその入力が変換中のものかはわかりますが 確定されたことが伝わらないので compositionend イベントのほうがいいです
maxlength 相当なものを作ってみました
const App = () => {
const [text1, setText1] = useState("")
const [text2, setText2] = useState("")
return (
<div>
<Input1 value={text1} onChange={setText1} max={2} />
<p>{text1}</p>
<hr/>
<Input2 value={text2} onChange={setText2} max={2} />
<p>{text2}</p>
</div>
)
}
const Input1 = ({ value, onChange, max }) => {
const [composing, setComposing] = useState(false)
return (
<input
value={value}
onChange={(event) => {
if (composing) {
onChange(event.target.value)
} else {
onChange(event.target.value.slice(0, max))
}
}}
onCompositionStart={() => {
setComposing(true)
}}
onCompositionEnd={() => {
setComposing(false)
onChange(value.slice(0, max))
}}
/>
)
}
const Input2 = ({ value, onChange, max }) => {
const [local, setLocal] = useState("")
const [composing, setComposing] = useState(false)
useEffect(() => {
setLocal(value)
}, [value])
return (
<input
value={local}
onChange={(event) => {
const v = event.target.value
if (composing) {
setLocal(v)
} else {
const fixed = v.slice(0, max)
setLocal(fixed)
onChange(fixed)
}
}}
onCompositionStart={() => {
setComposing(true)
}}
onCompositionEnd={() => {
setComposing(false)
const fixed = local.slice(0, max)
setLocal(fixed)
onChange(fixed)
}}
/>
)
}
Input1 と Input2 があり どちらも IME の変換中は最大文字数を超えられます
Input1 は最大文字数を超えた状態でも onChange を呼び出します
input イベントに近い感じで 入力があれば不正状態でも親に伝えます
Input2 ではローカルステートを用意して IME の変換中は親には変更があったことを伝えず内部のステートのみ更新します
ここでは maxlength 相当の文字数処理しかしてませんが 電話番号入力欄で確定時に数字とハイフン以外を消すようにすれば 「でんわ」 から電話番号に変換できるよう IME に登録してるユーザーを考慮することができたりします
ちなみに React などで IME の変換中に文字数オーバーや使えない文字を消しても IME は内部で入力状態を持っているので 画面には見えないだけで変換候補はちゃんと入力したものとして出てきます
しかし 変換候補を切り替えるときに 見えている文字を置き換えるために元の文字数分を消して現在の変換候補の文字を入力するので その間で無理やり書き換えると関係ない文字が消えてしまい正しく入力できなくなります (Google IME で確認)
そもそも入力自体を不可にする処理っているのですかね?