[jQuery] 特定の要素内だけスクロールに追随するサイドバーを作る方法
画面をスクロールしてもついてくるサイドバーのレイアウトをよく見かけます。応用として、特定要素の中だけサイドバーを追随させたいという場合もあるかと思います。海外のサイトですが、そのパターンのチュートリアルが公開されていたので参考にさせて頂きサンプル(IE7まで動作確認済みです)を作ってみました。作成の手順とコードのメモなどです。
作ったサンプルデモ
main要素内だけ追随するのがわかりやすいようヘッダーとフッターがやたら大きいです。
#main要素内だけ追随するサイドボックスのデモ|memocarilog
作成の手順
HTML
このサンプルでは#main要素内でのみ可動するようにする為、#main要素内に可動部分の#side要素を置きます。
<div id="main"> <div id="side">サイドバー部分</div> </div>
CSS
CSSはまず #mainに対して position: relative を設定します。次に#sideに対して position: absolute を設定して位置を指定します。
#main{
position: relative;
}
#side{
position: absolute;
top: 20px;
left: 20px;
width: 200px;
height: 200px;
}
jQuery
元記事のコードより追随できる範囲の計算部分のコードを参考にさせて頂きましたが、よりシンプルになるように端折った為だいぶ短いコードになっています。
ヘッド内でjQuery本体を読み込みます。
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
続けて以下のように記入します。作ったサンプルのコードはこのページの下にまとめてあります。
下のコメント内にもありますが、min_move と max_move にて追随できる範囲を決めて、その中のみ margin-top の値を変えて追随させています。それ以外の領域になったときには main要素の上か下にくっつくような margin-top 値を与えます。
<script>
$(function(){
var side = $("#side");
// #sideのオブジェクトをsideへ
var main = $("#main");
// #mainのオブジェクトをmainへ
var min_move = main.offset().top;
// #side が動ける最初の地点(main要素のtopの位置)
var max_move = main.offset().top + main.height() - side.height() - 2*parseInt(side.css("top") );
// max_move は トップから #side が動ける最終地点までの長さ → #main 要素の内側の高さ内
// max_move ←( #mainボックスのトップの位置の値 + #mainボックスの高さ ー #sideの高さ ー サイドのトップ値✕2)
var margin_bottom = max_move - min_move ;
// side要素の一番下にいる時のmargin-top値の計算
// スクロールした時に以下の処理
$(window).bind("scroll", function() {
var wst = $(window).scrollTop();
// スクロール値が wst に入る
// スクロール値が main 要素の高さ内にいる時以下
if( wst > min_move && wst < max_move ){
var margin_top = wst - min_move ;
// スクロールした値から min_move(#mainのtopの表示位置)を引いたのを margin_top へ
side.animate({"margin-top": margin_top},{duration:600,queue:false});
// サイド CSSの margin-top の値を、変数の margin_top にする
// スクロールした値が min_move(main要素の高さより小さい)以下の場合はCSSのマージントップ値を0にする
}else if( wst < min_move ){
side.animate({"margin-top":0},{duration:600,queue:false});
// スクロールした値が max_move (main要素の高さより大きい)以上の場合以下
}else if( wst > max_move ){
side.animate({"margin-top":margin_bottom},{duration:600,queue:false});
}
});
});
</script>
これで、指定要素の中だけスクロールに応じてサイドがついてくるようになります。
メモ:scrollTop() の挙動について
以前にエントリーした記事の(スクロールしてもついてくるサイドメニューをjQueryとcssで作るチュートリアル)、追随するサイドバーのコードはオブジェクトを $(document).scrollTop() にしていましたが、$(document) ではブラウザによっては正しく座標を取れないため、$(window)にしたほうがよいようです。
作ったサンプルのコード
HTML-head内
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script>
$(function(){
var side = $("#side");
var main = $("#main");
var min_move = main.offset().top;
var max_move = main.offset().top + main.height() - side.height() - 2*parseInt(side.css("top") );
var margin_bottom = max_move - min_move ;
$(window).bind("scroll", function() {
var wst = $(window).scrollTop();
if( wst > min_move && wst < max_move ){
var margin_top = wst - min_move ;
side.animate({"margin-top": margin_top},{duration:600,queue:false});
}else if( wst < min_move ){
side.animate({"margin-top":0},{duration:600,queue:false});
}else if( wst > max_move ){
side.animate({"margin-top":margin_bottom},{duration:600,queue:false});
}
});
});
</script>
HTML-body内
<body> <div id="header"> <p>#main要素内だけ追随するサイドボックスのデモ|memocarilog</p> </div> <div id="main"> <div id="side"> <p>#side</p> </div> <p>↑</p> <p>↑</p> <p>#main</p> <p>↓</p> <p>↓</p> </div> <div id="footer"> <p><!-- footer --></p> <p class="back"><a href="#">▲元のページへ戻る</a></p> </div> </body>
CSS
ody{
margin:0 auto;
padding:0;
width:960px;
border:0;
text-align:center;
font-size:30px;
font-family:arial;
}
#header{
height:350px;
background:#eee;
}
#header h1{
padding: 130px 50px 0;
font-weight: normal;
}
#footer{
height:700px;
background:#eee;
}
#footer .back{
font-size: 16px;
}
#main{
position: relative;
min-height: 500px;
border: dotted 5px #faa;
background:#F6E4ED;
}
#side{
position: absolute;
top: 20px;
left: 20px;
width: 200px;
height: 200px;
background: #F14052 ;
}
p{
padding: 50px 20px;
}
No Comments & Tracbacks