4

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

JoshG
  • 6,472
  • 2
  • 38
  • 61
TestingInProd
  • 349
  • 10
  • 26
  • The most efficient approach is to get called by the code that updated the dom, not to listen for changes yourself. But apparently you can't / don't want to do that. So, what updates does the code make that you need to detect? What kind of dom changes are we talking about? Who is plugging the library into their website and how? – Bergi Oct 08 '20 at 09:13
  • @Bergi Its a search bar that displays result just like github example i showed above. If the search is updated upon query change, i need to detect those new refreshed results.. – TestingInProd Oct 08 '20 at 18:42
  • I don't know what you mean by "search bar", or how this is related to the standardjs linter/formatter you linked as an example(?). Who updates the query or refreshes results, and what does your code do with them? – Bergi Oct 08 '20 at 18:46
  • 1
    Does this answer your question? [Detect changes in the DOM](https://stackoverflow.com/questions/3219758/detect-changes-in-the-dom) – Denis P Jun 27 '23 at 23:19

2 Answers2

12

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
});
JoshG
  • 6,472
  • 2
  • 38
  • 61
0

Depending on your constraints, as @JoshG mentioned, you could use a setInterval. I need to observe a specific DOM element value changing so I was just able to target the specific elementID with the following:

var watchID ;
var el = document.getElementById('elementID') ;
var oldValue = el.value ;
var watchCount = 0 ;  // create a built in timeout function
watchID = setInterval(function() {
  watchCount++ ;
  if (el.value != oldValue) {
    console.log("Element Changed:" +el.value) ;
    ... do something ...
    clearInterval(watchID) ;
  }
  // 30 second timeout
  if (watchCount == 60) {
    console.log("Interval Timeout")          
    clearInterval(watchID) ;
  }
},500) ;  // execute every 1/2 second for 30 seconds

In my case, I had a complex file download that needed a bit of a different hacky solution and the above worked without any eventListeners or other more complex solutions that monitored everything (like: MutationObserver)

rolinger
  • 2,787
  • 1
  • 31
  • 53