【问题标题】:document.querySelector inside MutationObserver: good or bad practice?MutationObserver 中的 document.querySelector:好还是坏的做法?
【发布时间】:2021-03-25 08:28:50
【问题描述】:

目标和我的尝试

我正在尝试获取一种能够等到元素已经在 DOM 中的方法。我已经阅读了一些关于MutationObserver 的文章,并且我得到了这个方法,它应该可以完成我所需要的:

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,
      });
    });
};

那么,我可以简单地这样使用它:

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

问题

问题是它实际上没有按预期工作console.log 只记录“父母”元素,如果要搜索的元素在树的深处,似乎不是记录它(这是在更复杂的应用程序中使用的,因此它可能与异步调用等有关)。

问题

但是,我发现,我不需要遍历突变和节点数组,而只需要查看实际元素是否在 DOM 中,因此我实现了这个:

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,
      });
    });
  };

这种方法将使用querySelector 方法检查每次突变后元素是否实际位于DOM 上。 它确实有效(另一个失败了),而且我发现它更容易阅读和理解,并且中间的循环更少

这是一种值得推荐的方法吗?它会影响性能,还是和第一种方法一样?

谢谢!

【问题讨论】:

标签: javascript asynchronous document-ready mutation-observers queryselector


【解决方案1】:

对选择器的单个查询应该非常、非常、非常快,所以我不认为这是一个问题,但它会在很大程度上取决于您使用的 DOM测试你的用例,看看你是否发现了性能问题。

我至少可以指定在 DOM 中查看的位置,而不是在每次更改时都查看整个内容。这最大限度地减少了对您的突变观察者的调用,并最大限度地减少了它在调用时所做的搜索量。如果一个特定的用例必须查看整个事情,那么它可以使用documentElement,但我至少可以避免这种情况。 (也没有理由在实际使用范围之上的范围内声明 element,并且“查询字符串”在 Web 编程中具有特定含义,这不是您在这里使用的含义,所以我会只需使用“选择器”或“选择器字符串”。)

以下是您可以如何做到这一点的想法:

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,
        });
    });
};

有选择地接受AbortSignal 可能也很有意义,这样您就可以停止等待元素,让它拒绝一些适当的“已取消”错误。

【讨论】:

  • 哦!我懂了!那太好了!我已经按照您的建议实现了rootElement,并更正了变量声明。我将搜索如何实现您提到的 AbortSignal,那太好了!非常感谢!
猜你喜欢
  • 1970-01-01
  • 2016-11-10
  • 1970-01-01
  • 1970-01-01
  • 2011-05-29
  • 2020-05-19
  • 2021-07-06
  • 2013-01-24
  • 2011-04-17
相关资源
最近更新 更多