概要: このチュートリアルでは、JavaScriptのMutationObserver
APIを使用して、DOMツリーに加えられた変更を監視する方法を学びます。
JavaScript MutationObserver APIの紹介
MutationObserver
APIを使用すると、DOMツリーに加えられた変更を監視できます。DOMノードが変更されたときに、コールバック関数を呼び出して変更に対応できます。
MutationObserver
APIを使用するための基本的な手順は次のとおりです。
まず、DOMが変更されたときに実行されるコールバック関数を定義します。
function callback(mutations) {
//
}
Code language: JavaScript (javascript)
次に、MutationObserver
オブジェクトを作成し、MutationObserver()
コンストラクターにコールバックを渡します。
let observer = new MutationObserver(callback);
Code language: JavaScript (javascript)
3番目に、observe()
メソッドを呼び出して、DOMの変更の監視を開始します。
observer.observe(targetNode, observerOptions);
Code language: JavaScript (javascript)
observe()
メソッドには2つのパラメーターがあります。target
は、変更を監視するノードのサブツリーのルートです。observerOptions
パラメーターには、監視のコールバックにどのDOM変更を報告する必要があるかを指定するプロパティが含まれています。
最後に、disconnect()
メソッドを呼び出して、DOM変更の監視を停止します。
observer.disconnect();
Code language: JavaScript (javascript)
MutationObserverのオプション
observe()
メソッドの2番目の引数を使用すると、MutationObserver
を記述するためのオプションを指定できます。
let options = {
childList: true,
attributes: true,
characterData: false,
subtree: false,
attributeFilter: ['attr1', 'attr2'],
attributeOldValue: false,
characterDataOldValue: false
};
Code language: JavaScript (javascript)
すべてのオプションを使用する必要はありません。ただし、MutationObserver
を機能させるには、childList
、attributes
、またはcharacterData
の少なくとも1つをtrue
に設定する必要があります。そうしないと、observer()
メソッドはエラーをスローします。
子要素の変更の監視
次のリストがあると仮定します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MutationObserver Demo: ChildList</title>
</head>
<body>
<ul id="language">
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
<li>TypeScript</li>
</ul>
<button id="btnStart">Start Observing</button>
<button id="btnStop">Stop Observing</button>
<button id="btnAdd">Add</button>
<button id="btnRemove">Remove the Last Child</button>
<script src="app.js"></script>
</body>
</html>
Code language: HTML, XML (xml)
次の例は、子ノードの変更を監視するために、mutation options
オブジェクトのchildList
プロパティを使用する方法を示しています。
まず、querySelector()
メソッドを使用して、list
やbuttons
などの要素を選択します。デフォルトでは、Stop Observing
ボタンはdisabled
です。
// selecting list
let list = document.querySelector('#language');
// selecting buttons
let btnAdd = document.querySelector('#btnAdd');
let btnRemove = document.querySelector('#btnRemove');
let btnStart = document.querySelector('#btnStart');
let btnStop = document.querySelector('#btnStop');
btnStop.disabled = true;
Code language: JavaScript (javascript)
次に、MutationObserver
のコールバックとして使用されるlog()
関数を宣言します。
function log(mutations) {
for (let mutation of mutations) {
if (mutation.type === 'childList') {
console.log(mutation);
}
}
}
Code language: JavaScript (javascript)
3番目に、新しいMutationObserver
オブジェクトを作成します。
let observer = new MutationObserver(log);
Code language: JavaScript (javascript)
4番目に、Start Observing
ボタンがクリックされたときに、options
オブジェクトのchildList
をtrue
に設定してobserve()
メソッドを呼び出すことにより、リスト要素の子ノードへのDOM変更の監視を開始します。
btnStart.addEventListener('click', function () {
observer.observe(list, {
childList: true
});
btnStart.disabled = true;
btnStop.disabled = false;
});
Code language: JavaScript (javascript)
5番目に、add
ボタンがクリックされたときに、新しいリストアイテムを追加します。
let counter = 1;
btnAdd.addEventListener('click', function () {
// create a new item element
let item = document.createElement('li');
item.textContent = `Item ${counter++}`;
// append it to the child nodes of list
list.appendChild(item);
});
Code language: JavaScript (javascript)
6番目に、Remove
ボタンがクリックされたときに、list
の最後の子を削除します。
btnRemove.addEventListener('click', function () {
list.lastElementChild ?
list.removeChild(list.lastElementChild) :
console.log('No more child node to remove');
});
Code language: JavaScript (javascript)
最後に、MutationObserver
オブジェクトのdisconnect()
メソッドを呼び出すことにより、Stop Observing
ボタンがクリックされたときにDOM変更の監視を停止します。
btnStop.addEventListener('click', function () {
observer.disconnect();
// set button states
btnStart.disabled = false;
btnStop.disabled = true;
});
Code language: JavaScript (javascript)
すべてをまとめる
(function () {
// selecting the list
let list = document.querySelector('#language');
// selecting the buttons
let btnAdd = document.querySelector('#btnAdd');
let btnRemove = document.querySelector('#btnRemove');
let btnStart = document.querySelector('#btnStart');
// disable the stop button
let btnStop = document.querySelector('#btnStop');
btnStop.disabled = true;
function log(mutations) {
for (let mutation of mutations) {
if (mutation.type === 'childList') {
console.log(mutation);
}
}
}
let observer = new MutationObserver(log);
btnStart.addEventListener('click', function () {
observer.observe(list, {
childList: true
});
btnStart.disabled = true;
btnStop.disabled = false;
});
btnStop.addEventListener('click', function () {
observer.disconnect();
// Set the button state
btnStart.disabled = false;
btnStop.disabled = true;
});
let counter = 1;
btnAdd.addEventListener('click', function () {
// create a new item element
let item = document.createElement('li');
item.textContent = `Item ${counter++}`;
// append it to the child nodes of list
list.appendChild(item);
});
btnRemove.addEventListener('click', function () {
list.lastElementChild ?
list.removeChild(list.lastElementChild) :
console.log('No more child node to remove');
});
})();
Code language: JavaScript (javascript)
すべてのコードがIIFE(即時実行関数式)に配置されていることに注意してください。
属性の変更の監視
属性の変更を監視するには、options
オブジェクトの次のattributes
プロパティを使用します。
let options = {
attributes: true
}
Code language: JavaScript (javascript)
他の属性を無視しながら、1つ以上の特定のattributes
への変更を監視する場合は、attributeFilter
プロパティを使用できます。
let options = {
attributes: true,
attributeFilter: ['class', 'style']
}
Code language: JavaScript (javascript)
この例では、MutationObserver
は、class
またはstyle
属性が変更されるたびにコールバックを呼び出します。
サブツリーの変更の監視
ターゲットノードとそのノードのサブツリーを監視するには、options
オブジェクトのsubtree
プロパティをtrue
に設定します。
let options = {
subtree: true
}
Code language: JavaScript (javascript)
文字データの変更の監視
ノードのテキストコンテンツの変更を監視するには、options
オブジェクトのcharacterData
プロパティをtrue
に設定します。
let options = {
characterData: true
}
Code language: JavaScript (javascript)
古い値へのアクセス
属性の古い値にアクセスするには、options
オブジェクトのattributeOldValue
プロパティをtrue
に設定します。
let options = {
attributes: true,
attributeOldValue: true
}
Code language: JavaScript (javascript)
同様に、options
オブジェクトのcharacterDataOldValue
プロパティをtrue
に設定することで、文字データの古い値にアクセスできます。
let options = {
characterData: true,
subtree: true,
characterDataOldValue: true
}
Code language: JavaScript (javascript)
MutationObserverの実用的な例
JavaScriptアプリケーションでは、ページ上の要素は通常、動的に生成されます。動的な要素を待つには、MutationObserver
を使用する必要があります。
次のwaitForElement()
関数は、MutationObserver
を使用してセレクターで指定された1つ以上の要素を待ちます。
function waitForElement(selector) {
return new Promise((resolve) => {
if (document.querySelector(selector)) {
return resolve(element);
}
const observer = new MutationObserver(() => {
const element = document.querySelector(selector);
if (element) {
resolve(element);
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
});
}
Code language: JavaScript (javascript)
仕組み。
waitForElement()
関数は、プロミスを返します。要素が利用可能になると、プロミスが解決されます。
まず、要素が利用可能な場合は要素を解決します。
if (document.querySelector(selector)) {
return resolve(element);
}
Code language: JavaScript (javascript)
次に、要素が利用できない場合は、DOMツリーを監視するための新しいMutationObserver
オブジェクトを作成します。
const observer = new MutationObserver(() => {
const element = document.querySelector(selector);
if (element) {
resolve(element);
observer.disconnect();
}
});
Code language: JavaScript (javascript)
オブザーバーオブジェクトは、要素が利用可能になるとresolve()関数を呼び出し、DOMツリーの監視を停止します。
3番目に、DOMツリー全体の要素を監視します。
observer.observe(document.body, {
childList: true,
subtree: true,
});
Code language: CSS (css)
waitForElement()
はPromise
を返すため、次のようにthen()
メソッドを使用できます。
waitForElement()('.a-class').then((element) => {
console.log('Element is ready');
console.log(element.textContent);
});
Code language: JavaScript (javascript)
または、await
構文を使用できます。
const element = await waitForElement()('.a-class');
console.log(element.textContent);
Code language: JavaScript (javascript)
このチュートリアルでは、DOMの変更を監視し、変更が発生するたびにコールバックを実行するJavaScript MutationObserver
APIについて学習しました。