[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