-->

Detect when a node is deleted (or removed from the

2020-07-12 07:44发布

问题:

I want to detect when a node (nodeX, say) is no longer available, either because it was deleted or because its parent (or its parents parent) was deleted.

So far, all I can think of is to use Mutation Observer to see any deletions on the page, and check if the deleted nodes was nodeX or had nodeX for a descendant.

Is there an easier way?


Please note: as far as I understand, the linked question (that this question "is a duplicate of") asks "how can I detect a [direct] deletion of a node". Mine asks "How can I detect the deletion of a node or its parent (or any other ancestor)".

As far as I understand, this is not straightforward with mutation observers: You need to check every deleted node to see if it was an ancestor.

This is what I seek to confirm or deny.

As far as I understand, that is different from the linked question.

回答1:

Here is an implementation that identifies how the element was removed (either directly or because a parent was removed)

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>



回答2:

The accepted answer will fail if the removed subtree is mutated after removal from the document. For example:

target.parent.remove();
target.remove();

will generate one call to the mutation observer for the parent node removal (the target node removal will not be reported to the observer as it happened when the subtree was already removed from the document).

var parentMatch = nodes.some(parent => parent.contains(target));

in the accepted answer will return false as the target is no longer a child. The problem is that mutation event reporting is batched and you cannot rely on state at the time of node removal staying the same as at the time of the call to your mutation observer.

For this reason, facing a similar issue as the questioner, I created a WeakSet of target node ancestors. Using a mutation observer attached to the document root, I compared mutations against this set and the target. If a mutation node removal event includes a node in this set or the target node, I know the target node was removed from the tree. That doesn't imply the node is still removed (it may have been added back) or that the node is still a child of the ancestors in my set. But I can be sure that the node was removed in the past.

You have to be careful to differentiate DOM state at the moment after a mutation and the state at the time you receive the mutation event.



回答3:

This has been asked before on stack overflow. How to detect element being added/removed from dom element?

If you just want to check whether something exists at a particular point in time, you could obviously do something like:

if (!document.querySelector(".nonexistent")) {
  console.log("doesn't exist");
}

Otherwise Mutation Observers are your only option.