JavaScript | Web API:DOM 拡張 API - MutationObserver

JavaScript JavaScript
スポンサーリンク

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
});
JavaScript

subtree: 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: truedocument.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 を使いこなす準備はもうできています。

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