当添加子时,什么可能导致整个文档和子树上的突变观察者不着火?



有时,当我期望它触发时,突变观察者回调不会触发。

如果我在开发人员工具控制台运行以下代码:

// callback for mutations observer
callbackForAllChanges = function (mutationsList, observer) {
console.log("mutations: ", mutationsList);
};
// create mutation observer
allChanges = new MutationObserver(callbackForAllChanges);

// attach mutation observer to document
allChanges.observe(document, {
childList: true,
subtree:true
});
// create new child
document.body.appendChild(document.createElement("div"));

我希望当我创建一个新的子线程时,回调函数会被触发。但有时会,有时不会。

当我在stackoverflow上运行devtools控制台的代码时,它可以工作:我看到mutations: [MutationRecord]记录到控制台。

当我去twitter并在devtools控制台运行上述代码时,它似乎不起作用:mutations: [MutationRecord]没有记录到控制台。

什么会导致突变观察者不能在twitter上工作?

重新创建issue

  1. 去twitter
  2. 打开devtools控制台
  3. 粘贴以上代码,看到mutations: [MutationRecord]不记录

更新现在它在twitter上对我有效,所以看起来断断续续。

更新2

推特又不能用了。我还发现,如果我添加一个突变观察者到我创建的div,这也不起作用,但在其他网站上工作。
const newDiv = document.createElement('div')
newDiv.setAttribute('id','newDiv')
// callback for mutations observer
callbackForAllChanges = function (mutationsList, observer) {
console.log("mutations: ", mutationsList);
};
// create mutation observer
allChanges = new MutationObserver(callbackForAllChanges);

// attach mutation observer to document
allChanges.observe(newDiv, {
childList: true,
subtree:true
});
// create new child
newDiv.appendChild(document.createElement("div"));

3

更新
  • 变异观察者在隐身状态下没有停止工作
  • 关闭所有扩展不修复突变观察者当它不工作
  • 退出和重启chrome似乎修复停止工作时的突变观察者
  • 清除cookies不能解决问题

更新4

它可能与无限循环有关,在无限循环中,当观察到突变时,添加一个节点。这会导致观察到一个突变(新添加的节点)。等等......

例子
const callbackForAllChanges = function (mutationsList, observer) {
if (mutationsList.find((record) => record.type === "childList")) {      
document.body.appendChild(document.createElement("div"));
}
};

我认为如果这种情况发生,它可能会触发突变工作停止工作。

我正面临着同样的问题,现在我修补了回调使用循环和检查是否有一些更改手工。

setInterval(() => {
const recs = allChanges.takeRecords()
if (recs.length === ) return
yourcallback(recs)
}, 30)

我可以在这些特定的情况下复制这个:

  1. Dev Tools是打开的
  2. 代码中有断点或调试器语句在MutationObserver回调
  3. 第一次加载页面,页面在断点处成功停止
  4. 仍然时重新加载页面在断点
  5. MutationObserver回调永远不会再运行任何未来的页面重载
  6. 关闭页面和任何其他网站,并开始一个新的选项卡(即。创建新页面进程)将状态重置回第一步

其他轶事:

  • 我尝试了一些旧版本的chromium(大约v97),有趣的是,我发现如果试图从突变观察者回调断点重新加载页面,其中许多会崩溃。
  • 存储对MutationObserver的引用并使用takeRecord()按预期工作。因为,它充满了突变记录。
  • 使用queueMicrotask(<cb>)将您的工作与突变观察者回调解耦并不能解决问题。
  • 使用setTimout(<cb>, 0)确实解决了这个问题。

因此,如果您遇到与我相同的问题,那么解决方法将是在setTimeout()中包装您的突变观察者回调,如下所示…

new MutationObserver(() => setTimeout(myHandler, 0));

老实说,我觉得把这个叫做答案不太舒服。我相信Chromium中有一个海森堡bug。如果你从不打开开发工具,这个解决方法是多余的。

这个回答与mayfield的回答相吻合,mayfield的回答非常好。我同意这是一个Chromium Devtools的bug,可以通过他在回答中提到的步骤重现。setTimeout()似乎可以减轻这种情况(尽管不一致)。我想知道这个bug是否也会影响其他观察者…

我想避免重构大量代码,所以我想出了这个hack的解决方案,将本机MutationObserver类包装在您自己的匿名类中(需要在实例化任何观察者之前运行):

function fixObserver(name) {
let original=window[name];
window[name]=class {
constructor (cb) {
return new original((...params) => { 
setTimeout(() => cb(...params));
});
}
};
}
fixObserver('MutationObserver');
fixObserver('ResizeObserver');
fixObserver('IntersectionObserver');

最新更新