input[type=number] の矢印(スピンボタン)のデザインをCSSで自由に変える方法

HTMLのinputボタンのtype属性にnumberを指定すると
数字以外の入力が不可になり
さらに上の画像のように、増減ボタンが右端につくのですが、
この増減ボタンにはブラウザによって
見た目がコロコロ変わってしまうという困った問題があります。

そこで本日は
CSSとjavascriptを駆使してこの増減ボタンのデザインを
自分の好きな見た目に変更する方法
をご紹介します。

デモとソースコード

まずは実物をば。
上にあるのが今回作成する入力フィールドです。

ボタンを押して頂ければ数値が増減しますし、
ソースを見ればHTMLとCSSをボタンが再現していることも
ご確認頂けるかと思います。

<div class="p-qty js-qty">
  <div class="__arrow __up js-qty_up"></div>
  <div class="__arrow __down js-qty_down"></div>
  <input type="number" class="p-qty__input js-qty_target" value="1">
</div>

/* デフォルトの装飾は消しておく */
/* Chrome/Safari */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
}
/* Firefox */
input[type="number"] {
  -moz-appearance: textfield;
}
.p-qty {
  position: relative;
  display: inline-block;
}
.p-qty > .__arrow {
  position: absolute;
  top: calc(50% - 9px);
  right: 10px;
  transform: translate(0px, -50%);
  display: inline-block;
  border-width: 6px 4px;
  border-style: solid;
  border-color: transparent transparent rgb(0, 0, 0);
  cursor: pointer;
}
.p-cart_detail .p-qty > .__arrow {
  top: calc(50% - 7px);
  border-width: 5px 3px;
  border-style: solid;
  border-color: transparent transparent rgb(0, 0, 0);
}
.p-qty > .__arrow.__down {
  top: calc(50% + 9px);
  border-bottom: 6px solid transparent;
  border-top: 6px solid rgb(0, 0, 0);
}
.p-cart_detail .p-qty > .__arrow.__down {
  border-bottom: 5px solid transparent;
  border-top: 5px solid rgb(0, 0, 0);
  top: calc(50% + 7px);
}
.p-qty__input {
  width: 91px;
  height: 40px;
  border: 1px solid rgb(112, 112, 112);
  border-radius: 5px;
  padding-left: 25px;
}
//vanilla jsで親要素探索する用の関数
function getParents(el, parentSelector /* optional */) {
    if (parentSelector === undefined) {
        return false;
    }

    var p = el.parentNode;
    
    while (!p.classList.contains(parentSelector)) {
        var o = p;
        p = o.parentNode;
    }
    return p;
}



document.addEventListener('click',function(e){
  e = e || window.event;
  var target = e.target || e.srcElement,
      text = target.textContent || target.innerText;

  var val = 0;

  //クリックしたDOMが.js-qty_upだったら
  if(target.classList.contains('js-qty_up')){
    val = 1;
  } else if(target.classList.contains('js-qty_down')) {
    val = -1;
  } else {
    return false;
  }
  var parent = getParents(target,'js-qty');//親の.js-qtyを取得して
  var input = parent.querySelectorAll('.js-qty_target');//親の.js-qtyの子の.js-qty_targetを取得して
  //Nodelistを回す
  for (let i = 0; i < input.length; i++) {
    if(input[i].classList.contains('js-qty_target')){
      //.js-qty_target持ってるDOMに対して
      var num = parseInt(input[i].value);
      num = isNaN(num) ? 1 : num;
      input[i].value = num + val < 1 ? 1 : num + val;
    }
  }

},false);

コードはこんな感じ。

HTMLとCSSはともかくjavascriptが処理の内容に比べて
やたら長くなっているのは今回あえてjQueryを使わず
vanillaJSで実装したからです。

最初はjQueryでサクッと行けるかと思ったのですが、
実装途中で「一度でも数値以外の文字列を入力すると
その後javascriptでinput内のvalueを書き換えられなくなる※」という問題が発生。

色々頑張ったがどうにも解決できなかったので
もしかしたらと思いvanillaJSで書いたらいい感じに動いてくれたという次第でした。

原因まではちょっと断定できなかったのですが
調べた限りではどうもinput[type=number]の仕様が関係しているみたいです。

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

Wordpressサイトの
ご相談・ご依頼
承ります
ご相談は無料!詳しくはこちら >