JS スクロールするとヘッダーのナビが画面上部に固定されついてくる方法


今回はヘッダー内のナビゲーションが、スクロールすることで画面上部に接触した際に、
画面上部に固定される方法をjQueryを使用せずにやっていきます。
(完成系コードは最下部)

HTMLコード

<body>
  <header>
    <h1 id="headerH1">タゴ'sクリエイト</h1>
    <nav>
      <p id="headerFixed">Global-Navigation</p>
    </nav>
  </header>
  <main>
    <div>
      <h2>Content</h2>
    </div>
    <div>
      <h2>Content</h2>
    </div>
             ・
             ・
             ・
             ・
  </main>
</body>


とりあえずHTMLとCSSを使用して写真のようなものをブラウザに表示しました。
この「Global-Navigation」に対して、
下へスクロールすると画面上部にくっついて固定されるものをJSで記述していきます。

JSコード

window.addEventListener('load',fixedCheck); //1
window.addEventListener('resize',fixedCheck); //2
window.addEventListener('scroll',fixedCheck,{passive:true}); //3
function fixedCheck(){ //4
  let globalN=document.getElementById('headerFixed'); //5
  let fixedJudge=globalN.getBoundingClientRect().top; //6
  if(fixedJudge<=0){  //7
    globalN.classList.add('fixed'); //8
  }
}

CSSコード

.fixed{
  position: fixed;
  width: 100%;
  top: 0;
  left: 0;
}


この記述で画像の様な感じになります。
それでは「//番号」の順で1行ずつ説明していきます。

1、まず「addEventListener」で、ウィンドウをロードした時に「fixedCheck」関数を呼び出します。(この「fixedCheck」は後で説明しますが、大まかに言うと「HTMLの要素にclassを追加するか判定」という意味です)
2、次にまた「addEventListener」を使い、今度はウィンドウの画面サイズを変更した時に「fixedCheck」関数を呼び出します。
3、またまた「addEventListener」を使い、ウィンドウをスクロールした時にも「fixedCheck」関数を呼び出します。
ここで第三引数に

4、次は先ほどの「fixedCheck」関数を記述していきます。
5、まずはHTMLのグローバルナビ(今回は「headerFixed」)を「getElementById」で取得し、変数「globalN」変数に代入します。
6、次に「getBoundingClientRect().top」で「globalN」要素の上辺を基準とする、ブラウザ上の高さ位置を取得し、「fixedJudge」変数へ代入します。

ここで「getBoundingClientRect」について説明です。
これは「要素.getBoundingClientRect().top」とすることで、
(今回Xは「top」を指定してますが、他の指定種類については後述します)
要素のボックス(border、padding含む)の上辺の座標が、ブラウザの表示部分(ビューポート)の基準点である左上(左上が「0,0」となっている)を基準として、どの位置にいてるかを数値として取得できます。
この「top」の他にも「bottom,left,right」等があり、それぞれボックスの下辺、左辺、右辺を意味します。
例えば「スクロールしてh1要素が完全に画面から上になくなるとイベントを起こしたい」等の時は、
「bottom」を使用するといいと言うことですね。

7、「fixedJudge(「globalN」上辺のブラウザ上での高さ位置)」が0以下(画面上部よりさらに上に行き、見えない状態)の場合という条件式を記述。
8、「DOM要素.classList.add('クラス名');」とすることで、取得したDOM要素に、CSSで使用できるクラス名(HTMLでの「class=""」と同じ)を追加できます。今回の記述だと、ID名「headerFixed」の要素に「fixed」というクラスを付与する、と言う意味になります。

記述コードに関しての説明は以上になりますが、このコードだと一度付与されたクラスはずっと付与された状態のままで、

画像の様な状態になってしまいます。
そこで「fixedJudge」が0より上(画面上部より下に行き、見える状態)になると付与したクラスを削除したいと思います。
クラス付与が「classList.add」なのに対し、クラス削除は「classList.remove」で行えます。

ここでよく間違いがちなのが付与を追加したとき同様に「グローバルナビ」要素に「remove」メソッドを適用してしまうことです。
「グローバルナビ」が上部固定された状態だと、どれだけスクロールしたとしても座標が「0」から変わることはあり得ません。なので、

JSコード

window.addEventListener('load',fixedCheck);
window.addEventListener('resize',fixedCheck);
window.addEventListener('scroll',fixedCheck,{passive:true});
function fixedCheck(){
  let h1Bottom=document.getElementById('headerH1'); //変更点1
  let globalN=document.getElementById('headerFixed');
  let fixedJudge=h1Bottom.getBoundingClientRect().bottom; //変更点2
  if(fixedJudge<=0){
    globalN.classList.add('fixed');
  }else{ //変更点3
    globalN.classList.remove('fixed');  //変更点4
  }
}

変更点1、HTMLのヘッダー内にある「h1」を取得し変数「h1Bottom」に代入し、
変更点2、次に「getBoundingClientRect().bottom」で「h1Bottom」要素の下辺を基準とする、ブラウザ上の高さ位置を取得し、「fixedJudge」変数へ代入。(この時点で「if文」条件式の基準となる座標が「グローバルナビの上辺」から「h1要素の下辺」に変わっています。)
変更点3、if文条件式以外の場合の「else」を追加。
変更点4、「globalN」から「fixed」クラスを削除する文を記述。

この様に記述することで、
h1要素の下辺がビューポート上辺より上(h1要素が見えない状態)にある時は、「globalN」に「fixed」クラスを付与し、
ビューポート上辺より下(h1要素が見える状態)の時は「globalN」から「fixed」クラスを削除する、
と言う記述の意味になりました。


↓↓↓↓↓↓↓↓↓↓↓

↓↓↓↓↓↓↓↓↓↓↓

実際にブラウザに表示してみるとこのようになりましたね。

これでナビが画面上部に固定されついてくる方法の説明は以上になります。
ご購読ありがとうございました!

追記

コメントで「position: sticky;」だと同じことができると言っていただき、検証してみました!
先ほどまでのJSコードを全て消し、CSSコードの「nav」要素に、

CSSコード

nav{
  height: 100px;
  margin-bottom: 40px;
  position: sticky; /* sticky指定 */
  top: 0px; /* stickyとして固定したい位置 */
}

このように追加してみます。なお、この「top」というのは画面上部に要素を固定するという意味で、
これを「bottom」にすると画面下部に固定するという意味になります。

上記コードを記述したところうまく適用できませんでした。
これは、「nav」要素に対して「position: sticky;」を記述すると、
今回だと「nav」要素の親要素である「header」要素内でのみ「sticky」が適用し追従するからです。
例として「h1」要素に対して「sticky」を適用してみると、

最初は画面上部に固定されていますが、

「header」の最下部(「nav」要素の下部の位置)まで「h1」要素が達すると、
それ以上したに行くことができず、追従が終わっているのがわかります。
この様に「sticky」を設定すると、設定した要素に対する親要素の範囲内でのみ、上部に固定する様になります。

この「sticky」を使用して最初に記述したHTMLに対してナビを上部固定する時は、

HTMLコード

<body>
  <header>
    <h1 id="headerH1">タゴ'sクリエイト</h1>
  </header>
  <nav>
    <p id="headerFixed">Global-Navigation</p>
  </nav>
  <main>
             ・
             ・
             ・
             ・
  </main>
</body>

CSS

nav{
  height: 100px;
  margin-bottom: 40px;
  position: sticky; /* sticky指定 */
  top: 0px; /* stickyとして固定したい位置 */
}

このコードの様に「nav」要素をheaderから抜き出して、
「nav」要素の親要素が「body」要素という状態を作ると

この様に画面上部にしっかりと固定されました!