Produced by Fourier

今のところ input[type=”number”] を使わない方が良い理由と代替案

Kyouhei Horizumi Kyouhei Horizumi カレンダーアイコン 2024.12.23

はじめに

💡
この記事は、2024/12/20現在の情報です。

inputタグで何かの個数など、数値を入力したい時type属性は何を使いますか?

<input type="number">

input[type="number"] がありますが・・・これを使うとユーザビリティ的に良くないことが多かったので、理由と代替案を書きたいと思います。

理由

ChromiumとSafariで全角数字が入力できない

Chromiumでは全角数字を入力すると、そもそも入力ができないようになっています。 全角数字から半角数字に変換するJavaScriptコードを書いても、入力がそもそも出来ないので効果がありません。

色々なユーザー層が多いEdgeでもこの問題が発生してしまうので、問題になりやすいです。

💡
Firefoxでは使えることを確認しました。

代替案

代替案としては、以下のようにします。

<input type="text" role="spinbutton" inputmode="numeric" pattern="[0-9]+">

最大値と最小値を指定したい場合は、 aria-valueminaria-valuemax を使います。

<input type="text" role="spinbutton" inputmode="numeric" pattern="[0-9]+" aria-valuemin="1" aria-valuemax="100">

さらにJavaScriptで制御を加えます。

document.querySelectorAll('input[role="spinbutton"]').forEach(function () {
		element.addEventListener('input', function () {
        let value = this.value;
				value = value.replace(/[A-Za-z0-9]/g, s => String.fromCharCode(s.charCodeAt(0) - 0xFEE0));
        const number = parseInt(value, 10);
        if (isNaN(number)) {
            this.removeAttribute('aria-valuenow');
        } else {
            if (this.hasAttribute('aria-valuemax')) {
                const max = parseInt(this.getAttribute('aria-valuemax'), 10);
                if (number > max) {
                    if (this.required || this.ariaRequired) {
                        this.setAttribute('aria-valuenow', max.toString(10));
                    } else {
                        this.removeAttribute('aria-valuenow');
                    }
                }
            }
            if (this.hasAttribute('aria-valuemin')) {
                const min = parseInt(this.getAttribute('aria-valuemin'), 10);
                if (number < min) {
                    if (this.required || this.ariaRequired) {
                        this.setAttribute('aria-valuenow', min.toString(10));
                    } else {
                        this.removeAttribute('aria-valuenow');
                    }
                }
            }
            this.setAttribute('aria-valuenow', number.toString(10));
        }
    }, {passive: true});
		element.addEventListener('change', function () {
        let value = this.value;
				value = value.replace(/[A-Za-z0-9]/g, s => String.fromCharCode(s.charCodeAt(0) - 0xFEE0));
				const number = parseInt(value, 10);
		    value = (() => {
		        if (isNaN(number)) {
		            return '';
		        } else {
		            return (() => {
		                if (this.hasAttribute('aria-valuemax')) {
		                    const max = parseInt(this.getAttribute('aria-valuemax'), 10);
		                    if (number > max) {
		                        if (this.required || this.ariaRequired) {
		                            return max.toString(10);
		                        } else {
		                            return '';
		                        }
		                    }
		                }
		                if (this.hasAttribute('aria-valuemin')) {
		                    const min = parseInt(this.getAttribute('aria-valuemin'), 10);
		                    if (number < min) {
		                        if (this.required || this.ariaRequired) {
		                            this.setAttribute('aria-valuenow', min.toString(10));
		                            return min.toString(10);
		                        } else {
		                            return '';
		                        }
		                    }
		                }
		                return number.toString(10);
		            })();
		        }
		    })();
        this.value = value;
		}, {passive: true});
});

このコードにより、以下のような動作になります。

  • 全角数字での入力は、半角数字に置き換えられます。
  • parseIntの基数10で解釈されます。
    • 指数指定はできません。
  • 入力欄が必須の場合かつ、値が aria-valuemax を超えている場合、最大値を aria-valuenow に設定します。
  • 入力欄が必須の場合かつ、値が aria-valuemin 未満の場合、最小値を aria-valuenow に設定します。
  • 現在の数値は aria-valuenow に随時入力されます。

さいごに

何かとより良い方法を探して色々なやり方に飛びつきがちですが、検証はしっかりやりたいところです。

おまけ

全角数字が入力できないのはブラウザの問題なので、Issueを上げました。 何も気にせずに type=”number” が使えるようになるといいですね。

Chrome

https://issuetracker.google.com/issues/383232110

Safari

https://bugs.webkit.org/show_bug.cgi?id=284363

関連記事