web-dev-qa-db-ja.com

まだ存在しないターゲットノードの変異を観察する

まだ存在しないDOMノードの突然変異を観察することは可能ですか?

例:

私のアプリはある時点でdivを作成します:<div id="message" data-message-content="foo" data-message-type="bar" />

このdivの作成と変更を監視したい。

var mutationObserver = new MutationObserver(function(mutations){
  // Some code to handle the mutation.
});

mutationObserver.observe(
    document.querySelector('#message'),
        { 
            attributes: true, 
            subtree: true, 
            childList: true, 
            characterData: false 
        }
    );
);

現在、これはerrorを返します。これは、#messageがnullであるためです(divはまだ作成されていません)。

Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.

明白な解決策は、bodyを監視し、ミューテーションのいずれかがdiv#Messageの作成であるかどうかを確認することですが、これは悪いアイデアのようです/おそらくパフォーマンスが悪いようです。

21
Don P

監視できるのは既存のノードのみです。

ただし、心配しないでください。getElementByIdは、すべてのミューテーションの追加ノードの列挙に比べて非常に高速であるため、Devtools-> Profilerパネルに表示されるように、要素が表示されるのを待つことはまったく負担になりません。

_function waitForAddedNode(params) {
    new MutationObserver(function(mutations) {
        var el = document.getElementById(params.id);
        if (el) {
            this.disconnect();
            params.done(el);
        }
    }).observe(params.parent || document, {
        subtree: !!params.recursive,
        childList: true,
    });
}
_

使用法:

_waitForAddedNode({
    id: 'message',
    parent: document.querySelector('.container'),
    recursive: false,
    done: function(el) {
        console.log(el);
    }
});
_

常にdevtoolsプロファイラーを使用し、オブザーバーコールバックがCPU時間の1%未満を消費するようにしてください。

  • 可能な限り、将来のノードの直接の親を観察します(_subtree: false_)
  • MutationObserverコールバック内でgetElementById、getElementsByTagName、およびgetElementsByClassNameを使用し、querySelector、特に非常に遅いquerySelectorAllを避けます。
  • MutationObserverコールバック内でquerySelectorAllが絶対に避けられない場合は、最初にquerySelectorチェックを実行します。平均すると、そのようなコンボははるかに高速になります。
  • ForEach、filterなどの、MutationObserverコールバック内でのコールバックを必要とする配列メソッドは使用しないでください。JavaScript関数の呼び出しは、従来のfor (var i=0 ....)ループに比べて負荷が高く、MutationObserverコールバックは1秒あたり100回起動する可能性があるためです。複雑な最新のページでは、突然変異の各バッチに数十、数百、数千のaddedNodesがあります。
  • トランスコンパイルして結果のコードが従来のforループと同じ速さで実行されない限り、MutationObserverコールバック内で 遅いES2015ループfor (v of something)のように使用しないでください。
49
wOxxOm