
はじめに
inputタグで何かの個数など、数値を入力したい時type属性は何を使いますか?
<input type="number">
input[type="number"]
がありますが・・・これを使うとユーザビリティ的に良くないことが多かったので、理由と代替案を書きたいと思います。
理由
ChromiumとSafariで全角数字が入力できない
Chromiumでは全角数字を入力すると、そもそも入力ができないようになっています。
全角数字から半角数字に変換するJavaScriptコードを書いても、入力がそもそも出来ないので効果がありません。
色々なユーザー層が多いEdgeでもこの問題が発生してしまうので、問題になりやすいです。
代替案
代替案としては、以下のようにします。
<input type="text" role="spinbutton" inputmode="numeric" pattern="[0-9]+">
最大値と最小値を指定したい場合は、 aria-valuemin
と aria-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
Safari