JavaScript
HTML5

HTML5で追加された input[type=number] を使う上での注意

More than 3 years have passed since last update.

input[type=number] とは

HTML5 の input[type=number] は数値だけを受け付ける入力部品です(デフォルトでは小数の入力も想定されています)。

これを使えば、スマートフォンなら自動でキーパッドが数値用になるらしいです。なるほど、普通の input[type=text] だと文字入力用のキーボードが出る可能性があるので、数値だけしか入力しない場合は input[type=number] のほうが便利そうです。

また、PCでは(ブラウザの実装等にもよると思いますが)「増やす」「減らす」ボタンが出てきたりします。以下がサンプルです。

PCで表示した例(アクティブ状態)

(一応言っておきますが、↑のサンプルは画像なので、操作できません。)

注意点

せっかく数値入力専用の部品があるのだから、数値入力でこれを使わない手はないですねぇ。もし input[type=number] に未対応のブラウザで表示しても、その場合は input[type=text] と同じ表示になるだけなので、特に互換性の問題もないはずです。

そう思って使おうと思ったところ、ちょっとした問題が発生しました。

この数値用の入力部品、見かけ上は数値以外の文字も入力できます。
まあ、それはいいとしましょう。
もし数値以外を入力した場合は、 submit イベントが発生するときにブラウザが自動で以下のようなメッセージを出してくれます。

数字を入力してください

しかし、問題は JavaScript を使う場合です。
type=number の入力部品に数値以外を入力した場合、JavaScript でその値を取得することができないのです。
これはちょっと困りました。

とりあえずその事実を確認するために、以下のサンプルを実行してみてください。

sample.html
<input type="number" onchange="alert(this.value);">

数値以外を入力すると、まず onchange イベントが発生しません。
(onchange イベントは文字入力されたタイミングではなく部品がフォーカスを失うタイミングで発生するので注意してください)

もちろん単に onchange イベントが発生しないだけなら大した問題ではありませんが、値を取得しようとしても空文字が返ってきます。イベントを onkeyup に変えて試してみます。

sample.html
<input type="number" onkeyup="alert(this.value);">

もしかするとブラウザによって動作が異なるかもしれませんが、こちらで試したすべてのモダンブラウザで、数値以外を入力すると空のアラートが表示されました。

そして、W3Cは以下のようなことを言っています。

User agents must not allow the user to set the value to a non-empty string that is not a valid floating point number.
http://www.w3.org/TR/2011/WD-html5-20110113/number-state.html

おそらく各ブラウザのこの実装はバグではなく、上記に従った仕様だと思われます。

これで具体的にどういう問題があるのかというと、たとえば……

  • 全角で入力された数字を JavaScript で半角に変換するということができません。
  • 変な値が入力されたときのバリデーションを JavaScript で実装することができません(ただし正規表現で対応できる程度のバリデーションなら pattern 属性でどうにかできるかもしれません)。

どうすれば良いのか

1. type="tel" という入力部品を使ってみる

type="number" と同様にHTML5で追加された入力部品に type="tel" というものがあります。
これを指定すると、スマートフォンのキーパッドが電話番号入力用のものになり、アルファベット等は入力できなくなる一方で、# とか * とか - とかが使えます(これもやはり環境によっては異なる可能性があります。もちろんPCではなんでも入力できますし、スマートフォンでもクリップボードからの貼付けはできます)。
そして、なぜかよくわかりませんが、type="tel の場合は、 type="number" とは異なり、全角文字等を入力しても問題なく JavaScript で値が取得できます。
【補足】number は Interger あるいは Float 型であるのに対して、電話番号にはハイフンや#などが入り得るので String として保存されるということなんでしょうね。

ただし、これを使用する場合の懸念点が2つあります。

第一に、スマートフォンのキーパッドが変わることで入力できなくなる文字が出てきます。PCの「増やす」「減らす」ボタンも出なくなります。したがって、単に代替可能というわけではなく、場合によっては使えないこともあるわけです。

第二に、電話番号じゃないのに type="tel" を使うのは、HTMLの意味合い的になんか気持ち悪いです。

昔ながらの type="text" を使ってみる

結局 type="number" なんか使わないで、昔ながらの type="text" を使うのは、どうでしょうか。
しかし、これだとスマートフォンで見たときにIMEが数字入力モードになってくれないし、CSSの ime-mode も基本的にブラウザが対応していない(もともとIEの独自拡張です)ので、これはダメそうな気がしますね。

実は、iOSだと以下のように正規表現でパターン指定しておくと、数字だけしか入力できないテンキーを表示してくれます。
<input type="text" pattern="\d*"/>

しかしこれ、Android 4.3ではうまくいきませんでした。OSによるのかブラウザによるのかIMEによるのかは詳しく調べていませんが、うまくいかない環境もあるようです。スマートフォンのデフォルトキーパッドは、なかなか重要だと思うので、これはあまりよろしくないですね……。

PCとスマートフォン(タブレット端末含む)でHTMLを切り替える

あとはもう、上記踏まえてケースバイケースの対応です。

僕の場合は「全角数字を入力したら半角数字に変換してほしい」というクライアントの要望にできるだけ応えることが目的で、たまたまそれがPCとスマートフォンが別HTMLのサイトでの話だったので、PCとスマートフォンで別の対応をすることにしました。

まずPCでは、 type="text" を使います。PCではキーパッドなど基本的にないし、 type="number" 特有の「増える」「減らす」のボタンはあまり重要ではなかったので、これで特に問題はありません。JavaScript で値も取得できてハッピーです。
そしてスマートフォンでは、 type="number" を使います。JavaScript で値の取得はできないので、全角数字を半角数字に変換はできませんが、とりあえずパターン指定をして iOS では数字以外を入力できないようにしておきます。
<input type="number" pattern="\d*"/>
既に書いた通り、これですべての環境で対応できるわけではないですが、そもそもスマートフォンで半角ではなくわざわざ全角の数字を入力するということ自体が確率としてかなり低いと思うので、まぁ良しとすることにしました。

最後に

やはり目に見えている値が取得できないというのがおかしいと思います。
そんなことするなら、入力時点で弾いてほしいですね。

補足

数値以外が入力されたときの不正な値自体は取得できませんが、一応、「不正な状態な値である」ということ自体はvalidity というプロパティを使うことで取得できます。