本記事は執筆時点(2024年04月12日)の情報をベースにしております。掲載している情報が最新ではない可能性がありますので何卒ご容赦ください。
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も設定しないと同じ幅を維持できないので注意が必要です。