日本のWEBフォームにおいて、数値入力ほどユーザーに不親切な箇所はない。
「半角数字で入力してください」というエラー。全角の「1」を打つと弾かれる。カンマを入れると消される。「1.5万」なんて論外。この「システム側の都合をユーザーに押し付ける」仕様が、入力体験を著しく損なわせていると感じていた。
そこで、日本語特有の数値入力をよしなに処理し、Vue 3 / TypeScript 環境でスマートに扱うためのライブラリを作成した。
解決したい悩み
- 全角・記号のバリデーションが煩わしい: ユーザーに半角を強要するのではなく、システム側で正規化したい。
- 「1.5万」や「参拾萬」を扱いたい: 日本語話者にとって自然な単位表記を無視せず、正確な数値としてパースしたい。
- IME変換を壊したくない: 入力中にリアルタイムで値を書き換えてしまい、変換中の文字が消える現象を防ぎたい。
- Vue 3でロジックが散らばる: 監視、バリデーション、整形といった一連の流れを使い回したい。
数値入力の「タイミング」を設計する
このライブラリの核は、処理を「入力中」「停止中」「確定時」の3フェーズに分離したことにある。
1. 入力中:ユーザーを自由にさせる
入力イベント(input)の最中には、データの書き換えを一切行わない。全角、漢字、記号、何を入力しても許容する。
ここを制限したり自動置換したりすると、IME(日本語入力)の挙動と衝突し、ユーザーは激しいストレスを感じるからだ。
2. 停止中:そっと診断する
ユーザーの手が止まってから debounce を介してバリデーションを走らせる。
ここで初めて「内部的に」パースを行い、エラーがあれば表示する。ポイントは、画面上の入力ボックスの文字はまだ書き換えないこと。「裏側でこっそりチェックする」のがミソだ。
3. 確定時(Blur):体裁を整える
フォーカスが外れた瞬間に、初めて正規化された値を画面に書き戻す。
ここで全角は半角になり、「1.5万」は「15000」になり、指定された小数点桁数にピシッと整う。ユーザーがその項目を離れたとき、データがクリーンになる。この「離脱時」というタイミングが、ユーザーにとってもシステムにとっても最も安全で納得感がある。
内部で行われる3段階の処理パイプライン
「汚い入力文字列」が「クリーンな数値」になるまで、ライブラリ内部では以下の3つのステップを順に踏んでいる。ここを分離したことで、柔軟なカスタマイズが可能になった。
- 正規化レイヤー(Normalization)
全角から半角への置換、不要な記号の除去、漢数字や「万・億」といった単位を絶対値に展開する。ここでは「意味を変えずに、プログラムが読める形」に整える。 - 修正処理レイヤー(Correction)
小数点以下の桁数制限、切り捨て、四捨五入といった「丸め」を行う。ユーザーの曖昧な入力を、ビジネスルールに基づいた正確な数値に補正するステップ。 - バリデーションレイヤー(Validation)
確定した最終的な値に対して、最小値・最大値・必須チェックなどの制約を検証し、エラーメッセージを確定させる。

モバイルでの入力しやすさを担保する
HTML側の属性についても触れておく。数値入力だからといって type="number" を使うのは、実はおすすめしない。PCブラウザで予期せぬスピンボタン(上下矢印)が出たり、モバイルでの挙動が不安定になるからだ。
今回のライブラリを使用する場合は、以下の属性指定を推奨する。
<input
type="tel"
inputmode="numeric"
/>type="tel" はモバイル端末で数字キーパッドを呼び出すために最も安定した指定であり、inputmode="numeric" を組み合わせることで、OSに対して「数値入力である」ことを明確に伝えられる。
漢数字パース
「情報量を落とさない」ための工夫として、漢数字や位単位(兆・億・万)、大字(壱萬、参拾萬)の解析を入れている。
// 解析例
parseKanjiNumerals("1.5億"); // "150000000"
parseKanjiNumerals("参拾萬"); // "300000"
parseKanjiNumerals("壱萬弐拾"); // "10020"これは単なる多機能自慢ではない。「万」と打った瞬間にそれを「ノイズ」として消去してしまう既存の正規化へのアンチテーゼだ。ユーザーが単位まで含めて入力したなら、その意図を汲み取って数値化する。
ライブラリの構成と使い方
ソースコードは GitHub に公開した。

導入
Vue 3 の Composable (useNumericField) を使い、設定を渡すだけで「自由な入力・一呼吸置いた診断・離脱時の整形」がセットで手に入る。
import { useNumericField } from 'moglog-numeric-input-kit';
const amount = useNumericField("0", "資産額", {
decimalPlaces: 0, // 整数モード
mode: "trunc", // 切り捨て
min: 0 // 0以上
});テンプレートへの紐付け
v-model、blur、focus のイベントをバインドする。
<input
type="tel"
inputmode="numeric"
v-model="amount.rawInput.value"
@blur="amount.onBlur"
@focus="amount.onFocus"
/>
<p v-if="amount.error.value">{{ amount.error.value }}</p>良い点と微妙な点
自作してみて感じた正直な感想。
良い点:
- 入力中に勝手に文字が消えたり書き換わったりしない安心感。
- Vueのコンポーネント側が驚くほどスッキリする。
- 「1.5万」や「壱千万」のようなコピペ入力に耐えられる。
微妙な点:
- 「一億」が「10000(万円)」か「100000000(円)」かはプロジェクトの単位設計に依存するため、表示側の
baseUnit設定で調整が必要になる。 - 単純な正規表現に比べればコード量は増えるが、保守性とUXの向上を考えれば妥当なトレードオフだと思う。
まとめ
数値入力は、突き詰めると「どこまでユーザーを信頼し、どのタイミングでデータをクリーンにするか」という設計の話になる。
制約でユーザーを縛るのではなく、入力の自由を保証した上で、離脱時にシステムが責任を持ってデータを整える。この「タイミングの設計」と「3段階の処理フロー」こそが、日本語入力における数値フォームの最適解だと考えている。


コメント