P粉0088297912023-08-22 20:21:16
对于这个答案,我将querySelector
和querySelectorAll
称为querySelector*,将getElementById
、getElementsByClassName
、getElementsByTagName
和getElementsByName
称为getElement*。
这些信息的很多可以在规范中进行验证,很多是我在编写时运行的各种基准测试得出的。规范:https://dom.spec.whatwg.org/
querySelector
和getElementById
都返回单个元素。querySelectorAll
和getElementsByName
都返回NodeList。getElementsByClassName
和getElementsByTagName
都返回HTMLCollection。NodeList和HTMLCollection都被称为元素的集合。这些概念在下表中总结。
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)
HTMLCollection不像NodeList那样类似于数组,不支持.forEach()。我发现扩展运算符对绕过这个问题很有用:
[...document.getElementsByClassName("someClass")].forEach()
每个元素和全局document
都可以访问所有这些函数,除了getElementById
和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]
,以获取集合的第一个元素,最终结果只有一个元素,就像使用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副本。
当我在处理无限滚动页面时,我不得不研究这个问题,我认为这可能是一个常见的情况,性能成为一个问题。我们的代码中有onScroll事件,其中包含querySelectorAll调用。即使调用被限制速率,如果您滚动到足够远的位置,页面也会崩溃,此时将有太多调用迭代太多元素,浏览器无法跟上。DOM的大小在这种用例中是相关的,因此在无限滚动页面上运行的代码中,更倾向于使用getElement*调用。
P粉2383558602023-08-22 16:58:04
语法和浏览器支持。
querySelector
在你想要使用更复杂的选择器时更有用。
例如,所有从属于foo类的元素的列表项:.foo li
:
字符在选择器中有特殊含义。你需要对它进行转义。(选择器转义字符在JS字符串中也有特殊含义,所以你也需要转义它)。
document.querySelector("#view\:_id1\:inputText1")