Closed5
数値のinput要素に3桁ごとのカンマを振る
星の数ほど解説があるが……。例えば
など。
実装上考慮すべき問題は、
-
type='number'
のままだとカンマが入らない -
type='text'
にすると- 任意のテキストが入力可能になってしまう
- スマートフォンなどで数値キーボードが開かない
- カンマの挿入によって直感に反した挙動になりがち
- CSSはブラウザ間差異の吸収が煩雑
といくつかある。
入力の最中はカンマを表示できないという点を妥協できるなら、onblur
と onfocus
で切り替える方法が簡易。
カンマ区切りは Intl.NumberFormat
を使うのが良い。
React では input
には onBlur
と onFocus
が生えているのでそれを使う。
input
の type
属性は state として持って、"number"
と "text"
の合併(下記コード中では InputType
)にする。
要素にフォーカスが当たった瞬間(onFocus
)に "number"
へ、外れた瞬間(onBlur
) に "text"
に切り替わるようにする。こうすると、入力中は数値入力欄として機能する。
全体では、大体以下のようになる:
CommifiedNumberInput.tsx
import { useState } from "react";
type Props = {
value: number | null;
onChange: (value: number | null) => void;
};
type InputType = "number" | "text";
const format = (inputType: InputType, value: number | null) => {
if (value === null) {
return "";
}
switch (inputType) {
case "number":
return value.toString();
case "text":
const formatter = new Intl.NumberFormat("ja-JP");
return formatter.format(value);
}
};
export const CommifiedNumberInput = (props: Props) => {
const [inputType, setInputType] = useState<InputType>("text");
return (
<input
type={inputType}
value={format(inputType, props.value)}
onChange={({ target: { value } }) =>
props.onChange(value === "" ? null : parseInt(value, 10))
}
onBlur={() => setInputType("text")}
onFocus={() => setInputType("number")}
/>
);
};
Commify.
parseInt
の第2引数は入れておきましょう。
parseInt('0x1000')
// -> 4096
CodeSandboxのデモ
State に持っている value
の値として null
を許容するのは、空欄になったときのため。
0に置き換えるのもNaNを露出させるのも具合が悪い。バリデーションは別途行う。
このスクラップは2022/08/09にクローズされました