jQuery | スクロール量によって固定要素を表示

jQuery | スクロール量によって固定要素を表示

ページ内にボタンやバナーを画面固定したい時、position: fixed などで固定できますが、「最初に目に入るメインビジュアルには被らせたくない」「footerには被らせたくない」というように、決まった範囲内でのみ表示しておきたいときに使えます。

動作イメージ

スクロールがsectionCに入ると(sectionCが画面内に入ると)画面下部に固定要素が表示され、sectionDが画面内に入ると非表示となります。

See the Pen スクロール量によって固定要素を表示 by uta (@utamaro) on CodePen.

下の画像はページ全体をざっくり分けたイメージ図になります。

各セクションをA~Dに割り振り、Cを表示開始トリガー、Dを表示終了のトリガーとしています。(要素の上部がトリガーの位置となります)

スクロールをしていき、Cが画面内に入ると固定要素が表示され、Dが画面内に入ると固定要素が非表示となります。

おおまかな作り方

表示し始めたい箇所の要素と表示を終わらせたい箇所の要素にID名を指定し、トリガーとします。固定要素にもID名をつけておきます。

javascriptでページトップからのそれぞれのトリガー位置とスクロールの量を取得。スクロール量が1つ目のトリガーよりオーバーしたら表示、2つ目のトリガーを過ぎたら非表示するようにします。

なお、fadeInやfadeOut動作はjQueryのほうが滑らかなので、部分的にjQueryを使用しました。

サンプルコード

HTML

表示開始のトリガーとなる要素にidjs-appearTrigger、表示終了のトリガーとなる要素にidjs-hiddenTrigger、固定要素にidjs-fixedBlockを指定します。

固定要素はここでは一番最後に記述しています。

<div class="blockA">....</div>
<div class="blockB">....</div>
<div class="blockC" id="js-appearTrigger">....</div>
<div class="blockD" id="js-hiddenTrigger">....</div>
<div class="btn" id="js-fixedBlock">....</div>

CSS

cssでは固定要素を予め非表示にしておき、前面に来るよう念のためz-indexを指定しています。
※HTMLで固定要素を先に書くと、後に書いた要素が前面に被ってしまうので、z-indexは必須です。z-indexが効いていない場合は、全面にきている要素にposition: relative;を指定してあげるとうまくいったりします。

.btn {
  display: none;
  width: 300px;
  position: fixed;
  bottom: 0;
  left: 50%; /* 中央に表示 */
  transform: translateX(-50%); /* ずらし過ぎた分元に戻す */
  z-index: 10; /*前面に表示されていれば1でも2でもOK*/
}

上記のcssは要素の幅を300pxに指定し中央寄せにしていますが、幅100%の場合はleft: 0;でぴったり左端にくっつけてあげて、transform~の記述も必要なし。

javascript

</body>直前辺りに<script>タグで囲って下記のコードを記述。

  const fixedBlock = document.getElementById('js-fixedBlock');
  const appearTrigger = document.getElementById('js-appearTrigger');
  const hiddenTrigger = document.getElementById('js-hiddenTrigger');

  // スクロール量で下部のボタンを表示・非表示
  window.addEventListener('scroll', function () {

    //スクロール量を取得
    var scroll = window.scrollY;
    var windowHeight = window.innerHeight; //画面の高さを取得

    //ターゲット要素の位置を取得
    var appearTriggerPos = appearTrigger.getBoundingClientRect().top + scroll;

    //ターゲット要素の位置を取得
    var hiddenTriggerPos = hiddenTrigger.getBoundingClientRect().top + scroll;

    if (scroll >= appearTriggerPos - windowHeight && scroll <= hiddenTriggerPos - windowHeight) {
      $('#js-fixedBlock').fadeIn();
    } else if (scroll < appearTriggerPos - windowHeight) {
      $('#js-fixedBlock').fadeOut();
    } else if (scroll > hiddenTriggerPos - windowHeight) {
      $('#js-fixedBlock').fadeOut();
    }
  });

※jQueryを使用しているので、上記のスクリプトコードより上にjQueryを読み込んでおいてください。
(CDNで読み込む場合は以下のコードを貼り付ければOK。GoogleHostedLibrariesより)

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

いくつか分岐点を作って表示と非表示を分けているだけなので、分岐点を増やせばもっと細かく表示非表示を切り替えられます。

上記では、表示トリガー <= スクロール <= 非表示トリガー のときに表示、表示トリガー > スクロール もしくは 非表示トリガー < スクロール のときに非表示としています。

備考

元々全てjavascriptで作ろうとしたが、fadeInやfadeOut部分の動作が不安定だったため、結局一部jQeryを使用。javascriptでなめらかにfadeIn・fadeOutが出来たら、ここに追記しようと思う。