CSSのstickyのような動きをJSで実装する
JavaScriptを使って、「ある要素がページ上部に当たるまでスクロールされたら、その位置でその要素を固定表示し続ける」という仕様を作る機会がありました。
CSSのstickyでも同様の仕様が作れますが、今回はstickyが使えない状況だったので素のJavaScriptのみ(jQueryも不要)で実装した方法を備忘録として残しておこうと思います。
下のGifが出来上がりのイメージです。固定したい要素がページの途中にあり、スクロールされてページ上部に達したらヘッダーとして固定し、再度上にスクロールされたら固定を解除するという仕様です。
ソースコード
上のサンプルを作ったソースコードです(インラインで記述しているCSSはスクロールをわかりやすくするためのものなので実際は不要です)。
HTML
<div class="area"> <div class="preElm" style="height:calc(50vh);background:#e9e9e9;"></div> <div class="fixedElm" style="width:100%;background:#999;color:#fff;font-weight:bold;padding:10px;">固定したい要素</div> <div class="dummyElm" style="height:calc(100vh + 250px);background:#d9d9d9;"></div> <div class="dummyElm" style="height:calc(100vh + 250px);background:#c9c9c9;"></div> <div class="dummyElm" style="height:calc(100vh + 250px);background:#b9b9b9;"></div> <div class="dummyElm" style="height:calc(100vh + 250px);background:#a9a9a9;"></div> </div>
「固定させたい要素」と「固定させたい要素より上にある要素」を合わせた高さをスクロール量が上回ったら、position:fixedを指定したクラスを付与する(下回ったらクラスを削除する)という仕組みをJSで作ります。
JavaScript
function scrollFixed(){ var fixedElm = document.querySelectorAll(".fixedElm");//固定させたい要素 var preElm = document.querySelectorAll(".preElm");//固定させたい要素より上にある要素 var hh = preElm[0].clientHeight + fixedElm[0].clientHeight;//固定させたい要素までの高さを取得 window.addEventListener("scroll",function(){ var scroll = window.pageYOffset;//スクロール量を取得 if(hh < scroll){ fixedElm[0].classList.add("myFixed");//固定するためのクラスを付与 }else{ fixedElm[0].classList.remove("myFixed");//クラスを削除 } }); } window.addEventListener("load",function(){ console.log("load!"); scrollFixed(); });
position:fixedで固定表示するスタイルを用意したら完成です(box-shadowで影を付けるかどうかはお好みで)。
CSS
.fixedElm.myFixed{ box-shadow:0 2px 5px #333; position:fixed; top:0; left:0; z-index:999; }
サイドバーを固定する場合
少し修正すればサイドバーの固定にも応用することができます。
上記サンプルのソースコードは以下の通りです(インラインで記述しているCSSはスクロールをわかりやすくするためのものなので実際は不要です)。
HTML
<header style="background:#333; color:#fff;padding:30px 10px;">ヘッダー</header> <div class="area"> <aside> <div class="fixedElm" style="width:100%;background:#999;color:#fff;font-weight:bold;padding:10px;">固定したい要素</div> </aside> <div class="mainElm"> <div class="dummyElm" style="height:calc(100vh + 250px);background:#d9d9d9;"></div> <div class="dummyElm" style="height:calc(100vh + 250px);background:#c9c9c9;"></div> <div class="dummyElm" style="height:calc(100vh + 250px);background:#b9b9b9;"></div> <div class="dummyElm" style="height:calc(100vh + 250px);background:#a9a9a9;"></div> </div> </div>
JavaScript
function scrollFixed(){ var header = document.querySelectorAll("header"); var fixedElm = document.querySelectorAll(".fixedElm"); var mainElm = document.querySelectorAll(".mainElm"); var hh = header[0].clientHeight + fixedElm[0].clientHeight; window.addEventListener("scroll",function(){ var scroll = window.pageYOffset; //console.log(scroll); if(hh < scroll){ fixedElm[0].classList.add("myFixed"); }else{ fixedElm[0].classList.remove("myFixed"); } }); } window.addEventListener("load",function(){ scrollFixed(); });
CSS
.area{ display:flex; } .area .mainElm,.area aside{ flex-basis:50%; margin:10px; } .fixedElm.myFixed{ width:calc(50% - 20px) !important; position:fixed; top:10px; left:10px; z-index:999; }
position:fixedと同時にwidthも設定しないと同じ幅を維持できないので注意が必要です。