ウェブページのスクロール量(読了率)を計算する

スクロール量

はじめに

スクロール量を取得します。ウェブページをどこまでスクロールしたのかの値です。ブログ等では、読了率として記事の良し悪しの指標としてよく利用されています。

高さやスクロール量の取得と計算といった単純な処理ですが、クロスブラウザの互換周りを考慮すると実装までだいぶ面倒だったため、記事に落とし込みました。

スクロール位置を取得

まずは、スクロール位置の取得です。スクロール位置は、window.scrollYで取得できます。ただし、クロスブラウザー互換性を考慮するとwindow.pageYOffsetで取得するほうが無難です。次にコード例を示します。IE8-用のpolyfillも合わせて記載します。

// IE除く
var scroll = window.scrollY;
// IE9+
var scroll = window.pageYOffset;
// polyfill(IE8-)
var scroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;

補足

pageYOffset プロパティは、scrollY プロパティのエイリアスです。

window.pageYOffset === window.scrollY; // 常に true

ドキュメント全体の高さを取得

これは、ウィンドウに収まる範囲ではなく、ウィンドウの領域外を含めたドキュメント全体の高さです。

// IE8+
var documentHeight = document.documentElement.scrollHeight;

画面表示領域の高さを取得

実際、画面内に表示されている領域の高さを取得します。

// IE6+
var viweHeight = document.documentElement.clientHeight;

補足

Element.clientHeightは、通常「CSSの高さ+ CSSのパディング-水平スクロールバーの高さ」が格納されています。ですが、ルート要素の場合は、ビューポートの高さが格納されています。

完全にスクロールしたか判定する

以下の式が成り立つ時、スクロールが完全に完了しています。

ドキュメントの高さ - スクロール位置 === 画面領域の高さ

ちなみに、ページ全体のどれだけスクロールしたかは、次のようになります。

// ページ全体のスクロール量(0-1, 0:スクロールなし, 1:完全にスクロール)
スクロール位置 / (ドキュメントの高さ - 画面領域の高さ)

補足

The following equivalence returns true if an element is at the end of its scroll, false if it isn't.

element.scrollHeight - element.scrollTop === element.clientHeight

コード(スクロール量を取得)

// スクロール量を取得(0-1, 0:スクロールなし, 1:完全にスクロール)
// IE9+
function getScrollAmount() {
  var scroll = window.pageYOffset;
  var documentHeight = document.documentElement.scrollHeight;
  var viweHeight = document.documentElement.clientHeight;
  var scrollHeight = documentHeight - viweHeight;
  return scroll / scrollHeight;
};

コード(ページ表示後の最大スクロール量)

// ページ表示後の最大スクロール量
// IE9+
(function() {
  var maxPageY = 0;
  var timer = null;
  function getScrollAmount() {
    var documentHeight = document.documentElement.scrollHeight;
    var viweHeight = document.documentElement.clientHeight;
    var scrollHeight = documentHeight - viweHeight;
    return maxPageY >= scrollHeight ? 1 : maxPageY/scrollHeight;
  };
  function updateScroll() {
    var y = window.pageYOffset;
    if (maxPageY < y) {
      maxPageY = y;
      //console.log(maxPageY);
      //console.log(getScrollAmount());
    }
  };
  function onScroll() {
    // 高頻度呼び出し対策
    window.clearTimeout(timer);
    timer = window.setTimeout(updateScroll, 300);
  };
  window.addEventListener('scroll', onScroll, {capture:false, passive:true});

  // 最大スクロール量の送信
  //window.addEventListener('beforeunload', function() {
  //  updateScroll();
  //  var scroll = Math.floor(getScrollAmount()*100);
  //  var label = (Math.ceil(scroll / 10) * 10)+'%';
  //  ga('send', 'event', 'scroll', 'post', label, scroll);
  //});
})();

コメント