74

Say I had a div#parent and I append and remove elements to it using jquery. How would I be able to detect when such an event happens on the div#parent element?

Derek
  • 11,980
  • 26
  • 103
  • 162
  • Possible duplicate of [Event when element added to page](https://stackoverflow.com/questions/7434685/event-when-element-added-to-page) – Heretic Monkey Jan 25 '19 at 17:36

5 Answers5

152

Don't use mutation events like DOMNodeInserted and DOMNodeRemoved.

Instead, use DOM Mutation Observers, which are supported in all modern browsers except IE10 and lower (Can I use). Mutation observers are intended to replace mutation events (which have been deprecated), as they have been found to have low performance due to flaws in its design.

var x = new MutationObserver(function (e) {
  if (e[0].removedNodes) console.log(1);
});

x.observe(document.getElementById('parent'), { childList: true });
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
  • 15
    Thanks for answer, but *Mutation Observers* has a bad behaviour: I want to set observer to node which will be attached to tree later. I don't know the parent ( With 'DOMNodeRemoved' I could. Any ideas? I want node listen self destruction. – DenisKolodin Aug 13 '16 at 07:10
  • 1
    @DenisKolodin wow, I didn't notice this comment, sorry. For posterity's sake, that should be answered by [this question](https://stackoverflow.com/q/31798816/2074608). It does mean placing an event at the document level, but mutation events have such a large performance penalty it probably doesn't matter anyway. – Qantas 94 Heavy Sep 25 '17 at 12:51
  • 2
    not sure if this is worth mentioning in your answer as it doesn't directly answer this question, but custom elements have a disconnectedCallback – Mr5o1 Jun 06 '20 at 23:33
  • @Qantas94Heavy What do you mean? If performance penalty is real why are we recommended away from DOMNodeRemoved? I am also considering one on document level. Please see https://stackoverflow.com/questions/69426920/javascript-mutationobserver-observing-a-child-element-after-observing-a-parent – mjs Oct 03 '21 at 16:50
  • @mmm as noted by Mr5o1 you could use custom elements. If you can't, you could bind it to the parent and re-bind as that changes: https://stackoverflow.com/q/50391422/2074608 – Qantas 94 Heavy Oct 07 '21 at 02:41
  • It is perfect fit for the case of this question, however, if I have 1-2 elements that I want to track in the entire page, using mutation observer for the whole tree seems like a waste of energy (performance) - correct me if I am wrong.... Then how to do in this case? I would think of wrapping the remove() function of that particular element in a function that performs the needed action "onremove" before physically calling the built-in remove() -> Correct me again if this solution sounds stupid... – Xerix Sep 14 '22 at 13:30
  • mutation.addedNodes always returns true. You might need to check mutation.addedNodes.length in if condition. – Shashank Bhatt Dec 01 '22 at 05:03
  • Note the `isConnected` prop on HTMLElements makes this a little nicer. `var x = new MutationObserver(() => { console.log(element.isConnected) }); x.observe(element.parentElement, { childList: true });` – SethWhite Jun 13 '23 at 14:37
41

Use Mutation Observers as suggested by @Qantas in his answer


Following methods are deprecated

You can use DOMNodeInserted and DOMNodeRemoved

$("#parent").on('DOMNodeInserted', function(e) {
    console.log(e.target, ' was inserted');
});

$("#parent").on('DOMNodeRemoved', function(e) {
    console.log(e.target, ' was removed');
});

MDN Docs

Satpal
  • 132,252
  • 13
  • 159
  • 168
  • 19
    Mutation events are deprecated, this answer should no longer be the correct one. Mutation Observers were _designed as a replacement for Mutation Events_, so Qantas answer is the correct one. https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events and https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver – Parziphal Mar 04 '17 at 23:05
  • used this method, but with config only to listen child dom updated , ref: https://academy.byidmore.com/post/Listen-Added-and-Deleted-Node-in-HTML-DOM-5be15ad61bde484c8fbedd35 – yussan Nov 07 '18 at 00:39
  • It may be slow and a bad idea for code in production, but this looks like the only method that works synchronously (unlike MutationObserver). This is a good option for debugging when nothing else is suitable, for example, when trying to find the line of code that changed the DOM. – CertainPerformance Mar 20 '19 at 03:53
  • @satpal Is it possible to combine these, instead of having two sets of code? If `DOMNodeInserted or DOMNodeRemoved, function() {` etc. – wharfdale Dec 15 '19 at 22:29
  • If performance penalty is real why are we recommended away from DOMNodeRemoved? I am also considering one on document level. Please see https://stackoverflow.com/questions/69426920/javascript-mutationobserver-observing-a-child-element-after-observing-a-parent – mjs Oct 03 '21 at 16:50
3

You should bind DOMSubtreeModified event

$("#parent").bind("DOMSubtreeModified",function(){
  console.log('changed');
});

http://jsfiddle.net/WQeM3/

mdolbin
  • 936
  • 4
  • 8
  • 3
    `DOMSubtreeModified` events are deprecated: [Can I Use](https://caniuse.com/#search=DOMSubtreeModified) – Cody Dec 17 '17 at 01:41
  • 1
    If performance penalty is real why are we recommended away from DOMNodeRemoved? I am also considering one on document level. Please see https://stackoverflow.com/questions/69426920/javascript-mutationobserver-observing-a-child-element-after-observing-a-parent – mjs Oct 03 '21 at 16:50
1

It wouldn't let me comment below on the top answer but to answer your question @DenisKolodin, in order to listen for self-destruction, you can do something like this:

function watchElForDeletion(elToWatch, callback, parent = document.querySelector('body')){
  const observer = new MutationObserver(function (mutations) {

    // loop through all mutations
    mutations.forEach(function (mutation) {

        // check for changes to the child list
        if (mutation.type === 'childList') {

            // check if anything was removed and if the specific element we were looking for was removed
            if (mutation.removedNodes.length > 0 && mutation.removedNodes[0] === elToWatch) {
                callback();
            }
        }
    });
  });

  // start observing the parent - defaults to document body
  observer.observe(parent, { childList: true });
};

It defaults to the parent being the body element and only runs the callback if the specific element you're looking for is deleted.

itsbhenry
  • 11
  • 2
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/31424877) – Ethan McTague Apr 01 '22 at 17:47
-4

I think we use socket.io!.

because of we detect which element should be added and then possibly to guess which element will be removed

Maybe it's possible

Thanks

maranR
  • 363
  • 8