スクロール時にfooter部分で止まるフローティングバナー(追従バナー)をJavaScriptで実装してみる(備忘録)

今回はJavaScriptを用いて、スクロール時にfooter部分で止まるフローティングバナー(追従バナー)を実装してみる。もちろん、×をクリック(タップ)すると閉じる仕様である。

今回はスクロールの計算をしないといけないので、考え方が少し難しくなる。

やっていることとしては、

  • ①bodyの高さ
  • ②footerの高さ
  • ③ウィンドウ枠の高さ
  • ④スクロール量の高さ(スクロール量によって数値は変動する)

これらの高さを取得した上で、計算していくことになる。

今回はfooterの一番上の部分に、フローティングバナー(追従バナー)が追従してきたときに固定をするので、先述した①〜④で計算式を作ると下記のようになる。

計算式:①ー(③+④)<=②

これを言語化すると、
bodyの高さから、(ウィンドウ枠の高さとスクロールした分の高さ)を引いた結果が、footerの高さ以下になるということだ。

これをjavascript側でif文を使って条件をつけて、行いたい処理を書いていけば実現できる。

それでは解説に入っていく。

HTMLの記述について

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>フローティングバナー(追従バナー)</title>
  <link rel="stylesheet" href="../reset.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <div class="bl_floatingBanner" id="js_floatingBanner">
      <a href="" class="bl_floatingBanner_img"><img src="img_floating.jpg" alt=""></a>
      <div class="bl_floatingBanner_close" id="js_floatingBanner_close">
        <span></span>
        <span></span>
      </div>
    </div>
    <section>
      <div class="ly_mainvisual">
        <img src="img_mainvisual.jpg" alt="MV">
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu1">
        <h2>menu1</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu2">
        <h2>menu2</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu3">
        <h2>menu3</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu4">
        <h2>menu4</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu5">
        <h2>menu5</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
  </main>
  <footer id="js_footer">
    <div class="ly_inner">
      <h2>footer</h2>
      <div class="md_textblock">
        <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
      </div>
    </div>
  </footer>
  <script src="style.js"></script>
</body>
</html>

前回の記事と変わっている箇所は、footerを追加している点だ。

今回の目的は、footer部分でフローティングバナー(追従バナー)が固定されるようにするので、このために記述している。

なお、footerにはid(js_footer)をふっている。

前回の記事はこちら

CSSの記述について

@charset "utf-8";

/* ==========================
  初期設定
========================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}
body {
  position: relative;
}
img {
  width: 100%;
}

/* ==========================
  フローティングバナー(追従バナー)
========================== */
.bl_floatingBanner {
  position: fixed;
  bottom: 0;
  right: 10px;
  width: 150px;
  max-width: 100%;
}
.bl_floatingBanner.js_close {
  display: none;
}

.bl_floatingBanner_img {
  display: block;
  transition: 0.3s;
}
.bl_floatingBanner_img:hover {
  opacity: 0.6;
}
.bl_floatingBanner_close {
  width: 15px;
  height: 15px;
  position: absolute;
  right: 0;
  top: -15px;
  cursor: pointer;
}
.bl_floatingBanner_close span:nth-of-type(1) {
  position: absolute;
  top: 7px;
  right: 0;
  width: 15px;
  height: 1px;
  background-color: #000;
  transform: rotate(45deg);
}
.bl_floatingBanner_close span:nth-of-type(2) {
  position: absolute;
  top: 0;
  right: 7px;
  width: 1px;
  height: 15px;
  background-color: #000;
  transform: rotate(45deg);
}

/* ==========================
  コンテンツの中身
========================== */
.ly_inner {
  width: 100%;
  max-width: 1080px;
  margin: 100px auto;
  padding: 20px;
  background-color: #ccc;
}
.ly_inner h2 {
  font-size: 150%;
  font-weight: bold;
  margin-bottom: 30px;
}
.md_textblock > * + * {
  margin-top: 10px;
}

/* ==========================
  footer
========================== */
footer {
  background-color: #2b2822;
  color: #fff;
}
footer > .ly_inner {
  background-color: transparent;
  margin: 0 auto;
}

CSSについても、footer部分の見た目を調整するのに記述を追加している。

また、bodyに対して、positionをrelativeに設定している。これはフローティングバナー(追従バナー)をfooterに固定するときに、positonをabsoluteに変更するための指定である。

そのほかは前回の記事とは変わっていない。実装内容はそこまで難しくないので、解説は省略する。

前回の記事はこちら

JavaScriptの記述について

'use strict';

// ×を押すと閉じる
window.onload = function () {
    document.getElementById("js_floatingBanner_close").onclick = function () {
        this.parentNode.classList.add('js_close');
    };
}

// footerで追従バナーを固定
window.addEventListener('scroll', function () {
    const targetElement = document.getElementById('js_floatingBanner');
    const bodyHeight = document.body.clientHeight;
    // ↓document.documentElement.clientHeight +以降の記述はスクロールの高さを取得するための記述であるが、クローム、IEなどの各ブラウザに対応するために記述を分けている
    const windowScrollHeight = document.documentElement.clientHeight + document.documentElement.scrollTop || document.body.scrollTop || document.scrollingElement.scrollTop || window.pageYOffset || window.scrollY;
    const footerHeight = document.getElementById('js_footer').clientHeight;
    if (bodyHeight - windowScrollHeight <= footerHeight) {
        targetElement.style.position = 'absolute';
        targetElement.style.bottom = footerHeight + 'px';
    } else {
        targetElement.style.position = 'fixed';
        targetElement.style.bottom = 0;
    }
});

×を押すと閉じる部分は前回の記事で解説しているので省略する。

前回の記事はこちら

footerで追従バナーを固定するための処理

window.addEventListenerでscrollした時の処理を記述していく。

まず流用しやすいように、constを使って定数を定義していく。
※定数の名前は任意でつけていただければOK。

  • targetElement:id(js_floatingBanner)の取得
  • bodyHeight:document.body.clientHeightでbodyの高さを取得
  • windowScrollHeight:下記①と②を合算した高さを取得(難しければ分けて定義してもOK)

①document.documentElement.clientHeightでウィンドウ枠の高さ

②document.documentElement.scrollTop || document.body.scrollTop || document.scrollingElement.scrollTop || window.pageYOffset || window.scrollYでスクロールした分の高さ

  • footerHeight:document.getElementById(‘js_footer’).clientHeightでfooterの高さを取得

あとは、bodyHeightからwindowScrollHeightを引いた値が、footerHeight以下になることを条件にすれば良い。

計算方法についての図解

画像にすると上のような感じだ。

黒枠で囲まれている部分が、自分のPCやスマホ上で見えている画面だと捉えていただいてよい。

ここから、ウィンドウ枠の高さを見ていくと、上部のURLを入れるようなバー部分であったり、ブックマークバーだったりが表示されていると思うが、これらはウィンドウ枠の高さには入らず、それ以外の内部の画面上に表示されている部分がウィンドウ枠の高さになる。詳しくはMDNなどで調べてみるといいかと思う。

スクロールした分の高さについては、点線の矢印部分に当たる。この点線はスクロールすればするほど伸びていくとイメージしていただければよい。

そのほか、bodyの高さとfooterの高さは画像の通りである。

この画像を元に、スクロール時にfooter部分で止まるフローティングバナー(追従バナー)の計算式を考えていくとわかりやすいかと思う。

計算式

bodyの高さー(ウィンドウ枠の高さ+スクロールした分の高さ)<=footerの高さ

このような感じで考えていける。

最後にif文に任意のCSSを指定する

今回は、スクロール時にfooter部分で止まるフローティングバナー(追従バナー)を実装するため、positionをabsoluteに。

そして、bottomからの距離をfooterの高さに指定した。これによりfooterの一番上の部分でフローティングバナー(追従バナー)が固定されるようになる。ここで注意なのが、bottomからの距離には単位がついていないため、pxを+を用いて繋げてあげることだ。これによりCSS側で動くようになる。

あとはelseのときに、元のCSSに戻す記述をすればOKだ。ちなみにここのbottomは0のため、単位をつけていない。0以外の値にする場合は、pxが必要になるので注意してほしい。

以上で実装完了だ。

1 返信

コメントはクローズされています。