Produced by FOURIER

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

HirayamaHirayama calender 2022.4.15

Intro

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

この記事では、スライダーを縦に表示する2種類の方法とそれぞれの問題点、そしてレスポンシブ対応について解説します。

方法

縦スライダーを表示するには主に以下の2通りがあります。

  • webkit-appearanceで設定
  • transformで回転させる

それぞれの方法と問題点について解説します。

webkit-appearance

2021年の9月末、Firefoxを除くChromeやSafariなどの主要ブラウザが縦スライダーの表示をサポートしました。

"vertical slider" | Can I use... Support tables for HTML5, CSS3, etc

"Can I use" provides up-to-date browser support tables for support of front-end web technologies on desktop and mobile web browsers.

https://caniuse.com/?search=vertical%20slider

やり方は簡単で、inputのCSSプロパティに-webkit-appearance: slider-verticalを設定すれば縦方向に表示されます。

input[type=range].vertical {
  -webkit-appearance: slider-vertical;
}

以下が実際に動作するデモです。

See the Pen spectrum-web-analyzer by FOURIER Inc. (@fourier-inc) on CodePen.

この方法の利点として、後述する回転させるやり方と比較すると、親要素が回転後のスライダーの大きさに合うためレスポンシブ対応がしやすいという利点があります。

ただ、以下のような多くの欠点があります。

  • Firefoxがこの文法に対応していない
  • thumb(丸い部分)のスタイリングが効かない
  • track(thumbの可動範囲)が細すぎると表示が崩れる

現状この機能を使うにはまだ時期尚早と言え、実用的な解決策とは言えません。

transform

次に、昔からある方法として、input要素のCSSプロパティにtransform: rotate(-0.25turn);を追加する方法があります。これはスライダーを90°回転させて表示させるやり方です。

input[type=range].vertical {
  transform: rotate(-0.25turn);
}

このやり方にwebkit-apparanceで発生したスタイリングが当たらない問題や対応ブラウザの問題はないため、こちらのほうが実用的です。

ただ、そのままだと以下の例のように親要素をはみ出してしまいます。

See the Pen spectrum-web-analyzer by FOURIER Inc. (@fourier-inc) on CodePen.

これは、親要素の大きさが回転前の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として実装したのが以下のコードです。白い枠がリサイズできるようになっており、ドラッグしてスライダーの大きさが変わることが確認できます。

See the Pen spectrum-web-analyzer by FOURIER Inc. (@fourier-inc) on CodePen.

まとめ

この記事では縦スライダーを表示する方法をwebkit-appearanceで設定する方法とtransformで回転させる方法の2つを紹介し、それぞれの問題点を取り上げた後に、transformで回転させた場合のレスポンシブ対応について取り上げました。

現状では実装するにはなかなか難易度が高く複雑なため、早く-webkit-appearance: slider-vertical;で設定できるようになってほしいと思います。

新しいメンバーを募集しています

Hirayama

Hirayama / Engineer

1997年生まれ、南伊豆出身。学生時代にC#で画像処理アプリケーションを作ったりしていました。業務では主にLaravelを使用してサーバーサイドのプログラミングをしています。趣味はドライブとシミュレーションゲーム。