Skip to content Skip to sidebar Skip to footer

Using Mutationobserver To Detect When A Node Is Added To Document

I'd like to detect when a specific DOMNode is added to the document, from within the context of the creation of that DOMNode. Here's what I have so far: function getThingThatFormat

Solution 1:

  1. MutationObserver callback runs at the end of an event loop cycle during the microtask queue processing phase which occurs after the main code phase has completed which is why doFormatting() is called after the currently running code completes (the entire function call stack so to speak).

    Unless there's something else in your other code that makes assumptions on doFormatting being called in the current event loop or depends on DOM layout being updated it should be more or less the same as using setTimeout which schedules the callback to run in the next event loop cycle.

    The reason MutationObserver accumulates batches of mutations and reports them all in the microtask queue is to provide a much faster observation capability compared to the deprecated synchronous DOM Mutation Events.

    Solution 1: use callbacks to run code after doFormatting()

    functiononNodeAdopted(node, callback) {
      newMutationObserver((mutations, observer) => {
        if (node.parentNode) {
          observer.disconnect();
          callback(node);
        }
      }).observe(document, {childList: true, subtree: true});
      return node;
    }
    
    functiongetThingThatFormatsItself(callback) {
      returnonNodeAdopted(createNode(), node => {
        doFormatting(node);
        console.log('Formatted');
        callback(node);
      });
    }
    
    $("#Foo").append(getThingThatFormatsItself(node => {
      console.log('This runs after doFormatting()'); 
      doMoreThings();
    }));
    console.log('This runs BEFORE doFormatting() as MutationObserver is asynchronous')
    

    Solution 2: don't use MutationObserver, instead intercept Node.prototype.appendChild:

    const formatOnAppend = (() => {
      const hooks = newMap();
      let appendChild;
      functionappendChildHook(node) {
        appendChild.call(this, node);
        const fn = hooks.get(node);
        if (fn) {
          hooks.delete(node);
          // only restore if no one chained laterif (!hooks.size && Node.prototype.appendChild === appendChildHook) {
            Node.prototype.appendChild = appendChild;
          }
          fn(node);
        }
        return node;
      } 
      return {
        register(node, callback) {
          if (!hooks.size) {
            appendChild = Node.prototype.appendChild;
            Node.prototype.appendChild = appendChildHook;
          }
          hooks.set(node, callback);
          return node;
        },
      }
    })();
    

    Usage:

    functiongetThingThatFormatsItself() {
      return formatOnAppend.register(createNode(), node => {
        console.log('%o is added', node);
      });
    }
    

    Other things to try: window.queueMicrotask(callback) instead of setTimeout to enqueue some of the dependent code in the microtask queue. For older browsers there's a simple polyfill right there in the article.

  2. Check document.contains(MY_NODE) (won't help if inside ShadowDOM) or MY_NODE.parentNode instead of enumerating the mutations:

    newMutationObserver((mutations, observer) => {
      if (MY_NODE.parentNode) {
        observer.disconnect();
        doFormatting();
      }
    }).observe(document, {childList: true, subtree: true});
    

    This is also more reliable because in a general case the node may be a child of another added node, not as as a separate item in the addedNodes array.

Post a Comment for "Using Mutationobserver To Detect When A Node Is Added To Document"