ユーザーの選択した要素を検知して、後に出てくる要素のhtmlを書き換えたい!
ということを行う必要が出てきました。
その際、MutationObserverという便利な監視機能を使いましたので、改めてmdnを確認していきます。
MutationObserver
MutationObserver インターフェイスは、 DOM ツリーへ変更が加えられたことを監視することができる機能を提供します。これは DOM3 Events の仕様で定義されていた Mutation Events 機能の置き換えとして設計されたものです。
DOMに変更が加わると、しっかりそれを検知してくれるので、そのタイミングで任意のコールバックを起動すればよいです。
使い方
mdnより引用
(mdnの説明少ないので、補足しておきますね...)
// 変更を監視するノードを選択
const targetNode = document.getElementById("some-id");
// (変更を監視する) オブザーバーのオプション
const config = { attributes: true, childList: true, subtree: true };
// 変更が発見されたときに実行されるコールバック関数
const callback = function (mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === "childList") {
console.log("行いたい操作1");
} else if (mutation.type === "attributes") {
console.log("行いたい操作2");
}
}
};
// コールバック関数に結びつけられたオブザーバーのインスタンスを生成
const observer = new MutationObserver(callback);
// 対象ノードの設定された変更の監視を開始
observer.observe(targetNode, config);
// その後で、監視を停止することができる
observer.disconnect();
const targetNode = document.getElementById("some-id");
監視対象です。
このIDに対して変更が加わる、子や孫に変更が加わったら〜という対象となります。
ページ全体を見たいならbodyを指定してもよいですが、Next.jsのサイトだとレンダリングの度に発火するので、パフォーマンスを考える必要があります。
const config = { attributes: true, childList: true, subtree: true };
どのように監視するかを細かく指定できます
- childList
- 子ノードの追加や削除を監視するかどうかを指定します。trueに設定すると、子ノードの変更が監視されます。
- attributes
- 属性の変更を監視するかどうかを指定します。trueに設定すると、属性の変更が監視されます。
- characterData
- テキストノードの変更を監視するかどうかを指定します。trueに設定すると、テキストノードの変更が監視されます。
- subtree
- ターゲットノードの子孫ノードも監視するかどうかを指定します。trueに設定すると、子孫ノードも監視されます。
- attributeOldValue
- 属性の変更前の値を記録するかどうかを指定します。trueに設定すると、変更前の属性の値がoldValueプロパティに記録されます。
- characterDataOldValue
- テキストノードの変更前の値を記録するかどうかを指定します。trueに設定すると、変更前のテキストノードの値がoldValueプロパティに記録されます。
- attributeFilter
- 監視する属性名のリストを指定します。このプロパティを設定すると、指定された属性だけが監視されます。
ターゲット自身なのか、子なのか孫なのか分かり辛いので、表にしておきます!
プロパティ | 監視対象 |
---|---|
childList | 直接の子ノード |
attributes | ターゲット自身の属性 |
characterData | ターゲット自身のテキストノード |
subtree | ターゲットの全ての子孫ノード |
attributeOldValue | 変更前の属性値 |
characterDataOldValue | 変更前のテキストノード値 |
attributeFilter | 特定の属性名リスト |
const callback = function (mutationsList, observer) {
- mutationsList
- 監視対象に対して発生した変更を表すMutationRecordオブジェクトの配列です。
- DOM に生じた個々の変更を保持しています
- observer
- MutationObserverのインスタンス
- 監視を開始したり、止めたりすることができます。
for (const mutation of mutationsList) {
変更があった箇所を洗い出すために、検出された全ての変更(MutationRecordオブジェクト)を順番に処理しています。
mutation.type === "childList"
現在のmutationがchildList型の変更(子ノードの追加または削除)であるかどうかをチェックしています。
subtree:trueをobserver.observeに食わせていた場合、ここで検知できます。
- typeの中には
attributes
,CharacterData
,childList
のどれかが入ります。 - MutationRecordオブジェクトは、target(変更が発生したDOMノード)や、nextSibling(変更前の次の兄弟ノード)などいろいろ持っています
const observer = new MutationObserver(callback);
コールバックは、DOMツリーに変更が発生したときに呼び出される関数です。
observer.observe(targetNode, config);
インスタンス化したMutationObserverにターゲットと設定を指定します。
observer.disconnect();
これを呼び出すと、MutationObserverがターゲットノードおよびその子孫ノードの変更を監視するのを停止します。
これにより、リソースの使用を抑えたり、不要な監視を避けたりすることができます。
おわりに
1行づつ見ていくと、それほど難しいことをしているわけではないことがわかりました。
mdnにあるJsの、 targetNode
、config
、行いたい操作
部分をさわればすぐに使い始めることができます。
さらに考える必要があるのは下記2点です。
- エラーハンドリング : 要素がなかった時や、callback内のエラーを考慮する
- パフォーマンス : ターゲットを絞る、subtreeは設定しない
面白いのでぜひ一度、使ってみてください!