对于这样的回答,我指的是querySelector
和querySelectorAll
作为querySelector *和getElementById
,getElementsByClassName
,getElementsByTagName
,和getElementsByName
作为getElement *。
主要区别
- querySelector *更加灵活,因为您可以将任何CSS3选择器传递给它,而不仅仅是ID,标记或类的简单选择器。
- querySelector的性能随调用它的DOM的大小而变化。*确切地说,querySelector *调用在O(n)时间运行,而getElement *调用在O(1)时间运行,其中n是被调用的元素或文档的所有子代的总数。这个事实似乎鲜为人知,因此我在此加粗。
- getElement *调用返回对DOM的直接引用,而querySelector *在内部将所选元素的副本复制回DOM之前。这些被称为“活动”和“静态”元素。这与它们返回的类型并不严格相关。我没有办法知道是通过编程方式确定元素是活动的还是静态的,因为它取决于元素是否在某个时刻被复制了,并且不是数据的固有属性。对活动元素的更改会立即应用-更改活动元素会直接在DOM中对其进行更改,因此JS的下一行可以看到该更改,并且该更改会传播到立即引用该元素的任何其他活动元素。对静态元素的更改仅在当前脚本执行完成后才写回到DOM。
- 这些调用的返回类型各不相同。
querySelector
并且getElementById
都返回一个元素。querySelectorAll
并且getElementsByName
都返回NodeLists,这是HTMLCollection过时后添加的新功能。年长的getElementsByClassName
和getElementsByTagName
都返回HTMLCollections。再次,这本质上与元素是活动的还是静态的无关。
下表总结了这些概念。
Function | Live? | Type | Time Complexity
querySelector | N | Element | O(n)
querySelectorAll | N | NodeList | O(n)
getElementById | Y | 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
和以外,每个元素以及全局元素都可以访问所有这些功能,而这些功能getElementsByName
仅在上实现document
。
链接getElement *调用而不是使用querySelector *可以提高性能,尤其是在非常大的DOM上。即使在小型DOM和/或链条非常长的情况下,它通常也更快。但是,除非您知道需要性能,否则应该优先选择querySelector *的可读性。querySelectorAll
通常很难重写,因为您必须在每个步骤中从NodeList或HTMLCollection中选择元素。例如,下面的代码并不能正常工作:
document.getElementsByClassName("someClass").getElementsByTagName("div")
because you can only use getElements* on single elements, not collections. For example:
`document.querySelector("#someId .someClass div")`
could be written as:
document.getElementById("someId").getElementsByClassName("someClass")[0].getElementsByTagName("div")[0]
Note the use of `[0]` to get just the first element of the collection at each step that returns a collection, resulting in one element at the end just like with `querySelector`.
由于所有元素都可以访问querySelector *和getElement *调用,因此您可以使用这两个调用建立链,如果希望获得一些性能提升,这将很有用,但不能避免无法使用getElement *调用编写的querySelector 。
尽管通常很容易分辨出是否只能使用getElement *调用编写选择器,但有一种情况可能并不明显:
document.querySelectorAll(".class1.class2")
可以改写成
document.getElementsByClassName("class1 class2")
在使用querySelector *提取的静态元素上使用getElement *会导致元素相对于querySelector复制的DOM的静态子集处于活动状态,而不是相对于整个文档DOM处于活动状态...这就是简单的地方元素的实时/静态解释开始瓦解。您可能应该避免需要为此担心的情况,但是如果这样做,请记住,querySelector *在返回对它们的引用之前会调用它们找到的复制元素,但是getElement *调用会获取直接引用而不进行复制。
如果存在多个匹配项,则两个API均未指定应首先选择哪个元素。
由于querySelector *会遍历DOM直到找到匹配项(请参见主要区别#2),因此上述内容还意味着您不能依靠要在DOM中查找的元素的位置来保证能够快速找到它-浏览器可以向后,向前,深度优先,广度优先或其他方式遍历DOM。无论元素的位置如何,getElement *仍将在大约相同的时间内找到它们。