【初心者向け】JavaScriptで要素の監視を行う方法

はじめに

こんにちは。株式会社TOKOSのタクヤです。

「HTMLが変化したときに自動で監視できたら便利なのに…」と感じたことはありませんか?

この記事では、JavaScriptでHTMLの変化を監視する方法として、MutationObserverの使い方を解説します。初学者の方でも理解できるよう、構文と具体的なコード例を交えて紹介していきます。

対象読者

  • JavaScriptを学び始めた方
  • JavaScriptを用いて要素の変更を監視したい方

MutationObserverとは

MutationObserver(ミューテーションオブザーバー)とは、Webページ上の要素(DOM)が変化したことを監視するためのJavaScriptの仕組みです。

例えば、HTMLの要素が追加・削除されたり、テキストが書き換えられたり、属性(classやidなど)が変更された場合に、JavaScriptがリアルタイムでそれを監視してくれると言った具合です。

Webサイトでは、要素が追加・削除されたり、属性が変更されたりすることがあります。こうしたDOMの変化をリアルタイムで監視して、任意の処理を実行できるのがMutationObserverの役割です。

以前はMutation Eventsという仕組みがありましたが、今ではパフォーマンスと柔軟性の面で優れたMutationObserverが主流だそうです。

基本的な使い方

index.html
<span id="target-element">ISSENブログ</span>
main.js
// 監視対象の要素を取得
const targetElement = document.getElementById('target-element');

// 変更があったときに実行される関数の定義
const callback = () => {
    console.log("変更がありました");
};

// オプションの設定(どの変更を監視するか)
const config = {
  characterData: true // タグ内のテキストの変更を監視
};

// インスタンス生成と監視開始
const observer = new MutationObserver(callback);
observer.observe(targetElement, config);

configでは、どの種類の変化を監視するかを細かく設定できます。
characterDataはタグの中にあるテキストの書き換えを監視するオプションです。
callbackはコールバック関数で監視対象に変更があった際に自動で呼び出される関数です。

実例

少し複雑な実例として、もとからあるHTML要素の中に新しく要素を作成してそれを監視してみます。

index.html
<button id="add-btn">テキストを追加</button>
<div id="target-element"></div>
main.js
// 監視対象の要素を取得
const targetElement = document.getElementById('target-element');
// ボタンの要素を取得
const btnElement = document.getElementById('add-btn');

// 変更があったときに実行される関数の定義
const callback = () => {
    console.log("新しい要素が追加されました");
};

// オプションの設定(どの変更を監視するか)
const config = {
  childList: true // 子要素が追加・削除を監視
};

// インスタンス生成と監視開始
const observer = new MutationObserver(callback);
observer.observe(targetElement, config);

// ボタンをクリックで要素を追加
btnElement.addEventListener('click', () => {
  const newElement = document.createElement('span');
  newElement.textContent = '新しいテキストです';
  targetElement.appendChild(newElement);
});

監視オプションの詳細解説

以下は、MutationObserverで指定できる主なオプションとその意味をまとめた表です。

オプション名説明
childList子要素の追加・削除を監視します。
attributesHTMLタグの属性(classやidなど)の変更を監視します。
characterDataテキストノードの中身が変更されたときに検出します。
subtree指定した要素の中にある子や孫の要素もすべて含めて監視対象にします。

テキスト内容の変化を監視(characterData)

タグの中にあるテキストそのものが書き換えられたときに監視したいときは、characterData: true を使います。
ただし、テキストそのものを監視対象に指定する必要があります。

index.html
<span id="target-element">ISSENブログ</span>
main.js
// 監視対象の要素を取得
const targetElement = document.getElementById('target-element');

// 変更があったときに実行される関数の定義
const callback = () => {
    console.log("テキストに変更がありました");
};

// オプションの設定(タグ内のテキストの変更)
const config = {
  characterData: true
};

// インスタンス生成と監視開始
const observer = new MutationObserver(callback);
observer.observe(targetElement, config);

// テキストの書き換え
targetElement.textContent = "新しい文字"; // ここで監視される

タグの属性の変更を監視 (attributes)

HTMLタグには classiddisabled などの属性があります。
これが変更されたときに監視したい場合は、attributes: true を設定します。

index.html
<span id="target-element">ISSENブログ</span>
main.js
// 監視対象の要素を取得
const targetElement = document.getElementById('target-element');

// 変更があったときに実行される関数の定義
const callback = () => {
    console.log("属性に変更がありました");
};

// オプションの設定(属性の変更)
const config = {
  attributes: true
};

// インスタンス生成と監視開始
const observer = new MutationObserver(callback);
observer.observe(targetElement, config);

// 属性の変更
target.setAttribute('class', 'changed'); // ここで監視される

子要素の追加・削除を検知する(childList)

指定した要素の「直下の子要素」に変更があったかどうかを監視します。 例えば、appendChild()removeChild()で子要素が追加・削除されたときに反応します。
ただし、子孫要素(子の中の子など)の変更までは監視できません。 それらも含めて監視したい場合は、次のsubtreeオプションを追加します。

index.html
<div id="target-element"></div>
main.js
// 監視対象の要素を取得
const targetElement = document.getElementById('target-element');

// 変更があったときに実行される関数の定義
const callback = () => {
    console.log("子要素に変更がありました");
};

// オプションの設定(子要素以下の変更)
const config = {
  childList: true // 子要素以下も対象になる
};

// インスタンス生成と監視開始
const observer = new MutationObserver(callback);
observer.observe(targetElement, config);

const newElement = document.createElement('span');
newElement.textContent = '新しいテキストです';
targetElement.appendChild(newElement); // ここで監視される

子要素や孫要素もまとめて監視(subtree)

subtree: true を指定すると、指定した要素の「子ども」や「孫」の要素もすべてまとめて監視できます。
これがない場合、指定した要素自身に変化があったときしか反応しません。

index.html
<div id="target-element">
  <div id="child-element">
    <span>テキスト</span>
  </div>
</div>
main.js
// 監視対象の要素を取得
const targetElement = document.getElementById('target-element');

// 変更があったときに実行される関数の定義
const callback = () => {
    console.log("子要素に変更がありました");
};

// オプションの設定(子要素以下の変更)
const config = {
  childList: true,
  subtree: true // ← 子要素以下も対象になる
};

// インスタンス生成と監視開始
const observer = new MutationObserver(callback);
observer.observe(targetElement, config);

よくあるミスと注意点

監視対象の要素が存在しない

main.js
const targetElement = document.getElementById('target-element');

この行で null が返ると、その後の observer.observe() がエラーになります。監視前に必ず対象があるか確認しましょう。

オプション設定の漏れ

たとえば子要素の追加を監視したいのに childList: true を設定していなければ、何も監視されません。目的に応じて正しく config を指定しましょう。

不要な範囲の監視でパフォーマンスが低下

subtree: trueにすると、対象の子孫すべてを監視します。大量のDOMを対象にする場合は、必要最小限に絞ることが重要です。

終わりに

今回はJavaScriptでHTMLの変化を監視する方法を解説しました。
JavaScriptのMutationObserverを使えば、DOMの変更を効率的に監視ができ非同期通信やユーザー操作によって変化するWebサイトではとても役立つ技術です。是非参考にしてみてください。