Intersection Observer
要素がビューポートに入ったことを非同期で検知する Web API
APIパフォーマンス
Intersection Observer とは
Intersection Observer は、要素がビューポート (または指定したコンテナ) に入った/出たことを非同期で検知する Web API である。スクロールイベントのリスナーと異なり、メインスレッドをブロックせずに高パフォーマンスで動作する。
scroll イベントとの比較
| 観点 | scroll イベント | Intersection Observer |
|---|---|---|
| パフォーマンス | メインスレッドで実行 (重い) | 非同期 (軽い) |
| スロットリング | 自前で実装が必要 | 不要 |
| 精度 | ピクセル単位で計算 | 交差率 (threshold) で検知 |
基本的な使い方
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target); // 一度だけ検知
}
});
}, { threshold: 0.1 }); // 10% 見えたら発火
document.querySelectorAll('.lazy').forEach(el => observer.observe(el));
画像の遅延読み込み
const imgObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement;
img.src = img.dataset.src!;
imgObserver.unobserve(img);
}
});
}, { rootMargin: '200px' }); // 200px 手前で読み込み開始
document.querySelectorAll('img[data-src]').forEach(img => imgObserver.observe(img));
rootMargin: '200px' で、ビューポートの 200px 手前から読み込みを開始し、スクロール時に画像が表示される前にロードを完了する。
無限スクロール
const sentinel = document.getElementById('sentinel')!;
const observer = new IntersectionObserver(async ([entry]) => {
if (entry.isIntersecting) {
const items = await fetchNextPage();
renderItems(items);
}
});
observer.observe(sentinel); // リストの末尾に配置した要素を監視
スクロール連動アニメーション
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in');
}
});
}, { threshold: 0.2 });
document.querySelectorAll('.section').forEach(el => observer.observe(el));
.section { opacity: 0; transform: translateY(30px); transition: all 0.5s ease; }
.section.fade-in { opacity: 1; transform: translateY(0); }
React での使い方
function useInView(ref: RefObject<HTMLElement>) {
const [inView, setInView] = useState(false);
useEffect(() => {
if (!ref.current) return;
const observer = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting));
observer.observe(ref.current);
return () => observer.disconnect();
}, [ref]);
return inView;
}
オプション
| オプション | 説明 |
|---|---|
root |
交差を判定するコンテナ (デフォルト: ビューポート) |
rootMargin |
root の余白 ('200px' で手前から検知) |
threshold |
交差率 (0.1 = 10% 見えたら発火) |
Intersection Observer の理解を深めるには関連書籍が参考になる。