はじめに|この記事で得られる価値
「サイト表示がなぜか遅い……先輩から『画像の遅延読み込み、Intersection Observerで実装しといて』と指示されたけど、何から手をつければいいんだ……?」
私たちフロントエンドエンジニアが現場でよく遭遇する、このモヤモヤ。<code>scroll</code>イベントには慣れていても、Observer APIの仕組みやパフォーマンス面での強みを自信を持って説明するのは意外と難しいものです。
まずは安心してください。この記事で、JavaScript Observer APIの基本から、よくある実装課題である「画像の遅延読み込み」まで、一緒に“腹落ち”させていきましょう。
React Hooksによる実践コードも用意したので、コピペで“まずは動かす”ことから始められます。
(APIの基礎については『APIとは?SES現場で役立つ基礎〜Postman活用まで完全ガイド』をご参照ください)
用語解説:scrollイベント
ページや要素がスクロールされたときに発生するイベント。頻繁に発火するため、パフォーマンスに注意が必要。用語解説:Observer API
DOMや要素の状態変化を効率的に監視できるJavaScriptの仕組み。IntersectionObserver、MutationObserver、ResizeObserverの3種が代表的。用語解説:SPA(Single Page Application)
ページ遷移をせずに、1つのWebページ上で動的にコンテンツを切り替えるWebアプリの構造。メモリ管理やイベント監視が重要。用語解説:React Hooks
Reactで状態管理や副作用処理を関数型コンポーネントで扱うための仕組み。useStateやuseEffectなどがある。
1. なぜ今Observerなのか?- scrollイベントの課題とパフォーマンス
「なぜIntersectionObserverが推奨されるのか?」——私たちが直面する最初の疑問です。
従来は、<code>scroll</code>イベントリスナーで要素の表示状態を監視するのが定番でした。
しかし最大の課題は“発火頻度”。
1ピクセルでもスクロールが発生すれば連続的にイベントが走るため、
- レンダリング遅延
- 他の操作の“カクつき”
- パフォーマンス低下
こうした問題を招きやすいのが現実です。
一方、<code>IntersectionObserver</code>は、
(Webパフォーマンス改善の具体的な手順については『パフォーマンスタブ徹底解説|Chrome DevToolsでWebパフォーマンス改善の全手順』をご参照ください)
- 非同期処理
- 要素が画面に「入る/出る」タイミングだけでコールバック
という性質を持つため、無駄な計算・イベント発火を大幅に削減できます。
この“必要最小限”の監視が現代のWebパフォーマンス改善のカギです。
2. 目的別に選ぶ!3つのObserver API使い分けマップ
JavaScriptには3種類の主要なObserver APIがあります。それぞれの特徴と使い分けを整理します。
1. 要素の「表示」を監視する:IntersectionObserver
用途例:
- 画像の遅延読み込み(Lazy Loading)
- 無限スクロール
- 要素が表示された瞬間のアニメーション
- 広告のインプレッション計測
「要素が画面に入ったか/出たか」を知りたい場合に最適です。
用語解説:IntersectionObserver
要素が画面(ビューポート)に表示されたタイミングを効率よく検知できるWeb API。パフォーマンス改善や遅延読み込みに活用される。用語解説:遅延読み込み(Lazy Loading)
画像やコンテンツを必要なタイミングで読み込むことで、初期表示を高速化し、通信量も削減できる手法。
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('要素が画面内に入りました');
}
});
});
observer.observe(document.querySelector('#target'));
2. DOM要素の「変化」を監視する:MutationObserver
用途例:
- 要素の追加・削除タイミングで処理を走らせる
- 外部ライブラリによるDOM書き換え検知
- テキストエディタでの変更追跡
「DOMの構造・属性の変化」をフックに使いたい場合に活躍します。
用語解説:MutationObserver
DOM(HTML構造)の変更を検知できるAPI。要素の追加・削除・属性変更などをリアルタイムで監視できる。用語解説:DOM(Document Object Model)
HTMLやXML文書をプログラムから操作するための構造。要素や属性をツリー状に管理する。
const observer = new MutationObserver((mutations) => {
console.log('DOMが変更されました', mutations);
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
3. 要素の「サイズ変更」を監視する:ResizeObserver
用途例:
- レスポンシブデザインでのサイズ検知
- テキスト量によるコンテナサイズ追跡
「要素の幅・高さの変化」に反応したい時に最適です。
用語解説:ResizeObserver
要素のサイズ(幅・高さ)が変化したタイミングを検知できるAPI。レスポンシブ対応や動的レイアウトに便利。用語解説:レスポンシブデザイン
画面サイズやデバイスに応じてレイアウトを自動調整するWebデザイン手法。
const observer = new ResizeObserver((entries) => {
for (let entry of entries) {
const { width, height } = entry.contentRect;
console.log(`要素のサイズ: ${width}px x ${height}px`);
}
});
observer.observe(document.querySelector('#resizable-element'));
3. 【コピペOK】Intersection Observerで画像の遅延読み込みを実装する全手順
ここからは画像の遅延読み込みを“すぐ動くコード”で解説します。
Step1: HTMLの準備(data-*属性を使う)
<img src="placeholder.gif" data-src="real-image.jpg" alt="遅延読み込みされる画像" class="lazyload">
<img src="placeholder.gif" data-src="real-image-2.jpg" alt="遅延読み込みされる画像" class="lazyload">
用語解説:data-*属性
HTML5で導入された、独自データを要素に持たせるための属性。JavaScriptから簡単に参照・操作できる。
Step2: new IntersectionObserver()で監視を開始
const options = {
root: null,
rootMargin: '0px 0px 100px 0px',
threshold: 0
};
const observer = new IntersectionObserver(handleIntersect, options);
document.querySelectorAll('.lazyload').forEach(img => {
observer.observe(img);
});
Step3: コールバック関数で画像の読み込み処理を実装
function handleIntersect(entries, observer) {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazyload');
observer.unobserve(img);
}
});
}
Step4: unobserveで監視を停止・メモリリークを防止
一度読み込んだ画像はunobserve()で監視解除しましょう。
SPAでは特に重要です。
4. 【React実践編】useIntersectionObserverカスタムフックを作ろう
Reactプロジェクトでも“現場に即投入できる”カスタムフック例を紹介します。
(ReactのuseStateやuseEffectの使い分けについては『【保存版】ReactのuseStateとuseEffectの違いとは?初心者が実務で迷わない使い分け完全ガイド』をご参照ください)
用語解説:カスタムフック
Reactで独自のロジックや状態管理を再利用可能にする関数。use○○の形で定義する。
useRefで監視対象を指定+useEffectで監視開始/停止
import { useState, useRef, useEffect } from 'react';
function useIntersectionObserver(options) {
const [entry, setEntry] = useState(null);
const [node, setNode] = useState(null);
const observer = useRef(null);
useEffect(() => {
if (observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver(([entry]) => {
setEntry(entry);
}, options);
const { current: currentObserver } = observer;
if (node) currentObserver.observe(node);
return () => currentObserver.disconnect();
}, [node, options]);
return [setNode, entry];
}
// カスタムフックを使ったコンポーネント
function LazyImage({ src, alt, placeholderSrc }) {
const [ref, entry] = useIntersectionObserver({ threshold: 0.1 });
const [isLoaded, setIsLoaded] = useState(false);
const isIntersecting = entry ? entry.isIntersecting : false;
useEffect(() => {
if (isIntersecting && !isLoaded) setIsLoaded(true);
}, [isIntersecting, isLoaded]);
return (
<img
ref={ref}
src={isLoaded ? src : placeholderSrc}
alt={alt}
style={{
transition: 'opacity 0.3s',
opacity: isLoaded ? 1 : 0.8
}}
/>
);
}
// 使用例
// <LazyImage src="real-image.jpg" placeholderSrc="placeholder.gif" alt="Reactで遅延読み込み" />
5. FAQ|よくある疑問に回答
-
Q1.
unobserve()を呼ばないと?
A1. 監視が残り続け、SPAではメモリリークの温床になります。不要な監視は必ず解除しましょう。 -
Q2. Safariや古いブラウザ対応は?
A2. IntersectionObserverはモダンブラウザで広くサポート。
IE等ではPolyfill(例:intersection-observer
npmパッケージ)が必要です。 - Q3. コールバックが一度しか呼ばれない?
A3.thresholdや要素サイズ、監視対象のCSS設定を見直しましょう。高さ0だと交差が検知できません。 -
Q4.
thresholdを配列指定するメリットは?
A4. 例えば[0, 0.5, 1]とすると、「少し表示」「半分表示」「完全表示」…各タイミングでコールバックできます。リッチなアニメーションのトリガーに便利です。 -
Q5. TypeScriptでの型定義は?
A5.IntersectionObserverEntryとIntersectionObserver型を活用しましょう。const handleIntersect: IntersectionObserverCallback = ( entries: IntersectionObserverEntry[], observer: IntersectionObserver ): void => { /* ... */ }; const options: IntersectionObserverInit = { rootMargin: '100px', threshold: 0.1 }; const observer = new IntersectionObserver(handleIntersect, options);
用語解説:メモリリーク
不要になったデータや監視が解放されず、メモリを消費し続ける現象。SPAや長時間稼働するWebアプリで特に注意。用語解説:Polyfill
古いブラウザでも新しい機能を使えるようにするための補助スクリプト。用語解説:TypeScript
JavaScriptに型定義を加えた言語。大規模開発や保守性向上に役立つ。
6. まとめ|Observerを使いこなし、パフォーマンス改善のプロへ
- scrollイベントの弱点を理解し、
- 3種のObserver APIの違いを整理、
- IntersectionObserverによる画像遅延読み込みの実装・Reactカスタムフック化
まで、一気に振り返りました。
まずは動くコードで感覚を掴み、手元のプロジェクトに適用する。
この一歩が、Webパフォーマンス改善と実務力アップへの道です。
ぜひコードをコピペして、まずは動かしてみてください。