🐡

VanillaJSでスクロール時の要素固定を行う-(基礎)

2022/10/20に公開

introduction

今回実装しないといけないのは、スクロールをした時にposition:absoluteからposition:fixに変更することで、グローバルナビゲーションを固定するというシステム。まぁJqueryを利用すればそんなんコピペで行けるんやけど、時代はVanilla JS。だからJqueryからVanillaJSを置き換えることをする。またその過程でVanillaJSの知識を深めることを目標にしていくで。

補足:なんか書いてたらただの備忘録みたいになってしまった。解説していこうとしたけど、学んだ過程をみてくれ〜〜みたいな記事になっているので、そこらへんは注意してほしい。

JqueryのコードからVanilla JSを学ぶ際

まず必要なのは、JqueryのコードがVanilla JSのどんな記述に対応しているのかを見つけること。それに関しては以下のサイトである程度まとめられてあるから、自分で勉強する際はこれを読むといいと思う。
https://qiita.com/nightyknite/items/668c112c40931515ed67
https://q-az.net/without-jquery-height-width-offset-scrolltop/

さらにそこからVanilla JSの中身を知ろうとするなら、mdn公式ドキュメントが有用だと思う。以下のサイトがそのドキュメントやで。1から勉強するならこれがいいな。
https://developer.mozilla.org/ja/docs/Web/JavaScript

windowとdocumentの利用

そもそもDOMの取得とか、addeventlistenerを使う際に、documentとwindowを適当に使っていたんだけど、どんな違いがあるのだろうか。参考文献を見てみると、それぞれの説明としては、windowでは、windowを操るもの、documentにはhtmlのdocument自体を操るものだそう。明確に違いがわかるのが、使うメソッドの違い。以下を見てほしい。

windowのメソッド
window.location //urlの値を取得する
window.document //話題に上がっているdocumentはwindowが持っているもの
window.alert //アラートも実はwindowオブジェクト
window.screen //windowのスクリーンサイズを出す
window.history //閲覧履歴などを取得する
documentのメソッド
document.getElementById //htmlの中にあるidを取得
document.querySelectorsAll //htmlの中にあるclassを取得

それぞれをみてみると、windowに関するメソッドはwindowにおける操作であることがわかるだろうか。windowとはブラウザ内の機能を操作するということであり、urlの値の取得とか、screensizeの取得とかはすべてそれに当たる。たいして、documentに関してはdocument内のhtmlに関する操作であり、html内のDOMを取得することなどに利用することができる。

windowのloadイベントとdocumentのDOMcontentLoadedイベント

この2つは似ているが、何が異なっているのか。違いは、どこまで読み込まれているかだ。webページの読み込みには、document内のDOMの読み込みと、リソース(cssや画像)の読み込みが存在する。loadイベントはその全てが読み込み完了時に実行が行われるのに対して、DOMcontentLoadedイベントはリソースの読み込みが完了するのを待たずに発生することができる。これはローディングアニメーションを制作するときなどに非常に有効に働く場合がある。もっと詳しく実践的に学ぶなら以下のサイトが良い。
https://takayamato.com/eventlistener/

位置取得

位置取得に関してはたくさんのメソッドが存在するが、それぞれのメソッドをわざわざ丁寧に解説していると僕の仕事時間が無限にすり減る編に突入してしまうので、それぞれで参考になるサイトを以下に示すので、それぞれで勉強してくれると嬉しい。おすすめの見方としては、下にあるソースコードを見てから、わからないところをこれで調べるという手法を取ると学びやすいと思う。

getBoundingClientRectメソッド

このメソッドは、その要素の寸法と、ビューポートからの相対位置に関する情報をわたしてくれる。詳しくは以下のサイトである。これで、画面内の相対位置を取得しよう。
https://developer.mozilla.org/ja/docs/Web/API/Element/getBoundingClientRect

window.innerWidth,window.outerWidthメソッド

このメソッドによって、画面の幅を取り込むことができる。詳しくは以下のサイトである。
https://www.javadrive.jp/javascript/webpage/index6.html

scrollY,scrollTopメソッド

window.pageYOffsetによって、距離を取得することができる。他にもscrollYメソッドがあるのだが、クロスブラウザー互換性に対応しているので、pageYOffsetメソッドを使いたい。
https://www.bugbugnow.net/2020/01/webpage-scroll-amount.html

補足:orの特殊用法

これに関しては、上のサイトを読む時に必要となった||演算子の利用に関してだ。僕と同じようにわからんくなったら、これを読んでほしい。
https://zero-plus-one.jp/javascript/javascript-logical-operators-or/

その他の補足動画

dcodeさん(いや、dcode様というべきか)は英語でしゃべるけど、めちゃくちゃわかりやすいのでもしよかったら見てほしい(布教)
https://youtu.be/V9CY0F4Wc7M
https://youtu.be/MKpZadkuT-0

実践

では今回実際に書いたコードを見てみる。fixの要素は途中で高さが変化するので、それを基準にするとバグが起こる。以下のmyTopは、(viewのtopであるscrollTop)+(viewからの高さであるrect)をとってくることによって、ページトップからの高さをとっている(文章として伝えにくい!)

実践
const target = document.getElementsByClassName("js-fix")[0];
const viewHeight = document.documentElement.clientHeight;
const rectLoad = target.getBoundingClientRect().height;
const fixedBound = viewHeight - rectLoad;

window.addEventListener("scroll",function(){
    // const wrapper = document.getElementsByClassName("js-fix-wrapper")[0];
    // const rect = target.getBoundingClientRect();
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
    // const myTop = rect.top + scrollTop;
    if(scrollTop > fixedBound){
      target.classList.add("is-fixed");
    }else{
      target.classList.remove("is-fixed");
    }
    
  });

参考になったサイト

これを基本的にVanilla JSにするように努めたが、自分としてはwrapperを作るよりもviewHeightとrectで取得した高さから操作したほうがいいと思う(wrapperによる依存性が無くなるから)。
https://cly7796.net/blog/javascript/fix-an-element-in-a-specific-scroll-range/

Discussion