CSS、スクロール時の固定表示について

HTML、CSS

ウェブページを閲覧する際、特定の要素をスクロールしても常に表示させたい場合があります。例えば、ナビゲーションメニュー、目次、広告バナーなどが挙げられます。このような固定表示を実現する方法として CSS の position: fixed; を使う方法 や、JavaScript を活用して動的に固定する方法 があります。

本記事では、スクロール時に要素を固定する様々な方法について解説します。ページのレイアウトをより効果的に制御し、ユーザーエクスペリエンスを向上させることができます。次のセクションでは、それぞれの方法について具体的に解説していきます。

今回はサイドバーの一部のみ、(toc-3)だけを固定させたい場合の例です。汎用できますので参考に。

<div id="content-in">
    <div id="main">
    </div>
    <div id="sidebar">
        <div id="sidebar-scroll">
            <aside id="toc-1"></aside>
            <aside id="categories-2"></aside>
            <aside id="toc-3"></aside>
        </div>
    </div>
</div>
HTML

CSS の position: fixed; を利用する方法

この方法では、CSS の position: fixed; を使用して、ページをスクロールしても #toc-3(目次)を常に画面の特定の位置に固定する というシンプルな手法です。

常に一定の場所に固定

#toc-3 {
    position: fixed; /* 画面スクロールしても常に固定 */
    top: 60px; /* 画面の上から60pxの位置に固定 */
    right: 20px; /* 画面の右側から20pxの位置 */
    width: 250px; /* 適切な幅を指定 */
    background-color: #fff; /* 背景色を白に(必要に応じて変更) */
    padding: 10px;
    border: 1px solid #ddd;
    box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1); /* 影を追加して視認性を向上 */
    z-index: 1000; /* 他の要素より前面に配置 */
}
CSS

動作の仕組み

  • position: fixed; を指定すると、#toc-3 は ブラウザのウィンドウを基準 に位置が固定されます。
  • top: 60px; により、スクロールしても常に 上から 60px の位置 に配置されます。
  • right: 20px; により、画面の右端から 20px の位置 に固定されます。

メリット

✅ 簡単な CSS 設定だけで実装可能
✅ JavaScript を使用しないため動作が軽い

デメリット

常に固定されるため、動的な表示制御ができない(例えば「ある要素が消えたら固定する」といった条件付き制御ができない)
画面幅が狭いと要素が見切れる可能性がある

適用ケース

✅ 目次を常に表示したい場合
✅ JavaScript を使用せず、シンプルな固定を行いたい場合

JavaScript で categories-2 が消えたら固定する方法

この方法では、JavaScript を使って、#categories-2(カテゴリーボックス)が 画面の外に消えたら #toc-3 を固定するようにします。

//JavaScript + CSS で categories-2 が消えたら toc-3 を固定
document.addEventListener("DOMContentLoaded", function () {
    const toc = document.getElementById("toc-3");//固定対象の要素➀
    const categories = document.getElementById("categories-2");//固定の基準となる要素➁

    function checkScroll() {
        //➁の、サイズと画面上の位置を取得
        const categoriesRect = categories.getBoundingClientRect();

        if (categoriesRect.bottom <= 0) {
            //➁が画面の外に消えたら固定
            toc.style.position = "fixed";
            toc.style.top = "60px";
            toc.style.right = "20px";
            toc.style.width = "250px";
            toc.style.zIndex = "1000";
        } else {
            //➁が見えている間は固定解除
            toc.style.position = "static";
        }
    }

    window.addEventListener("scroll", checkScroll);
});
JavaScript

動作の仕組み

  1. categories.getBoundingClientRect().bottom <= 0 をチェックし、categories-2 が画面外に消えたら #toc-3 を固定 します。
  2. それ以外の場合は position: static; に戻し、通常のレイアウトに戻します。

メリット

動的に固定を切り替えられる(特定の要素が消えたタイミングで固定可能)
必要なときだけ固定されるため、視認性が向上

デメリット

❌ JavaScript に依存するため、スクリプトが無効な場合は機能しない

適用ケース

✅ ページ上の特定の要素(例えば #categories-2)が消えたタイミングで目次を固定したい場合。

親要素を超えないように親要素の left を基準に固定する方法

この方法では、#sidebar-scroll の left を基準に #toc-3 を固定し、親要素を超えないようにします。

//親要素の位置を基準にして、子要素(#toc-3)をスクロール時に固定する
document.addEventListener("DOMContentLoaded", function () {
    const toc = document.getElementById("toc-3");//固定対象の要素➀
    const sidebarScroll = document.getElementById("sidebar-scroll");//親要素(スクロール領域)➁
    const categories = document.getElementById("categories-2");//固定の基準となる要素➂

    function handleScroll() {
        //➁の、サイズと画面上の位置を取得
        const sidebarRect = sidebarScroll.getBoundingClientRect();
        //➂の、サイズと画面上の位置を取得
        const categoriesRect = categories.getBoundingClientRect();

        //➂が画面外に消えたら➀を➁の範囲内で固定
        if (categoriesRect.bottom <= 0) {
            toc.style.position = "fixed";
            toc.style.width = `${sidebarRect.width}px`; // ➁と同じ幅を維持
            toc.style.top = "60px"; // ヘッダーを考慮して固定
            toc.style.zIndex = "1000";
            toc.style.left = `${sidebarRect.left}px`; // ➁に基づいて固定
        } else {
            //➂が見えている間は固定解除
            toc.style.position = "static";
        }
    }

    window.addEventListener("scroll", handleScroll);
});
JavaScript

動作の仕組み

  • left を sidebar-scroll の left に揃える ことで、親要素の幅内で固定されるようにします。
  • topは環境に合わせて変えてください。getBoundingClientRect()でheaderを取得し足りする事も出来ます。

メリット

✅ 親要素の範囲を超えないため、レイアウトが崩れない

デメリット

❌ sidebar-scroll の位置が変わると調整が必要

適用ケース

レスポンシブなデザインを意識しつつ、目次を固定したい場合

要素があふれた時

高さの要素があふれた時にtoc-3内でスクロールできるようにします。

document.addEventListener("DOMContentLoaded", function () {
    const toc = document.getElementById("toc-3"); // 固定対象の要素➀
    const sidebarScroll = document.getElementById("sidebar-scroll"); // 親要素(スクロール領域)➁
    const categories = document.getElementById("categories-2"); // 固定の基準となる要素➂

    function handleScroll() {
        //➁の、サイズと画面上の位置を取得
        const sidebarRect = sidebarScroll.getBoundingClientRect();
        //➂の、サイズと画面上の位置を取得
        const categoriesRect = categories.getBoundingClientRect();

        //➂が画面外に消えたら➀を➁の範囲内で固定
        if (categoriesRect.bottom <= 0) {
            toc.style.position = "fixed";
            toc.style.width = `${sidebarRect.width}px`; // ➁と同じ幅を維持
            toc.style.top = "75px"; // ヘッダーを考慮して固定
            toc.style.left = `${sidebarRect.left}px`; // ➁に基づいて固定
            
            // 画面の高さに応じて `#toc-3` の高さを調整(親要素の高さを考慮)
            const availableHeight = window.innerHeight - 75; // 75px の余白を確保
            toc.style.maxHeight = `${availableHeight}px`;
            toc.style.overflow = "auto"; // スクロール可能に
        } else {
            //➂が見えている間は固定解除
            toc.style.position = "static";
            toc.style.maxHeight = ""; // リセット
            toc.style.overflow = "";
        }
    }

    window.addEventListener("scroll", handleScroll);
});
JavaScript

これにより親の幅に基づいて、固定することができ、高さがあふれた時もスクロールできるようになりました。

コメント

タイトルとURLをコピーしました