document.querySelector MutationObserver 内部:好的还是坏的做法?
document.querySelector inside MutationObserver: good or bad practice?
objective 和我尝试过的方法
我正在尝试获得一种方法,以便能够等到某个元素已经在 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
只记录“parents”元素,如果要搜索的元素很深在树中,它似乎没有记录它(这是在更复杂的应用程序中使用的,因此它可能与异步调用等有关)。
问题
然而,我发现,我不需要遍历突变和节点数组,我只需要查看实际元素是否在 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,
});
});
};
这种方法只会检查,在每次突变之后,元素是否确实在 DOM 上,使用 querySelector
方法。 它确实有效(另一个失败了),我发现它更容易阅读和理解,并且中间的循环更少。
这是值得推荐的方法吗?它会影响性能,还是与第一种方法相同?
谢谢!
选择器的单个查询应该非常、非常、非常快,所以我认为这不会成为问题,但这将很大程度上取决于DOM 您正在使用它。测试您的用例以查看是否发现性能问题。
我至少会允许在 DOM 中指定要查看的位置,而不是在每次更改时都查看整个内容。这最大限度地减少了对变异观察者的调用,并最大限度地减少了它在被调用时所做的搜索量。如果一个特定的用例 必须 来查看整个事情,那么它可以使用 documentElement
,但我至少可以避免这种情况。 (也没有理由在实际使用范围之上的范围内声明 element
,并且“查询字符串”在网络编程中具有特定含义,这不是您在这里使用它的目的,所以我只需使用“选择器”或“选择器字符串”即可。)
以下是您可以如何做到这一点的想法:
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
可能也很有意义,这样您就可以停止等待该元素,让它拒绝并出现一些适当的“已取消”错误。
objective 和我尝试过的方法
我正在尝试获得一种方法,以便能够等到某个元素已经在 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
只记录“parents”元素,如果要搜索的元素很深在树中,它似乎没有记录它(这是在更复杂的应用程序中使用的,因此它可能与异步调用等有关)。
问题
然而,我发现,我不需要遍历突变和节点数组,我只需要查看实际元素是否在 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,
});
});
};
这种方法只会检查,在每次突变之后,元素是否确实在 DOM 上,使用 querySelector
方法。 它确实有效(另一个失败了),我发现它更容易阅读和理解,并且中间的循环更少。
这是值得推荐的方法吗?它会影响性能,还是与第一种方法相同?
谢谢!
选择器的单个查询应该非常、非常、非常快,所以我认为这不会成为问题,但这将很大程度上取决于DOM 您正在使用它。测试您的用例以查看是否发现性能问题。
我至少会允许在 DOM 中指定要查看的位置,而不是在每次更改时都查看整个内容。这最大限度地减少了对变异观察者的调用,并最大限度地减少了它在被调用时所做的搜索量。如果一个特定的用例 必须 来查看整个事情,那么它可以使用 documentElement
,但我至少可以避免这种情况。 (也没有理由在实际使用范围之上的范围内声明 element
,并且“查询字符串”在网络编程中具有特定含义,这不是您在这里使用它的目的,所以我只需使用“选择器”或“选择器字符串”即可。)
以下是您可以如何做到这一点的想法:
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
可能也很有意义,这样您就可以停止等待该元素,让它拒绝并出现一些适当的“已取消”错误。