Reactで、入力した全角数字を半角数字に変換する処理を実装したい。
数字のみの入力を受け付けるパターンとしてよくあるのが、郵便番号の入力や電話番号の入力かと思います。
下記のように実装してみました。
const Example: React.FC = () => {
const [zipCode, setZipCode] = useState('');
// 全角数字は半角へ、数字以外は除去
const convertNumber = (value) =>
value.replace(/[^0-90-9]/g, '').replace(/[0-9]/g, (c) =>
String.fromCharCode(c.charCodeAt(0) - 0xfee0)
);
return (
<>
<label>
<input
type="text"
value={zipCode}
placeholder="郵便番号を入力"
onChange={(e) => setZipCode(convertNumber(e.target.value))}
/>
</label>
</>
);
}
export default Example;
Safariでのみ上手く動かない
上記の内容で動作確認してみたところ、Chrome等では問題なく動作しましたが、
Safariでのみ数字が重複して入力される現象が発生しました。
画像の現象は、全角で「123」と入力 -> Enter(確定)を押した
挙動になります。
調べてみましたが、似たような事象を解説しているのは下記の記事しか見当たりませんでした。
IME(全角)入力におけるjsイベント現状調査
onBlurを使う
onBlurは、フォーカスが離れた時に実行される処理となります。
元々入力時点で変換される想定で作っていましたが、
下記の記事も参考にさせていただき、結果的にonBlurで変換処理を実行させる方がいいかもと思い至りました。
入力直後に変換を入れる場合のUX
最終的に下記の形に修正しました。
const Example: React.FC = () => {
const [zipCode, setZipCode] = useState('');
// 全角数字は半角へ、数字以外は除去
const convertNumber = (value) =>
value.replace(/[^0-90-9]/g, '').replace(/[0-9]/g, (c) =>
String.fromCharCode(c.charCodeAt(0) - 0xfee0)
);
return (
<>
<label>
<input
type="text"
value={zipCode}
placeholder="郵便番号を入力"
onChange={(e) => setZipCode(e.target.value)}
onBlur={() => setZipCode(convertNumber(zipCode))}
/>
</label>
</>
);
}
export default Example;
これで、inputからフォーカスが離れた際に全角数字を半角に、数字以外の文字は除去される挙動を実装することができました。
ChromeでもSafariでもfirefoxでも、きちんと動作していることを確認しました。