在JavaScript中,querySelector和querySelectorAll与getElementsByClassName和getElementById的区别
P粉442576165
2023-08-21 21:45:39
<p>我想知道<code>querySelector</code>和<code>querySelectorAll</code>与<code>getElementsByClassName</code>和<code>getElementById</code>之间到底有什么区别?</p>
<p>从这个链接中,我了解到使用<code>querySelector</code>,我可以写<code>document.querySelector(".myclass")</code>来获取具有类名<code>myclass</code>的元素,以及<code>document.querySelector("#myid")</code>来获取具有ID<code>myid</code>的元素。但是我已经可以使用<code>getElementsByClassName</code>和<code>getElementById</code>来实现这个功能。哪个应该优先选择?</p>
<p>另外,我在XPages中工作,ID是动态生成的,包含冒号,看起来像这样<code>view:_id1:inputText1</code>。所以当我写<code>document.querySelector("#view:_id1:inputText1")</code>时,它不起作用。但是当我写<code>document.getElementById("view:_id1:inputText1")</code>时,它起作用。有任何想法为什么会这样?</p>
对于这个答案,我将
querySelector
和querySelectorAll
称为querySelector*,将getElementById
、getElementsByClassName
、getElementsByTagName
和getElementsByName
称为getElement*。这些信息的很多可以在规范中进行验证,很多是我在编写时运行的各种基准测试得出的。规范:https://dom.spec.whatwg.org/
主要区别
querySelector
和getElementById
都返回单个元素。querySelectorAll
和getElementsByName
都返回NodeList。getElementsByClassName
和getElementsByTagName
都返回HTMLCollection。NodeList和HTMLCollection都被称为元素的集合。这些概念在下表中总结。
详细信息、提示和示例
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*调用。
语法和浏览器支持。
querySelector
在你想要使用更复杂的选择器时更有用。例如,所有从属于foo类的元素的列表项:
.foo li
:
字符在选择器中有特殊含义。你需要对它进行转义。(选择器转义字符在JS字符串中也有特殊含义,所以你也需要转义它)。