モバイルファーストなイマドキ、縦に長いWEBページというのはよく見かけますね。そんなページでは必須の操作がスクロール。ブログやコンテンツメディアなどでは、このスクロールにより要素が画面内に表示される(ここではスクロールインと呼びます)ことが重要だったりします。

  • 一度表示した記事は二度表示しないようにする(既読管理)
  • インプレッション数をカウントする

今回はこの「ある要素がスクロールされ画面内に(ちょっとでも)表示されたとき」のイベントをキャッチする方法をまとめます。

概要

  1. scrollイベントをキャッチ
  2. 対象の要素のBoundingClientRectを取得
  3. BoundingClientRectが0以上&viewportHeight以下かどうかを判定
  4. 一度スクロールインをキャッチしたらアンバインド(※)

スクロールインイベントをキャッチしたいという場面は、インプレッション数のカウントのように「一度だけキャッチしたい」という例が多そうなので、スクロールインイベントを何度もキャッチしないように実装してみます。

ソースコード

(function(){
    window.addEventListener('scroll', this._scrollIn = (event) => {
        let viewportHeight = window.innerHeight;
        let targetTop = 
        	document.getElementById('target').getBoundingClientRect().top;
        if(0 < targetTop && targetTop <= viewportHeight) {
            console.log('scrollin!'); // do something
            window.removeEventListener('scroll', this._scrollIn);
        }
    });
})();

ポイント

4行目 element.getBoundingClientRect

element.getBoundingClientRectは対象のElementの絶対位置や幅・高さを持つオブジェクトを返してくれます。

let clientRect = document.getElementById('target').getBoundingClientRect();
console.log(clientRect); 

/* ClientRect {
 *   top: 402, 
 *   right: 368, 
 *   bottom: 654, 
 *   left: 7, 
 *   width: 361,
 *   (略)
 * }
 * /

このtopは画面(viewport)の左上を起点として数えるので、対象のElementが画面上部にピタッとくっついていたら0になります。つまり、viewportHeight(画面の高さ) > topとなっているときは、Elementが画面内に表示されていない(画面左上からの距離が画面の高さより大きい)ということになります。

7行目 アンバインド

特別なことはしていませんが、scrollInをキャッチしたときに走る処理の中で、その処理自身をアンバインドしています。今回は変数に代入して再帰的に呼び出しましたがarguments.calleeを使っても大丈夫です。

window.removeEventListener('scroll', arguments.callee);

おわり

こんな感じでできますよっと。一応動くサンプルのHTML/CSS/JavaScriptを。。 あ、ES6で書いてるので修正するなりトランスパイルするなりよろしくどうぞ。