我正在构建一个非漫游者,类似于在各种浏览器中尚未准备好的W3C undomanager。我实现了一个简单的transact调用,在调用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()
吗?
答案 0 :(得分:2)
MutationObserver
s are async by specfication,因为在调用回调函数之前,它们会等待当前堆栈为空。这很有用,因此每次更改DOM时都不会调用回调,但只有在完成所有更改后才会调用。 See how are MutationObserver callbacks fired?
如果您查看规范链接,您会注意到MutationEvent
之前涉及的步骤:
takeRecords
setTimeout
将在超时和堆栈清空后调用该函数) 更新抱歉,为了解决您的实际问题,我认为这可能与alert
回调中的MutationObserver
有关。对于要处理的突变,它绝对不应该花费超过几毫秒的时间,它应该在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));
}