问题
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:
- 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, - 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