0

Based on this Q&A, I try to write some native JS code (no use of libraries) that will have elements addition hierarchy/dependency, which means that I'll have to wait for some elements to show up/load before I go on with the function. I have the following function to create and use MutationObservers in order to track DOM changes:

/**
 * Creates an observer that tracks added DOM elements and executes a function when they're added 
 * @param {HTMLElement} observedObject - the dom element to observe
 * @param {Function} elementFunc - the function to execute when elements are added
 * @param {Number} timeoutMs - the amount of miliseconds to wait before disconnecting the observer
 */
function observeDOM(observedObject, elementFunc, timeoutMs) {
  var connected = false;
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (!mutation.addedNodes) return
      for (var i = 0; i < mutation.addedNodes.length; i++) {
        if(elementFunc(mutation.addedNodes[i])) {
          // disconnect when the function returns true 
          observer.disconnect();
          break;
        }
      }
    })
  });

  observer.observe(observedObject, {
      childList: true
    , subtree: true
    , attributes: false
    , characterData: false
  });
  connected = true;
  
  // disconnect the observer after N seconds
  if(timeoutMs >= 0) {
    setTimeout(function() {
      if(connected) {
        observer.disconnect();
        connected = false;
      }
    }, timeoutMs);  
  }
}

And I use the function above like that:

// do some staff...
observeDOM(document.body, function(node) {
  if(node.className === "class1") {
    // do some staff and wait for another element 
    observeDOM(document.body, function(node) {
      if(node.className === "class2") {
        // do some staff and wait for another element 
        observeDOM(document.body, function(node) {
          if(node.className === "class2") {
            //[...]
            return true;
          }
          return false;
        }
        return true;
      }
      return false;
    }
    return true; // stop the observer
  }
  return false; // go on listening to elements addition
} 

Do you think there is more elegant way to achieve it? What bothers my eyes the most is the tonnes of nested block I'll create when I have a big amount of elements to wait for !

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
benams
  • 4,308
  • 9
  • 32
  • 74
  • 1. You can use Promises and await/async, 2. one added node may be a container that has the node you want inside so in addition to the direct comparison of className you may need to also check node.getElementsByClassName (super fast) or node.querySelector, 3. some nodes are text nodes (node.nodeType == Node.TEXT_NODE) so there's probably no need to pass them to the callback. – wOxxOm Dec 26 '17 at 21:09
  • There are existing libraries like MutationSummary. See also [this one](https://greasyfork.org/en/scripts/12228-setmutationhandler). – wOxxOm Dec 26 '17 at 21:12
  • @wOxxOm, Thanks for you detailed answers! I know about the container and text node issues and it was just an example. Are you sure promises and/or await/async can help me to track newly added DOM elements? Can you post an example please? I need to wait for an element to be added in a page I haven't built (so I don't control the function adds it). I'd prefer not to use libraries, as I want to keep it light and controlled (you can see that one of the libs you offered uses `querySelector` which will slow me down) – benams Dec 27 '17 at 09:30

0 Answers0