目次⇒ IntersectionObserverの活用 MutationObserverでDOMの変化を監視 ResizeObserverでサイズ変化を監視 イベントループとマイクロタスクの順序 TOP
この応用編では、DOMの変化・可視範囲・サイズなどに反応して処理を実行するための技術を紹介します。
また、setTimeout や Promise などがいつ実行されるのかといったタイミング制御の仕組み(イベントループ)についても掘り下げます。
解説:
IntersectionObserver は、特定の要素が画面内に現れたかどうかを検知できる便利なAPIです。
画像の遅延読み込みやアニメーションの発火タイミング制御など、スクロールに応じた処理に広く活用されています。
まずは IntersectionObserver の基本的な使い方を見てみましょう。
const target = document.getElementById('box');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('要素が画面内に入りました');
}
});
});
observer.observe(target);このように、observe() で監視対象を指定すると、要素が画面内に入った瞬間にコールバックが実行されます。
第二引数で threshold や rootMargin などのオプションを設定できます。
const observer = new IntersectionObserver(callback, {
threshold: 0.5, // 要素が50%以上見えたら発火
rootMargin: '0px 0px -20% 0px' // 少し手前でトリガーさせる
});スクロールの余裕や発火の閾値を調整したいときに便利です。
最もよく使われるのが、画像の遅延読み込みです。data-src で画像パスを一時的に持たせておき、表示直前に読み込みます。
const imgs = document.querySelectorAll('img[data-src]');
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
obs.unobserve(img); // 一度読み込んだら監視解除
}
});
});
imgs.forEach(img => observer.observe(img));表示されていない画像の読み込みを遅らせることで、ページの初期表示を高速化できます。
次は、DOMの「変化」そのものを監視できる MutationObserver について解説します。
下にスクロールして、灰色のブロック(#box)が画面内に入ると、背景が濃い緑になり、テキストも変わります。
解説:
MutationObserver は、DOMツリーに対する変更(ノードの追加・削除、属性の変更など)を検知するためのAPIです。
旧来の Mutation Events よりも高速・安定しており、SPAや動的UIでの監視処理に多用されます。
監視対象としたい要素を指定し、その中で発生したDOMの変更を検出します。
const target = document.getElementById('target');
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
console.log('DOMに変更がありました:', mutation);
});
});
observer.observe(target, {
childList: true, // 子ノードの追加・削除を監視
attributes: true, // 属性の変更を監視
subtree: true // 子孫ノードも含めて監視
});
監視内容は observe() の第2引数で細かく指定できます。
ユーザー操作や非同期処理で追加された要素に対して、後から処理を加えるようなケースに活用できます。
const logArea = document.getElementById('log');
const observer = new MutationObserver(() => {
logArea.textContent = '✅ 新しい要素が追加されました!';
});
observer.observe(logArea, { childList: true });
innerHTML の操作などで中身が書き換えられたタイミングを検出できます。
下のボタンを押すと、ログエリアに新しい段落が追加され、それをMutationObserverが検出します。
observe() のオプション指定で柔軟な監視が可能次は、要素のサイズ変化を検出できる ResizeObserver を見ていきましょう。
解説:
ResizeObserver は、要素の表示サイズ(コンテンツボックス)の変化をリアルタイムに検知できるAPIです。
画面幅に応じたレイアウト切り替え、エディタやグラフの自動リサイズ、文字の折返しに伴う高さ変化への追従などに役立ちます。
監視したい要素を observe で登録すると、サイズが変化したタイミングでコールバックが呼ばれます。
const box = document.getElementById('target');
const ro = new ResizeObserver((entries) => {
for (const entry of entries) {
const cr = entry.contentRect; // 直近のコンテンツ領域のサイズ
console.log(`width: ${cr.width}, height: ${cr.height}`);
}
});
ro.observe(box);入力量に応じて高さが変わる要素を監視し、サイズに合わせて別UIを更新できます。
const textarea = document.getElementById('memo');
const sizeLabel = document.getElementById('sizeLabel');
const ro = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
sizeLabel.textContent = `サイズ: ${Math.round(width)} x ${Math.round(height)}`;
});
ro.observe(textarea);
textarea.addEventListener('input', () => {
textarea.style.height = 'auto';
textarea.style.height = textarea.scrollHeight + 'px';
});ボタンを押すとボックスの幅が増減し、ResizeObserver が変化を検知してサイズ表示を更新します。
次は、非同期処理の順序を理解するためにイベントループとマイクロタスクの順序を解説します。
解説:
JavaScriptはシングルスレッドで動作し、処理の順序はイベントループが管理します。
非同期処理は大きくマクロタスク(例:setTimeout / setInterval)とマイクロタスク(例:Promise.then / queueMicrotask / MutationObserver)に分かれ、同一フレーム内ではマイクロタスクが先に処理されます。
1) 同期処理(現在のタスク) → 2) マイクロタスクをすべて実行 → 3) 次の描画前コールバック(requestAnimationFrame など)→ 4) 次のマクロタスク(例:setTimeout)…という順に進みます。
console.log('同期処理1');
setTimeout(() => {
console.log('setTimeout(マクロタスク)');
}, 0);
Promise.resolve().then(() => {
console.log('Promise.then(マイクロタスク)');
});
queueMicrotask(() => {
console.log('queueMicrotask(マイクロタスク)');
});
console.log('同期処理2');
// 期待される出力順:
// 同期処理1
// 同期処理2
// Promise.then(マイクロタスク) ← 今回は、先に登録しているためこちらが先
// queueMicrotask(マイクロタスク)
// (※ rAF を追加していない例なのでここでは出ません)
// setTimeout(マクロタスク)| 種類 | 代表例 | 実行タイミング |
|---|---|---|
| マクロタスク | setTimeout, setInterval, メインのI/Oコールバック など | イベントループの「次のラウンド」で実行 |
| マイクロタスク | Promise.then / catch / finally, queueMicrotask, MutationObserver | 現在のマクロタスクが終わった直後に全て実行 |
以下のスクリプトは、同期処理・マイクロタスク・マクロタスク・描画タイミング(requestAnimationFrame)の順序をコンソールに記録します。
ブラウザの開発者ツールでconsoleを開いて確認してください。
※この実行例は、上記の「基本の流れ」に加えて「requestAnimationFrame」と「MutationObserver」を追加し、描画タイミングやDOM変化検知も含めた順序を確認できるようにしています。
※スマホやタブレットでconsoleを開いて確認するには、この記事参考にしてね! ≫ここから記事へ≪