こんにちは。inputで数字と言えばinput type=”number”が思いつくかもしれませんが、実はそれだけでは不十分なんです。
- 実は数字以外も入力できてしまう
- 属性で制限をかけても数字以外の入力を防ぐことができない
といった点があるからです。したがって、完全に数字のみ受け付けるならJavaScriptを追加する必要があります。本記事ではその方法を解説していきます。
なぜ不十分なのか、より詳しく知りたい方はこちらも合わせてご覧ください。
結論(コピペ可)
まずデモを用意しました。
コードは以下です。
<label>
年齢(整数のみ):
<input
type="text"
id="age"
inputmode="numeric"
placeholder="例)25"
pattern="[0-9]*"
/>
</label>
<script>
// 対象の input を取得
const ageInput = document.getElementById("age")
// 入力されるたびに数字以外を取り除く = 数字以外を空文字に置き換える
ageInput.addEventListener("input", () => {
ageInput.value = ageInput.value.replace(/\D/g, "")
})
</script>
日本語入力を考慮した応用版
<label>
年齢(整数のみ):
<input
type="text"
id="age"
inputmode="numeric"
placeholder="例)25"
pattern="[0-9]*"
/>
</label>
<script>
const ageInput = document.getElementById("age")
ageInput.addEventListener("input", () => {
ageInput.value = ageInput.value
.replace(/[\uFF10-\uFF19]/g, (c) =>
String.fromCharCode(c.charCodeAt(0) - 0xfee0)
)
.replace(/\D/g, "")
})
</script>
<label>
年齢(整数のみ):
<input
type="text"
id="age"
inputmode="numeric"
placeholder="例)25"
pattern="[0-9]*"
/>
</label>
<script>
const ageInput = document.getElementById("age")
ageInput.addEventListener("input", (event) => {
if (event.isComposing) return // 変換中は何もしない
sanitize(event.target)
})
// 変換確定タイミング(compositionend)でサニタイズを実行
ageInput.addEventListener("compositionend", (event) => {
sanitize(event.target)
})
const sanitize = (element) => {
element.value = element.value
.replace(/[\uFF10-\uFF19]/g, (c) =>
String.fromCharCode(c.charCodeAt(0) - 0xfee0)
)
.replace(/\D/g, "")
}
</script>
解説
ベースのHTML
まずinputはtype=”text”にしておきましょう。あとはinputmode=”numeric”にしておくとスマホユーザーは数字キーボードがデフォルトで表示されます。UX的に良いので指定しておきましょう。
<input
type="text"
id="age"
inputmode="numeric"
placeholder="例)25"
pattern="[0-9]*"
/>
また、万が一数字以外が入力されてもエラーを出すようにpattern属性を付けておきましょう。これは正規表現(後述)を使っていて、ここでの意味は「0〜9のどれかが0文字以上」という意味になります。0文字以上というのは空文字でもOKということです。
type=”number”ではこのpattern属性が使用できません。完全に数字のみの入力を求める場合、フォーム送信時のバリデーションを考えるとこの形の方が厳密なのでおすすめです。
参考:https://developer.mozilla.org/ja/docs/Web/HTML/Reference/Attributes/pattern
JavaScriptで「整数のみ」を確実に制御する
ここからがポイントですが、inputに入力されるたびに、数字以外を除去するという方針で実装します。文字列(特にユーザーに入力させるもの)を特定に目的に合わせて整形したり、安全な形に整えることをサニタイズと言います。
具体的な実装ですが、以下です。
<script>
// 対象の input を取得
const ageInput = document.getElementById("age")
// 入力されるたびに数字以外を取り除く
ageInput.addEventListener("input", () => {
ageInput.value = ageInput.value.replace(/\D/g, "")
})
</script>
対象となるinputを取得し、文字が入力されるたび = inputイベントが発火するたびにvalueの値から数字以外を消します。数字以外の削除は、数字以外を空文字に置き換えることで実現しています。
実装にはreplaceメソッドと正規表現を使います。
ageInput.value = ageInput.value.replace(/\D/g, "")
replaceメソッド
replaceメソッドは、
文字列.replace("置換対象の文字列、もしくは正規表現", "置換後の文字列")
という形式で使用します。そうすると第1引数の値に合致した文字列が置き換えられます。以下に例を載せます。
console.log("I am Taro.".replace("Taro", "Daiki"))
// 出力:I am Daiki.
const text = "I am Taro."
console.log(text.replace("Taro", "Daiki"))
// 出力:I am Daiki.
// もちろん変数にも利用できる。
console.log("I am Taro. My friends call me Taro.".replace("Taro", "Daiki"))
// 出力:I am Daiki. My friends call me Taro.
// 文字列で指定した場合は合致した1つ目の文字列だけ置き換わる。
console.log("I am Taro. My friends call me Taro.".replace(/Taro/g, "Daiki"))
// 出力:"I am Daiki. My friends call me Daiki."
// 正規表現を使うと合致した全ての文字列を置き換えることもできる。
ageInput.valueは文字列なので、replaceメソッドが使えます。何を置き換え対象にするかですが、今回は「数字以外」です。このような複雑な条件は文字でやろうとすると表現ができないので、正規表現を使います。
ちなみに文字列指定で全て置き換えるには代わりにreplaceAll()を使えばOKです。
参考:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replace
正規表現
正規表現とは、ある文字列内で特定の条件で文字を検索したり照合することができる仕組みです。pattern属性にも使用していましたね。
正規表現の書き方は次のように/(スラッシュ)で囲んだ中に書く形式です。
// ==== 正規表現 かんたん早見表 ====
// 書式: /パターン/ : 意味
// ---------------------------------
// 文字クラス
/[abc]/ : a / b / c のどれか1文字
/[abc]*/ : a / b / c のどれか0文字以上(=空文字を許可)
/[abc]+/ : a / b / c のどれか1文字以上
/[a-c]/ : a〜c のどれか1文字
/[^abc]/ : a / b / c 以外の1文字
// メタ文字
/./ : 改行を除く任意1文字
/\d/ /\D/ : 数字 / 数字以外 ←今回使用しているのはこれ
/\w/ /\W/ : 英数字と _ / それ以外
/\s/ /\S/ : 空白文字 / 空白以外
これ以外にもいろんな正規表現がありますが、詳しくはこちらをご覧ください。

この正規表現は、例えばJavaScriptでは以下のような使い方をします。文字列と違ってダブルクウォーテーションは付けず、直接指定します。
// 1) 先頭から最初にヒットした「1桁の数字」を取得する
console.log("abc123def456".match(/\d/));
// => ["1"]
// 2) 先頭から最初にヒットした「数字以外の1文字」を取得する
console.log("abc123def456".match(/\D/));
// => ["a"]
// 3) 文字列全体の「すべての数字」を配列で取得する(g フラグ)
console.log("abc123def456".match(/\d/g));
// => ["1", "2", "3", "4", "5", "6"]
// 4) 文字列全体の「Taro」を「Daiki」で置き換える
console.log("I am Taro. My friends call me Taro.".replace(/Taro/g, "Daiki"))
// 出力:"I am Taro. My friends call me Taro."
このように、文字列に対して正規表現に合致した部分を取得したり、置き換えたりすることができます。
ポイントをまとめると、以下です。
- /で条件を囲う
- gをつけると文字列全体を対象にする
正規表現とreplaceを組み合わせる
さて、今回の目的は「数字以外を空文字に置き換える」ことなので、以下の3つを使います。
- 数字以外:/\D/
- 空文字:””
- 置き換える:replace
これで数字以外の除去ができます。
ageInput.value.replace(/\D/g, "")
これを元のinputに戻す必要があるので、
ageInput.value = ageInput.value.replace(/\D/g, "")
とします。そしてこれを入力されるたびに実行したいので、
ageInput.addEventListener("input", () => {
ageInput.value = ageInput.value.replace(/\D/g, "")
})
とする形です。ageInputの取得も含めて、次のコードが完成形です。
// 対象の input を取得
const ageInput = document.getElementById("age")
// 入力されるたびに数字以外を取り除く
ageInput.addEventListener("input", () => {
ageInput.value = ageInput.value.replace(/\D/g, "")
})
応用編:IME(日本語入力)時の対応をする
日本語入力の場合、分かっている人なら初めから半角数字を入力してくれる可能性が高いですが全員がそうとは限りません。かな入力時はここまでの実装だと何も反応しないため離脱の原因になるかもしれません。
実際に次のinputでかな入力を試してみてください。何も起きません。
よりUXを向上させるためにコードを追加していきましょう。2パターン用意したので要件に合わせて試してみてください。
パターン1:全角数字の入力を許可し、自動で半角に変換する
数字以外を削除する前に、全角数字を半角数字に変換する処理を挟みます。これでかな入力モードでも数字キーが問題なく反応します。デモを触ってみてください。
コードはこちら。
const ageInput = document.getElementById("age")
ageInput.addEventListener("input", () => {
ageInput.value = ageInput.value
.replace(/[\uFF10-\uFF19]/g, (matchedCharacter) =>
// 全角数字を半角数字に変換
String.fromCharCode(matchedCharacter.charCodeAt(0) - 0xfee0)
)
.replace(/\D/g, "") // 数字以外を除去
})
全角→半角変換は以下の部分で行なっています。replaceメソッドは、実は第2引数に関数を受け取ることが可能です。
ageInput.value
.replace(/[\uFF10-\uFF19]/g, (matchedCharacter) =>String.fromCharCode(matchedCharacter.charCodeAt(0) - 0xfee0))
- /[\uFF10-\uFF19]/g:全角数字全て
- 全角数字を半角数字に変換する:(matchedCharacter) =>String.fromCharCode(matchedCharacter.charCodeAt(0) – 0xfee0)
第2引数の関数は、第1引数の条件に合致した文字列を受け取ります。
変換処理は文字コードをずらすことで実現しています。文字コードは16進数なので計算が可能で、全角数字と半角数字の文字コードの差分だけ引き算をしてあげるとうまく変換ができます。
参考:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
パターン2:IMEの変換を挟んだ上で半角数字以外を除去する
通常通りの入力手順を踏んだ上で入力値を制限します。こちらのパターンはどのキーを押しても一旦普通の反応をし、変換確定時にサニタイズが行われ半角数字以外が除去されます。下のデモをお試しください。
こちらのパターンだと、「ぜろ→変換→0」という入力パターンもカバーできます(あまりなさそうですが)。
コードはこちら。サニタイズの処理を関数化していますが、中身はこれまでと全く同じです。
const ageInput = document.getElementById("age")
ageInput.addEventListener("input", (event) => {
if (event.isComposing) return // 変換中は何もしない
sanitize(event.target)
})
// 変換確定タイミング(compositionend)でサニタイズを実行
ageInput.addEventListener("compositionend", (event) => {
sanitize(event.target)
})
const sanitize = (element) => {
element.value = element.value
.replace(/[\uFF10-\uFF19]/g, (c) =>
String.fromCharCode(c.charCodeAt(0) - 0xfee0)
)
.replace(/\D/g, "")
}
inputイベントには、変換中かどうかを判定するevent.isComposingというプロパティが存在します。これがtrueの時は変換中なので、サニタイズの処理をスキップします(下コード)。
ageInput.addEventListener("input", (event) => {
if (event.isComposing) return // 変換中は何もしない
sanitize(event.target)
})
ただし、変換確定時もevent.isComposingはtrueになってしまうのでこのままでは半角数字以外が入力できてしまいます。そこで変換確定時に発火するcompositionendをキャッチし、このタイミングにもサニタイズ処理が実行されるようにします。
// 変換確定タイミング(compositionend)でサニタイズを実行
ageInput.addEventListener("compositionend", (event) => {
sanitize(event.target)
})
参考:
https://developer.mozilla.org/ja/docs/Web/API/InputEvent/isComposing
https://developer.mozilla.org/ja/docs/Web/API/Element/compositionend_event
まとめ
ということでinputで完全に整数のみを受け付けるよううにする方法でした。日本語入力まで考慮すると結構複雑でしたね。
フォームに関しては他にも記事を書いているので合わせてご覧ください。
コメント