【入門編】 Intersection Observerによる遅延レンダリング – Webブラウザの仕組み実践ガイド

こんにちは!フロントエンドの現場を長年歩んできたチーフアーキテクトの私です。

Webサイトを作っていて、「画像や重たいコンテンツを全部一気に読み込むせいで、最初の表示がカクカクする……」なんて悩みに直面したことはありませんか?「うわ、また初期表示が重いって怒られそうだな……」と、夜な夜な首を傾げているそこのあなた、大丈夫ですよ。その悩み、今日の主役である「Intersection Observer(インターセクション・オブザーバー)」を使えば、驚くほどスッキリ解決できます。

今回は、難しいブラウザの仕組みの裏側を、身近な例えを交えながら、一緒に紐解いていきましょう!

—

1. なぜ初期表示は重くなるのか?(ブラウザの気持ちになってみる)

私たちが普段何気なく見ているWebページですが、ブラウザ(ChromeやSafariなど)の裏側では、ものすごい量のタスクが同時進行で行われています。

HTMLを読み込み、CSSを解釈し、画面上のどこに何を配置するかを計算して……と、ブラウザ君は休む暇もありません。ここに、画面の下の方に隠れているような大きなお肉(重たい画像や動画、複雑なパーツ)まで「全部ひとまず最初に作って!」と押し付けてしまったらどうなるでしょう?

そう、ブラウザ君はキャパオーバーになり、ページを開いた瞬間にフリーズしたり、スクロールがカクついたりしてしまうのです。

例え話:回転寿司のコンベアに例えると分かりやすい

これを「回転寿司」に例えてみましょう。
悪い例は、お客さんがまだ席に座ってもいないのに、100皿以上のお寿司をすべて一斉にコンベアに乗せて、すごいスピードで流し始める状態です。まだ食べていないお寿司のせいで、お皿がぶつかり合い、店員(ブラウザ)はてんてこ舞いになりますよね。

では、どうすればいいでしょうか?
正解は、「お客さんの目の前に流れてきたお皿だけを、その場で作って提供する」ことです。これなら厨房(CPUやメモリ)の負担は最小限で済みますし、いつでも新鮮なお寿司を出せますよね。

この「お客さん(画面=ビューポート)の視界に入ってきたタイミングで、はじめてコンテンツを準備する」というスマートな仕組みこそが、今回学ぶ遅延レンダリング(Lazy Rendering)です。

—

2. 昔のやり方と、これからの主役「Intersection Observer」

昔のWeb制作では、この「要素が画面に入ったかどうか」を検知するために、ブラウザの「スクロールイベント(`scroll`)」を監視していました。

しかし、これが現場のエンジニア泣かせの代物でした。スクロールするたびに、数ミリ秒単位で「今、画面のどこにいますか!?」とブラウザが何回も質問を繰り返すため、メインスレッドが悲鳴を上げ、かえってページを重くする原因になっていたのです。

そこで登場したのが、救世主「Intersection Observer API」です。

監視カメラを置くイメージ

Intersection Observerは、自分ですり減るようなスクロール監視をしません。その代わり、ブラウザにこうお願いしておきます。

> 「この要素の近くに、監視カメラを置いておいてください。もしこの要素がユーザーの画面(ビューポート)に入ってきたら、私にこっそり教えてね」

ブラウザが裏側でいい感じにタイミングを計ってくれるため、JavaScriptの処理が圧倒的に軽くなり、スマホのバッテリーにも優しいという、いいことずくめの機能なんです。

—

3. 実装してみよう!やさしいコード解説

百聞は一見にしかず。実際に、画面に入ってきたタイミングで画像をふわっと表示させるコードを見てみましょう。

そのままコピペして、お使いのエディタやCodePenなどで試してみてくださいね。

HTMLのイメージ

下にスクロールしてね!


綺麗な海

CSSのイメージ

.spacer {
height: 150vh; / スクロールさせるために高さをわざと高くしています /
background: #f0f0f0;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2rem;
color: #666;
}

/ 読み込み前の画像スタイル(最初は少しぼかしたり、透明にしておく) /
.lazy-image {
width: 100%;
max-width: 600px;
height: 400px;
object-fit: cover;
opacity: 0;
transition: opacity 0.8s ease-in-out; / ふわっと表示させるためのCSS /
background-color: #ddd; / 読み込み中のプレースホルダーカラー /
}

/ 読み込み完了後に付与するクラス /
.lazy-image.is-loaded {
opacity: 1;
}

JavaScript(Intersection Observerの魔法)

// 1. 監視対象となる要素をすべて取得します
const targets = document.querySelectorAll(‘.lazy-image’);

// 2. 交差(画面に入る)したときのルール(コールバック関数)を定義します
const handleIntersection = (entries, observer) => {
entries.forEach(entry => {
// 要素が画面に入ってきたかどうかをチェック
if (entry.isIntersecting) {
const img = entry.target;

// data-src に隠しておいた本当の画像URLを、srcにセットする
const realSrc = img.getAttribute(‘data-src’);
if (realSrc) {
img.src = realSrc;
// 読み込み完了したらCSSクラスをつけてふわっと表示
img.classList.add(‘is-loaded’);
}

// 一度画面に入って画像を読み込んだら、もう監視する必要はないので解除します
observer.unobserve(img);
}
});
};

// 3. オプション設定(今回はデフォルトのままでOKですが、画面に入る少し手前で読み込ませることも可能です)
const options = {
root: null, // nullを指定するとブラウザのビューポート(画面)が基準になります
rootMargin: ‘0px 0px 100px 0px’, // 画面の下端から100px手前に来たら読み込みを開始する
threshold: 0 // 要素が1ピクセルでも画面に入ったら反応する
};

// 4. Observerインスタンスを作成し、監視をスタート!
const observer = new IntersectionObserver(handleIntersection, options);

// 取得したすべてのターゲット要素を監視カメラに登録します
targets.forEach(target => {
observer.observe(target);
});

—

4. チーフアーキテクトからのアドバイスとまとめ

お疲れ様でした!コードの動きはイメージできたでしょうか?

このIntersection Observerによる遅延レンダリングを導入するだけで、初期表示のネットワーク負荷やメモリ消費は劇的に軽くなります。ユーザーがスクロールした「その瞬間」にだけリソースを使うという姿勢は、現代のWeb開発において欠かせない優しさです。

最初は少し難しく感じるかもしれませんが、
1. 「見えないものは、まだ作らない(読み込まない)」
2. 「画面に入ってきたら、そっと教えてもらう」

この2つの基本原則さえ頭の片隅に置いておけば、もう怖くありません。ぜひあなたのプロジェクトでも試してみてくださいね。

それでは、快適なフロントエンドライフを!わからないことがあれば、いつでもまた私の部屋をノックしに来てください。

コメント

タイトルとURLをコピーしました