![今のところinput[type=”number”]を使わない方が良い理由と代替案 アイキャッチ](https://www.fourier.jp/storage/blog/post-outline/1TpqPR6JTi44vdLZj22ViH8IsZV4bfVP.jpg)
Intro
サイトを作成する際、 input
タグのRangeスライダーを縦に配置したくなる場面が稀にあると思います。
![](https://www.fourier.jp/storage/blog/block/YQOwwDf9EgS60D7bcpT8haOLiU4jm8zj.png)
この記事では、スライダーを縦に表示する2種類の方法とそれぞれの問題点、そしてレスポンシブ対応について解説します。
方法
縦スライダーを表示するには主に以下の2通りがあります。
-
webkit-appearance
で設定 -
transform
で回転させる
それぞれの方法と問題点について解説します。
webkit-appearance
2021年の9月末、Firefoxを除くChromeやSafariなどの主要ブラウザが縦スライダーの表示をサポートしました。
https://caniuse.com/?search=vertical%20slider
やり方は簡単で、 input
のCSSプロパティに -webkit-appearance: slider-vertical
を設定すれば縦方向に表示されます。
input[type=range].vertical {
 -webkit-appearance: slider-vertical;
}
以下が実際に動作するデモです。
この方法の利点として、後述する回転させるやり方と比較すると、親要素が回転後のスライダーの大きさに合うためレスポンシブ対応がしやすいという利点があります。
ただ、以下のような多くの欠点があります。
- Firefoxがこの文法に対応していない
- thumb(丸い部分)のスタイリングが効かない
- track(thumbの可動範囲)が細すぎると表示が崩れる
現状この機能を使うにはまだ時期尚早と言え、実用的な解決策とは言えません。
transform
次に、昔からある方法として、 input
要素のCSSプロパティに transform: rotate(-0.25turn);
を追加する方法があります。これはスライダーを90°回転させて表示させるやり方です。
input[type=range].vertical {
 transform: rotate(-0.25turn);
}
このやり方に webkit-apparance
で発生したスタイリングが当たらない問題や対応ブラウザの問題はないため、こちらのほうが実用的です。
ただ、そのままだと以下の例のように親要素をはみ出してしまいます。
これは、親要素の大きさが回転前の input
タグの大きさに合わせており、回転後のスライダーの大きさ( height
、 width
が逆になる)に合ってないからです。
はみ出さないようにする
スライダーを親要素からはみ出さないようにする最も簡単な解決方法は、親要素の大きさをスライダーに合わせて指定する方法です。しかし、そのやり方だとレスポンシブ対応になっていないという問題があります。
レスポンシブなスライダーを作成するために、以下のコンポーネントと機能を実装します。
まず、 input
要素をラップしたコンポーネントを作成します。そして、ラッパー要素の width
、 height
プロパティを読み込み、 input
要素の height
、 width
プロパティに適応させるJavaScriptコードを書きます。
customElements.define(
 'vertical-slider',
 class extends HTMLElement {
 get min() {
 return this.getAttribute('min') || 0;
 }
 
 set min(val) {
 this.setAttribute('min', val);
 }
 
 get max() {
 return this.getAttribute('max') || 100;
 }
 
 set max(val) {
 this.setAttribute('max', val);
 }

 get step() {
 return this.getAttribute('step') || 1;
 }
 
 set step(val) {
 this.setAttribute('step', val);
 }
 
 get value() {
 return this.getAttribute('value') || 50;
 }
 
 set value(val) {
 this.setAttribute('value', val);
 }
 
 render() {
 const input = document.createElement('input');
 input.setAttribute('type', 'range');
 input.setAttribute('min', this.min);
 input.setAttribute('max', this.max);
 input.setAttribute('step', this.step);
 input.setAttribute('value', this.value);
 
 wrapper.appendChild(input);
 
 const style = document.createElement('style');
 style.textContent = `
 :host {
 --width: 16px;
 --height: 175px;
 
 display: inline-block;
 position: relative;
 margin-right: calc(var(--width) / 2);
 width: var(--width);
 height: var(--height);
 }

 input[type=range] {
 position: absolute;
 left: 0;
 bottom: -0.75em;
 transform: rotate(-90deg) translateY(calc(var(--width) / 2));
 transform-origin: left;
 width: var(--height);
 height: var(--width);
 }
 `;
 
 const shadowRoot = this.attachShadow({mode: 'open'});
 shadowRoot.append(input);
 shadowRoot.append(style);
 }
 
 connectedCallback() {
 this.render();
 }

 attributeChangedCallback() {
 this.render();
 }
 }
)
次に、親要素の height
を読み取り、スライダーに適用させる機能を実装します。これは ResizeObserver
というクラスを使用して実装します。
new ResizeObserver(entries => {
 const height = entries[0].contentRect.height;
 const slider = document.getElementById('vs');
 slider.setAttribute('style', `
 --height: ${height}px;
 `);
}).observe(document.getElementById('frame'));
このコンポーネントによって、外側から見たときにレスポンシブに大きさが変化する縦スライダーを使用することができます。
<div class="container">
 <p>vertical</p>
 <div class="frame" id="frame">
 <vertical-slider id="vs"></vertical-slider>
 </div>
</div>
これらをWeb Componentとして実装したのが以下のコードです。白い枠がリサイズできるようになっており、ドラッグしてスライダーの大きさが変わることが確認できます。
まとめ
この記事では縦スライダーを表示する方法を webkit-appearance
で設定する方法と transform
で回転させる方法の2つを紹介し、それぞれの問題点を取り上げた後に、 transform
で回転させた場合のレスポンシブ対応について取り上げました。
現状では実装するにはなかなか難易度が高く複雑なため、早く -webkit-appearance: slider-vertical;
で設定できるようになってほしいと思います。