【问题标题】:MutationObserver behavior after the element was removed移除元素后的 MutationObserver 行为
【发布时间】:2016-02-07 12:38:43
【问题描述】:

我有一个MutationObserver 绑定到一个#foo html 元素。

var target = document.querySelector("#foo");
var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        // ...
    });
});

当用户点击#button 元素时,#foo 会被移除并在一秒钟后再次出现。

<div id="button">Click me to remove the Foo!</div>
<div id="foo">Hello, I'm Foo!</div>

我注意到,#foo 被删除并重新创建后,观察者停止工作。

  • 这是正常行为吗?
  • 那么,我需要再次启动观察者吗?

还有第三个问题:

  • 第一个观察者是否被完全删除?或者,取而代之的是,它仍在运行,只是“无所事事”? - 我在问这个问题,因为我不希望多个观察者运行,只要其中一个在做真正的工作。

【问题讨论】:

  • “...并在一秒钟后再次出现...”是什么意思?具有相同 ID 的新元素?或者你把相同的元素放回去?
  • 更多读者 - 请参阅 T.J. 的 cmets。克劳德的帖子。

标签: javascript mutation-observers


【解决方案1】:

并在一秒钟后再次出现

行为将完全取决于您的意思。

如果您的意思是创建了具有相同 id 元素,那么是的,这是预期的行为:当您在一个元素上调用 observer.observe 时,您是在 该特定元素,而不是任何与其 ID 匹配的元素。如果你从 DOM 中移除该元素并用另一个看起来相同的不同元素替换它,观察者不会神奇地连接到那个新的不同元素:

var target = document.querySelector("#foo");
var observer = new MutationObserver(function(mutations) {
    console.log("#foo was modified");
});
observer.observe(target, {subTree: true, childList: true});
var counter = 0;
tick();
setTimeout(function() {
    console.log("Replacing foo with a new one");
    target.parentNode.removeChild(target);
    target = document.createElement("div");
    target.id = "foo";
    target.innerHTML = "This is the new foo";
    document.body.insertBefore(target, document.body.firstChild);
}, 1000);
function tick() {
    target.appendChild(document.createTextNode("."));
    if (++counter < 20) {
        setTimeout(tick, 200);
    }
}
&lt;div id="foo"&gt;This is the original foo&lt;/div&gt;

如果您的意思是要将 相同元素 放回 DOM,那么不,这不是预期的行为,也不是本示例中发生的情况:

var target = document.querySelector("#foo");
var observer = new MutationObserver(function(mutations) {
    console.log("#foo was modified");
});
observer.observe(target, {subTree: true, childList: true});
var counter = 0;
tick();
setTimeout(function() {
    console.log("Removing foo for a moment");
    target.parentNode.removeChild(target);
    setTimeout(function() {
        console.log("Putting the same foo back in");
        document.body.insertBefore(target, document.body.firstChild);
    }, 100);
}, 1000);
function tick() {
    target.appendChild(document.createTextNode("."));
    if (++counter < 20) {
        setTimeout(tick, 200);
    }
}
&lt;div id="foo"&gt;This is the original foo&lt;/div&gt;

这里的关键信息是观察者正在观察您传递给observe一个特定元素


这些问题仅适用于您移除旧元素并用全新元素替换它的情况:

那么,我需要再次启动观察者吗?

如果您要删除原始元素并在其位置放置一个新元素,您应该告诉观察者停止观察:

observer.disconnect();

一旦你有了一个新元素,你就可以将观察者连接到它:

observer.observe(theNewElement, /*...options go here...*/);

第一个观察者是否被完全移除?或者,它仍然在运行,只是“什么都不做”?

理论上,由于the spec 明确表示它是引用其观察者的元素,那么如果你释放对元素的唯一引用,理论上观察者会断开连接,因为元素及其观察者列表被清理。但是,尚不清楚观察者的disconnect 方法如何在观察者没有对元素的引用的情况下实现,因此它们可能具有相互引用。如果您保留对观察者的引用,则可以将相互引用保留在内存中。可以肯定的是,我会故意断开它。

我在问这个问题,因为我不希望多个观察者运行,只要他们中的一个在做真正的工作。

观察者不会“跑”,他们反应。观察者将继续观察原始元素,直到/除非您断开它。如果你没有对原始元素做任何事情,那么观察者除了占用内存之外没有做任何事情。但同样,如果你要移除元素,你应该断开观察者的连接。

【讨论】:

  • 能否请您扩展关于第 2 点和第 3 点的答案? :-)
  • @john1121:也许你可以回答我问你的问题。
  • 哦,对不起,我看到了,但是忘记写了。目前,我不知道实际发生了什么,因为我正在为一个非常复杂的页面编写用户脚本,这是网络应用程序的一部分。但是,正如我所想,#foo 是新元素。所以,根据你的说法,现有的行为是正常的。
  • 刚刚偶然发现了这个问题。根据规范,在4.3.4 Garbage Collection 中,节点对其观察者具有强引用,而观察者对其正在观察的节点仅具有弱引用。因此,如果没有对它的引用,删除一个节点最终应该删除观察者。
猜你喜欢
  • 2018-07-30
  • 1970-01-01
  • 1970-01-01
  • 2020-07-16
  • 2015-08-09
  • 2021-08-21
  • 2019-08-12
  • 2021-05-18
  • 1970-01-01
相关资源
最近更新 更多