场景:
我们有一个MutationObserver
处理函数handler
。
在handler
中,我们进行了一些DOM操作,这些操作将再次触发handler
。从概念上讲,我们将有一个可重入的handler
调用。除了MutationObserver
未在线程中运行外,它会在handler
已完成执行后触发。
因此,handler
将触发自己,但会通过异步队列而不是线程内触发。 JS调试器似乎知道这一点,它将在调用栈(即使用Chrome)中作为异步祖先出现。
为了实现事件的有效反跳,我们需要对其进行检测;也就是说,如果handler
是由于自身触发的更改而被调用的。
该怎么办?
mutationObserver=new MutationObserver(handler);
mutationObserver.observe(window.document,{
attributes:true,
characterData:true,
childList:true,
subtree:true
});
// Trigger a MutationObserver change
document.getElementById('foo').setAttribute('class','bar');
document.getElementById('foo').setAttribute('class','');
var isHandling;
function handler(){
console.log('handler');
// The test below won't work, as the re-entrant call
// is placed out-of-sync, after isHandling has been reset
if(isHandling){
console.log('Re-entry!');
// Throttle/debounce and completely different handling logic
return;
}
isHandling=true;
// Trigger a MutationObserver change
document.getElementById('foo').setAttribute('class','bar');
document.getElementById('foo').setAttribute('class','');
isHandling=false;
}
<div id="foo"></div>
一种可能的解决方法是在触发一个突变时停止突变观察者
mutationObserver=new MutationObserver(handler);
mutationObserver.observe(window.document,{
attributes:true,
characterData:true,
childList:true,
subtree:true
});
// Trigger a MutationObserver change
document.getElementById('foo').setAttribute('class','bar');
document.getElementById('foo').setAttribute('class','');
function handler(){
console.log('Modification happend')
mutationObserver.disconnect();
// Trigger a MutationObserver change
document.getElementById('foo').setAttribute('class','bar');
document.getElementById('foo').setAttribute('class','');
mutationObserver.observe(window.document,{
attributes:true,
characterData:true,
childList:true,
subtree:true
});
}
请参见JS小提琴