はじめに
Web サイトの入力フォームとして input や textarea タグで文字列を受け付けることは多いと思います。
サーバー側でのバリデーションは当然として、クライアント側でも UX 向上のために入力制御を行いたい場面はよくあります。
ただ、標準の入力要素だけでは思い通りに制御できないことも多いです。
例えば、以下のようなケースで困ったことはないでしょうか
-
maxlengthを指定しているのに、type="number"だと効かない - 全角数字が入力されてしまい、サーバーでエラーになる
- IME入力中に制御しようとして挙動が壊れる
- カンマ区切りや通貨表示をしたいが、入力中の制御が難しい
このように「やりたいことは単純なのに実装が面倒」というのが、入力制御の厄介なポイントです。
今回は、これらの問題をシンプルに解決できる TextInputGuard ライブラリ を紹介します。
TextInputGuard とは
TextInputGuard は、入力欄の入力制御に特化した MIT ライセンスのライブラリです。
自作で入力制御を実装しようとすると、以下のような問題に直面します。
- IME変換の扱いが難しい
- Undo / Redo の挙動
- オートコンプリートやペースト時の挙動
これらをすべて正しく扱うのは意外と大変です。
TextInputGuard を使うことで、これらを意識せずに ルールを組み合わせるだけで入力制御を実現 できます。
使用例
数値入力
ESモジュールとして数値入力の制限をかけた例です。
See the Pen TextInputGuard - price by natade (@natade-jp) on CodePen.
HTML
<input id="price" type="text" inputmode="decimal" style="text-align: right" value="123.45" />
JavaScript
import { attach, rules } from "https://esm.sh/text-input-guard";
const input = document.querySelector("#price");
const guard = attach(input, {
rules: [
rules.numeric({
allowFullWidth: true,
allowMinus: true,
allowDecimal: true,
allowEmpty: false
}),
rules.digits({
int: 8,
frac: 2,
modeInt: "block",
modeFrac: "block",
fixFracOnBlur: "round",
forceFracOnBlur: true
}),
rules.prefix({
text: "¥",
showWhenEmpty: true
}),
rules.suffix({
text: " JPY"
}),
rules.comma()
],
onChange: () => {
render();
return;
}
});
解説
attach に対して rules を配列で渡すだけで入力制御を構築できます。
例では役割ごとにルールを分けています。
-
rules.numeric()→ 数値として入力できるようにする -
rules.digits()→ 整数部・小数部の桁数を制御する -
prefix()/suffix()/comma()→ 表示用の整形
また onChange のコールバックの設定で次の結果を出力するように作っています。
guard.getDisplayValue(): ¥123.45 JPY
guard.getRawValue() : 123.45
見た目(getDisplayValue)と実データ(getRawValue)は別管理になっており prefix() / suffix() / comma() は見た目に関するルールで、フォーカスを外したタイミングで自動的に付与されます。
一方で、フォーム送信時には getRawValue() の値が使われるため、サーバー側では純粋な数値として扱えます。
※ 投稿時点は見た目と実データが別管理になるようなルールは textarea に対応していないので注意
文字入力
ライブラリを UMD 形式で利用し、英数字入力を制限する例です。
See the Pen TextInputGuard - price sample umd by natade (@natade-jp) on CodePen.
HTML
<script src="
https://cdn.jsdelivr.net/npm/text-input-guard@1.2.1/dist/umd/text-input-guard.min.js
"></script>
<input id="code" type="text" inputmode="url" value="123abc" />
JavaScript
const input = document.getElementById("code");
const guard = TextInputGuard.attach(input, {
rules: [
TextInputGuard.rules.imeOff(),
TextInputGuard.rules.ascii({
case: "upper"
}),
TextInputGuard.rules.filter({
category: ["alpha-upper", "digits"],
allow: /[-_@]/
}),
TextInputGuard.rules.bytes({
max: 10,
mode: "block",
unit: "cp932"
})
]
});
解説
英数字コード入力を想定して制限を行っています。
それぞれのルールの役割は以下の通りです。
-
imeOff()→ 全角入力されても半角相当に補正 -
ascii()→ 英数字を半角に統一し、大文字へ変換 -
filter()→ 許可した文字(英大文字・数字・一部記号)のみ通す -
bytes()→ バイト数(ここでは CP932)で最大長を制限
また、inputmode="url" を指定することで、モバイル環境では英数字入力しやすいキーボードが表示されます。ただし、これだけでは完全には制御できないため imeOff() と組み合わせて補正しています。
このように、rules を組み合わせることで「入力補正」「入力制限」「長さ制御」を段階的に適用できるのが特徴です。
利用できるルールについて
説明してきたようにルールを組み合わせることで入力制御を構築します。
ルールは自作することも可能ですが、標準で多くの実用的なルールが用意されています。
特に、文字数制限に関しては以下のように用途に応じた制御が可能です。
- 文字数単位(グラフェム)
- バイト数単位(UTF / SJIS / CP932)
- 表示幅単位(半角=1 / 全角=2)
日本語環境特有の要件にも対応しやすいのが特徴です。
また、filter() を利用することで、
- 許可した文字種のみ通す
- サロゲートペア(絵文字など)を除外する
- 特定の記号のみ許可する
といった細かい入力制御も行えます。
文字列系
| ルール | 主な用途 | 主な機能 | よく使う組み合わせ |
|---|---|---|---|
kana() |
かな入力 | ひらがな・全角カタカナ・半角カタカナへ正規化 |
trim() / filter()
|
imeOff() |
ASCII入力欄 | 日本語IMEで入りやすい記号や全角ASCIIをASCII入力相当に補正 | ascii() |
ascii() |
英数字・コード入力 | 全角英数字・記号・スペースを半角ASCIIへ正規化し、英字の大小文字も統一可能 |
imeOff() / filter()
|
filter() |
入力可能文字の制限 | 許可カテゴリ・追加許可・禁止文字を指定し、不許可文字を削除またはエラー化 |
ascii() / kana()
|
trim() |
前後空白の除去 | 入力値の先頭・末尾の空白を削除 | 文字列系全般 |
length() |
文字数制限 | グラフェム・UTF-16・UTF-32単位で最大長を制御 | filter() |
width() |
半角全角の幅制限 | 半角を1、全角を2として最大幅を制御 | 氏名・住所・品名など |
bytes() |
バイト数制限 | UTF-8 / UTF-16 / UTF-32 / SJIS / CP932 のバイト数で最大サイズを制御 |
filter("sjis-only") / filter("cp932-only")
|
数値系
| ルール | 主な用途 | 主な機能 | よく使う組み合わせ |
|---|---|---|---|
numeric() |
数値入力の基本処理 | 全角数字の半角化、マイナス、小数点、空文字の扱いを制御 | digits() |
digits() |
数値の桁数制限 | 整数部・小数部の桁数、超過時の挙動、blur時の丸めや0埋めを制御 | numeric() |
表示整形系
| ルール | 主な用途 | 主な機能 | よく使う組み合わせ |
|---|---|---|---|
comma() |
金額・数量の表示整形 | 整数部に3桁区切りカンマを付与 |
numeric() / digits()
|
prefix() |
表示用の接頭辞 | 先頭に ¥ などの文字列を付与 |
comma() |
suffix() |
表示用の接尾辞 | 末尾に 円 などの文字列を付与 |
comma() |
より詳細なオプションや挙動については、以下の API ドキュメントを参照してください。
さいごに
入力制御は一見シンプルですが、実際にやるとブラウザ差異やIMEの影響でかなり厄介な分野です。
TextInputGuard を使うことで、その複雑さを意識せずに実用的な入力制御を実現できます。
同じように悩んでいる方の参考になれば幸いです。