MutationObserver は「DOM の変化を見張る監視カメラ」
MutationObserver は、
「DOM(要素の追加・削除・属性変更・テキスト変更など)が変わった瞬間を教えてくれる監視カメラ」 です。
ある要素の中で、誰かが
- 子要素を追加した
- 子要素を削除した
- 属性(class, id, style など)を変えた
- テキストを書き換えた
といったことをしたときに、
「今こういう変更が起きたよ」とイベントのように教えてくれます。
「いつ変わるか分からないけど、変わったら何かしたい」
というときに、とても頼りになる API です。
基本形:「監視カメラを設置して、どこを見るか設定する」
一番シンプルな構造
MutationObserver の基本構造はこうです。
const observer = new MutationObserver((mutations, observer) => {
mutations.forEach((mutation) => {
console.log(mutation);
});
});
observer.observe(targetNode, options);
JavaScriptここで押さえるべきポイントは 3 つです。
- 監視役を作る:
new MutationObserver(callback) - どこを見るか決める:
observer.observe(どの要素, どんな変化) - 変化が起きたら callback が呼ばれる:
mutationsに「何がどう変わったか」が入る
まずは、この「監視カメラを設置する」イメージをしっかり持ってください。
監視対象の要素を決める
const target = document.querySelector("#app");
JavaScript例えば、#app の中で起きる変化を見たいなら、
この要素を observe に渡します。
observer.observe(target, options);
JavaScript重要ポイント①:options で「何を監視するか」をきちんと決める
MutationObserver は、何でもかんでも勝手に監視するわけではありません。
「どんな種類の変化を見たいか」を options で指定します。
childList(子要素の追加・削除)
observer.observe(target, {
childList: true
});
JavaScriptこれで、
- 子要素が追加された
- 子要素が削除された
ときに通知されます。
例えば、チャットログに新しいメッセージが追加されたとき、
通知したい、スクロールしたい、などの用途で使えます。
attributes(属性の変更)
observer.observe(target, {
attributes: true
});
JavaScriptこれで、
classが変わったidが変わったstyleが変わった- その他の属性が変わった
ときに通知されます。
「この要素の class が変わったら何かしたい」
といったときに使えます。
characterData(テキストノードの変更)
observer.observe(targetTextNode, {
characterData: true
});
JavaScriptこれは少しマニアックですが、
テキストノードそのものが変わったときに通知されます。
ふつうは、subtree: true と組み合わせて
「この要素の中のテキストが変わったら知りたい」
という形で使うことが多いです。
subtree(子孫要素も含めて全部見る)
observer.observe(target, {
childList: true,
subtree: true
});
JavaScriptsubtree: true を付けると、target だけでなく、その中のすべての子孫要素も監視対象になります。
「このコンテナの中で起きる変化は全部知りたい」
というときは、subtree: true を付けるのが定番です。
実例①:子要素の追加・削除を監視する
HTML
<div id="list"></div>
<button id="add">追加</button>
<button id="clear">全部消す</button>
JavaScript
const list = document.querySelector("#list");
const addBtn = document.querySelector("#add");
const clearBtn = document.querySelector("#clear");
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
console.log("子要素の変化がありました");
console.log("追加されたノード:", mutation.addedNodes);
console.log("削除されたノード:", mutation.removedNodes);
}
});
});
observer.observe(list, {
childList: true
});
addBtn.addEventListener("click", () => {
const div = document.createElement("div");
div.textContent = "アイテム " + (list.children.length + 1);
list.appendChild(div);
});
clearBtn.addEventListener("click", () => {
list.innerHTML = "";
});
JavaScriptここで起きていることはこうです。
#listの「子要素の追加・削除」を監視- 追加されたノードは
mutation.addedNodes - 削除されたノードは
mutation.removedNodes
ボタンを押して要素を追加・削除すると、
コンソールに「何が追加されたか・削除されたか」が表示されます。
「DOM がどう変わっているか」を目で確認するのに、とても良い練習になります。
実例②:属性の変更を監視する(class の変化など)
HTML
<div id="box" class="normal">ボックス</div>
<button id="toggle">クラス切り替え</button>
JavaScript
const box = document.querySelector("#box");
const toggleBtn = document.querySelector("#toggle");
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "attributes") {
console.log("属性が変わりました:", mutation.attributeName);
console.log("現在の class:", box.className);
}
});
});
observer.observe(box, {
attributes: true
});
toggleBtn.addEventListener("click", () => {
box.classList.toggle("active");
});
JavaScriptここでは、
attributes: trueで属性の変化を監視mutation.attributeNameで「どの属性が変わったか」が分かるbox.classNameで現在のクラス名を確認
という流れになっています。
「CSS クラスが変わったタイミングで何かしたい」
というのは、実務でもよくあるパターンです。
重要ポイント②:いつ MutationObserver を使うべきか
「自分で変更しているだけなら、たいてい不要」
例えば、自分のコードの中で
list.appendChild(item);
box.classList.add("active");
JavaScriptのように DOM を変更しているなら、
その直後に自分で処理を書けばよく、
わざわざ MutationObserver で監視する必要はありません。
list.appendChild(item);
doSomethingAfterAppend();
JavaScriptこのほうがシンプルです。
MutationObserver が本領を発揮するのは、
「いつ・どこで DOM が変わるか自分では分からないとき」です。
典型的に向いている場面
- 外部ライブラリやフレームワークが DOM を勝手に書き換える
- サーバーサイドレンダリングや他のスクリプトが DOM を更新する
- 「このコンテナの中で何か変わったら、とにかく知りたい」
例えば、
「外部チャットウィジェットがメッセージを追加したら、自分も何かしたい」
「CMS が生成する HTML が変わったら、そこにフックしたい」
といったときに、MutationObserver はとても強力です。
重要ポイント③:パフォーマンスと「監視しすぎない」感覚
監視範囲が広すぎると重くなる
subtree: true で document.body 全体を監視して、childList: true, attributes: true, characterData: true を全部オンにすると、
ページ中のほぼすべての変化が通知されます。
これは一見便利ですが、
変化が多いページでは、コールバックが大量に呼ばれて重くなる 可能性があります。
なので、
- 監視する要素はできるだけ狭く
- 必要な種類の変化だけをオンにする
という意識が大事です。
使い終わったら disconnect する
もう監視がいらなくなったら、observer.disconnect() で監視を止められます。
observer.disconnect();
JavaScript例えば、
- 一度だけ変化を検知できればいい
- ページの特定のフェーズだけ監視したい
といった場合は、
適切なタイミングで disconnect することで、
無駄な監視を減らせます。
初心者として MutationObserver で本当に掴んでほしいこと
- MutationObserver は「DOM の変化を監視するための API」
new MutationObserver(callback)で監視役を作り、observe(要素, options)でどこを見るか決めるchildList(子要素の追加・削除)、attributes(属性変更)、characterData(テキスト変更)を必要に応じて使い分ける- 「自分で変えているだけなら不要」「他人(ライブラリ・外部コード)が変えるときに真価を発揮する」
- 監視範囲を広げすぎると重くなるので、対象とオプションはできるだけ絞る
まずは、
- 「子要素の追加・削除をログに出す」
- 「class が変わったらログに出す」
この 2 つの小さな例を、自分の手で書いてみてください。
DOM が「静的な木」ではなく、
「変化し続けるものを、コードで“観察”できる対象」 に見えてきたら、
MutationObserver を使いこなす準備はもうできています。
