简体   繁体   中英

document.querySelector inside MutationObserver: good or bad practice?

The objective and what I've tried

I'm trying to get a method to be able to wait until an element is already in the DOM. I've read a few articles about MutationObserver , and I got this method which should accomplish what I need:

const waitForElement = async (queryString) => {
    return new Promise((resolve) => {
      const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          const nodes = Array.from(mutation.addedNodes);
          nodes.forEach((node) => {
            console.log('NODE CUSTOM', node);
            if (node.matches && node.matches(queryString)) {
              observer.disconnect();
              resolve(node);
            }
          });
        });
      });

      observer.observe(document.documentElement, {
        childList: true,
        subtree: true,
      });
    });
};

Then, I could simply use it this way:

await waitForElement('#id-of-element');

The problem

The thing is that it is actually not working as expected : the console.log only logs the "parents" elements, and if the element to be searched is deep in the tree, it seems not to log it (this is being used in a more complex application, so it may have to do with async calls and so on).

The question

However, I found that, in stead of going through arrays of mutations and nodes, I only need to see if the actual element is in the DOM, so I implemented this:

const waitForElement = async (queryString) => {
    return new Promise((resolve) => {
      let element;
      const observer = new MutationObserver(() => {
        element = document.querySelector(queryString);
        if (element) {
          observer.disconnect();
          resolve(element);
        }
      });

      observer.observe(document.documentElement, {
        childList: true,
        subtree: true,
      });
    });
  };

This approach will just check if, after each mutation, the element is actually on the DOM using the querySelector method. It actually works (where the other one fails), and I find it easier to read and understand, and with less loops in between .

Is this a recommendable approach? Will it affect performance , or it will be just the same as the first approach?

Thank you!

A single query for a selector should be very, very, very fast, so I wouldn't expect this to be a problem, but it will depend a lot on the DOM you're using it in. Test your use cases to see if you find a performance problem.

I would at a minimum make it possible to specify where in the DOM to look, rather than looking through the entire thing on every change. That minimizes calls to your mutation observer, and minimizes the amount of searching it does when it's called. If a particular use case has to look through the entire thing, well, then it can use documentElement , but I'd at least make it possible to avoid that. (There's also no reason to declare element in a scope above the scope where it's actually used, and "query string" has a specific meaning in web programming that isn't what you're using it for here, so I'd just use "selector" or "selector string".)

Here's an idea of how you might do that:

const waitForElement = async (selector, rootElement = document.documentElement) => {
    return new Promise((resolve) => {
        const observer = new MutationObserver(() => {
            const element = document.querySelector(selector);
            if (element) {
                observer.disconnect();
                resolve(element);
            }
        });
      
        observer.observe(rootElement, {
            childList: true,
            subtree: true,
        });
    });
};

It would probably also make sense to optionally accept an AbortSignal so you can stop waiting for the element, having it reject with some appropriate "cancelled" error.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM