是否可以在尚不存在的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
        }
    );
);

现在,这会返回错误,因为#message为空(尚未创建div)。
Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
一个明显的解决方案是观察body并检查是否有任何突变是div#Message的创建,但这似乎是个坏主意,或者可能对性能不利。

最佳答案

只能观察到现有节点。
但是不用担心,由于与所有突变添加的节点的枚举相比,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 || !params.parent,
        childList: true,
    });
}
用法:
waitForAddedNode({
    id: 'message',
    parent: document.querySelector('.container'),
    recursive: false,
    done: function(el) {
        console.log(el);
    }
});
始终使用devtools分析器,并尝试使观察者回调消耗的CPU时间少于1%。
  • 尽可能观察 future 节点的直接父级(subtree: false)
  • 在MutationObserver回调中使用getElementById,getElementsByTagName和getElementsByClassName,避免使用querySelector尤其是非常慢的querySelectorAll。
  • 如果在MutationObserver回调中绝对无法使用querySelectorAll,请首先执行querySelector检查,平均而言,这样的组合会快得多。
  • 不要使用诸如forEach,filter等之类的需要在MutationObserver回调内部进行回调的数组方法,因为与经典的for (var i=0 ....)循环相比,在Javascript函数调用中,该方法是一项昂贵的操作,而且MutationObserver回调可能每秒触发数十次,数百次或数百次触发复杂的现代网页上每批突变中都有成千上万的addedNodes
  • 请勿在MutationObserver回调中使用像for (v of something)这样的the slow ES2015 loops,除非您进行反编译,并且生成的代码的运行速度与经典for循环一样快。
  • 10-05 20:51