Intersection Observer API の使ってQiita風目次を作ってみる

Intersection Observer API の使ってQiita風目次を作ってみる

JavaScript

JavaScript の 要素の監視ができる Intersection Observer API の使い方をご紹介します。

この記事を書いた人

かみーゆ/フロントエンドエンジニア

資金ゼロからフィリピンで起業した海外ノマドエンジニア。IT業界10年以上でテクニカルディレクター(技術責任者)・エンジニア講師・ブリッジSEを経てLenzTechnologies Inc.を設立し、代表を務める。CMS concreteCMSエバンジェリスト。テックブログ以外も「磨耗しない人生」や「海外ノマド」のライフスタイルについて発信。好きなものは肉とハイボール。

Intersection Observer API とは?

MDN Web Docs によると祖先要素または文書の最上位のビューポートと交差する変化を非同期的に監視。
参照 : Intersection Observer API MDN

小難しいので平たく言うと、このAPIを使うと画面上に表示されている要素の変化を監視できます。

ちなみに Intersectionは交差、Observerは観察(者)という意味でです。

具体的にどんなことに使えるかと言うと、、、

  • ページがスクロールした際の画像やその他のコンテンツの遅延読み込み
  • インフィニティスクロール
  • 見出しの表示されたら対になる目次の項目への装飾(Qiita風)
  • ユーザーのアクションに応じた(ボタンクリック等)、処理

Can I Use によると IE、Opera Mini を除く最新ブラウザに対応しているとのこと。

その他の Observer API

他にもObserver APIはあります。

  • MutationObserver…要素の変化を監視
  • ResizeObserver…要素のリサイズを監視
  • PerformanceObserver…パフォーマンス測定イベントを監視し、ブラウザーのパフォーマンスタイムラインに記録されているので、新しいperformance entries の通知を受けるために使用。

基本の使い方

まずは監視する領域(observer)を作成します。コンストラクター(newするやつ)を呼び出してしきい値が一方向また他の方向に交差する度に実行されるコールバック関数を渡します。

const options = {
  root: document.querySelector('article'),
  rootMargin: '0px',
  threshold: 0.5
}
const observer = new IntersectionObserver(callback, options);
オプション説明
root観測したい要素が見えるかどうかを確認するための表示領域(ビューポート)として使用される要素。指定しない場合はブラウザーの表示領域(ビューポート)。CSS の margin プロパティのように指定可能。
rootMarginroot で指定した要素の周りのマージン。
rootしきい値。ターゲットがどのくらいの割合で見えている場合にコールバックを実行させるかの値。既定値は0なので1pxでも表示されたら実行。50%の場合は 0.5。25%毎に実行する場合は[0, 0.25, 0.5, 0.75, 1]という感じで配列で指定。

ターゲットとなる要素を指定します。

const target = document.querySelector('.target');
observer.observe(target);

.targetが表示されたらコールバック関数が実行されます。

let callback = (entries, observer) => {
  entries.forEach((entry) => {
    console.log(`ターゲット要素が表示されたよ`)
  }
}

IntersectionObserverEntryから取得できるプロパティです。プロパティは操作不可で読み取り専用です。

取得できるプロパティ説明
time交差が記録された時刻
targetターゲット要素
rootBounds交差を監視しているルート
intersectionRatiointersectionRect と boundingClientRect の比率
intersectionRect対象の表示領域
boundingClientRectビューポートに対する相対位置
isIntersecting論理値で、対象要素が、交差を監視しているルートを超えたどうか
isVisibleターゲットの要素が表示されているか

実際Qiita風の目次を作ってみる

簡易的ですが、Qiita風の目次を作ってみます。該当の見出しが表示されたら、目次のリンクの背景の色を変えます。

<main>
  <article>
  <h2 id="h1">見出し1個めです</h2>
  <p>ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
  <h2 id="h2">見出し1個めです</h2>
  ...
  <article>
  <div class="table-of-content">目次
    <ol>
      <li><a href="#h1">見出し1個めです</a></li>
      <li><a href="#h2">見出し2個めです</a></li>
      ...
    </ol>
  </div>
</main>

JavaScriptです。isIntersectingプロパティがtrueになったら、IDとリンクURLが一致するか判定。class="current"を付与し、そうじゃない場合はクラスを削除します。

JavaScript
const tocList = document.querySelectorAll('.table-of-content a')

const callback = (entries, observer) => {
  entries.forEach((entry) => {
    if(entry.isIntersecting){
      tocList.forEach((item, num) => {
        if(`#${entry.target.id}` == item.getAttribute('href')) {
          item.classList.add('current')
        } else {
          item.removeAttribute('class')
        }
      })
    }
  })
}

const options = {
  root: null,
  rootMargin: '0px',
  threshold: 0.5
}

const observer = new IntersectionObserver(callback, options);

//NodeListを配列に
const targets = Array.from(document.querySelectorAll('h2'))

targets.forEach(target => observer.observe(target))

Intersection Observer API sample

まとめ・ Intersection Observer APIを使えばscroll系のJSを使わずカンタンに実装できる

昔はこんな感じの実装はscroll系のJSを使って処理していましたがIntersection Observer APIを使えば手軽に実装できますね。

Intersection Observer APIを使えばパララックスもカンタンに実装できます。

この記事が皆さんの、Web制作の一助となれば幸いです。

最後までお読みいただきありがとうございました。

メソッド説明
disconnect()対象を監視することを停止
unobserve()ターゲット要素の監視を停止
observe()ターゲット要素の監視
takeRecords()ターゲット要素のうち、前回交差状態がチェックされたもしくは自動的にオブザーバーのコールバックが呼び出された以降に交差状態が変化した要素の配列を返す。コールバックを使用してこれらの変更を監視している場合は、このメソッドを呼び出す必要はない。かつ処理待ちの交差リストをクリアしてしまうのでコールバックが実行されない

参照 : Intersection Observer API MDN

関連記事もあわせてお読みください

Mutation Observer を使ってiframe内の要素を監視する

要素の監視ができる、Mutation Observer APIの使い方をご紹介。iframe内の変化を感知して、親要素の...