---
title: 交差オブザーバー API
slug: Web/API/Intersection_Observer_API
tags:
- API
- Clipping
- 交差
- 交差オブザーバー API
- IntersectionObserver
- 概要
- パフォーマンス
- リファレンス
- ウェブ
translation_of: Web/API/Intersection_Observer_API
---
{{DefaultAPISidebar("Intersection Observer API")}}
交差オブザーバー API (Intersection Observer API) は、ターゲットとなる要素が、祖先要素または文書の最上位の{{Glossary("viewport", "ビューポート")}}と交差する変化を非同期的に監視する方法を提供します。
従来、ある要素の可視状態、あるいは 2 つの要素の相対的な可視状態を検出することは困難であり、その解決策は信頼性が低く、ブラウザーやアクセスするサイトの動作が重くなる傾向がありました。ウェブが成熟していくにつれて、このような情報の必要性は高まっていきます。交差情報 (Intersection information) についての情報は次の理由から必要とされています。
- ページがスクロールした際の画像やその他のコンテンツの遅延読み込み。
- 「無限スクロール」をするウェブサイトを実装し、スクロールに従って次々とコンテンツを読み込んで、ユーザーがページの切り替えをせずに済むようにすること。
- 広告費を計算するための広告が表示されたかどうかのレポート。
- ユーザーが結果を見るかどうかで、タスクを実行するかどうか、アニメーションを処理するかどうかを決定すること。
以前は、要素同士の交差の検出を実装するには、イベントハンドラーやループで {{domxref("Element.getBoundingClientRect()")}} などのメソッドを呼び出し、影響を受けるすべての要素について必要な情報を蓄積していました。このコードはすべてメインスレッドで実行されるため、これらのうち 1 つでもあればパフォーマンスの問題を引き起こす可能性があります。サイトでこのような検出が大量に行われると、まったく醜くなる可能性があります。
ウェブページで無限スクロールを使用することを考えてみてください。ベンダーから提供されるライブラリーを使用して、ページ全体に定期的に配置された広告を管理し、アニメーショングラフィックスを表示し、通知ボックスなどを描画するカスタムライブラリーを使用します。それぞれには独自に交差を検出するためのルーチンがあり、すべてがメインスレッド上で実行されます。ウェブサイトの作者は、これが起こっていることを認識していないかもしれません。内部の働きについてほとんど知らずに 2 つのライブラリーを使用しているからです。ユーザーがページをスクロールすると、スクロール処理中にこれらの交差の検出ルーチンが絶えず起動し、ユーザーはブラウザー、ウェブサイト、およびコンピューターにイライラさせられることになります。
交差オブザーバー API では、監視したい要素が他の要素(または{{Glossary("viewport", "ビューポート")}})に入ったり出たりしたとき、あるいは両者が交差する量が要求された量だけ変化したときに実行されるコールバック関数をコードに登録することができます。この方法により、サイトはこの種の要素の交差を監視するためにメインスレッドで何もする必要がなくなり、ブラウザーは自由に交差の管理を最適化することができます。
交差オブザーバー API は、重複したピクセルの正確な数や、それがどのピクセルであるかを具体的に示すことはできません。しかし、「_N_ % 前後のどこかで交差していたら、何かをする必要がある」という、より一般的な利用法はカバーします。
## 交差オブザーバーの概念と使い方
交差オブザーバー API を使用すると、以下のいずれかの状況が発生したときに呼び出されるコールバックを構成することができます。
- **ターゲット**要素が端末のビューポートまたは指定された要素と交差すること。この指定された要素は、交差オブザーバー API の用途では**ルート要素**または**ルート**と呼びます。
- オブザーバーがターゲット要素を監視するよう最初に指示されたとき。
通常、ターゲット要素の最も近いスクロール可能な祖先、またはターゲット要素がスクロール可能な要素の子孫でない場合は、端末のビューポートを基準にして交差の変化を監視したいと思うでしょう。端末のビューポートを基準にして交差を監視するには、 `root` オプションに `null` を指定します。交差オブザーバーのオプションについてのより詳しい説明は、このまま読み進めてください。
ビューポートとその他の要素のどちらがルートとして使用されていても、 API は同じように動作し、ターゲット要素の表示状態が変わってルートとの間で交差の量の期待値を通るたびに、提供したコールバック関数が実行されます。
ターゲット要素とそのルート要素の交差する度合いが**交差率**です。これはターゲット要素のパーセント値を 0.0 から 1.0 の間の値で表現したものです。
### 交差オブザーバーの作成
交差オブザーバーは、コンストラクターを呼び出して閾値が一方向また他の方向に交差する度に実行されるコールバック関数を渡すことで生成します。
```js
let options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
let observer = new IntersectionObserver(callback, options);
```
閾値 (threshold) の 1.0 は、 `root` オプションで指定された要素内でターゲットが 100% 表示された時にコールバックが呼び出されることを意味しています。
#### 交差オブザーバーのオプション
`options` オブジェクトは {{domxref("IntersectionObserver.IntersectionObserver", "IntersectionObserver()")}} コンストラクターに渡され、オブザーバーのコールバックが呼び出される状況を制御します。以下のようなフィールドがあります。
- `root`
- : ターゲットが見えるかどうかを確認するためのビューポートとして使用される要素です。指定されなかった場合、または `null` の場合は既定でブラウザーのビューポートが使用されます。
- `rootMargin`
- : root の周りのマージンです。 CSS の {{cssxref("margin")}} プロパティに似た値を指定することができます。例えば、"`10px 20px 30px 40px"` (top, right, bottom, left) のようなものです。この値はパーセント値にすることができます。この一連の値は、交差を計算する前にルート要素の範囲のボックスの各辺を拡大または縮小させることができます。既定値はすべてゼロです。
- `threshold`
- : 単一の数値または数値の配列で、ターゲットがどのくらいの割合で見えている場合にオブザーバーのコールバックを実行するかを示します。見える範囲が 50% を超えたときのみ検出する場合は値 0.5 を使用します。 25% を超える度にコールバックを実行する場合は、 \[0, 0.25, 0.5, 0.75, 1] という配列を指定します。既定値は 0 です(つまり、 1 ピクセルでも表示されるとコールバックが実行されます)。 1.0 の値は全てのピクセルが見えるようになるまで、閾値を超えたとはみなされないことを意味します。
#### 監視される要素をターゲットにする
オブザーバーを作成した後は、監視するターゲット要素を与える必要があります。
```js
let target = document.querySelector('#listItem');
observer.observe(target);
// オブザーバーに設定したコールバックが初めて実行され、オブザーバーにターゲットが
// 割り当てられるまで待機します(ターゲットが現在表示されていない場合でも同様)。
```
ターゲットが `IntersectionObserver` に指定された閾値を満たす度にコールバックが呼び出されます。コールバックは {{domxref("IntersectionObserverEntry")}} オブジェクトのリストとオブザーバーを受け取ります。
```js
let callback = (entries, observer) => {
entries.forEach(entry => {
// それぞれのエントリーは、観測された 1 つの対象要素の交差状態の変化を示している。
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
```
コールバックが受信したエントリーのリストには、交差状態の変化を報告した各ターゲットに対して 1 つのエントリーが含まれます。 {{domxref("IntersectionObserverEntry.isIntersecting", "isIntersecting")}} プロパティの値をチェックして、そのエントリーが現在ルートと交差している要素を表しているかどうかを確認します。
コールバックはメインスレッドで実行される点に注意してください。可能な限り早く動作する必要があります。もし時間を要する処理であるなら、 {{domxref("Window.requestIdleCallback()")}} を使ったほうがいいでしょう。
また `root` オプションを指定した場合、ターゲットはルート要素の子孫でなければなりません。
### 交差の計算方法
交差オブザーバー API によって考慮される領域はすべて矩形です。不規則に整形された要素は、要素全体を囲む最小の矩形で占有しているとみなされます。同様に、要素の可視部分が矩形ではない場合、要素が交差する矩形は要素の可視部分全体を含む最小の矩形であると解釈されます。
{{domxref("IntersectionObserverEntry")}} オブジェクトによって提供される様々なプロパティがどのように交差を表現しているかを知るともっと役に立つでしょう。
#### 交差するルートとルートマージン
ある要素とそのコンテナーとの交差を監視するまえに、まずはコンテナーを知る必要があります。ここでのコンテナーとは**交差ルート**または**ルート要素**です。これは監視される要素の親要素となる文書内の特定の要素になるか、文書のビューポートをコンテナーとして使用する際は `null` になるかいずれかになります。
**ルート交差矩形**はターゲットをチェックするために使用される矩形です。この矩形は次のように決まります。
- 交差ルートが暗黙のルート(すなわち最上位の {{domxref("Document")}})である場合、ルート交差矩形はビューポートの矩形になります。
- 交差ルートのあふれた部分が切り取られていた場合、ルート交差矩形はルート要素のコンテンツ領域になります。
- それ以外の場合は、ルート交差矩形は交差ルートのクライアント矩形({{domxref("Element.getBoundingClientRect", "getBoundingClientRect()")}} を呼び出して返されるもの)です。
交差するルートとして使用される矩形は、**ルートマージン** `rootMargin` を {{domxref("IntersectionObserver")}} の作成時に設定することで調整することが可能です。 `rootMargin` の値は交差するルートの境界ボックスの各辺にオフセットを追加定義して、最終的な交差のルートの境界を作成します(コールバックが実行された際には {{domxref("IntersectionObserverEntry.rootBounds")}} で取得できるものです)。
#### 閾値
交差オブザーバー API は、ターゲット要素が見える量の微細な変化が発生するたびに知らせるのではなく、**閾値** (threshold) を使用します。オブザーバーを作成する際に、表示されるターゲット要素がどの程度見えているかの割合を表す 1 つ以上の数値を指定できます。API はこれらの閾値を超えて見えたかどうかの変更のみを知らせます。
例えば、ターゲット要素が 25% 見える度に通知を受けたい場合は、オブザーバーを作成する際に \[0, 0.25, 0.5, 0.75, 1] という配列を閾値のリストとして指定します。
コールバックが呼び出されると、 `IntersectionObserverEntry` オブジェクトのリストを受け取ります。これは、ルートと交差する度合いが変化し、露出量がいずれかの方向に閾値を越えた観測対象ごとに 1 つずつあります。
ターゲットが*現在*ルートと交差しているかどうかは、エントリーの {{domxref("IntersectionObserverEntry.isIntersecting", "isIntersecting")}} プロパティを見ることで確認できます。これにより、そのエントリーが、要素が交差している状態から交差しなくなるまでの遷移を表すのか、交差していない状態から交差する状態への遷移を表すのかを判断することができます。
交差する矩形がゼロでないこともあり得ることに注意してください。これは、交差部分が両者の境界線にぴったり沿っているか、または {{domxref("IntersectionObserverEntry.boundingClientRect", "boundingClientRect")}} の面積がゼロの場合に起こり得ることです。このようにターゲットとルートが境界線を共有している状態は、交差した状態に遷移したとみなすには不十分です。
閾値の仕組みを感じ取るには、下のボックスをスクロールして見てください。その中にある各色のボックスには四隅全てにパーセント値が表示されています。コンテナーをスクロールする時にこれらのパーセント値が変化することが分かります。各ボックスには異なる閾値が設定されています。
- 最初のボックスは可視点の各パーセント値がセットされています。つまり{{domxref("IntersectionObserver.thresholds")}} の配列は `[0.00, 0.01, 0.02, ..., 0.99, 1.00]` となります。
- 2 つ目のボックスには単一の閾値が、 50% の位置にあります。
- 3 つ目のボックスは可視率が 10% 毎の閾値があります (0%, 10%, 20%...)
- 最後のボックスの閾値は 25% 毎です。
```html hidden
```
```css hidden
.contents {
position: absolute;
width: 700px;
height: 1725px;
}
.wrapper {
position: relative;
top: 600px;
}
.sampleBox {
position: relative;
left: 175px;
width: 150px;
background-color: rgb(245, 170, 140);
border: 2px solid rgb(201, 126, 17);
padding: 4px;
margin-bottom: 6px;
}
#box1 {
height: 200px;
}
#box2 {
height: 75px;
}
#box3 {
height: 150px;
}
#box4 {
height: 100px;
}
.label {
font: 14px "Open Sans", "Arial", sans-serif;
position: absolute;
margin: 0;
background-color: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(0, 0, 0, 0.7);
width: 3em;
height: 18px;
padding: 2px;
text-align: center;
}
.topLeft {
left: 2px;
top: 2px;
}
.topRight {
right: 2px;
top: 2px;
}
.bottomLeft {
bottom: 2px;
left: 2px;
}
.bottomRight {
bottom: 2px;
right: 2px;
}
```
```js hidden
let observers = [];
startup = () => {
let wrapper = document.querySelector(".wrapper");
// オブザーバーのオプション
let observerOptions = {
root: null,
rootMargin: "0px",
threshold: []
};
// それぞれのボックスに設定する閾値の配列。最初のボックスの
// 閾値は、(各パーセント点対して)非常にたくさんあるため、
// プログラムで設定します。
let thresholdSets = [
[],
[0.5],
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
[0, 0.25, 0.5, 0.75, 1.0]
];
for (let i=0; i<=1.0; i+= 0.01) {
thresholdSets[0].push(i);
}
// それぞれのボックスを追加し、それぞれに新しいオブザーバーを生成
for (let i=0; i<4; i++) {
let template = document.querySelector("#boxTemplate").content.cloneNode(true);
let boxID = "box" + (i+1);
template.querySelector(".sampleBox").id = boxID;
wrapper.appendChild(document.importNode(template, true));
// このボックスのオブザーバーを設定
observerOptions.threshold = thresholdSets[i];
observers[i] = new IntersectionObserver(intersectionCallback, observerOptions);
observers[i].observe(document.querySelector("#" + boxID));
}
// 開始位置までスクロール
document.scrollingElement.scrollTop = wrapper.firstElementChild.getBoundingClientRect().top + window.scrollY;
document.scrollingElement.scrollLeft = 750;
}
intersectionCallback = (entries) => {
entries.forEach((entry) => {
let box = entry.target;
let visiblePct = (Math.floor(entry.intersectionRatio * 100)) + "%";
box.querySelector(".topLeft").innerHTML = visiblePct;
box.querySelector(".topRight").innerHTML = visiblePct;
box.querySelector(".bottomLeft").innerHTML = visiblePct;
box.querySelector(".bottomRight").innerHTML = visiblePct;
});
}
startup();
```
{{EmbedLiveSample("Thresholds", 500, 500)}}
#### クリッピングと交差矩形
ブラウザーは次のように最終的な交差矩形を計算します。これはすべて完了した後の状態が見えますが、交差がいつ発生するかを正確に把握するために、これらの手順を理解すると役立ちます。
1. ターゲット要素の境界矩形(つまり、要素を構成するすべてのコンポーネントの境界ボックスを完全に囲む最小の矩形)は、ターゲットに対して {{domxref("Element.getBoundingClientRect", "getBoundingClientRect()")}} を呼び出すことによって取得されます。これは、交差する矩形の最大の大きさです。残りの手順では、交差しない部分を削除します。
2. ターゲットの直接の親ブロックから始まり、外側に向かって移動し、それぞれの包含ブロックのクリッピングが(存在すれば)交差する長方形に適用されます。ブロックのクリッピングは、 2 つのブロックの交差と、 {{cssxref("overflow")}} プロパティで(存在すれば)指定されたクリッピングモードに基づいて決定されます。 `overflow` に `visible` 以外を設定すると、クリッピングが行われます。
3. 包含する要素の1つがネストされた閲覧コンテキストのルートである場合 ({{HTMLElement("iframe")}} に含まれる文書など)、交差する矩形は含まれているコンテキストのビューポートで切り取られ、コンテナー群を通して上方に再帰的にコンテナーの包含ブロックを続けます。ですから、最上位の `