我想检测节点(例如nodeX)何时不再可用,要么是因为它被删除了,要么是因为它的父节点(或其父节点)被删除了。
到目前为止,我能想到的就是使用Mutation Observer来查看页面上的任何删除,并检查删除的节点是否是nodeX或有nodeX作为后代。
有更简单的方法吗?
请注意:据我了解,链接的问题(该问题“是重复的”)询问“如何检测节点的[直接]删除”。我的问题是“如何检测节点或其父节点(或任何其他祖先)的删除”。
据我了解,这对于突变观察者来说并不简单:您需要检查每个删除的节点以查看它是否是祖先。
这是我想要确认或否认的。
据我了解,这与链接的问题不同。
这是一个实现,用于标识元素如何被删除(直接删除或因为父元素被删除)
var target = document.querySelector('#to-be-removed');
var observer = new MutationObserver(function(mutations) {
// check for removed target
mutations.forEach(function(mutation) {
var nodes = Array.from(mutation.removedNodes);
var directMatch = nodes.indexOf(target) > -1
var parentMatch = nodes.some(parent => parent.contains(target));
if (directMatch) {
console.log('node', target, 'was directly removed!');
} else if (parentMatch) {
console.log('node', target, 'was removed through a removed parent!');
}
});
});
var config = {
subtree: true,
childList: true
};
observer.observe(document.body, config);
var qs = document.querySelector.bind(document);
qs('#ul').addEventListener('click', function(){qs('ul').remove();}, false)
qs('#li').addEventListener('click', function(){qs('#to-be-removed').remove();}, false)
<ul>
<li>list item 1</li>
<li>list item 2</li>
<li id="to-be-removed">list item 3</li>
<li>list item 4</li>
</ul>
<button id="ul">remove ul</button>
<button id="li">remove li</button>
如果删除的子树在从文档中删除后发生变异,则接受的答案将失败。例如:
target.parent.remove();
target.remove();
将生成对突变观察者的一次调用,以删除父节点(目标节点删除不会报告给观察者,因为它发生在子树已从文档中删除时)。
var parentMatch = nodes.some(parent => parent.contains(target));
在接受的答案中将返回 false,因为目标不再是孩子。问题在于突变事件报告是批量的,您不能依赖节点删除时的状态与调用突变观察者时保持相同。
出于这个原因,面对与提问者类似的问题,我创建了一个目标节点祖先的WeakSet。使用附加到文档根的突变观察器,我将突变与该集和目标进行了比较。如果突变节点删除事件包括该集合中的节点或目标节点,我知道目标节点已从树中删除。这并不意味着该节点仍然被删除(它可能已被添加回来)或者该节点仍然是我的集合中祖先的子节点。但我可以确定该节点过去已被删除。
您必须小心区分突变后此刻的 DOM 状态和收到突变事件时的状态。
之前在堆栈溢出时已经问过这个问题。 如何检测dom元素中添加/删除的元素?
如果你只是想检查某个特定时间点是否存在某些东西,你显然可以这样做:
if (!document.querySelector(".nonexistent")) {
console.log("doesn't exist");
}
否则突变观察者是你唯一的选择。
实际上有一个有趣的解决方法。以下是我们需要满足的一些假设:
使用requestAnimationFrame,检查节点是否连接。
即
let watchAbleNode = document.getElementById("el");
let callback = () => {
if ( watchAbleNode != null ) {
if ( !watchAbleNode.isConnected() ) {
//useful stuff you need to do
watchAbleNode = null; //to garbage collect
}
}
window.requestAnimationFrame(callback)
}
callback()