はりをきば

そこにピカマンがいる限り 私はテイッハットウッをやめない

【JavaScript】 MutationObserverの使い方 個人的まとめ

¯注意
この記事はを最後に更新されていない。
更新日が1年以上前の記事はリンク切れしていたり、情報としては役に立たなくなっている可能性あり。
ˆMixed Contentについて
この記事は投稿日が古い為、記事中の画像は非SSL(http://)で貼られている。(※投稿画像以外は全てSSL)
この件に関して詳しくはこちらを参照。

DOMの変更を検知するイベントが非推奨になり
代わりに新しく作られたMutationObserverなるもの。

MutationObserver - Web API インターフェイス | MDN

https://developer.mozilla.org/ja/docs/Web/API/MutationObserver


DOM変更検知は結構よく使うもの(特にユーザースクリプト)なのに
日本語の解説サイトが↑のMDNとMSDNここくらいしかなく
その都度ググったり手探りで片っ端から試すのが面倒なので以下に自分用にまとめる。



コンストラクタ

まずインスタンスを作る。
window.MutationObserverコンストラクタの引数に、DOM変更を検知した際に実行する関数を渡す。

function omega(data1, data2) {
	alert("ワレハメシアナリ");
	console.log(data1);
	console.log(data2);
}

var mo = new MutationObserver(omega);

これで呼び出された関数には
第一引数に変更されたDOMの詳細が入った連想配列(MutationRecordsと呼ばれる)を含む配列が、
第二引数に呼び出し元のMutationObserverのインスタンスが入る。



メソッド

observe

インスタンスを作成しただけでは監視は行われない。
observeメソッドを実行して初めて監視が始まる。

var pikaman = document.getElementById("pikaman"),
    options = {childList: true};

var mo = new MutationObserver(omega);
    mo.observe(pikaman, options);

第一引数に監視対象のノード、第二引数に何をどこまで監視するかのオプションを書いた連想配列を入れる。
指定できるオプションは以下の通り。

基本オプション
名前 監視対象
attributes 真偽値 対象ノードの属性全般
(属性名/属性値は問わない)
characterData 真偽値 対象ノードの子となるテキストノード
childList 真偽値 対象ノードの子ノード

どれもデフォルトでfalse。trueで有効化。
ここで言う監視とは、「新規/変更/削除」全てのアクションを指す。
上記の3つが基本にして、どれか1つ必須。
じゃないと下記の追加オプションは指定できない。

追加オプション
名前 必須 効果
attributeFilter 配列 attributes 監視する属性名を限定する
attributeOldValue 真偽値 attributes 記録するDOMデータに変更前の属性データを加える
characterDataOldValue 真偽値 characterData 記録するDOMデータに変更前のテキストノードを加える
subtree 真偽値 childList 対象ノードの子孫ノードも監視する



  • attributeFilter

監視する属性名を指定したものだけに絞る。
例えばaタグのhrefとtargetの変更だけキャプチャしたい場合は以下のようにする。

var attr    = ["href", "target"],
    options = {attributes: true, attributeFilter: attr};

var mo = new MutationObserver(omega);
    mo.observe(pikaman, options);



  • attributeOldValue

MutationRecordsのoldValueというプロパティに変更前の属性値を記録するようにする。

  • characterDataOldValue

同上。属性値の代わりに変更前のテキストノードを記録。

  • subtree

対象の子ノードだけでなく、子孫(その配下にある全ての子ノード)の変更もキャプるようにする。
これを使うにはchildList指定必須。
それを知らなかった私は{subtree: true}とだけ書いたオプションを渡して
なんで動かないのかと軽く詰まった…。

MutationObserverで置き換えられるDOMイベント
DOMイベント MutationObserverプロパティ
(括弧内は任意)
DOMAttrModified attributes: true
(, attributeOldValue: true)
(, attributeFilter: ["属性名"])
DOMAttributeNameChanged attributes: true
(, attributeOldValue: true)
(, attributeFilter: ["属性名"])
DOMCharacterDataModified characterData: true
(, characterDataOldValue: true)
DOMNodeInserted childList: true (, subtree: true)
DOMNodeInsertedIntoDocument childList: true (, subtree: true)
DOMNodeRemoved childList: true (, subtree: true)
DOMNodeRemovedFromDocument childList: true (, subtree: true)
DOMSubtreeModified childList: true, subtree: true



disconnect

observeで開始した監視を中止する。

var mo = new MutationObserver(omega);
    mo.observe(pikaman, options);

    mo.disconnect();

当然だが、監視するプロセスが増えれば増えるほど負荷がかかる。
監視する必要が無くなったらdisconnectして解放しよう。

takeRecords

それまでobserveで記録されたMutationRecordsの内容を全て配列として返す。
と同時に、それまでの記録をクリアする。

var mo = new MutationObserver(omega);
    mo.observe(pikaman, options);

    console.log( mo.takeRecords() );



MutationRecords

記録されるデータ一覧。

名前 戻り値
attributeName 文字列 変更された属性値が属する属性名
attributeNamespace 文字列 変更された属性名の名前空間
addedNodes ノードリスト 追加されたノードの一覧
removedNodes ノードリスト 削除されたノードの一覧
previousSibling ノード 追加(削除)されたノードの直前にあるノード
nextSibling ノード 追加(削除)されたノードの直後にあるノード
target ノード 変更されたノード
type 文字列 基本オプションの内容(attributes、characterData、childList)
oldValue 文字列 上述の追加オプションで指定した内容



動作環境

で、問題のブラウザ対応状況だが
FirefoxとChromeはとっくの前に対応済み。
IEは11から。OperaはBlink版から。(Presto版非対応)


IE11のシェアが伸びてきたとは言え、まだまだIE10も多いのが現実。
フォールバックするのも面倒だし、jQueryの.on()で手軽に使えるという点でも
結局DOMイベントに甘えちゃうんだよねぇ。。