読者です 読者をやめる 読者になる 読者になる

P N R A

東京都中野区に所在を構えるウェブ制作会社PNRA DESIGN OFFICE

jQuery基礎講座:スクロール量に応じて要素(ヘッダーなど)を固定する(ZIP付)

scroll-down

jQuery基礎講座[第6回]

どーも、中野区でホームページ作成をやっています。@PNRAです!
備忘録もかねて随時更新していくjQuery基礎講座の時間がやって参りました。

今回はちょっといつもより高度な内容を見ていきたいと思います。表題の通り、ウィンドウのスクロール量に応じて要素を固定したり、動かしたりするという内容です。よくある例でいえば、ページをスクロール中に、ナビゲーションエリアを上部に固定する。といったものですが、応用次第ではサイドバーの固定なども可能です。要素の出現位置を抽出するため、要素が現れたらアニメーションを開始する、といった内容にも繋がってきますね。

そこで今回は使用頻度の高い「スクロール途中でナビゲーションエリアがウィンドウの最上部にきたら要素を固定する」というものを作っていきたいと思います。

それでは、具体的にみていきましょう。

実装実例

どんな動きになるのかは下記の動画をご覧ください。(動画は『placeit』で作成させていただきました。)

スクロール途中でナビゲーションが固定されていますね。また、ついでにフッターエリアの固定を途中で解除しています。

今回作成したデモ『Scroll fix Navigation』のポイントは、

jQueryでWindowTopからの相対位置を取得する

という内容です。

実装内容

ちょっと複雑な内容になり始めたので順を追って説明をしていきたいと思います。

概要を掴む

一定の位置で要素を固定するためには、
・要素そのものがページの上部からどれだけの位置にあるのか取得する
・スクロール量に応じてスタイル/クラスを付与する
という指示が必要になってきます。

.scrollTop()メソッドを使う

jQueryでは.scrollTop()メソッドというものがあり、指定した要素の上部からのスクロール量を抽出してくれるメソッドです。これをウィンドウの最上部に設定することで画面のスクロール量に応じたアクションを付与することができます。

固定したい要素を定義する

今回の例では、トップナビゲーションエリアを固定するため、ナビゲーションエリアに「#top-bar」を指定します。そして、「#top-bar」の相対位置を$().offset().topで取得し、変数「topbar」にします。

//変数「topbar」に「#top-bar」の最上部からの相対位置を取得
var topbar = $("#top-bar").offset().top;

更に、今回はフッターエリアも固定・解除を指示してあるので、こちらも定義します。
こちらは、フッターエリアに「#bottom-bar」を指定し、それ以下のコンテンツを「#underst」で囲みます。そして、変数「bottombar」の高さを次のように定義します。

//変数「bottombar」に「#underst」とウィンドウの相対距離を定義する
var bottombar = $("#underst").offset().top - $(window).height()
+ $("#top-bar").height();

ここでは、フッターエリア以下の要素「#underst」の最上部からの相対位置とposition:fixed;の高さを加えた総量からウィンドウの高さを引いて、ウィンドウの下部と「#underst」の最上部との距離を定義しています。

固定したい要素に追加するクラスを指定する

ここでは、トップナビゲーションエリアをposition:static;からposition:fixed;に。フッターエリアは、position:fixed;を付与・解除を指示していきます。

指示内容はそれぞれ、

//トップナビゲーションエリア
//「#top-bar」を固定
$("#top-bar").css({"position": "fixed", "top": "0"});
//「#top-bar」の固定を解除
$("#top-bar").css("position", "static");

//フッターエリア
//「.fixed-bottom」を追加して固定
$("#bottom-bar").addClass("fixed-bottom");
//「.fixed-bottom」を削除して固定を解除
$("#bottom-bar").removeClass("fixed-bottom");

トップナビゲーションエリアは直接スタイルを与え、フッターエリアはクラスの付け外しで設定しています。
ここでは、「.fixed-bottom」にこのような指示を与えています。

//トップナビゲーションエリア
.fixed-bottom {
position: fixed;
bottom:0;
left:0;
}

絶対位置の落とし穴

直前で要素のクラス指定をみましたが、相対位置指定と絶対位置指定ではちょっと落とし穴が発生します。
それは、絶対位置の要素は独立した高さを持つということです。いままで相対関係にあったトップナビゲーションエリアが絶対関係になったり、その逆になったりすることで前後にあるコンテンツの位置がずれてしまいます。そこで、要素全体を「entry」で囲み、「body」との相対位置で要素のずれを修正します。

//トップナビゲーションエリアの固定時
$("#entry").css({"position": "relative", "top": $("#top-bar").height() + "px"});
//トップナビゲーションエリアの固定解除時
$("#entry").css({"position": "relative", "top": 0});

ここまでみてきた内容をまとめると次のような記述になります。

JS

// ページの読み込みが完全に完了したら以下の処理を実行
window.onload = function() {

// 変数「topbar」を定義。
// 「#top-bar」の最上部からの相対位置を取得
var topbar = $("#top-bar").offset().top;

// 変数「bottombar」を定義。
// 「#bottom-bar」とウィンドウトップとの相対位置を取得
var bottombar = $("#underst").offset().top - $(window).height()
+ $("#top-bar").height();

// 画面がスクロールされたら以下の処理を実行
$(window).scroll(function() {

// ScrollTopの位置が「topbar」よりも値が大きければ、「#top-bar」を固定し、
// 「#entry」を「#top-bar」の分だけ下げる
if($(window).scrollTop() > topbar) {

$("#top-bar").css({"position": "fixed", "top": "0"});
$("#entry").css({"position": "relative", "top":$("#top-bar").height() + "px"});

// ScrollTopの位置が「topbar」よりも値が小さければ、「#top-bar」の固定を解除し、
// 「#entry」を元に戻す
} else {

$("#top-bar").css("position", "static");
$("#entry").css({"position": "relative", "top": 0});

}

// ScrollTopの位置が「bottombar」よりも値が小さければ、
// 「#bottom-bar」に「.fixed-bottom」を追加
if($(window).scrollTop() < bottombar) {
$("#bottom-bar").addClass("fixed-bottom");
// ScrollTopの位置が「bottombar」よりも値が大きければ、
//「#bottom-bar」から「.fixed-bottom」を削除
} else {
$("#bottom-bar").removeClass("fixed-bottom");
}

});

}

HTML

<body>

<div id="entry">

<sction>
<!-- ファーストビューコンテンツエリア -->
</sction>

<!-- #top-bar -->
<div id="top-bar">
<!-- 固定するトップナビゲーションエリア -->
</div>

<section>
<!-- メインコンテンツエリア -->
</section>

<!-- #bottom-bar -->
<div id="bottom-bar" class="fixed-bottom">
<!-- フッターエリアのコンテンツエリア -->
</div>

<section id="underst">
<!-- フッターエリア以下のコンテンツ -->
</section>

</div>

</body>

CSS

.fixed-bottom {
position: fixed;
bottom:0;
left:0;
}

あとはHTMLの中にコンテンツを詰め込んでいけば完成です。
僕の方でも簡単に実装したDEMO(上の動画にみる内容)を作ったので、是非実際に動作を確認してみて下さい。

Scroll fix navigation DEMO

最後に

上記サンプル内容をまとめたZIPファイルを用意したので、是非オリジナルにカスタマイズして使ってみて下さい。

ダウンロードはこちらから

ちょくちょく更新していきますので、是非こちらもご覧ください。

過去5回の記事はこちら
jQuery基礎講座[第1回]:任意のid属性を使ったクリックアクション
jQuery基礎講座[第2回]:ハンバーガーナビゲーションボタンを作ろう!
jQuery基礎講座[第3回]:ウィンドウサイズを取得してブラウザ100%のぴったりHeightを指定する!
jQuery基礎講座[第4回]:ハンバーガーナビゲーション実装サンプル(ZIP付)
jQuery基礎講座[第5回]:スクロール量に応じて出現するナビゲーションエリア(ZIP付)

参考にさせてもらった記事はこちら

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

Siyabonga ekufundeni kwakho.(訳:最後まで読んでくれてありがとう。 / 注:ズールー語)