Detect that given element has been removed from the DOM without sacrificing performance

一个人想着一个人 提交于 2020-08-04 18:19:54

问题


I've got these:

const element = this.getElementById("victim")

function releaseKraken(targetElement) {}

I want the function to be called when element is removed from DOM.

I can imagine something like this:

element.onRemove(() => releaseKraken(element))

I understand that I need MutationObserver, but all the documentation I found focuses on watching given element's children, whereas I need to watch the element itself.

UPD: the question How to detect element being added/removed from dom element? focuses on watching children of a given parent. I don't want to watch children or parent. I want to be notified when given element is removed from the DOM. Not it's children. And I don't want to set up a watcher on given element's parent (unless that's the only option) because it'll be a performance impact.

UPD2: if I set up a MutationObserver on document, this will result in the callback being triggered thousands or even millions of times per session, and each time the callback will have to filter a huge list of removed elements to see if it contains the one in question. That's just crazy.

I need something simple like I showed above. I want the callback to be triggered exactly once: when the given element is removed.


回答1:


As you said, MutationObserver only allows you to detect when the children of an element are manipulated. That means you'll need to listen to the parent and check what changes were made to see if the target element was removed.

function onRemove(element, callback) {
  const parent = element.parentNode;
  if (!parent) throw new Error("The node must already be attached");

  const obs = new MutationObserver(mutations => {
    for (const mutation of mutations) {
      for (const el of mutation.removedNodes) {
        if (el === element) {
          obs.disconnect();
          callback();
        }
      }
    }
  });
  obs.observe(parent, {
    childList: true,
  });
}

then with your example instead of

element.onRemove(() => releaseKraken(element));

you can do

onRemove(element, () => releaseKraken(element));

This approach should be plenty fast if all you are doing is watching a single element. While it may seem like a decent amount of looping, it is pretty rare for removedNodes to be more than one node, and unless something is removing tons of siblings all at once, mutations is going to be quite small too.

You could also consider doing

callback(el);

which would allow you to do

onRemove(element, releaseKraken);



回答2:


Here is a solution I found on this page

document.getElementById("delete_one_div").addEventListener('click', function() {
  var divToDelete = document.getElementsByTagName("div")[0];
  divToDelete.parentNode.removeChild(divToDelete);
});

var element = document.getElementById("div_to_be_watched")
var in_dom = document.body.contains(element);
var observer = new MutationObserver(function(mutations) {
  if (in_dom && !document.body.contains(element)) {
    console.log("I was just removed");
    in_dom = false;
    observer.disconnect();
  }

});
observer.observe(document.body, { childList: true });
<div id="test">Test</div>
<div id="div_to_be_watched">Div to be watched</div>
<div class="div_to_be_watched">Second test</div>
<button id="delete_one_div">Delete one div</button>

EDIT

I edited snippet a little bit. You have two options:

  1. Use it the way it is. And it is not very memory consuming since the if condition is not really complicated (only to check whether the body contains an element) and it observes only to the moment of remove and then it stops,
  2. Make observer observe only specific element to limit the event triggers.



回答3:


An alternative, shorter version of loganfsmyth's excellent solution:

function onRemove(el, callback) {
  new MutationObserver((mutations, observer) => {
    if(!document.body.contains(el)) {
      observer.disconnect();
      callback();
    }
  }).observe(el.parentNode, { childList: true });
}

Usage is the same:

onRemove(myElement, function() {
  console.log("The element was removed!");
})


来源:https://stackoverflow.com/questions/50391422/detect-that-given-element-has-been-removed-from-the-dom-without-sacrificing-perf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!