みかづきブログ・カスタム

基本的にはちょちょいのほいです。

交差オブザーバー API (Intersection Observer API) を使ってページスクロールに連動したイベントを実行する 🖱️

DEMO

一昔前に、ページスクロールに連動してDOMをあれこれしたい場合、documentのスクロールイベントのコールバックで、対象となるDOMが範囲内に入っているか否かを判定する必要がありました。

developer.mozilla.org

なので、僕もかつてページのスクロール量を管理するカスタムフックを作って、その中で、対象となるDOMを監視する実装をしていました。

blog.kimizuka.org

が。最近、交差オブザーバー API (Intersection Observer API) たるものを知りまして、ターゲットとなる要素がビューポートに交差しているか否かが簡単に判定できることに気づきました。

developer.mozilla.org

それが、一番上に記載しているDEMOです。

干支がビューポートと交差した際、サイズが2倍に変化するように実装しています。
JSFiddleでDEMOをつくってみて気づいたのですが、iframe自体がスクロールした際にも発火してますね。
交差オブザーバー APIを使わずにこれを実現しようとすると、なかなか骨が折れる気がします。

ソースコード

HTML

<ol>
  <li>
    <p>🐭</p>
  </li>
  <li>
    <p>🐮</p>
  </li>
  <li>
    <p>🐯</p>
  </li>
  <li>
    <p>🐰</p>
  </li>
  <li>
    <p>🐲</p>
  </li>
  <li>
    <p>🐍</p>
  </li>
  <li>
    <p>🐴</p>
  </li>
  <li>
    <p>🐏</p>
  </li>
  <li>
    <p>🐵</p>
  </li>
  <li>
    <p>🐔</p>
  </li>
  <li>
    <p>🐶</p>
  </li>
  <li>
    <p>🐗</p>
  </li>
</ol>

JavaScript

const observer = new IntersectionObserver(updateEntry, {
  threshold: 1 // 対象全てが交差した際
});

[].slice.call(document.querySelectorAll('li')).forEach((elm) => {
  observer.observe(elm);
});

function updateEntry(entries) {
  entries.forEach((entry) => {
    entry.target.dataset.isShow = entry.isIntersecting; // 交差しているか否かをカスタムデータ属性に代入
  });
}

SCSS

body {
  font-size: 80px;
}

ol {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: 80px auto;
}

li {
  p {
    transform: scale(1);
    transition: transform .4s ease-in-out; // トランジションをつける
  }

  + li {
    margin-top: 80px;  
  }

  &[data-is-show='true'] {
    p {
      transform: scale(2);  // data-is-showがtrueになった際にサイズを倍にする
    }
  }
}

という感じです。
めちゃめちゃ簡単にページスクロールに連動したイベントが管理できて嬉しい限り。
ブラウザの対応状況をみても、基本的な機能は実践投入しても問題なさそうです。

developer.mozilla.org

対応状況に不安が残る場合は、一応polyfillも用意されているので、こちらを使えばより安心です。

github.com

注意事項

対象となるDOMとビューポートの交差にはtransformが考慮されます。
なので、対象となるDOM自身を変形させてしまうと思わぬ挙動になることがあるので注意が必要です。

こちらのDEMOは、対象となるDOM自身を拡大しているため、変形した際に交差したり交差しなかったりしてしまっています。