当ブログの今年の目標は、ずばり「UIの強化」としています。そのためにもJavaScriptやjQueryの習得は必須条件となりますから、このブログを通して、いろいろ試していければよいなと思っています。今回は、タイトルの通り、「スクロールの途中で任意の要素の位置を固定/解除する機能」を試すことにしました。指定した要素が画面上のある位置までスクロールされると動きが止まるといった機能です。実装方法を紹介します。
まず最初に今回の投稿の記事部分の上と下に「#top-bar」、「#bottom-bar」という2つのバーが設置されているのがわかると思います。

「#top-bar」は、「ヘッダーバー」の下まで画面がスクロールされると、その場所で位置が固定され、動かなくなります。
「#bottom-bar」は、逆に最初の表示では「フッターバー」の上に固定されていますが、「関連記事」の見出しの位置まで画面がスクロールされると、固定されていたのが解除され画面と一緒に動くようになります。
この機能は今後リッチなユーザーインターフェースを実装する上でかかせない機能になるのではないかと勝手に思っています。実装方法については以下の通りとなります。
.scrollTop()メソッド
この機能の実装には、jQueryの.scrollTop()メソッドを使用しています。この.scrollTop()を使用すると、画面のスクロールの上側の位置情報を取得することができます。つまり「画面を上からどれだけスクロールしたのか」わかります。
.scrollTop()メソッドで取得したスクロール位置の範囲によって、指定要素を固定するのか、それとも固定していたのを解除するのか決めていきます。
スクロール位置の範囲の取得
ただこのスクロール位置の範囲を把握するのが、かなりややこしくて頭が混乱します。考え方としては、「基準となる値」を見つけて、その基準点の上をスクロールするか、下をスクロールするかで、「固定 / 解除」の処理を行うようにさせます。
今回は基準となる値が見つかりやすいように、jQueryで以下の一文を追加し、「#top-bar」上にスクロールトップの位置の値が表示されるようにしました。
$("#top-bar").text("「#top-bar」scrollTop: " + $(this).scrollTop() + "px");
「#top-bar」を固定/解除するためのスクロール位置の範囲
「#top-bar」は、画面の一番上からヘッダーバーの手前までスクロールされると固定されます。つまり、「基準となる値」は、「#top-bar」がヘッダーバーの手前までスクロールされたときのスクロールトップの位置となります。
「基準となる値」を出すには、まず「#top-bar」の最初に表示されている位置の上側の位置情報を取得し、そこから「ヘッダーバーの高さ」を引くことにします。これが「基準となる値」となります。
これをjQueryで書くと、以下のようになります。
$("#top-bar").offset().top - $("#header_bar").height();
上記で取得した値よりも、スクロールトップの位置の値が大きくなれば「#top-bar」は固定され、小さくなれば固定が解除されるようにします。
「#bottom-bar」を固定/解除するためのスクロール位置の範囲
「#bottom-bar」は、画面表示時は、「フッターバー」の真上に固定しています。この固定された状態は、画面を下へスクロールし、「関連記事の見出し」とぶつかるところで解除されます。つまり「基準となる値」は、「関連記事の見出し」がフッターバーの位置までスクロールされたときのスクロールトップの位置となります。
「基準となる値」を出すには、まず「関連記事の見出し」の上側の位置情報を取得しますが、「関連記事の見出し」が画面の下からちょうど現れる時のスクロールトップの位置を取得したいので、そこから「画面(ウィンドウ)の高さ」の分だけ引くことにします。最後に「フッターバー」の高さまで上に上げるために、フッターバーの高さ「24px」を足します。
これをjQueryで書くと、以下のようになります。
$("#related-posts").offset().top - $(window).height() + 24;
上記で取得した値よりも、スクロールトップの位置の値が大きくなれば「#bottom-bar」の固定は解除され、小さくなれば固定されるようにします。
指定要素の固定/解除
指定要素を固定/解除するために、「#top-bar」では、jQueryの「.CSS()メソッド」を使ってCSSの「positionプロパティ」の設定(fixed ⇔ static)を直接行っています。
「#top-bar」の固定
$("#top-bar").css({"position": "fixed", "top": "36px"});
「#top-bar」の固定解除
$("#top-bar").css("position", "static");
「#bottom-bar」では、画面表示時に固定させておくために、クラス属性を与えて、そのクラス属性に対しCSSで固定(position:fixed)の設定を行っています。そしてjQueryの「.addClass()メソッド」と「.removeClass()メソッド」を使って、「#bottom-bar」に対し、そのクラス属性の付け外しを行っています。
「#bottom-bar」の固定
$("#bottom-bar").addClass("fixed-bottom");
「#bottom-bar」の固定解除
$("#bottom-bar").removeClass("fixed-bottom");
CSS
<style>
.fixed-bottom {
position: fixed;
bottom: 24px;
}
</style>
注意点
注意点として、指定要素を固定するために使っている「position: fixed」はIE6には対応していないので、割り切るか、別の対応を考える必要があります。
また、「position: fixed」を指定すると、指定した要素の高さが保持されなくなり、その分画面のスライドが発生します。対策として、「#top-bar」の固定/解除の際に、記事部分のブロック要素(#entry)を「#top-bar」の高さ分だけ位置の上げ下げを行います。
「#bottom-bar」の固定時
$("#entry").css({"position": "relative", "top": $("#top-bar").height() + "px"});
「#bottom-bar」の固定解除時
$("#entry").css({"position": "relative", "top": 0});
さらに、当記事においては「#bottom-bar」の固定/解除についても同時に設定しているので、記事部分のブロック要素(#entry)の位置に合わせて「#bottom-bar」を固定/解除するためのスクロール位置を変更しておく必要があります。
「#bottom-bar」を固定/解除するためのスクロール位置の範囲(「#top-bar」の高さ分を追加)
$("#related-posts").offset().top - $(window).height() + 24 + $("#top-bar").height();
まとめると
以上をまとめると、ソースは以下のようになります。
JavaScript / jQuery
<script>
// ページの読み込みが完全に完了したら以下の処理を実行
window.onload = function () {
// 「#top-bar」を固定/解除するための基準となる値を取得し、変数「topbar」に代入
var topbar = $("#top-bar").offset().top - $("#header_bar").height();
// 「#bottom-bar」を固定/解除するための基準となる値を取得し、
// 変数「bottombar」に代入
var bottombar = $("#related-posts").offset().top - $(window).height() + 24
+ $("#top-bar").height();
// 画面がスクロールされたら以下の処理を実行
$(window).scroll(function () {
// 「#top-bar」上にScrollTopの位置の値を表示
$("#top-bar").text(
"「#top-bar」scrollTop: " + $(this).scrollTop()
);
// ScrollTopの位置が「topbar」よりも値が大きければ、「#top-bar」を固定し、
// 記事部分のブロック要素の位置を「#top-bar」の高さ分だけ下げる
if ($(window).scrollTop() > topbar) {
$("#top-bar").css({"position": "fixed", "top": "38px"});
$("#entry").css({"position": "relative", "top": $("#top-bar").height() + "px"});
// 小さければ、「#top-bar」の固定を解除し、
// 記事部分のブロック要素の位置を元に戻す
} else {
$("#top-bar").css("position", "static");
$("#entry").css({"position": "relative", "top": 0});
}
// ScrollTopの位置が「bottombar」よりも値が小さければ、
// 「#bottom-bar」を固定
if ($(window).scrollTop() < bottombar) {
$("#bottom-bar").addClass("fixed-bottom");
// 大きければ、「#bottom-bar」の固定を解除
} else {
$("#bottom-bar").removeClass("fixed-bottom");
}
});
}
</script>
CSS
<style>
.fixed-bottom {
position: fixed;
bottom: 24px;
}
</style>
HTML
<div id="entry">
<!-- #top-bar -->
<div id="top-bar">「#top-bar」scrollTop : 0</div>
<!--
記事本文
-->
<!-- #bottom-bar -->
<div id="bottom-bar" class="fixed-bottom">「#bottom-bar」</div>
<section id="related-posts">
<!-- 関連記事の見出し -->
<h1 id="#related-posts-title">関連記事</h1>
</section>
</div>
この機能の当ブログでの使い所としては、「サイドバー」を考えています。記事部分の長さと比べると、サイドバーはどうしても短くなるので、記事を読み進めていくと、そのうちサイドバー部分には何も表示されなくなります。すごく無意味なスペースが表示され続けることになり、この状態のままだともったいないので、サイドバーをある位置になったら固定させるようにすることで、サイドバーの有効活用ができるのではないかと考えています。
追記
サイドバーを固定/解除させる方法について新しく記事を書きました。併せて参考にしてみてください。
- 『Web制作の現場で使うjQueryデザイン入門[改訂新版]』(通称: ドーナツ本)
- 著者: 西畑一馬
- 出版社: KADOKAWA/アスキー・メディアワークス
- 単行本: 312ページ
- 発売日: 2013年3月7日
- ISBN: 4048913913
コメント