DOM 監視: MutaionObserver

DOM に変更が走るかどうか監視を行えるものにMutationObserverAPI があります。使うにはまずインスタンスを作る必要があります。

const observer = new MutationObserver(callback);

引数にはcallbackが必要です。これは DOM に変更が走った時に発火するコールバック関数です。コールバック発火時この関数にはMutationRecordというオブジェクトが配列渡ってきます。なぜ配列かというとそのタイミングで置きたすべての記録が渡される為です。
MutationRecordにはどういう変更が走ったのか(type)やそれぞれのtypeで役立つ情報などが渡ってきます。

監視を始めるには作ったインスタンスobserverobserveを対象の要素を引数に実行します。

observer.observe(node, {/* ... */});

監視を終えたい時はdisconnectメソッドを実行します。これを実行すると今までにobserveへ渡した要素についてすべて監視が解除されます。

observer.disconnect();

MutationObserverInit

MutationObserver#observeする時、どういったものを監視するのかを指定する必要があります。このオプションで指定できる値は以下の7つです。

  • childList
  • subtree
  • attributes
  • attributeOldValue
  • attributeFilter
  • characterData
  • characterDataOldValue

この中で、attributescharacterDatachildListの内1つは必ずtrueで設定しなければなりません。

ざっと見れば7つと分かりづらいですが、グループ分して以下の3つに分けて考えても良いと思います。

  1. childListと`subTree
  2. attributesattributeOldValueattributeFilter
  3. characterDatacharacterDataOldValue

childList と subTree

要素が追加された、削除されたを監視したい場合はchildListtrueで置きます。例えば自身がelementだとして、element.appendChildelement.removeChildなどが呼ばれた時にコールバックが発火します。MutationRecordへは要素が追加された場合はaddedNodesに、削除された場合はremovedNodesにそれぞれ対象の要素が渡されます。

上記のelementように監視対象が自分自身だけの場合ならこれでも良いですが、子が持つ要素も同じように監視したい場合にsubTreetrueにします。これで何らかの手段である子要素に子要素ができた時にもコールバックを走らせれます。

observer.observe(item, {
  childList: true,
  subtree: true,
});

attributes と attributeOldValue、 attributeFilter

要素の属性が変わるかどうか監視したい場合はattributestrueで置きます。このtypeでコールバックが発火した時のみ、attributeNameに値が入ります。例えばstyle属性を変更して発火したのであればstyleのような感じです。

後の2つはオプショナルです。

attributeOldValuetrueにすると、変更があった際のMutationRecordoldValueに対象の属性の古い値が入ってきます。

attributeFilterは属性を絞り込みたい場合に使います。例えば「value属性は監視したいけど、id属性の監視は要らないや」という場合以下のように設定します。

observer.observe(item, {
  attributes: true,
  attributeOldValue: true,
  attributeFilter: ['id'],
});

characterData と characterDataOldValue

テキストが変わったかどうかを監視したい場合はcharacterDatatrueで置きます。
characterDataOldValueはオプショナルでtrueにすると、attributeOldValueのようにoldValueプロパティに変更前のテキストが入ってきます、

observer.observe(node.childNodes[0], {
  characterData: true,
  characterDataOldValue: true,
});

このように監視した状態で以下のようにテキストを更新するとコールバックが発火します。

node.childNodes[0].nodeValue = 'update text';

実際に動いてる様子はCodeSandboxで見ることができます。