CSSでスクロールバー非表示とJSでスクロールバーのデザインをカスタマイズする

スクロールバーはブラウザによってUIが違い、どのブラウザでも同じように表示させたいときがあると思います。色々方法はありますが、その1つとして紹介したいと思います。

スクロールバーの非表示

コンテナが固定幅

こちらの記事ではCSSのみでスクロールバーを非表示にする方法が紹介されています。


<div class="container">
  <div class="scrollable">
    <div class="adjustment">
      ...
    </div>
  </div>
</div>

.container {
  width: 250px;
  overflow: hidden;
}
.scrollable {
  width: 270px; /* 250 + 20 */
  height: 300px;
  overflow: hidden;
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}
.adjustment {
  width: 250px; /* 元に戻す */
}

ブラウザによってはスクロールバーの幅が17pxではないこともあるので、.scrollable の横幅を20pxと多めにとってあります。固定幅ならばこの手法で実現できますがレスポンシブデザインが主流の今、固定幅というのはあまりないと思います。やはり、可変幅での対応が必要となってきます。

コンテナが可変幅


<div class="container">
  <div class="scrollable">
     ...
  </div>
</div>

* {
  box-sizing: border-box;
}
body {
  /* JavaScriptでスクロールバーの幅を取得するために必要 */
  overflow-y: scroll;
}
.container {
  max-width: 400px;
  overflow: hidden;
}
.scrollable {
  margin-right: -17px; /* オーバーレイスクロールバーを消す */
  padding-right: 17px; /* オーバーレイスクロールバーを消す */
  height: 300px;
  overflow: hidden;
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}
.scrollable.is-scrollbar {
  padding-right: 0;
}

var scrollbar_width = window.innerWidth - document.body.scrollWidth;

// 固定スクロールバーのとき(オーバーレイでない)
if (scrollbar_width > 0) {
  $('.scrollable').addClass('is-scrollbar');
  
  // スクロールバーの幅が17pxでないとき
  if (scrollbar_width !== 17) {
    $('.scrollable').css('margin-right', '-' + scrollbar_width + 'px');
  }
}

Macなどの一部OSではスクロールバーがオーバーレイ表示されるため、margin-right を使ってはみ出させてその分を padding-right で調整し、非表示になるようにしています。スクロールバーの幅をJavaScriptで取得して、オーバーレイスクロールバーかそうでないかで場合分けします。また、スクロールバーの幅が17pxではないブラウザもあるので、そのときは style 属性で上書きするようにしています。

overflow-y: scroll のバグ

IEFirefoxでは overflow-y: scroll を指定した要素の padding-bottom が無視されるというバグがあります。これを回避する手段として2つ紹介します。

子要素に padding を定義


<div class="container">
  <div class="scrollable">
    <div class="adjustment">
      ...
    </div>
  </div>
</div>

.scrollable {
  overflow-y: scroll;
}
.adjustment {
  padding: 1em;
}

擬似要素で表現


<div class="container">
  <div class="scrollable">
    ...
  </div>
</div>

.scrollable {
  overflow-y: scroll;
}
.scrollable {
  padding: 1em 1em 0;
}
.scrollable::after {
  display: block;
  height: 1em;
  content: '';
}

擬似要素で padding-bottom だけ作る手法です。

固定スクロールバーを自作

固定スクロールバーをどの環境でも同じように表示されるように自作してみます。これを応用すれば、オーバーレイスクロールバーも作ることができます。

基本構造

ベースとして1.2-コンテナが可変幅のCSS/JSを使います。


<div class="container">
  <div class="scrollable">
    <div class="adjustment">
      ...
    </div>
  </div>
  <div class="scrollbar">
    <div class="scrollbar-thumb"></div>
  </div>
</div>

.container {
  position: relative;
}
.adjustment {
  margin-right: 17px; /* スクロールバーの幅 */
  padding: 1.3em 1.5em;
}
.scrollbar {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 17px; /* スクロールバーの幅 */
  background-color: #ece1e3;
}
.scrollbar-thumb {
  min-height: 50px;
  background-color: #baa4a9;
}

スクロールバーに必要なHTML/CSSを追加していきます。

スクロールバーの高さを設定


$(window).on('load', function() {
  var scrollable_height = $('.scrollable').height(),
      adjustment_height = $('.adjustment').outerHeight(),
      scrollbar_height = parseInt(scrollable_height * scrollable_height / adjustment_height);
  
  $('.scrollbar-thumb').css('height', scrollbar_height);
});

IEで outerHeight()$(function() { ... }); のタイミングで正確に読み取れなかったため、$(window).on('load') にしています。

スクロールに応じて位置変更


$(window).on('load', function() {
  ...

  var scrollbar_track = scrollable_height - scrollbar_height;
  
  $('.scrollable').on('scroll', function() {
    var offset = $(this).scrollTop() * scrollbar_track / (adjustment_height - scrollable_height);
    
    $('.scrollbar-thumb').css('transform', 'translateY(' + offset + 'px)');
  });
});

つまみでスクロールさせる


$(window).on('load', function() {
  ...

  var active = false, // つまみを操作しているかどうか
      scrollbar_thumb_cursor_y; // つまみ内のクリック位置
  
  $('.scrollbar-thumb').on('mousedown', function(event) {
    active = true;
    scrollbar_thumb_cursor_y = event.pageY - $(this).offset().top;
  });
  
  $(document).on('mouseup', function() {
    active = false;
  });
  
  $(document).on('mousemove', function(event) {
    if (!active) return;
    
    var scrollbar_thumb_y = ((event.pageY - $('.scrollbar').offset().top) / scrollbar_track * scrollbar_track) - scrollbar_thumb_cursor_y;
    
    // つまみが上下の領域外を超えないようにする
    if (scrollbar_thumb_y < 0) {
      scrollbar_thumb_y = 0;
    } else if (scrollbar_thumb_y > scrollbar_track) {
      scrollbar_thumb_y = scrollbar_track;
    }
    
    // つまみの位置設定
    $('.scrollbar-thumb').css('transform', 'translateY(' + scrollbar_thumb_y + 'px)');

    // つまみの位置に応じてスクロールさせる
    $('.scrollable').scrollTop(($('.scrollbar-thumb').offset().top - $('.scrollbar').offset().top) / scrollbar_track * (adjustment_height - scrollable_height));
  });
  
  // つまみを操作中はテキスト選択できないようにする
  $(document).on('selectstart', function() {
    if (active) return false;
  });
});

変数 active にスクロールバーのつまみを操作中かどうか保存しておきます。あとはつまみの位置を求め、何pxスクロールさせるかを設定するだけです。

jQueryプラグインの紹介

自作でもサクッと作れますが、とても素晴らしいjQueryプラグインがあったので紹介します。

こちらのプラグインはとても素晴らしいのですが、1つだけ残念な点があります。標準機能の overflow-y: scroll を使用せずJavaScriptで操作しているため、-webkit-overflow-scrolling: touch の気持ちいいスクロールができません。一応慣性スクロールはできますが、純正のものほど気持ちよくはないのです。それ以外はカスタマイズもしやすく便利だと思います。