IntersectionObserver は「要素が画面に入ったかどうかを見張る番人」
IntersectionObserver は、
「ある要素が、画面(または指定した枠)の中に入ったか・出たか」を自動で監視してくれる仕組み です。
「この画像が画面に見えたら読み込みたい」
「このセクションが見えたらアニメーションを始めたい」
「一番下までスクロールされたら次のページを読み込みたい(無限スクロール)」
こういう「スクロール位置に応じた処理」を、
スクロールイベントをゴリゴリ書かずに、きれいに実現できます。
まずは「何をしてくれるのか」のイメージを固めてから、
実際のコードと重要ポイントを見ていきます。
まずは一番シンプルな例を見てみる
「要素が画面に入ったらコンソールにログを出す」
HTML をざっくり想像します。
<div style="height: 100vh; background: lightgray;">上の余白</div>
<div id="target" style="height: 200px; background: pink;">
ここがターゲット
</div>
<div style="height: 100vh; background: lightgray;">下の余白</div>
JavaScript はこうです。
const target = document.querySelector("#target");
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log("ターゲットが画面に入りました!");
} else {
console.log("ターゲットが画面から出ました");
}
});
});
observer.observe(target);
JavaScriptやっていることを言葉で整理するとこうなります。
IntersectionObserverを作る(「監視役」を雇うイメージ)- コールバック関数(
(entries) => { ... })で「入った・出たときの処理」を書く observer.observe(target)で「この要素を見張って」と依頼する
あとはユーザーがスクロールするたびに、
ターゲット要素が画面に入ったり出たりすると、
コールバックが自動で呼ばれます。
ここでのキーワードは entry.isIntersecting です。
これが true なら「今、画面にかかっている」、false なら「画面から外れている」と思ってOKです。
重要ポイント①:IntersectionObserver の「基本構造」をしっかり掴む
コンストラクタの形
IntersectionObserver は new で作ります。
const observer = new IntersectionObserver(callback, options);
JavaScriptcallback と options の 2 つがポイントです。
callback は「交差状態が変わったときに呼ばれる関数」options は「どの枠を基準に、どのくらい見えたら反応するか」の設定
まずは callback から見ていきます。
callback の引数 entries と entry
コールバックは、だいたいこんな形で書きます。
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
// entry に「どの要素がどう見えているか」の情報が入っている
});
});
JavaScriptentries は「観察対象の変化の配列」です。
ふつうは forEach で 1 件ずつ処理します。
entry の中でよく使うのは次の 2 つです。
entry.target
どの要素か(observe した DOM 要素)
entry.isIntersecting
今、その要素が枠(デフォルトは画面)にかかっているかどうか(true / false)
初心者のうちは、
「entry.target と entry.isIntersecting だけ覚える」で十分です。
重要ポイント②:options で「どこを基準に、どのくらい見えたら」を決める
root(どの枠と交差しているか)
options.root は「どの要素を“画面”として扱うか」です。
何も指定しない(undefined)場合は、
ブラウザのビューポート(実際に見えている画面)が基準になります。
const observer = new IntersectionObserver(callback, {
root: null, // または省略
});
JavaScript特定のスクロールコンテナを基準にしたい場合は、その要素を渡します。
const container = document.querySelector(".scroll-container");
const observer = new IntersectionObserver(callback, {
root: container,
});
JavaScriptこうすると、「コンテナの中で見えているかどうか」を判定してくれます。
threshold(どのくらい見えたら「入った」とみなすか)
threshold は「要素がどのくらい見えたら反応するか」です。
0 のときは「1ピクセルでもかかったら」
1 のときは「完全に全部見えたら」
という意味になります。
const observer = new IntersectionObserver(callback, {
threshold: 0.5, // 要素の 50% 以上が見えたら isIntersecting が true
});
JavaScript配列で複数指定することもできます。
const observer = new IntersectionObserver(callback, {
threshold: [0, 0.5, 1.0],
});
JavaScriptこの場合、
0 → 0.5 → 1.0 と「見え方」が変わるタイミングでコールバックが呼ばれます。
初心者のうちは、
「0(ちょっとでも見えたら)」か「0.5(半分見えたら)」あたりから始めると感覚が掴みやすいです。
rootMargin(判定の枠を少し広げたり縮めたりする)
rootMargin は、CSS の margin っぽい書き方で、
「判定に使う枠を少し広げたり縮めたり」できます。
const observer = new IntersectionObserver(callback, {
root: null,
rootMargin: "0px 0px -100px 0px",
});
JavaScript例えば、下方向に -100px しておくと、
「画面の下から 100px 手前に来た時点で“見えた”とみなす」
といった調整ができます。
「ちょっと早めに読み込みたい」
「少し余裕を持ってアニメーションを始めたい」
といったときに便利です。
実用例①:画像の遅延読み込み(Lazy Load)
「見えた画像だけ読み込む」パターン
HTML で、実際の画像 URL を data-src に入れておきます。
<img class="lazy" data-src="photo1.jpg" alt="">
<img class="lazy" data-src="photo2.jpg" alt="">
<img class="lazy" data-src="photo3.jpg" alt="">
JavaScript はこうです。
const images = document.querySelectorAll("img.lazy");
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
const img = entry.target;
const src = img.getAttribute("data-src");
if (src) {
img.src = src; // ここで初めて読み込み開始
img.removeAttribute("data-src");
}
observer.unobserve(img); // 一度読み込んだら監視をやめる
});
}, {
threshold: 0.1, // 10% 見えたら読み込み開始
});
images.forEach((img) => observer.observe(img));
JavaScriptここでのポイントは次の通りです。
entry.isIntersecting が true になったタイミングでだけ処理するdata-src から本物の src に差し替えることで、その瞬間に読み込みが始まる
一度読み込んだ画像は observer.unobserve で監視を解除する
これで、
「画面に見えていない画像はまだ読み込まない」
という、パフォーマンスに優しい動きが簡単に実現できます。
実用例②:セクションが見えたらアニメーションを始める
CSS で「見えたらクラスが付く」前提を作る
HTML はざっくりこんな感じ。
<section class="section">セクション1</section>
<section class="section">セクション2</section>
<section class="section">セクション3</section>
CSS で、最初は透明にしておき、.in-view が付いたらフェードインするようにします。
.section {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.section.in-view {
opacity: 1;
transform: translateY(0);
}
JavaScript はこうです。
const sections = document.querySelectorAll(".section");
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("in-view");
} else {
entry.target.classList.remove("in-view");
}
});
}, {
threshold: 0.3, // 30% くらい見えたら in-view
});
sections.forEach((sec) => observer.observe(sec));
JavaScriptこれで、
ユーザーがスクロールしてセクションが画面に入るたびに、
ふわっとフェードインするようになります。
「スクロール位置に応じたアニメーション」は、
昔はスクロールイベントで自前計算していましたが、IntersectionObserver を使うとかなりスッキリ書けます。
重要ポイント③:なぜ scroll イベントより IntersectionObserver が嬉しいのか
自分で「位置計算」をしなくていい
スクロールイベントで同じことをやろうとすると、
スクロールのたびにイベントが発火する
各要素の位置(getBoundingClientRect)を計算する
画面内に入っているかどうかを自分で判定する
という処理を書かないといけません。
IntersectionObserver は、
この「位置計算」と「判定」をブラウザ側がやってくれます。
こちらは「見えたときに何をするか」だけ書けばいいので、
コードがシンプルになり、バグも減ります。
パフォーマンス的にも有利
スクロールイベントは、
スクロール中に何十回・何百回と発火します。
そのたびに重い処理をすると、
カクつきの原因になります。
IntersectionObserver は、
ブラウザがいい感じにタイミングをまとめてくれるので、
パフォーマンス的にも有利です。
「スクロールに応じた処理」を書くときは、
まず IntersectionObserver を思い出す癖をつけると、
設計がだいぶ楽になります。
初心者として IntersectionObserver で本当に掴んでほしいこと
IntersectionObserver は「要素が画面(または指定した枠)に入ったか・出たか」を監視する仕組み
コールバックの entry.isIntersecting が true なら「今見えている」と判断できる
options の root / threshold / rootMargin で「どこを基準に、どのくらい見えたら」を調整できる
画像の遅延読み込みや、スクロールに応じたアニメーションなどでよく使われる
スクロールイベントで自前計算するより、コードがシンプルでパフォーマンスも良くなりやすい
まずは、
「要素が画面に入ったら console.log するだけ」の超シンプルな例を、
自分の手で書いてみてください。
そこから、
見えたらクラスを付ける
見えたら画像を読み込む
と少しずつステップアップしていくと、IntersectionObserver が「難しい API」ではなく、
「スクロールと仲良くするための頼れる番人」 に感じられるようになります。

