Produced by Fourier

レスポンシブ縦スライダーの作り方

Hirayama Hirayama カレンダーアイコン 2022.04.15

Intro

サイトを作成する際、 input タグのRangeスライダーを縦に配置したくなる場面が稀にあると思います。

この記事では、スライダーを縦に表示する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 タグの大きさに合わせており、回転後のスライダーの大きさ( heightwidth が逆になる)に合ってないからです。

はみ出さないようにする

スライダーを親要素からはみ出さないようにする最も簡単な解決方法は、親要素の大きさをスライダーに合わせて指定する方法です。しかし、そのやり方だとレスポンシブ対応になっていないという問題があります。

レスポンシブなスライダーを作成するために、以下のコンポーネントと機能を実装します。

まず、 input 要素をラップしたコンポーネントを作成します。そして、ラッパー要素の widthheight プロパティを読み込み、 input 要素の heightwidth プロパティに適応させる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; で設定できるようになってほしいと思います。

Hirayama

Hirayama slash forward icon Engineer

業務では主にPHPやTypeScriptを使用したバックエンドアプリケーションやデスクトップアプリケーションの開発をしています。趣味は登山。

関連記事