スクロール位置によってサイドバーを固定/解除させる jQuery実装方法

scrollTopの値 : 0

2012年の始めに【 jQuery 】ページのスクロール途中で指定要素の位置を「固定 / 解除」する方法という記事を書きました。この記事で書いたのは、ヘッダーバーとフッターバーを対象にスクロール位置による要素を固定/解除する方法でした。当時はまだサイドバーを固定させるUIというのは主流ではありませんでしたが、昨年の後半から今年にかけていろんな所で見るようになってきました。

先日、1年前の記事でサイドバーを固定/解除させる方法についての質問もいただきましたので、その実装方法についてまとめてみたいと思います。このブログでも「サイドバーを固定/解除させている」ので、このブログを例に説明していきます。

ポイントは、以下の3つの値をどう取得するかということになります。この値さえ取得できてしまえば、あとはその値によってサイドバーを固定/解除するような命令を与るだけとなります。

  • スクロールトップの位置
  • サイドバーを固定したい位置
  • サイドバーの固定を解除したい位置

スクロールトップの位置を取得する

スクロールトップとはスクロールの画面上での上側の位置情報のことで、この値を取得することで、「画面を上からどれだけスクロールしたか」がわかります。スクロールトップはjQueryの.scrollTop()メソッドで取得できます。

スクロールトップ(scrolltop)の位置

以下で画面全体のスクロールトップの値を取得することができます。

$(window).scrollTop()

※念のため、このページではscrollTopの値がわかるようにバーに表示するようにしています。

サイドバーを固定したい位置を取得する

このブログの場合、サイドバーの一番下の部分がフッターバーのちょうど上にきたところでサイドバーを固定しています。その時のスクロールトップの位置がサイドバーを固定したい位置となります。

サイドバーを固定したい位置のスクロールトップの値を取得するには、まず以下の値を取得します。

  1. ページの一番上からサイドバーの一番下までの高さ(ヘッダーバー + 隙間 + ボーダー + サイドバー)
  2. ウィンドウ(表示部分)の高さ
  3. フッターバーの高さ
サイドバーを固定したい位置

上記3つの値を取得したら、「( 1 + 3 ) – 2」でサイドバーを固定したいスクロールバーの位置の値を導き出すことができます。

このブログの場合となりますが、jQueryで書くと以下のようになります。

$("#header_bar").height() + $("#sidebar_sub").height() + 36 // --- 1
+ $("#footer_bar").height() // --- 3
- $(window).height() // ---2

サイドバーの固定を解除したい位置を取得する

このブログの場合、コンテンツエリアの一番下の部分がフッターバーのちょうど上にきたところでサイドバーの固定を解除しています。その時のスクロールトップの位置がサイドバーの固定を解除したい位置となります。

サイドバーの固定を解除したい位置のスクロールトップの値を取得するには、まず以下の値を取得します。

  1. ページの一番上からコンテンツエリアの一番下までの高さ(ヘッダーバー + 隙間 + ボーダー + コンテンツエリア)
  2. ウィンドウ(表示部分)の高さ
  3. フッターバーの高さ
サイドバーの固定を解除したい位置

上記3つの値を取得したら、「( 1 + 3 ) – 2」でサイドバーの固定を解除したいスクロールバーの位置の値を導き出すことができます。

このブログの場合となりますが、jQueryで書くと以下のようになります。

$("#header_bar").height() + $("#content_border").height() // --- 1
+ $("#footer_bar").height() // --- 3
- $(window).height() // ---2

scrollTopの位置とサイドバーの固定/解除の考え方

「スクロールトップの位置」、「サイドバーを固定したい位置」、「サイドバーの固定を解除したい位置」のそれぞれの取得できるようになったら、あとは処理を書いていくだけとなりますが、ここで意外と混乱するのがサイドバーの固定/解除の処理に対するscrollTopの位置の把握といった部分だと思います。以下の画像のように図にしてみるとかなり把握しやすくなると思います。

サイドバーの固定/解除の処理に対するscrollTopの位置の把握

上の画像のように図にしてみると、以下のように考えることができます。

  • 画面の一番上からサイドバーを固定したい位置まで ⇒ サイドバーの状態: 通常
  • サイドバーを固定したい位置からサイドバーの固定を解除したい位置まで ⇒ サイドバーの状態: 固定
  • サイドバーの固定を解除したい位置から画面の最後まで ⇒ サイドバーの状態: 解除

もっとプログラミング的に言うと以下のように言いかえられます。

  • 「scrollTopの位置」が「サイドバーを固定したい位置」より小さければ、サイドバーの状態は通常
  • 「scrollTopの位置」が「サイドバーを固定したい位置」より大きく、「サイドバーの固定を解除したい位置」より小さければ、サイドバーの状態は固定
  • 「scrollTopの位置」が「サイドバーの固定を解除したい位置」より大きければ、サイドバーの状態は解除

ここまで把握できたら、後は簡単です。早速処理の方説明していきます。jQueryの.CSS()メソッドを使ってサイドバーに処理を与えていきます。

その際に、固定/解除の対象となるサイドバーの親要素とコンテンツ部分の高さを一致させておく必要があります。このブログでは以下のような処理で高さを一致させています。

// コンテンツ部分の高さ
var contentHeight = $("#content_border").height();
// 固定/解除の対象となるサイドバー(#sidebar_sub)の親要素の高さ
var sidebarHeight = $("#sidebar").height();

// サイドバーの親要素の高さがコンテンツ部分の高さより小さい場合、
// コンテンツ部分の高さに合わせる
if(sidebarHeight < entryHeight) {
  $("#sidebar").css("height", entryHeight);
}

サイドバーを固定させる時の処理

「scrollTopの位置」が「サイドバーを固定したい位置」より大きく、「サイドバーの固定を解除したい位置」より小さい時に、サイドバーを固定させます。

サイドバーを固定させる処理は、以下のようにcssの「position」プロバティを使って「fixed」させます。このブログの場合、フッターバーが常に表示されている状態なので、フッターバーの高さ24px分下から浮かせています。

$(#sidebar_sub).css({"position": "fixed", "bottom": "24px"});

サイドバーの固定を解除させる時の処理

「scrollTopの位置」が「サイドバーの固定を解除したい位置」より大きくなった時に、サイドバーの固定を解除させます。

ここで注意が必要なのは、「position」プロバティで「fixed」させていた状態を、「static」で戻してしまうと、それまで見えていたサイドバーがいきなり元の位置に戻ってしまい、おかしな挙動となってしまいます。

なので、以下のように「position」プロバティに「absolute」を指定して、さらに「bottom」プロバティを「0」とし、コンテンツ部分の一番下の部分にくっつけるようにして、あたかも固定状態が解除されたかのような演出をさせます。(あとサイドバーの親要素に対して、「position」プロパティに「relative」を指定して多く必要があります。)

$(#sidebar_sub).css({"position": "absolute", "bottom": "0"});

まとめると

上記まとめると、以下のようになります。

JavaScript(jQuery)

<script>
window.onload=function() {

  // コンテンツ部分の高さ
  var contentHeight = $("#content_border").height();
  // サイドバーの親要素の高さ
  var sidebarHeight = $("#sidebar_sub").height();
  var w = $(window);

  // サイドバーの親要素の高さがコンテンツ部分の高さより小さい時
  if(sidebarHeight < contentHeight) {

    // サイドバーの親要素の高さをコンテンツ部分の高さに合わせる
    $("#sidebar").css("height", contentHeight);

    // サイドバーを変数に代入
    var sidebarSub = $("#sidebar_sub");
    // サイドバーを固定したい位置を取得し変数に代入
  var sidebarScrollStop
    = $("#header_bar").height() + $("#sidebar_sub").height() + 36 + 24
    - w.height();
    // サイドバーの固定を解除したい位置を取得し変数に代入
    var sidebarScrollStart
    = $("#header_bar").height() + $("#content_border").height() + 24
    - w.height();

    // ウィンドウがスクロールされた時に処理を実行
    w.scroll(function() {

      // 「scrollTopの位置」が「サイドバーを固定したい位置」より大きく、
      // 「サイドバーの固定を解除したい位置」より小さい時
      if(sidebarScrollStop < w.scrollTop()
      && w.scrollTop() < sidebarScrollStart) {

        // サイドバーを固定する処理
        sidebarSub.css({"position": "fixed", "bottom": "24px"});

      // 「scrollTopの位置」が「サイドバーの固定を解除したい位置」より大きい時
      } else if(w.scrollTop() >= sidebarScrollStart) {

        // サイドバーの固定を解除する処理
        sidebarSub.css({"position": "absolute", "bottom": "0"});

      // それ以外のとき
      // (「scrollTopの位置」が「サイドバーを固定したい位置」より小さい時)
      } else {

        // サイドバーを通常の位置に戻す
        sidebarSub.css("position", "static");

      }

    });

  } 

}
</script>

HTML

<header>
  <div id="header_bar">ヘッダーバー</div>
</header>

<div id="content_border">コンテンツ</div>

<div id="sidebar">
  <div id="sidebar_sub">サイドバー</div>
</div>

<footer>
  <div id="footer_bar">フッターバー</div>
</footer>

まとめ

位置の取得がちょっとややこしかったりしますが、そうした時は落ち着いて考えてみることが一番だと思います。処理に関してはcssのpositionプロパティを使うだけなので意外とあっさりとできてしまうと思います。とにかく一度理解してしまえば、そう難しくない機能だと思っています。

Web制作の現場で使うjQueryデザイン入門[改訂新版](ドーナッツ本)
  • 『Web制作の現場で使うjQueryデザイン入門[改訂新版]』(通称: ドーナツ本)
  • 著者: 西畑一馬
  • 出版社: KADOKAWA/アスキー・メディアワークス
  • 単行本: 312ページ
  • 発売日: 2013年3月7日
  • ISBN: 4048913913

コメント一覧

  1. ピンバック: 【 jQuery 】ページのスクロール途中で指定要素の位置を「固定 / 解除」する方法 | mae's blog

     

  2. レスポンシブサイトで、様々な要素の高さが変わる場合、この方法ではうまくいきません。
    どのような方法がよいでしょうか。
    ご教示くだされば幸いです。

    あさひ  返信

    • あさひさん

      コメントありがとうございます。

      > レスポンシブサイトで、様々な要素の高さが変わる場合、この方法ではうまくいきません。
      > どのような方法がよいでしょうか。

      要素を「固定したい位置」と「固定を解除したい位置」を動的に取得するようにしたらよいかと思います。

      resizeイベントを使えば、ブラウザのサイズが変更される度に、「固定したい位置」と「固定を解除したい位置」を動的に取得できるようになりますよ。ぜひ試してみてください。

      何かあれば、またコメントなどいただけると幸いです。今後ともよろしくお願いいたします!

      Takanori Maeda  返信

  • 必須

コメント