ハシウェブ

Web制作、プログラミングに関する情報を発信するブログ

progress要素を使ったスクロールのプログレスバーの作り方【JavaScript】

progress要素を使ったスクロールのプログレスバーの作り方【JavaScript】

「ブログに全体のどこまでスクロールしたかがわかるものを設置したい」
「JavaScriptでプログレスバーを作りたい」

悩み

この記事では、こういった悩みにお答えします。

目次
  1. スクロールプログレスバーの作り方
    1. HTML
    2. CSS
    3. JavaScript
    4. jQuery (WordPress)
  2. まとめ

スクロールプログレスバーの作り方

HTML

まず、プログレスバーを表示させる「progress」要素をHTMLに追加します。
上部に固定する場合はどこに書いても大丈夫です。
このブログの場合は、以下をheaderの上に追加しました。

<progress id="js-progress-bar" class="progress-bar" max="100" value="100"></progress>

JavaScriptで書き替わるのでvalueは好みの数字でもかまいません。
ただ、JavaScriptが動かなかった場合にデザインとしてのラインになるので、よっぽど理由がなければ100がいいと思います。

CSS

次にCSSでプログレスバーの位置と色を指定します。
ブラウザの上部に固定し、横幅100%にします。
スクロール中に隠れることがないように、z-indexをそれぞれの環境にあわせて指定してください。

.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  margin: 0;
  width: 100%;
  height: 2px;
  border-radius: 0;
  z-index: 1;
  -webkit-appearance: none;
  -moz-appearance: none;
  border: none;
  background-color: #eee;
}
.progress-bar::-webkit-progress-bar {
  background-color: #eee;
}
.progress-bar::-moz-progress-bar {
  background-color: #5bbee5;
}
.progress-bar::-webkit-progress-value {
  background-color: #5bbee5;
}

「progress-bar」の反映場所が、Chrome(-webkit-)とfirefox(-moz-)で違うので、わかりにくいですね。

//firefoxの場合
//背景色
progress {
  background-color: #eee;
}
//value色
::-moz-progress-bar {
  background-color: #5bbee5;
}
//Chromeの場合
//背景色
::-webkit-progress-bar {
  background-color: #eee;
}
//value色
::-webkit-progress-value {
  background-color: #5bbee5;
}

IE11はCSSが効かないうえ、余計なアニメーションが効いてしまうのが若干気になりますがそのままにしています。

JavaScript

このブログでは「article」内の進捗がわかるようにしました。
ちょっとアレンジすれば他の要素にも使えます。

window.addEventListener("scroll", progressBar, false);
window.addEventListener("load", progressBar, false);
window.addEventListener("resize", progressBar, false);

function progressBar() {
  article = document.querySelector("article");

  if (article) {
    windowHeight = window.innerHeight;
    scrollTop = window.scrollY || window.pageYOffset;
    scrollbottom = scrollTop + windowHeight;

    articleHeight = article.clientHeight;
    articleTop = article.offsetTop;
    articleBottom = articleTop + articleHeight;

    progressTop = scrollbottom;
    progressBottom = articleBottom;

    progress = (progressTop / progressBottom) * 100;

    progressBar = document.getElementById("js-progress-bar");

    if (scrollbottom > windowHeight && scrollbottom < articleBottom) {
      progressBar.setAttribute("value", progressInt);
    } else {
      progressBar.setAttribute("value", 100);
    }
  }
}

スクロールだけでなく、ロード時とリサイズ時にもイベントが発火するようにしました。

window.addEventListener("scroll", progressBar, false);
window.addEventListener("load", progressBar, false);
window.addEventListener("resize", progressBar, false);

「article」を進捗度合いを取得する対象にします。

article = document.querySelector("article");

ブラウザウインドウの高さ、スクロールの上下の位置を取得します。
スクロールの上下の位置は変動します。

//ウインドウの高さ
windowHeight = window.innerHeight;
//スクロールの上ポジション
scrollTop = window.scrollY || window.pageYOffset;
//スクロールの下ポジション
scrollbottom = scrollTop + windowHeight;

「article」の高さ、「article」の上下の位置を取得します。

//articleの高さ
articleHeight = article.clientHeight;
//articleの始まり
articleTop = article.offsetTop;
//articleの終わり
articleBottom = articleTop + articleHeight;

ここは進捗の始めと終わりを把握していれば必要ありません。
スクロールの進捗を計算するための上下のポイントを指定しています。
画面の上を基準にしたい場合等は、ここか計算のところで変更してください。

//進捗の始まり
progressTop = scrollbottom;
//進捗の終わり
progressBottom = articleBottom;

スクロールの進捗を計算します。
整数にしなくてもいけました。

progress = (progressTop / progressBottom) * 100;

プログレスバーのvalueに進捗度合いを入れていきます。
ページ最初とarticleを超えた場合は100が入るようにしました。

progressBar = document.getElementById("js-progress-bar");

if (scrollbottom > windowHeight && scrollbottom < articleBottom) {
  progressBar.setAttribute("value", progressInt);
} else {
  progressBar.setAttribute("value", 100);
}

これで完成です。
このブログページ自体がデモになっています。

jQuery (WordPress)

ちなみに、WordPressのjQueryで書くとこんな感じです。

jQuery(function () {
  jQuery(window).on("scroll load resize", function () {
    article = jQuery("article");

    if (article) {
      win = jQuery(window);
      windowHeight = win.height();
      scrollTop = win.scrollTop();
      scrollbottom = scrollTop + windowHeight;

      articleHeight = article.outerHeight();
      articleTop = article.offset().top;
      articleBottom = articleTop + articleHeight;

      progressTop = scrollbottom;
      progressBottom = articleBottom;

      progress = (progressTop / progressBottom) * 100;

      progressBar = jQuery("#js-progress-bar");

      if (scrollbottom > windowHeight && scrollbottom < articleBottom) {
        progressBar.attr("value", progress);
      } else {
        progressBar.attr("value", 100);
      }
    }
  });
});

ネイティブJavaScriptとやってることは同じなので、解説は省略します。

まとめ

progress要素を使ったスクロールプログレスバーの作り方を紹介しました。
プラグインもあるようですが、そんなに難しくなさそうだったので、自分でつくってみました。
Intersection Observerを使ってもできそうな感じがします。

スクロールアニメーションを実現!Intersection Observer APIの使い方 デモあり

Webサイトにスクロールエフェクトを表現するのにどうすればいいか悩んでいますか?この記事ではIntersection Observer APIの使い方を解説します。さまざまなスクロールエフェクトのデモも用意しています。ぜひご覧ください。