对于这样的回答,我指的是querySelector
和querySelectorAll
作为querySelector *和getElementById
,getElementsByClassName
,getElementsByTagName
,和getElementsByName
作为getElement *。
很多这些信息可以在规范中得到验证,其中很多来自我在编写它时运行的各种基准测试。规范:https : //dom.spec.whatwg.org/
主要区别
- querySelector* 更灵活,因为您可以将任何 CSS3 选择器传递给它,而不仅仅是 id、tag 或 class 的简单选择器。
- querySelector* 的性能随着调用它的 DOM 的大小而变化。准确地说,querySelector* 调用在 O(n) 时间内运行,getElement* 调用在 O(1) 时间内运行,其中 n 是调用它的元素或文档的所有子元素的总数。这个事实似乎是最不为人知的,所以我加粗了。
- 这些调用的返回类型各不相同。
querySelector
并且getElementById
都返回一个元素。querySelectorAll
并且getElementsByName
都返回 NodeLists。旧的getElementsByClassName
和getElementsByTagName
都返回 HTMLCollections。NodeLists 和 HTMLCollections 都被称为元素的集合。
- 集合可以包含对 DOM 中元素的引用,或元素的副本。getElement* 调用返回引用集合,而 querySelectorAll 结果包含元素的副本。它们分别称为“实时”和“静态”集合。这与它们返回的类型无关。
这些概念总结在下表中。
Function | Live? | Type | Time Complexity
querySelector | | Element | O(n)
querySelectorAll | N | NodeList | O(n)
getElementById | | Element | O(1)
getElementsByClassName | Y | HTMLCollection | O(1)
getElementsByTagName | Y | HTMLCollection | O(1)
getElementsByName | Y | NodeList | O(1)
详细信息、提示和示例
HTMLCollections 不像 NodeLists 那样类似于数组,并且不支持 .forEach()。我发现传播运算符对解决这个问题很有用:
[...document.getElementsByClassName("someClass")].forEach()
document
除了仅在 上实现的getElementById
and之外,每个元素和 global都可以访问所有这些函数。getElementsByName
document
链接 getElement* 调用而不是使用 querySelector* 将提高性能,尤其是在非常大的 DOM 上。即使在小型 DOM 和/或非常长的链上,它通常也更快。但是,除非您知道自己需要性能,否则应首选 querySelector* 的可读性。querySelectorAll
通常更难重写,因为您必须在每一步都从 NodeList 或 HTMLCollection 中选择元素。例如,下面的代码并不能正常工作:
document.getElementsByClassName("someClass").getElementsByTagName("div")
因为您只能在单个元素上使用 getElements*,而不能在集合上使用。例如:
document.querySelector("#someId .someClass div")
可以写成:
document.getElementById("someId").getElementsByClassName("someClass")[0].getElementsByTagName("div")[0]
请注意,[0]
在返回集合的每个步骤中,使用仅获取集合的第一个元素,就像 with 一样,在最后生成一个元素querySelector
。
由于所有元素都可以访问 querySelector* 和 getElement* 调用,因此您可以使用这两个调用创建链,如果您想要一些性能提升,这会很有用,但无法避免无法根据 getElement* 调用编写的 querySelector .
虽然通常很容易判断是否可以仅使用 getElement* 调用编写选择器,但有一种情况可能并不明显:
document.querySelectorAll(".class1.class2")
可以改写为
document.getElementsByClassName("class1 class2")
在使用 querySelector* 获取的静态元素上使用 getElement* 将导致元素相对于由 querySelector 复制的 DOM 的静态子集有效,但相对于完整文档 DOM 无效......这是简单的地方元素的实时/静态解释开始分崩离析。您可能应该避免不得不担心这一点的情况,但如果您这样做了,请记住 querySelector* 在返回对它们的引用之前调用它们找到的复制元素,但 getElement* 调用获取直接引用而不进行复制。
querySelector* 并getElementById
以深度优先的预序遍历元素,在规范中称为“树序”。对于其他 getElement* 调用,规范中我不清楚 - 它们可能与树顺序相同,但getElementsByClassName(".someClass")[0]
可能无法在每个浏览器中可靠地给出相同的结果。getElementById("#someId")
但是,即使您的页面上有多个相同 ID 的副本,也应该如此。