目次

目次

スクロールイベント改善してみた

アバター画像
岡田暉
アバター画像
岡田暉
最終更新日2019/10/17 投稿日2019/10/17

4月にシステム部に異動してきました、岡田です。

業務的には、HTMLやCSSなどのデザイン部分と、JSもちょこちょこと触っています。

今回は、パフォーマンス改善のために、色々遅延読み込みをやってみよう ということで、IntersectionObserverを使ってみたので備忘録も兼ねてまとめてみました。

IntersectionObserverとは

特定の要素がViewPortに入っているかどうかと、その位置を取得できるAPIです。 スクロールイベントのように断続的に処理が行われるのではなく、 DOM要素が画面内に入った時と出た時のみにイベントを発生させることができます。

さらに、レスポンシブ時に要素の位置が変わっても、 その要素が画面内にあるかないかを見ているので、いちいち高さを再取得したりする必要が無くなります。

これだけでヘッダーをスクロールの途中で固定したり、ポップアップを出したりするのに使えることがわかりますが、 今回はいろんなサイトのパフォーマンス改善に繋がる、画像の遅延読み込みの例をご紹介します

使い方

画像の遅延読み込みをしたい場合は以下のようになります

  • HTML
<div class="hoge__block">
  <img class="hoge__image" data-src="imageurl"  alt="hogehoge">
  <img class="hoge__image" data-src="imageurl"  alt="hogehoge">
  <img class="hoge__image" data-src="imageurl"  alt="hogehoge">
  <img class="hoge__image" data-src="imageurl"  alt="hogehoge">
  <img class="hoge__image" data-src="imageurl"  alt="hogehoge">  
</div>

data-srcという属性に画像のURLを置いているので、 ページ読み込み時に、リクエストは発生しません。

  • JavaScript
const imgData = "data-src";
const options = {
  rootMargin: "100px 0px" // 要素が表示される100px前に読み込みを行う
}

let elements = [...document.querySelectorAll("img[" + imgData + "]")]; // data-srcの属性がついたimgタグを取得

let observer = new IntersectionObserver(callback, options); // IntersectionObserverのインスタンスを生成
elements.forEach(element => observer.observe(element)); // imgタグそれぞれを監視

/**
 * コールバック
 *
 * @param {object} entries // 監視している要素
 * @param {object} observer // 監視オブジェクト
 */
function callback(entries, observer) {
  for (const entry of entries) {
    if (!entry.isIntersecting) return; // 交差していないとき
    let element = entry.target;
    loading(element);
    observer.unobserve(element); // 監視を終了
  }
}

/**
 * 画像読み込み部分
 *
 * @param {object} element HTMl要素
 */
function loading(element) {
  let srcVal = element.getAttribute(imgData); // data-srcの値を取得

  if (srcVal) {
    element.src = srcVal; // 画像パスを設定
    element.onload = () => {
      element.removeAttribute(imgData); // data-src属性を削除
    }
  }
}

領域内に要素があるか否かを判定しているので、 それぞれで動作を変えたりして、面白い動きを作ったりするのも、 結構簡単にできそうです

また、callbackには以下のようなプロパティがあります

entry.time               // 変化が生じた時点でのタイムスタンプ
entry.rootBounds         // viewportの境界
entry.boundingClientRect // 監視している要素のviewportからの相対位置
entry.intersectionRect   // 交差領域の境界
entry.intersectionRatio  // 交差している領域の割合
entry.target             // target にされている要素

使ってみた感想

スクロールイベントは、スクロールのたびにイベントが発火してしまうため、 scroll junk(画面のカクつき)が起きてしまうこともあります。 そのため、パフォーマンスを改善するために、スクロールイベントの間引きをしたりする必要があって、めんどくさいです。

そしてIntersectionObserverでは、冒頭で述べた通り、 要素がViewPortに入ったか否かだけをみているので、イベントの発生頻度が大幅に減ります。 そのため、パフォーマンス改善にはもってこいになっています。

もしもスクロールで何かを実装しようとしている方がいらっしゃいましたら、 ぜひご参考にご利用ください。

IE11について

最後に大事なことですが IntersectionObserverさんはIEに対応していません、、、、、、、、、、、 そのため、以下のpolyfillはお忘れなく、、、

https://github.com/w3c/IntersectionObserver/tree/master/polyfill
アバター画像

岡田暉

写真好きのおじさんに見える、2017年入社のひよっこです。

目次