インターセクションオブザーバー

W3C作業草案,

この文書の詳細
このバージョン:
https://www.w3.org/TR/2023/WD-intersection-observer-20231018/
最新の公開バージョン:
https://www.w3.org/TR/intersection-observer/
編集者ドラフト:
https://w3c.github.io/IntersectionObserver/
以前のバージョン:
履歴:
https://www.w3.org/standards/history/intersection-observer/
テストスイート:
http://w3c-test.org/intersection-observer/
フィードバック:
GitHub
編集者:
(Google)
(Mozilla)
(Google)
前編集者:
(Google)

概要

この仕様は、DOM要素(「ターゲット」)の可視性と位置を、包含要素またはトップレベルのビューポート(「ルート」)に対して把握するために使用できるAPIについて説明します。位置情報は非同期で提供され、要素の可視性の把握や、DOMコンテンツの事前読み込み・遅延読み込みの実装に役立ちます。

この文書のステータス

このセクションでは、公開時点の文書のステータスについて説明します。現行のW3C出版物一覧やこの技術レポートの最新版は、W3C技術レポート索引(https://www.w3.org/TR/)で確認できます。

この文書は、Web Applications Working Groupにより作業草案として公開されました。本書はW3C勧告となることを目的としています。

この文書は、Web Applications Working Groupによって、勧告トラックを用いて作業草案として公開されました。 仕様へのフィードバックやコメントは歓迎します。GitHub issuesをご利用ください。過去の議論はpublic-webapps@w3.org アーカイブで閲覧できます。

作業草案として公開されていることは、W3Cおよびその会員による承認を意味しません。本書は草案文書であり、今後随時更新・置換・廃止される可能性があります。本書を進行中の作業以外として引用するのは不適切です。

この文書は、W3C特許ポリシーに基づき、グループによって作成されました。W3Cは、グループ成果物に関連する特許開示の公開リストを管理しています。また、そのページでは特許の開示方法についても説明されています。特許の実際の知識を有し、その特許に必要なクレームが含まれていると考える場合は、W3C特許ポリシー第6節に従い情報を開示する必要があります。

この文書は、2023年6月12日W3Cプロセス文書に従って管理されています。

1. はじめに

ウェブの従来の位置計算メカニズムは、DOMの状態を明示的にクエリすることに依存しており、これは(高コストな)スタイル再計算やレイアウトを引き起こすことで知られています。そして、多くの場合、この情報を継続的にポーリングすることによる大きなパフォーマンスのオーバーヘッドの原因となっています。

しかし、これらの挙動に依存した一般的な慣習が発展してきました。例として、以下のようなものがあります:

これらのユースケースにはいくつかの共通点があります:

  1. これらは、個々の要素の状態を他の要素(またはグローバルなビューポート)に対する受動的な「クエリ」として表現できます。

  2. 厳密なレイテンシ要件を課しません。つまり、情報は非同期(例えば別スレッドから)で提供されても問題ありません。

  3. これらは既存のウェブプラットフォーム機能のほぼすべての組み合わせで十分にサポートされていないため、広く使われているにも関わらず開発者に多大な労力を要求します。

注目すべき非目的は、実際に表示された内容についてのピクセル単位の正確な情報です(これは、フィルタ・webgl・その他の機能の影響下で、特定のブラウザアーキテクチャでは効率的に取得するのが非常に困難な場合があります)。 これらすべてのシナリオにおいて、情報は少し遅れて、完璧な合成結果データがなくても有用です。

Intersection Observer APIは、上記の問題に対処し、開発者がある要素の位置を他の要素やグローバルなビューポートに対して非同期にクエリできる新しい方法を提供します。 非同期での情報提供は、高コストなDOMやスタイルのクエリ、継続的なポーリング、カスタムプラグインの使用を不要にします。 これらの手法を排除することで、アプリケーションのCPU・GPU・エネルギー消費を大幅に削減できます。

var observer = new IntersectionObserver(changes => {
  for (const change of changes) {
    console.log(change.time);               // 変更が発生した時刻のタイムスタンプ
    console.log(change.rootBounds);         // ルートの切り取られていない領域
    console.log(change.boundingClientRect); // target.getBoundingClientRect()
    console.log(change.intersectionRect);   // boundingClientRect(包含ブロックの先祖でクリップされ、rootBoundsと交差)
    console.log(change.intersectionRatio);  // intersectionRectの面積とboundingClientRectの面積の比率
    console.log(change.target);             // Elementターゲット
  }
}, {});

// 特定のターゲットElementのインターセクションイベントを監視する。
observer.observe(target);

// 特定のターゲットElementのインターセクションイベントの監視を停止する。
observer.unobserve(target);

// すべてのターゲット要素のしきい値イベントの監視を停止する。
observer.disconnect();

2. インターセクションオブザーバー

Intersection Observer APIは、開発者が ターゲットDOM 要素の可視性と位置をインターセクションルートに対して把握できるようにします。

2.1. IntersectionObserverCallback

callback IntersectionObserverCallback = undefined (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer);

このコールバックは、ターゲットの インターセクションルートとの交差状態に変更があった場合、処理モデルに従って呼び出されます。

2.2. IntersectionObserverインターフェース

IntersectionObserver インターフェースは、インターセクションルートと1つ以上の ターゲット Element の交差状態の変化を監視するために使用できます。

インターセクションルートは、IntersectionObserver の root 属性が null でなければその値です。 null の場合は、トップレベル閲覧コンテキストの document ノードとなり、 これは 暗黙的ルートと呼ばれます。

IntersectionObserver で root が null でないものは 明示的ルートオブザーバー と呼ばれます。 この場合、ターゲット Element は 包含ブロックチェーン内で root の子孫である必要があります。 IntersectionObserver で root が null のものは 暗黙的ルートオブザーバー と呼ばれます。 暗黙的ルートオブザーバーの有効な ターゲットは、 トップレベル閲覧コンテキスト内のすべての Element、 および トップレベル閲覧コンテキストの子孫閲覧コンテキストのリストに含まれる ネストされた閲覧コンテキスト内のすべての Element です。

暗黙的ルートオブザーバーを扱う際、APIは次の区別を行います。 ターゲットの 関連設定オブジェクト の オリジン が 同一オリジンドメインであり、 トップレベルオリジンと一致している場合は 同一オリジンドメインターゲット となります。 それ以外は クロスオリジンドメインターゲット となります。 明示的ルートオブザーバーの ターゲット も 同一オリジンドメインターゲット となります。 これは ターゲットが document と インターセクションルートが同じである必要があるためです。

注: MutationObserverでは、 MutationObserverInit のオプションは observe() に渡されますが、IntersectionObserver では コンストラクタに渡されます。MutationObserverの場合、各Node ごとに異なるフィルタ属性セットを持つことができますが、 IntersectionObserverでは、同じオプションセットで複数のターゲットを追跡するために単一のオブザーバーを使うことも、各ターゲットごとに異なるオブザーバーを使うこともできます。 rootMargin やthreshold を各ターゲットごとに設定するのは、追加ユースケースを解決せず複雑さが増すだけです。必要に応じて、将来的にobserve()ごとのオプション提供も検討されます。

[Exposed=Window]
interface IntersectionObserver {
  constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
  readonly attribute (Element or Document)? root;
  readonly attribute DOMString rootMargin;
  readonly attribute DOMString scrollMargin;
  readonly attribute FrozenArray<double> thresholds;
  undefined observe(Element target);
  undefined unobserve(Element target);
  undefined disconnect();
  sequence<IntersectionObserverEntry> takeRecords();
};
new IntersectionObserver(callback, options)

新しいIntersectionObserverの初期化 アルゴリズムをcallbackとoptionsを指定して実行した結果を返します。

observe(target)

ターゲットElementの監視アルゴリズムをthisとtarget で実行します。

unobserve(target)

ターゲットElementの監視解除アルゴリズムをthisとtarget で実行します。

注: MutationObserver にはunobserve() は実装されていません。 IntersectionObserver では、 unobserve() は遅延読み込みユースケースへの対応です。targetが表示された後は、追跡する必要がありません。 全targetでdisconnect() を呼び出して残りをobserve() するか、各targetごとに別のIntersectionObserver を作るのは手間です。

disconnect()

thisの内部[[ObservationTargets]] スロット内の各targetについて:

  1. targetの内部[[RegisteredIntersectionObservers]] スロットから、IntersectionObserverRegistration レコードでobserver プロパティがthisに一致するものを削除します。

  2. thisの内部[[ObservationTargets]] スロットからtargetを削除します。

takeRecords()
  1. thisの内部[[QueuedEntries]] スロットのコピーをqueueとします。

  2. thisの内部[[QueuedEntries]] スロットをクリアします。

  3. queueを返します。

root, 型は(Element or Document)、readonly、nullable

root はIntersectionObserver のコンストラクタに渡された値、渡されていなければnullです。

rootMargin, 型はDOMString、readonly

ルート交差矩形に適用されるオフセットで、 交差判定に使うボックスを拡張・縮小します。これらのオフセットは同一オリジンドメインターゲットの 処理時のみ適用され、クロスオリジンドメインターゲット には無視されます。

取得時は、[[rootMargin]] の各要素をスペース区切りで直列化した結果を返します。ピクセル長は数値+"px"、パーセンテージは数値+"%"となります。 これは、options.rootMargin として IntersectionObserver コンストラクタに渡された値と必ずしも一致しません。IntersectionObserver コンストラクタに rootMargin が渡されなかった場合は"0px 0px 0px 0px"になります。

scrollMargin, 型はDOMString、readonly

インターセクションルートからターゲットまでのパス上の scrollportに適用されるオフセットで、交差判定に使うclip矩形を拡張・縮小します。

取得時は、[[scrollMargin]] の各要素をスペース区切りで直列化した結果を返します。ピクセル長は数値+"px"、パーセンテージは数値+"%"となります。 これはoptions.scrollMargin として IntersectionObserver コンストラクタに渡された値と必ずしも一致しません。IntersectionObserver コンストラクタに scrollMargin が渡されなかった場合は"0px 0px 0px 0px"になります。

thresholds, 型はFrozenArray<double>、readonly

しきい値のリストで、数値昇順に並んでいます。各しきい値は監視対象の交差面積/バウンディングボックス面積の比率です。 しきい値をまたぐ度に通知が発生します。 options.threshold が IntersectionObserver コンストラクタに指定されなかった場合や、シーケンスが空の場合は[0]となります。

Element のcomputed styleでoverflowプロパティにより内容が要素のpadding edgeでクリップされる場合は、コンテントクリップを持つと定義されます。

ルート交差矩形は、IntersectionObserver でターゲットと比較する矩形です。

もしIntersectionObserver が暗黙ルートオブザーバーなら
ルートはトップレベル閲覧コンテキストのdocument であるものとして扱い、以下のdocumentに関するルールを適用します。
もしインターセクションルートがdocument なら
documentの ビューポートのサイズ(この処理段階に到達するのは、documentが完全にアクティブな場合のみ)。
それ以外でインターセクションルートがコンテントクリップを持つ場合
要素のpadding area。
それ以外の場合
インターセクションルートのバウンディングボックス取得の結果。

ルート交差矩形の計算時、 同一オリジンドメインターゲットの場合は、 IntersectionObserverの [[rootMargin]] スロットのオフセットで拡張します。CSSのmarginプロパティのように、 4つの値はそれぞれ上・右・下・左の拡張量で、正の値は外側への拡張です。 パーセンテージは拡張前の矩形の幅に対して解決されます。

注: rootMargin はインターセクションルート自体にのみ適用されます。 ターゲットElement がインターセクションルート以外の祖先によってクリップされている場合、そのクリップにはrootMargin の影響はありません。

スクロールポートにスクロールマージンを適用する

scrollportの交差矩形を 同一オリジンドメインターゲットに対して計算する場合、 矩形は IntersectionObserverの [[scrollMargin]] スロットのオフセットに従って CSSのmarginプロパティと同様の方法で拡張されます。 4つの値は、それぞれ上、右、下、左の端がどれだけオフセットされるかを示し、正の長さは外側へのオフセットを意味します。 パーセンテージは拡張前の矩形の幅に対して解決されます。

これらのオフセットは同一オリジンドメインターゲットを扱う場合のみ適用されます。 クロスオリジンドメインターゲットの場合は無視されます。

Note: scrollMargin は、ターゲットがすべてのスクロール可能な先祖によって インターセクションルートまでクリッピングされる際に影響します。 scrollMargin と rootMargin の両方がスクロール可能なインターセクションルートの矩形に適用されます。

Note: ルート交差矩形およびscrollport交差矩形はピンチズームの影響を受けず、調整されていないビューポートを報告します。 これはピンチズームの意図(虫眼鏡のように動作し、レイアウトを変更しない)と一致します。

マージンをパースする(ルートまたはスクロール) 入力文字列marginStringから 4つのピクセル長またはパーセンテージのリスト、もしくは失敗を返す:

  1. コンポーネント値のリストをパースする marginString の結果をtokensとして保存する。

  2. tokensからすべての空白トークンを取り除く。

  3. tokensの長さが4より大きい場合、失敗を返す。

  4. tokensに要素が0個の場合、tokensを["0px"]に設定する。

  5. tokens内の各tokenを置き換える:

    • tokenが絶対長の次元トークンなら、同等のピクセル長に置き換える。

    • tokenが<percentage>トークンなら、同等のパーセンテージに置き換える。

    • それ以外の場合は失敗を返す。

  6. tokensが1要素の場合、その要素を3つ複製してtokensへ追加する。 2要素の場合は各要素を複製して追加する。 3要素の場合は2番目の要素を複製して追加する。

  7. tokensを返す。

2.3. IntersectionObserverEntryインターフェース

[Exposed=Window]
interface IntersectionObserverEntry {
  constructor(IntersectionObserverEntryInit intersectionObserverEntryInit);
  readonly attribute DOMHighResTimeStamp time;
  readonly attribute DOMRectReadOnly? rootBounds;
  readonly attribute DOMRectReadOnly boundingClientRect;
  readonly attribute DOMRectReadOnly intersectionRect;
  readonly attribute boolean isIntersecting;
  readonly attribute double intersectionRatio;
  readonly attribute Element target;
};

dictionary IntersectionObserverEntryInit {
  required DOMHighResTimeStamp time;
  required DOMRectInit? rootBounds;
  required DOMRectInit boundingClientRect;
  required DOMRectInit intersectionRect;
  required boolean isIntersecting;
  required double intersectionRatio;
  required Element target;
};
boundingClientRect, 型 DOMRectReadOnly、読み取り専用

DOMRectReadOnly は、targetの バウンディングボックスを取得して得られます。

intersectionRect, 型 DOMRectReadOnly、読み取り専用

boundingClientRect を、target の各祖先の クリップ矩形(ただし root を除く)で交差させ、 さらに root の交差矩形 と交差させます。 この値は、target が root の交差矩形内で実際に見える部分を表します。

isIntersecting, 型 boolean、読み取り専用

target が rootと交差していれば true、そうでなければ false です。 このフラグにより、 IntersectionObserverEntry が 交差状態から非交差状態への遷移を示す場合と、 IntersectionObserverEntry が 非交差状態から交差状態への遷移(交差矩形の面積がゼロの場合、例えば端同士が接する交差や boundingClientRect の面積がゼロの場合)を区別できます。

intersectionRatio, 型 double、読み取り専用

boundingClientRect の面積がゼロでない場合、 intersectionRect の面積を boundingClientRect の面積で割った比率になります。 それ以外の場合、isIntersecting が true なら 1、そうでなければ 0 となります。

rootBounds, 型 DOMRectReadOnly、読み取り専用、nullable

同一オリジンドメインターゲットの場合、 root の交差矩形 になります。 それ以外の場合は null です。 ターゲットが 閲覧コンテキストが intersection root と異なる場合、 この値は boundingClientRect や intersectionRect と 異なる座標系になります。

target, 型 Element、読み取り専用

Element は、 intersection root との交差状態が変更された要素です。

time, 型 DOMHighResTimeStamp、読み取り専用

この属性は、通知を生成した IntersectionObserver インスタンスに関連付けられたグローバルオブジェクトの タイムオリジン を基準とした、 交差が記録された時刻に対応する DOMHighResTimeStamp を返します。

2.4. IntersectionObserverInit辞書

dictionary IntersectionObserverInit {
  (Element or Document)?  root = null;
  DOMString rootMargin = "0px";
  DOMString scrollMargin = "0px";
  (double or sequence<double>) threshold = 0;
};
root, 型 (Element or Document)、nullable、デフォルトは null

交差判定に使用する root です。 指定されていない場合は 暗黙的ルートを使用します。

rootMargin, 型 DOMString、デフォルトは "0px"

CSS の margin プロパティに類似しており、 1~4個のコンポーネントからなる文字列です。 各コンポーネントは 絶対長またはパーセンテージです。

"5px"                // すべてのマージンが 5px
"5px 10px"           // 上下 = 5px、左右 = 10px
"-10px 5px 8px"      // 上 = -10px、左右 = 5px、下 = 8px
"-10px -5px 5px 8px" // 上 = -10px、右 = -5px、下 = 5px、左 = 8px
scrollMargin, 型 DOMString、デフォルトは "0px"

rootMargin と同様で、 1~4個のコンポーネントからなる文字列です。 各コンポーネントは 絶対長またはパーセンテージです。

例については上記 rootMargin を参照してください。

threshold, 型 (double or sequence<double>)、デフォルトは 0

コールバックをトリガーするしきい値(複数可)のリストです。 intersectionRect の面積が任意のしきい値以上から未満に変化した場合、またはその逆の場合、コールバックが呼び出されます。

しきい値は [0, 1.0] の範囲で、バウンディングボックスを取得した target の矩形面積に対する割合を表します。

Note: 0.0 は実質的に「1ピクセル以上が非ゼロ」の意味になります。

3. 処理モデル

このセクションでは、Intersection Observer APIを実装する際にユーザーエージェントが実行すべき手順を概説します。

3.1. 内部スロット定義

3.1.1. ドキュメント

各document は、IntersectionObserverTaskQueuedフラグを持ち、初期値はfalseです。

3.1.2. 要素

Element オブジェクトは、内部[[RegisteredIntersectionObservers]] スロットを持ち、初期値は空のリストです。 このリストはIntersectionObserverRegistrationレコードを保持します。 これらは、observerプロパティ(IntersectionObserverを保持)、 previousThresholdIndexプロパティ (observerのthresholdsプロパティの長さの範囲内で-1からの数値)、 previousIsIntersectingプロパティ (boolean値)を持ちます。

3.1.3. IntersectionObserver

IntersectionObserver オブジェクトは、内部[[QueuedEntries]]および[[ObservationTargets]]スロット(ともに初期値は空リスト)、 [[callback]]スロット (IntersectionObserver(callback, options)で初期化)を持ちます。 また、内部[[rootMargin]]および[[scrollMargin]]スロット(4つのピクセル長またはパーセンテージのリスト)も持ちます。

3.2. アルゴリズム

3.2.1. 新しいIntersectionObserverの初期化

新しいIntersectionObserverを初期化するには、IntersectionObserverCallback callbackとIntersectionObserverInit 辞書optionsを指定して、次の手順を実行します:

  1. thisを新しいIntersectionObserver オブジェクトとする

  2. thisの内部[[callback]] スロットにcallbackをセットする。

  3. options.rootMarginから マージンをパースすることを試みる。 リストが返された場合、 thisの内部[[rootMargin]] スロットにそれをセットする。 そうでなければ、throwでSyntaxError 例外を投げる。

  4. options.scrollMarginから マージンをパースすることを試みる。 リストが返された場合、 thisの内部[[scrollMargin]] スロットにそれをセットする。 そうでなければ、throwでSyntaxError 例外を投げる。

  5. thresholdsをoptions.thresholdと同じリストにする。

  6. thresholds内に0.0未満または1.0超の値があれば、 throwでRangeError 例外を投げる。

  7. thresholdsを昇順にソートする。

  8. thresholdsが空なら0を追加する。

  9. thresholds 属性のgetterはこの昇順のthresholdsリストを返す。

  10. thisを返す。

3.2.2. ターゲットElementの監視

ターゲットElementの監視は、IntersectionObserver observerとElement targetを指定して、次の手順を実行します:

  1. targetがobserverの内部[[ObservationTargets]] スロットに含まれていれば、returnする。

  2. intersectionObserverRegistrationを IntersectionObserverRegistration レコードとして、 observerプロパティにobserver、 previousThresholdIndexプロパティに-1、 previousIsIntersectingプロパティにfalseをセットする。

  3. intersectionObserverRegistrationをtargetの内部[[RegisteredIntersectionObservers]] スロットに追加する。

  4. targetをobserverの内部[[ObservationTargets]] スロットに追加する。

3.2.3. ターゲットElementの監視解除

ターゲットElementの監視解除は、IntersectionObserver observerとElement targetを指定して、次の手順を実行します:

  1. targetの内部[[RegisteredIntersectionObservers]] スロットから、IntersectionObserverRegistration レコード(observerプロパティがthisと等しいもの)を削除する(存在する場合)。

  2. thisの内部[[ObservationTargets]] スロットからtargetを削除する(存在する場合)。

3.2.4. Intersection Observerタスクのキューイング

IntersectionObserverタスクソースは タスクソースであり、 § 3.2.5 Intersection Observerへの通知のタスクをスケジューリングするために使われます。

intersection observerタスクをキューイングするには、 document documentを指定して、次の手順を実行します:

  1. documentのIntersectionObserverTaskQueuedフラグがtrueなら、returnする。

  2. documentのIntersectionObserverTaskQueuedフラグをtrueにセットする。

  3. documentの イベントループに関連付けられた IntersectionObserverタスクソース上で タスクをキューイングし、 Intersection Observerへの通知を実行する。

3.2.5. Intersection Observerへの通知

Intersection Observerへの通知は、 document documentについて、次の手順を実行します:

  1. documentのIntersectionObserverTaskQueuedフラグをfalseにセットする。

  2. notify listを、documentのDOMツリー内にrootを持つすべての IntersectionObserverのリストとする。

  3. notify list内の各IntersectionObserverオブジェクトobserverについて、以下を実行:

    1. observerの内部[[QueuedEntries]]スロットが空なら、次へ。

    2. queueをobserverの内部[[QueuedEntries]]スロットのコピーとする。

    3. observerの内部[[QueuedEntries]]スロットをクリアする。

    4. callbackをobserverの内部[[callback]]スロットの値とする。

    5. callbackを、第1引数にqueue、第2引数にobserver、 callback this valueとしてobserverを指定して呼び出す。 例外が投げられた場合、例外を報告する。

3.2.6. IntersectionObserverEntryのキューイング

IntersectionObserverEntryをキューイングするには、 IntersectionObserver observer、document document、DOMHighResTimeStamp time、DOMRect各種 rootBounds、boundingClientRect、intersectionRect、isIntersectingフラグ、 Element targetを指定して、次の手順を実行します:

  1. IntersectionObserverEntry を、time、rootBounds、boundingClientRect、 intersectionRect、isIntersecting、targetを引数として生成する。

  2. それをobserverの内部[[QueuedEntries]] スロットに追加する。

  3. intersection observerタスクをキューイングする(document)。

3.2.7. ターゲットElementとルートの交差領域の計算

交差領域を計算するには、 target targetと intersection root rootを指定して、次の手順を実行します:

  1. intersectionRectをtargetのバウンディングボックスを取得した結果とする。

  2. containerをtargetの包含ブロックとする。

  3. containerがrootでない間:

    1. containerがネストされた閲覧コンテキストのdocumentなら、 intersectionRectをそのdocumentの ビューポートでクリップし、 containerをその閲覧コンテキストコンテナに更新する。

    2. intersectionRectをcontainerの座標空間にマップする。

    3. containerがスクロールコンテナなら、 IntersectionObserverの [[scrollMargin]]を containerのクリップ矩形にスクロールポートにスクロールマージンを適用するに従って適用する。

    4. containerがcontent clipやcssのclip-pathプロパティを持つ場合、 containerのクリップをintersectionRectに適用する。

    5. containerが閲覧コンテキストのルート要素なら、 containerをその閲覧コンテキストのdocumentに更新する。 それ以外の場合は、containerの包含ブロックに更新する。

  4. intersectionRectをrootの座標空間にマップする。

  5. intersectionRectをrootの交差矩形と交差させて更新する。

  6. intersectionRectをtargetを含むdocumentの ビューポートの座標空間にマップする。

  7. intersectionRectを返す。

3.2.8. インターセクション監視の更新手順の実行

インターセクション監視の更新手順を実行するには、Document documentとタイムスタンプtimeを指定して、以下の手順を実行します:

  1. observer listを、documentのDOMツリー内にIntersectionObserverのrootが存在するすべてのリストとする。 トップレベル閲覧コンテキストの場合、暗黙的ルートオブザーバーも含まれます。

  2. observer list内の各observerについて:

    1. rootBoundsをobserverのルート交差矩形とする。

    2. observerの内部[[ObservationTargets]]スロット内の各targetについて、observe()が呼ばれた順で処理する:

      1. 以下を初期化:

        • thresholdIndex = 0

        • isIntersecting = false

        • targetRect = DOMRectReadOnly(x, y, width, heightを0に設定)

        • intersectionRect = DOMRectReadOnly(x, y, width, heightを0に設定)

      2. intersection rootが暗黙的ルートでない場合、 かつtargetがintersection rootと同じdocument内にない場合は、ステップ11へスキップ。

      3. intersection rootがElementの場合、 targetがintersection rootの 包含ブロックチェーンの子孫でなければ、ステップ11へスキップ。

      4. targetRectにDOMRectReadOnlyとして バウンディングボックスを取得した target の値をセットする。

      5. intersectionRectをtargetとobserverのintersection rootで交差領域を計算するアルゴリズムの結果とする。

      6. targetArea = targetRectの面積。

      7. intersectionArea = intersectionRectの面積。

      8. isIntersecting = targetRectとrootBoundsが交差または端同士が接していればtrue(交差面積がゼロでも、rootBoundsやtargetRectがゼロ面積でもtrue)。

      9. targetAreaがゼロでなければ、intersectionRatio = intersectionArea / targetArea。
        それ以外の場合、isIntersectingがtrueなら1、falseなら0。

      10. thresholdIndexを、observer.thresholdsの 各値がintersectionRatioを超える最初のインデックス、 もしくはintersectionRatioが observer.thresholds の最後の要素以上なら observer.thresholds の長さとする。

      11. intersectionObserverRegistrationをtargetの内部[[RegisteredIntersectionObservers]]スロット内で observerプロパティがobserverと等しい IntersectionObserverRegistration レコードとする。

      12. previousThresholdIndexを intersectionObserverRegistrationのpreviousThresholdIndexプロパティとする。

      13. previousIsIntersectingを intersectionObserverRegistrationのpreviousIsIntersectingプロパティとする。

      14. thresholdIndexがpreviousThresholdIndexと一致しない、またはisIntersectingがpreviousIsIntersectingと一致しない場合は、IntersectionObserverEntryのキューイングを行い、 observer、time、rootBounds、targetRect、intersectionRect、isIntersecting、targetを渡す。

      15. thresholdIndexをintersectionObserverRegistrationのpreviousThresholdIndexプロパティに代入する。

      16. isIntersectingをintersectionObserverRegistrationのpreviousIsIntersectingプロパティに代入する。

3.3. IntersectionObserverのライフタイム

IntersectionObserver は、次の両方の条件を満たすまで存続します:

IntersectionObserver は、observerのunobserve()メソッドがターゲットを引数に呼ばれるか、observerのdisconnect()が呼ばれるまで、ターゲットの監視を継続します。

3.4. 外部仕様との統合

3.4.1. HTML処理モデル: イベントループ

Intersection Observerの処理ステップは、「レンダリングの更新」ステップ内のサブステップとして、 HTMLイベントループ処理モデルに存在します。

3.4.2. 初期IntersectionObserverターゲットの保留

document は、以下の条件を満たすIntersectionObserverが1つ以上存在する場合、初期IntersectionObserverターゲットの保留があるとされます:
  1. observerのroot がdocument内に存在する(トップレベル閲覧コンテキストの場合、暗黙的ルートオブザーバーも含む)。
  2. observerの[[ObservationTargets]] スロットに、まだIntersectionObserverEntryがキューイングされていないtargetが1つ以上存在する。

HTMLイベントループ処理モデルの「レンダリングの更新」ステップで、 「不要なレンダリング」ステップには以下の要件を追加してレンダリング更新のスキップ条件とします:

4. アクセシビリティに関する考慮事項

このセクションは規範的ではありません。

IntersectionObserver仕様(本書)のコア部分に関して、既知のアクセシビリティの考慮事項はありません。 ただし、本仕様を活用・参照する関連仕様や提案には、それぞれアクセシビリティに関する考慮事項が存在する可能性があります。特に、HTML § 2.5.7 レイジーローディング属性やCSS Containment 2 § 4 要素の内容を完全に抑制する: content-visibilityプロパティの仕様は、HTML § 6.9 ページ内検索、 HTML § 6.6.3 tabindex属性、および 空間ナビゲーション に影響を及ぼす可能性があります。

5. プライバシーとセキュリティ

このセクションは規範的ではありません。

このAPIに関連する主なプライバシーの懸念は、クロスオリジンiframe(すなわち クロスオリジンドメインターゲットの場合)で実行されるコードに提供される情報に関するものです。特に:

なお、IntersectionObserver以前は、 ウェブ開発者は他のAPIを非常に巧妙(かつ時に不適切)な方法で利用して、 IntersectionObserverで得られる情報を引き出していました。 実際のところ、このAPIは他の手段ですでに取得可能だった情報以外を公開していません。

さらに、IntersectionObserverは DOMHighResTimeStampを使用しますが、これ自体もプライバシー・セキュリティ上の考慮事項があります。ただし、 IntersectionObserverがタイミング関連の攻撃に脆弱である可能性は低いです。 タイムスタンプはレンダリング更新ごとに最大1回しか生成されず(§ 3.4.1 HTML処理モデル: イベントループ参照)、 一般的なタイミング攻撃には頻度が不十分です。

6. 国際化

このセクションは規範的ではありません。

国際化に関する既知の問題はありません。

7. 謝辞

技術的な意見や提案を寄せて仕様の改善に貢献してくださったすべてのコントリビューターに感謝します。

適合性

文書の規約

適合要件は、記述的な断言とRFC 2119の用語の組み合わせで表現されます。 規範的な部分で使われる “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL” というキーワードは、 RFC 2119で説明されている通りに解釈されます。 ただし、可読性のため、本仕様書ではこれらの単語はすべて大文字では記載されていません。

この仕様書の本文は、明示的に非規範的と記載されたセクション・例・注記を除いてすべてが規範的です。[RFC2119]

この仕様書の例は「例えば」で始まるか、class="example"で規範的な本文と区別されています。 以下は例です:

これは参考例の一例です。

参考注記は「Note」で始まり、class="note"で規範的な本文と区別されています。 以下は例です:

Note: これは参考注記です。

適合性のあるアルゴリズム

アルゴリズムの一部として命令形で表現された要件(例: "strip any leading space characters" や "return false and abort these steps") は、アルゴリズムの導入部で使われているキーワード("must", "should", "may" など)と同じ意味として解釈されます。

アルゴリズムや個別ステップとして表現された適合要件は、結果が同等である限りどんな方法で実装しても構いません。 この仕様で定義されているアルゴリズムは理解しやすいことを目的としており、性能を意図したものではありません。 実装者は最適化することが推奨されます。

索引

この仕様で定義されている用語

参照によって定義されている用語

参考文献

規定参考文献

[CSS-BOX-4]
Elika Etemad. CSS Box Model Module Level 4. 2022年11月3日. 作業草案. URL: https://www.w3.org/TR/css-box-4/
[CSS-CONTAIN-2]
Tab Atkins Jr.; Florian Rivoal; Vladimir Levin. CSS Containment Module Level 2. 2022年9月17日. 作業草案. URL: https://www.w3.org/TR/css-contain-2/
[CSS-OVERFLOW-3]
Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. 2023年3月29日. 作業草案. URL: https://www.w3.org/TR/css-overflow-3/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 2021年12月24日. 勧告候補. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3. 2022年12月1日. 勧告候補. URL: https://www.w3.org/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 2023年4月6日. 作業草案. URL: https://www.w3.org/TR/css-values-4/
[CSSOM-VIEW-1]
Simon Pieters. CSSOM View Module. 2016年3月17日. 作業草案. URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
Anne van Kesteren. DOM Standard. 現行標準. URL: https://dom.spec.whatwg.org/
[GEOMETRY-1]
Simon Pieters; Chris Harrelson. Geometry Interfaces Module Level 1. 2018年12月4日. 勧告候補. URL: https://www.w3.org/TR/geometry-1/
[HTML]
Anne van Kesteren; 他. HTML標準. 現行標準. URL: https://html.spec.whatwg.org/multipage/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL標準. 現行標準. URL: https://webidl.spec.whatwg.org/

IDL索引

callback IntersectionObserverCallback = undefined (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer);

[Exposed=Window]
interface IntersectionObserver {
  constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
  readonly attribute (Element or Document)? root;
  readonly attribute DOMString rootMargin;
  readonly attribute DOMString scrollMargin;
  readonly attribute FrozenArray<double> thresholds;
  undefined observe(Element target);
  undefined unobserve(Element target);
  undefined disconnect();
  sequence<IntersectionObserverEntry> takeRecords();
};

[Exposed=Window]
interface IntersectionObserverEntry {
  constructor(IntersectionObserverEntryInit intersectionObserverEntryInit);
  readonly attribute DOMHighResTimeStamp time;
  readonly attribute DOMRectReadOnly? rootBounds;
  readonly attribute DOMRectReadOnly boundingClientRect;
  readonly attribute DOMRectReadOnly intersectionRect;
  readonly attribute boolean isIntersecting;
  readonly attribute double intersectionRatio;
  readonly attribute Element target;
};

dictionary IntersectionObserverEntryInit {
  required DOMHighResTimeStamp time;
  required DOMRectInit? rootBounds;
  required DOMRectInit boundingClientRect;
  required DOMRectInit intersectionRect;
  required boolean isIntersecting;
  required double intersectionRatio;
  required Element target;
};

dictionary IntersectionObserverInit {
  (Element or Document)?  root = null;
  DOMString rootMargin = "0px";
  DOMString scrollMargin = "0px";
  (double or sequence<double>) threshold = 0;
};

✔MDN

IntersectionObserver/IntersectionObserver

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android55+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserver/disconnect

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserver/observe

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserver/root

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserver/rootMargin

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserver/takeRecords

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserver/thresholds

In all current engines.

Firefox55+Safari12.1+Chrome52+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserver/unobserve

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserver

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserverEntry/boundingClientRect

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserverEntry/intersectionRatio

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserverEntry/intersectionRect

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserverEntry/isIntersecting

In all current engines.

Firefox55+Safari12.1+Chrome58+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserverEntry/rootBounds

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserverEntry/target

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserverEntry/time

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

IntersectionObserverEntry

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?