【PR】を含みます。

フロントエンド

【JavaScript】Mutation ObserverでDOMの変化を監視する方法

JavaScript Mutation ObserverでDOMの変化を監視する方法

JavaScriptでWebページを動的に操作する際、DOM(ドキュメントオブジェクトモデル)の変化をリアルタイムに検知したい場面は多々あります。

たとえば、Ajax通信で新しい要素が追加された時や、特定のクラスや属性が動的に付与されたとき、その変化をトリガーに処理を実行したいこともあるでしょう。

従来はsetIntervalやイベントリスナーで無理やり監視していたケースもありますが、実は「Mutation Observer」を使えば、効率よく簡単にDOMの変化を監視できます。

本記事では、Mutation Observerの基本と実践的な使い方、コピペですぐ使えるサンプルコードまで、初心者にも分かりやすく解説します。

Mutation Observerのオプション一覧

オプション
説明
childList
対象ノードの子ノードの追加と削除を監視します。
値:trueまたはfalse
attributes
対象ノードの属性の変更を監視します。
値:trueまたはfalse
characterData
対象ノードのテキストの変更を監視します。
値:trueまたはfalse
subtree
対象ノードおよびその子孫ノードの変化を監視します。
値:trueまたはfalse
attributeFilter
特定の属性の変更のみを監視します。
値:監視する属性名の配列
attributeOldValue
属性の変更前の値を記録します。
attributestrueの場合にのみ使用されます。
値:trueまたはfalse
characterDataOldValue
テキストの変更前の値を記録します。
characterDatatrueの場合にのみ使用されます。
値:trueまたはfalse

必須オプションについて

MutationObserverを正しく動作させるためには、以下のいずれか1つのオプションを指定する必要があります。

  • childList

  • attributes

  • characterData

【実装・テンプレートコード】Mutation ObserverでDOMの変化を監視

以下のコードはテンプレートのため複数のオプションを設定していますが、実務で使用する場合は必要なオプションのみを設定して使用することが多いです。

JavaScript
Copy
// 監視対象の要素を取得
const targetNode = document.querySelector('.watchElement');
// オプション設定
const config = {
  childList: true,
  attributes: true,
  characterData: true,
  subtree: true,
  attributeFilter: ['class', 'style', 'data-demo'],
  attributeOldValue: true,
  characterDataOldValue: true,
};
// 変更が発生したときに呼び出されるコールバック関数
const callback = function(mutationsList, observer) {
  for (let mutation of mutationsList) {
    // 変更があった箇所(要素)を取得
    let targetElement = mutation.target;
    if (mutation.type === 'childList') {
      // 子ノードに追加または削除があった場合の処理
      console.log(`子ノードが追加または削除されました。変更箇所:`, targetElement);
    } else if (mutation.type === 'attributes') {
      // 属性に変更があった場合の処理
      console.log(`${mutation.attributeName} 属性が変更されました。変更箇所:`, targetElement, `古い値: ${mutation.oldValue}`);
    } else if (mutation.type === 'characterData') {
      // テキストが変更された場合の処理
      console.log(`テキストが変更されました。変更箇所:`, targetElement, `古い値: ${mutation.oldValue}`);
    }
  }
};
// MutationObserverのインスタンスを作成
const observer = new MutationObserver(callback);
// 監視対象の要素にMutationObserverを設定
observer.observe(targetNode, config);

【サンプル】Mutation ObserverでDOMの変化を監視

「クリック」ボタンを押下すると、上記実装コードで実装しているconsole.log()の内容をディベロッパツールのコンソールで確認できます。

監視対象要素

サンプルで使用しているコード

HTML
Copy
<div class="watchElement">監視対象要素</div>
<button id="btn">クリック</button>
JavaScript
Copy
// 監視対象の要素を取得
const targetNode = document.querySelector('.watchElement');
// オプション設定
const config = {
  childList: true,
  attributes: true,
  characterData: true,
  subtree: true,
  attributeFilter: ['class', 'style', 'data-demo'],
  attributeOldValue: true,
  characterDataOldValue: true,
};
// 変更が発生したときに呼び出されるコールバック関数
const callback = function(mutationsList, observer) {
  for (let mutation of mutationsList) {
    // 変更があった箇所(要素)を取得
    let targetElement = mutation.target;
    if (mutation.type === 'childList') {
      // 子ノードに追加または削除があった場合の処理
      console.log(`子ノードが追加または削除されました。変更箇所:`, targetElement);
    } else if (mutation.type === 'attributes') {
      // 属性に変更があった場合の処理
      console.log(`${mutation.attributeName} 属性が変更されました。変更箇所:`, targetElement, `古い値: ${mutation.oldValue}`);
    } else if (mutation.type === 'characterData') {
      // テキストが変更された場合の処理
      console.log(`テキストが変更されました。変更箇所:`, targetElement, `古い値: ${mutation.oldValue}`);
    }
  }
};
// MutationObserverのインスタンスを作成
const observer = new MutationObserver(callback);
// 監視対象の要素にMutationObserverを設定
observer.observe(targetNode, config);
const btn = document.getElementById('btn');
btn.addEventListener('click', function () {
  // 要素の追加
  let createElement = document.createElement('div');
  createElement.textContent = '要素を追加';
  targetNode.appendChild(createElement);
  // 属性の変更
  targetNode.classList.add('demo');
  targetNode.setAttribute('data-demo', 'demo');
  // テキストの変更
  targetNode.firstChild.data = '新しいテキスト';
}, {once: true});

【実装コード】子ノードの追加と削除を監視

JavaScript
Copy
// 監視対象の要素を取得
const targetNode = document.querySelector('.watchElement');
// オプション設定
const config = {
  childList: true,
};
// 変更が発生したときに呼び出されるコールバック関数
const callback = function(mutationsList, observer) {
  for (let mutation of mutationsList) {
    if (mutation.type === 'childList') {
      // 子ノードに追加または削除があった場合の処理
      console.log('子ノードが追加または削除されました。');
    }
  }
};
// MutationObserverのインスタンスを作成
const observer = new MutationObserver(callback);
// 監視対象の要素にMutationObserverを設定
observer.observe(targetNode, config);

【実装コード】属性の変更を監視

class等の属性の変更を監視することができます。

JavaScript
Copy
// 監視対象の要素を取得
const targetNode = document.querySelector('.watchElement');
// オプション設定
const config = {
  attributes: true,
};
// 変更が発生したときに呼び出されるコールバック関数
const callback = function(mutationsList, observer) {
  for (let mutation of mutationsList) {
    if (mutation.type === 'attributes') {
      // 属性に変更があった場合の処理
      console.log('属性が変更されました。');
    }
  }
};
// MutationObserverのインスタンスを作成
const observer = new MutationObserver(callback);
// 監視対象の要素にMutationObserverを設定
observer.observe(targetNode, config);

【実装コード】テキストの変更を監視

JavaScript
Copy
// 監視対象の要素を取得
const targetNode = document.querySelector('.watchElement');
// オプション設定
const config = {
  characterData: true,
  subtree: true,
};
// 変更が発生したときに呼び出されるコールバック関数
const callback = function(mutationsList, observer) {
  for (let mutation of mutationsList) {
    if (mutation.type === 'characterData') {
      // テキストが変更された場合の処理
      console.log('テキストが変更されました。');
    }
  }
};
// MutationObserverのインスタンスを作成
const observer = new MutationObserver(callback);
// 監視対象の要素にMutationObserverを設定
observer.observe(targetNode, config);

テキストの変更を監視する場合の注意点

対象ノードのテキストの変更をcharacterDataとして検知したい場合、オプションにsubtree: trueを指定します。

テキストの変更は、dataプロパティを使用してテキストノードを直接変更する必要があります。

例:targetNode.firstChild.data = '新しいテキスト';

textContent等でテキスト変更が行われている場合、characterDataで検知することはできません。

textContentでテキストが変更されている場合、childListを使用して監視する必要があります。

【実装コード】Mutation Observerの監視を停止する方法

disconnect()メソッドを呼び出すことで、Mutation Observerの監視を停止できます。

以下のコードは、子ノードに追加または削除があった場合に監視を停止します。

JavaScript
Copy
// 監視対象の要素を取得
const targetNode = document.querySelector('.watchElement');
// オプション設定
const config = {
  childList: true,
};
// 変更が発生したときに呼び出されるコールバック関数
const callback = function(mutationsList, observer) {
  for (let mutation of mutationsList) {
    if (mutation.type === 'childList') {
      // 子ノードに追加または削除があった場合の処理
      console.log('子ノードが追加または削除されました。');
      observer.disconnect(); // 監視を停止
      break; // ループを抜ける
    }
  }
};
// MutationObserverのインスタンスを作成
const observer = new MutationObserver(callback);
// 監視対象の要素にMutationObserverを設定
observer.observe(targetNode, config);

まとめ

Mutation Observerを活用することで、JavaScriptでDOMの変化を効率良く監視し、動的なコンテンツやユーザー操作にも柔軟に対応できるようになります。

従来の方法よりも簡潔かつ高性能で、実務でも活用シーンが多いテクニックです。

本記事で紹介したサンプルコードや設定方法を活かして、ぜひご自身のプロジェクトに役立ててください。

「○○を検知したいけどどうすれば…」と感じたときは、Mutation Observerの利用を思い出しましょう!

-フロントエンド
-