JavaScript

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

More than 1 year has passed since last update.

JavaScriptのMutationObserverを用いて、ウェブページにおけるDOMの変化(属性の変化、文字データの変化、子要素リストの変化)を監視する方法を紹介します。

また、あわせて僕が作成したライブラリObserver.jsを用いて監視する方法も紹介します。


MutationObserverで監視する方法


基本的な監視方法

どの変化を監視する場合も、基本的には以下のような流れとなります。


  1. 監視ターゲットの取得

  2. オブザーバーの作成

  3. 監視オプションの作成

  4. 監視の開始

  5. 監視の停止


属性の変化を監視


すべての属性の変化を監視

//監視ターゲットの取得

const target = document.getElementById("target-id");

//オブザーバーの作成
const observer = new MutationObserver(records => {
//なんらかの処理を行なう
});

//監視オプションの作成
const options = {
attributes: true
};

//監視の開始
observer.observe(target, options);

//監視の停止(実際には特定条件下で実行)
let shouldStopObserving = false;
if(shouldStopObserving){
observer.disconnect();
}


特定属性の変化を監視

//監視ターゲットの取得

const target = document.getElementById("target-id");

//オブザーバーの作成
const observer = new MutationObserver(records => {
//なんらかの処理を行なう
});

//監視オプションの作成
const options = {
attributes: true,
attributeFilter: ["class", "style"]
};

//監視の開始
observer.observe(target, options);

//監視の停止(実際には特定条件下で実行)
let shouldStopObserving = false;
if(shouldStopObserving){
observer.disconnect();
}


文字データの変化を監視

//監視ターゲットの取得

const target = document.getElementById("target-id");

//オブザーバーの作成
const observer = new MutationObserver(records => {
//なんらかの処理を行なう
});

//監視オプションの作成
const options = {
characterData: true
};

//監視の開始
observer.observe(target, options);

//監視の停止(実際には特定条件下で実行)
let shouldStopObserving = false;
if(shouldStopObserving){
observer.disconnect();
}


子要素リストの変化を監視

//監視ターゲットの取得

const target = document.getElementById("target-id");

//オブザーバーの作成
const observer = new MutationObserver(records => {
//なんらかの処理を行なう
});

//監視オプションの作成
const options = {
childList: true
};

//監視の開始
observer.observe(target, options);

//監視の停止(実際には特定条件下で実行)
let shouldStopObserving = false;
if(shouldStopObserving){
observer.disconnect();
}


監視オプション

上記の例以外にも、監視オプションで各種プロパティを指定することで、監視方法を細かく設定することができます。

プロパティ

指定
内容

attributes
Boolean
いずれか必須
属性の変化を監視

characterData
Boolean
文字データの変化を監視

childList
Boolean
子要素リストの変化を監視

attributeOldValue
Boolean
任意
変更前の属性を使用

characterDataOldValue
Boolean
任意
変更前の文字データを使用

attributeFilter
String[]
任意
監視対象とする属性名リスト

subtree
Boolean
任意
子孫ノードを監視対象に含める

たとえば、以下のような監視オプションを指定すると、ありとあらゆる変化を監視できます(その分負荷はかかりそうですが)。

const options = {

attributes: true,
attributeOldValue: true,
characterData: true,
characterDataOldValue: true,
childList: true,
subtree: true
};


実例

MutationObserverを用いて、Twitterのタイムライン上に新着ツイートバーが現れた際、自動的にクリックして表示するユーザースクリプトです。


Twitterの新着ツイートを自動表示.user.js

// ==UserScript==

// @name Twitterの新着ツイートを自動表示
// @namespace http://munieru.jp
// @version 0.1
// @description Twitterの新着ツイートを自動表示します。
// @author @munieru_jp
// @match https://twitter.com/
// @grant none
// ==/UserScript==

(function() {
'use strict';

const CLASS_NEW_TWEETS_BAR = "new-tweets-bar";

//監視ターゲットの取得
const timeline = document.getElementById("timeline");
const streamContainer = timeline.querySelector('.stream-container');
const streamItem = streamContainer.querySelector('.stream-item');

//オブザーバーの作成
const observer = new MutationObserver(records => {
records.forEach(record => {
Array.from(record.addedNodes)
.filter(node => node.classList.contains(CLASS_NEW_TWEETS_BAR))
.forEach(node => node.click());
});
});

//監視オプションの作成
const options = {
childList: true
};

//監視の開始
observer.observe(streamItem, options);
})();



Observer.jsで監視する方法

MutationObserverのAPIがあまりイケていないため、独自のオブザーバークラスを実装し、ライブラリ化してGitHubで公開しました。

このライブラリを使うと、以下のように流れるようなインターフェースでコードを記述できます。


Twitterの新着ツイートを自動表示.user.js

// ==UserScript==

// @name Twitterの新着ツイートを自動表示
// @namespace http://munieru.jp
// @version 0.1
// @description Twitterの新着ツイートを自動表示します。
// @author @munieru_jp
// @match https://twitter.com/
// @grant none
// @require https://cdn.rawgit.com/munierujp/Observer.js/d0401132a1276910692fc53ed4012ef5efad25f3/Observer.min.js
// ==/UserScript==

(function() {
'use strict';

const CLASS_NEW_TWEETS_BAR = "new-tweets-bar";

//監視ターゲットの取得
const timeline = document.getElementById("timeline");
const streamContainer = timeline.querySelector('.stream-container');
const streamItem = streamContainer.querySelector('.stream-item');

//監視の開始
Observer.of(streamItem).childList().hasChanged(records => {
records.forEach(record => {
Array.from(record.addedNodes)
.filter(node => node.classList.contains(CLASS_NEW_TWEETS_BAR))
.forEach(node => node.click());
});
}).start();
})();


その他の使用方法については、上記リポジトリのREADMEを参照してください。


参考リンク