現場で役立つ、スクロールバーの必須知識 前編 スクロールバーの表示を考慮する

ウェブサイトを作るうえで、至るところに現れるのがスクロールバーです。そのスクロールバーついて実装時に配慮しなければいけないことを前後編で解説します。前編はスクロールバーの表示方法がページに与える影響をコントロールする方法です。

発行

著者 坂巻 翔大郎 フロントエンド・エンジニア
現場で役立つ、スクロールバーの必須知識 シリーズの記事一覧

はじめに

筆者がこれまでに得た知識・経験の中でも、とても地味ですが大事なスクロールバーに関して、実装上で配慮しなければならないポイントを前後編で紹介します。

前編はスクロールバーの表示の有無と、表示によって変化する表示域にどのように対応するのか、その実装方法を解説します。

スクロールバーは常に表示する

スクロールバーの表示のされ方はOSによっても異なります。macOSの場合は環境設定の一般にある「スクロールバーの設定」で変更でき、デフォルトは「マウスまたはトラックパッドに基づいて自動的に表示」となっています。筆者はデフォルトから変更して「常に表示」にしています。

なぜ、わざわざ設定をデフォルトから変更しているか、理由を説明します。

「マウスまたはトラックパッドに基づいて自動的に表示」は、トラックパットを使ったり、有線マウスを接続するとスクロールバーがスクロールできる領域の上に重なるように表示されます。

「スクロール時に表示」の場合は、スクロールするときだけ、スクロールバーがスクロールできる領域の上に重なるように表示されます。

ですが「常に表示」にした場合は、スクロールできる領域の中にスクロールバーが含まれます。つまりスクロールバーの分、コンテンツの幅が縮まっているわけです。

Windows 10の場合はmacOSの「常に表示」と同じ設定になっています。スクロールできる領域では、スクロールバーは常に表示され、その領域はスクロールバーの幅を含んだ幅・高さになっているのです。

macOSの設定を「常に表示」としておかないと、macOSのようにスクロールバーが重なるように表示される状態でチェックして問題なかったとしても、Windows 10や設定を変えたmacOSでスクロールバーが常に表示されているときにレイアウトが崩れるといったことを見逃してしまうのです。

なお、Windows 11ではmacOSと同じ、重なるように表示されるスクロールバーに変更されます。OSやそのバージョンによってスクロールバーのデフォルトの設定は異なることを覚えておきましょう。

Windowsでスクロールバーを重ねて表示する古の方法

余談ですが、WindowsのIEや昔のEdge(EdgeHTML)では、macOSのようにスクロールバーをスクロール中だけ表示する(スクロールできる領域に重なるように表示する)ための独自拡張プロパティがありました。スクロールできる要素に対して-ms-overflow-style: -ms-autohiding-scrollbar;とするのですが、いろいろと面倒なことが起きた記憶があります。

vw単位はスクロールバーを含む幅になる

画面幅いっぱいの要素を作りたいとき100vwとしますが、この100vwはbody要素に表示されるスクロールバーの幅を含みます。スクロールバーが表示されていないときは問題ありません。

ですが、WindowsやmacOSで「常に表示」にしている状態で、ブラウザの高さを縮めたときに、縦のスクロールバーが表示されると、同時に意図してない横スクロールバーも発生します。

<body>
  <div>100vw</div>
</body>
body {
  margin: 0;
}
div {
  width: 100vw;
  height: 500px;
  background-color: tomato;
}

次のデモを新規タブで開いて、ブラウザの高さをdiv要素よりも小さくしてみると、縦のスクロールバーが発生したことで横のスクロールバーも発生することが確認できます。macOSで確認する場合は「常にスクロールバーを表示」にしておく必要があります。

横スクロールバーがなぜ発生してしまうのかを段階的に追ってみましょう。まずは縦横どちらのスクロールバーも発生していないときの図です。100vwはスクロールバーの幅も含むことになりますが、スクロールバーが発生していないときは問題ありません。

次に、画面の高さを小さくしたときや、下図のように画面に収まりきらないテキストなどがあったときに、縦のスクロールバーが発生します。このとき、body要素はスクロールバーの分だけ縮みますが、div要素はスクロールバーの幅を含んだ幅の100vwですので、body要素よりも大きくなります。

図で赤く囲んだ部分がbody要素の幅からはみ出してしまうわけです。

すると、bodyの幅を超える100vw幅のdivを表示するために、横のスクロールバーが発生します。

このように「スクロールバーを常に表示」にしていないmacOSでのみで確認していたページを、Windowsでみてみたら横スクロールバーが出ている! ということはよくあります。

この現象の対処方法としてはいくつか方法があります。

対処方法1:横スクロールバーを非表示にする

手っ取り早い方法としては100vwを使う要素の祖先要素でoverflow-x: hidden;してしまうことです。

body {
  margin: 0;
  overflow-x: hidden;
}
div {
  width: 100vw;
  height: 300px;
  background-color: tomato;
}

横スクロールバーを表示しなければよいのです。とはいえ、これはスマートではないと筆者は考えます。

対処方法2:100vwからスクロールバーの幅分を引く

次に考えられるのは、スクロールバーの幅を取得して100vwから引く方法です。これはJavaScriptとカスタムプロパティを利用します。

まずJavaScriptでは、スクロールバーの幅を含んだウインドウの幅(window.innerWidth)から、スクロールバーの幅を含まないルート要素(html要素)の幅(document.documentElement.clientWidth)の差分を取ることでスクロールバーの幅を求めます。そして得た値をカスタムプロパティ--scrollbarの値に指定します。

幅は画面が表示されたときだけでなく、画面がリサイズしたときにも計算するようにします。最初はスクロールバーが表示されていなくても、あとから画面サイズを変えたことでスクロールバーが表示される場合があるからです。

幅を計算する

const setScrollbarWidth = () => {
  const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
  // カスタムプロパティの値を更新する
  document.documentElement.style.setProperty('--scrollbar', `${scrollbarWidth}px`);
};

// 表示されたとき
window.addEventListener('load', setScrollbarWidth);
// リサイズしたとき
window.addEventListener('resize', setScrollbarWidth);

CSS側では上記のJavaScriptが動かなかったときのために--scrollbarの値を0として定義しておきます。これは後々、JavaScriptによって値が更新されます。

JSで計算した値をCSSに反映する

:root {
  --scrollbar: 0;
}
body {
  margin: 0;
}

div {
  width: calc(100vw - var(--scrollbar));
  height: 500px;
  background-color: tomato;
}

100vwを使用したい箇所ではcalc()を使用して100vwからスクロールバーの幅(--scrollbar)を引きます。スクロールバーが表示されているときはその幅(大体の場合は15pxくらい)が引かれますし、スクロールバーが表示されていないときは0pxが引かれます。

次のデモではスクロールバーの幅を計算してその分を100vwから引いているため、縦のスクロールバーが発生したときでも横スクロールバーは発生しないことが確認できます。macOSで確認する場合は「常にスクロールバーを表示」にしておく必要があります。

この方法は比較的現実的な対応です。

まとめ

前編はスクロールバーに関連する知っておきたいことを解説しました。

  • スクロールバーを常に表示しているユーザーもいるので配慮する
  • vw単位はスクロールバーの幅を含んでいるので注意する

後半はスクロールバーを表示する位置についての考察や、スクロールバーの見た目をカスタマイズする方法を解説します。