如果在修改DOM后立即调用disconnect,则不调用MutationObserver回调



我正在构建一个undomanager,类似于尚未在各种浏览器中准备好的W3C undomanage。我实现了一个简单的transaction调用,它在监视DOM的更改时调用回调,然后将必要的结构添加到数组中,以便稍后用于撤消(或重做)更改。

一个简单的例子:

function transact(callback){
/* Watch content area for mutations */
observer = new MutationObserver(function(){
/* TODO: collect mutations in here */
alert('Mutations observed');
});
observer.observe(document.getElementById('content'), {
attributes: false,
childList: true,
characterData: false,
subtree: false
});
/* Perform the callback */
callback();
/* Stop observing */
//observer.disconnect();
setTimeout(function(){ observer.disconnect();}, 1);
}

使用此:

transact(function(){
var p = document.createElement('p');
p.innerHTML = 'Hello';
document.getElementById('content').appendChild(p);
});

如果我立即调用observer.disconnect(),突变观察器永远不会到达alert调用,但如果我使用setTimeout,它可以正常工作。

我非常乐意使用setTimeout调用,唯一的问题似乎是,对于更大的更改,您必须将断开连接延迟800毫秒。

这几乎就像断开连接发生在DOM更改实际完成之前,因此没有检测到任何内容。

这种情况在Firefox 25和Chrome 32中都会发生。

我想了一会儿,因为observer是一个局部变量,也许它过早地超出了范围,但将其更改为全局变量并没有帮助。我不得不延迟对disconnect()的调用,以便让DOM有机会赶上它。

这是浏览器错误吗?有没有更好的方法可以在DOM再次准备好后立即调用disconnect()

MutationObservers是异步,因为它们将等待当前堆栈为空,然后再调用回调函数。这很有用,因此不会在每次对DOM进行更改时调用回调,而是只有在进行了所有更改之后才调用回调。看看MutationObserver回调是如何触发的?

如果你查看规范链接,你会注意到MutationEvent之前涉及的步骤是:

  • MutationObserver收到突变通知
  • 将突变添加到自上次事件/takeRecords以来的当前突变集
  • 在当前堆栈为空后调用回调函数(这就是为什么您的代码在设置超时时按预期工作的原因——setTimeout将在超时后调用函数,堆栈为空)
  • 清空记录队列并继续观察

更新对不起,为了解决您的实际问题,我认为这可能与MutationObserver回调中的alert有关。处理突变肯定不应该超过几毫秒,而且它肯定应该发生在setTimeout之前。无论如何,一个肯定有效的解决方案是在MutationObserver回调中添加一个队列处理器,而不是使用超时。

function transact(callback){
var queue = [], listener; //queue of callbacks to process whenever a MO event occurs
/* Watch content area for mutations */
var observer = new MutationObserver(function(){ //made observer local
/* TODO: collect mutations in here */
alert('Mutations observed');
while(listener = queue.shift()) listener();
});
observer.observe(document.getElementById('content'), {
attributes: false,
childList: true,
characterData: false,
subtree: false
});
/* Perform the callback */
callback();
/* Stop observing */
//observer.disconnect();
queue.push(observer.disconnect.bind(observer));
}

相关内容

最新更新