To observe if a specific element was removed from DOM you can use the following function.
(Remove the export keyword if you don't want to use it as an ES6 module.)
Cheers!
// Usage example:
// observeRemoval(document.children[0], doSomething, 'document.children[0] removed') => doSomething('document.children[0] removed') will be executed if document.children[0] is removed
// observeRemoval(document.children[0], doSomething) => doSomething(document.children[0]) will be executed if document.children[0] is removed (removed element is passed to callback function)
// observeRemoval([document.children[0], document.children[1]], doSomething) => doSomething(document.children[0]) will be executed if document.children[0] is removed (removed element is passed to callback function), doSomething(document.children[1]) will be executed if document.children[1] is removed
// observeRemoval([document.children[0], document.children[1]], doSomething, ['document.children[0] removed', 'document.children[1] removed']) => doSomething('document.children[0] removed') will be executed if document.children[0] is removed, doSomething('document.children[1] removed') will be executed if document.children[1] is removed
// observeRemoval(document.querySelectorAll('body *'), doSomething) => doSomething(<removed-element>) will be executed if any element inside the document.body is removed
export function observeRemoval(elements, callback, callbackInputs){
let ecr = ecrTransform(elements, callback, callbackInputs);
for(let i=0;i<ecr.elements.length;i++){
let match = removalObserved.find(obj => obj.element === ecr.elements[i] && obj.callback === ecr.callback && obj.callbackInput === ecr.callbackInputs[i]);
if(!match){
removalObserved.push({
element: ecr.elements[i],
callback: ecr.callback,
callbackInput: ecr.callbackInputs[i],
});
}
}
}
export function unobserveRemoval(elements, callback, callbackInputs){
let ecr = ecrTransform(elements, callback, callbackInputs);
for(let i=0;i<removalObserved.length;i++){
let index = removalObserved.findIndex(obj => obj.element === ecr.elements[i] && obj.callback === ecr.callback && obj.callbackInput === ecr.callbackInputs[i]);
if(index > -1){
removalObserved.splice(index, 1);
}
}
}
export function getRemovalObservers(elements, callback, callbackInputs){
return removalObserved;
}
export function disconnectRemovalObserver(){
removalObserver.disconnect();
}
export function connectRemovalObserver(){
removalObserver.observe(document, {childList: true, subtree: true});
}
function ecrTransform(elements, callback, callbackInputs){
elements = transformNodeListHTMLCollectionToArray(elements);
callbackInputs = transformNodeListHTMLCollectionToArray(callbackInputs);
if(!Array.isArray(elements)){
elements = [elements];
}
if(!Array.isArray(callbackInputs)){
callbackInputs = [callbackInputs];
if(callbackInputs[0] === undefined){
callbackInputs[0] = elements[0];
}
}
if(elements.length > callbackInputs.length){
// let continuouscallbackInput = callbackInputs[callbackInputs.length-1];
// for(let i=0;i<(elements.length - callbackInputs.length);i++){
// callbackInputs.push(continuouscallbackInput);
// }
let continuouscallbackInput = callbackInputs[callbackInputs.length-1];
for(let i=(elements.length - callbackInputs.length);i<elements.length;i++){
callbackInputs.push(elements[i]);
}
}
return {elements, callback, callbackInputs};
}
function transformNodeListHTMLCollectionToArray(list){
if(NodeList.prototype.isPrototypeOf(list) || HTMLCollection.prototype.isPrototypeOf(list)){
return Array.from(list);
}
return list;
}
const removalObserved = [];
const removalObserver = new MutationObserver(mutations => {
for(let m=0;m<mutations.length;m++){
for(let i=0;i<mutations[m].removedNodes.length;i++){
let dO = removalObserved;
for(let j=0;j<dO.length;j++){
if(mutations[m].removedNodes[i].contains(dO[j].element) && !document.contains(dO[j].element)){
(dO[j].callbackInput !== undefined) ? dO[j].callback(dO[j].callbackInput) : dO[j].callback(dO[j].element);
}
}
}
}
});
connectRemovalObserver();