Efficiently detect DOM change in single page application

Viewed 4499

I am building a JS library and one of the use case requires me trigger an event on DOM change especially if its a single page application E.g.: github search bar After some research I came across MutationObserver:

// Dom change event listner
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var observer = new MutationObserver(function(mutations, observer) {
    // fired when a mutation occurs
    attachListners();
    // ...
});

observer.observe(documentAlias, {
  subtree: true,
  childList: true
  //...
});

Problem : The library is I'm building designed to be plugged into any website so I have no control on the html implementation. I'm bit concerned using mutation observer might enter infinite loop. Have seen many stack overflow question on same line.

Is there any alternate/better approach? How can I detect DOM update/change safely and efficiently? Or is mutation observer best bet

1 Answers

MutationObserver is probably your best bet. Before MutationObserver, there were Mutation events, but these are now deprecated and not recommended.

Another (arguably poor) option would be to use a setTimeout and periodically check the DOM for changes. But this would require you to write a function to get the nodes you want to check and compare the current value with the old value. This wouldn't be as efficient, since you'd be checking the nodes every X milliseconds, even if there are no changes.

You should be able to write your code in such a way that an infinite loop does not occur. If you are getting an infinite loop, you should add that code to your question above.

As far as performance goes, since MutationObserver will give you old and new values, you could compare these values instead of traversing the entire DOM. For example, something like this:

let observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    let oldValue = mutation.oldValue;
    let newValue = mutation.target.textContent;
    if (oldValue !== newValue) {
        // do something
    }
  });
});

observer.observe(document.body, {
  characterDataOldValue: true, 
  subtree: true, 
  childList: true, 
  characterData: true
});
Related