Closed5

数値のinput要素に3桁ごとのカンマを振る

roibanroiban

星の数ほど解説があるが……。例えば

https://qiita.com/ArimaRyunosuke/items/cbed9ed41055267be26e

など。

実装上考慮すべき問題は、

  • type='number' のままだとカンマが入らない
  • type='text' にすると
    • 任意のテキストが入力可能になってしまう
    • スマートフォンなどで数値キーボードが開かない
    • カンマの挿入によって直感に反した挙動になりがち
  • CSSはブラウザ間差異の吸収が煩雑

といくつかある。

入力の最中はカンマを表示できないという点を妥協できるなら、onbluronfocus で切り替える方法が簡易。

https://stackoverflow.com/a/70726755/16721230

カンマ区切りは Intl.NumberFormatを使うのが良い。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat

roibanroiban

React では input には onBluronFocus が生えているのでそれを使う。

inputtype 属性は 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.

https://en.wiktionary.org/wiki/commify

roibanroiban

State に持っている value の値として null を許容するのは、空欄になったときのため。
0に置き換えるのもNaNを露出させるのも具合が悪い。バリデーションは別途行う。

このスクラップは2022/08/09にクローズされました