添加子项时,什么会导致整个文档和子树上的 Mutation Observer 不触发?

问题描述 投票:0回答:3

有时突变观察者回调不会在我期望的时候触发。

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

// 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]
没有记录到控制台。

什么会导致 Mutation Observer 无法在推特上运行?

重现问题

  1. 推特
  2. 打开开发工具控制台
  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 似乎可以修复停止工作时的突变观察器
  • 清除cookie解决问题

更新 4

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

例子

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

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

javascript twitter mutation-observers
3个回答
1
投票

我面临同样的问题,现在我通过使用循环并检查是否手动进行了一些更改来修补回调。

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


0
投票

我能够在这些特定情况下重现:

  1. 开发工具已开放
  2. 代码在 MutationObserver 回调中有断点或调试器语句
  3. 第一次加载页面成功断点停止
  4. 在断点仍然时重新加载页面
  5. MutationObserver 回调永远不会为任何未来的页面重新加载再次运行
  6. 关闭页面和站点的任何其他页面并启动新选项卡(即创建新页面过程)会将状态重置回第一步

其他轶事:

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

因此,如果您遇到与我相同的问题,解决方法是将您的突变观察者回调包装在

setTimeout()
中,如下所示...

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

老实说,我不太愿意将此称为答案。我相信这里的 Chromium 中存在一个 heisen-bug。如果您从不打开 Dev Tools,则此解决方法是多余的。


0
投票

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

我想避免重构大量代码,所以我想出了这个 hacky 解决方案,将本机 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');
© www.soinside.com 2019 - 2024. All rights reserved.