JavaScript 中的 querySelector 和 querySelectorAll 与 getElementsByClassName 和 getElementById

IT技术 javascript
2021-01-11 21:39:18

我想知道querySelectorquerySelectorAll反对 getElementsByClassName到底有什么区别getElementById

这个链接我可以收集到,querySelector我可以写document.querySelector(".myclass")来获取带有类的元素myclassdocument.querySelector("#myid")获取带有 ID 的元素myid但我已经可以做到这一点getElementsByClassNamegetElementById应该首选哪一个?

我还在XPage工作,其中 ID 是用冒号动态生成的,看起来像这样view:_id1:inputText1所以当我写document.querySelector("#view:_id1:inputText1")它不起作用。但写作document.getElementById("view:_id1:inputText1")有效。任何想法为什么?

6个回答

对于这样的回答,我指的是querySelectorquerySelectorAll作为querySelector *和getElementByIdgetElementsByClassNamegetElementsByTagName,和getElementsByName作为getElement *。

很多这些信息可以在规范中得到验证,其中很多来自我在编写它时运行的各种基准测试。规范:https : //dom.spec.whatwg.org/

主要区别

  1. querySelector* 更灵活,因为您可以将任何 CSS3 选择器传递给它,而不仅仅是 id、tag 或 class 的简单选择器。
  2. querySelector* 的性能随着调用它的 DOM 的大小而变化。准确地说,querySelector* 调用在 O(n) 时间内运行,getElement* 调用在 O(1) 时间内运行,其中 n 是调用它的元素或文档的所有子元素的总数。这个事实似乎是最不为人知的,所以我加粗了。
  3. 这些调用的返回类型各不相同。querySelector并且getElementById都返回一个元素。querySelectorAll并且getElementsByName都返回 NodeLists。旧的getElementsByClassNamegetElementsByTagName都返回 HTMLCollections。NodeLists 和 HTMLCollections 都被称为元素的集合。
  4. 集合可以包含对 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除了仅在 上实现的getElementByIdand之外,每个元素和 global都可以访问所有这些函数getElementsByNamedocument

  • 链接 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 的副本,也应该如此。

到目前为止,关于这个主题的最准确的答案。应该多点赞。
2021-03-10 21:39:18
非常详细的答案,更重要的是,比较复杂性。谢谢。
2021-03-11 21:39:18
应该非常精确地放在你的博客中,萨沙
2021-03-13 21:39:18
“对静态元素的更改只会在当前脚本执行完毕后写回 DOM。” - 是否有可以澄清这一点的用例?很好的答案,顺便说一句
2021-03-18 21:39:18
有人愿意就我在某处听到的事情提供一些反馈吗?是 JS 引擎在我们调用 querySelector("#id") 时动态交换调用 getElementById(); 这在很多层面上似乎都是错误的,但我想知道是否有机会这样做。试图寻找答案,这个回复是迄今为止最接近我正在寻找的。
2021-03-27 21:39:18

我想知道 querySelector 和 querySelectorAll 与 getElementsByClassName 和 getElementById 的区别究竟是什么?

语法和浏览器支持。

querySelector 当您想使用更复杂的选择器时更有用。

例如,从属于 foo 类成员的元素派生的所有列表项: .foo li

document.querySelector("#view:_id1:inputText1") 它不起作用。但是编写 document.getElementById("view:_id1:inputText1") 有效。任何想法为什么?

:字符在选择器中具有特殊含义。你必须逃避它。(选择器转义字符在一个字符串JS特殊的意义,所以你必须逃离太)。

document.querySelector("#view\\:_id1\\:inputText1")
我支持@janaspage 的声明。网站今天也关闭了。
2021-03-12 21:39:18
关于类选择,另见jsperf.com/getelementsbyclassname-vs-queryselectorall/25结论:与 jquery 相比,人们应该更喜欢纯 javascript,以及特定的功能getElementByIdgetElementsByClassName. 没有 .className 选择可能会百倍getElementsByClassName
2021-03-13 21:39:18
它会因浏览器而异(以及不同版本)。我认为基于选择器的更昂贵(但不会以一种可能很重要的方式)
2021-03-17 21:39:18

Mozilla 文档中收集

NodeSelector 接口 此规范向实现 Document、DocumentFragment 或 Element 接口的任何对象添加了两个新方法:

查询选择器

返回节点子树中第一个匹配的 Element 节点如果没有找到匹配的节点,则返回 null。

查询选择器全部

返回一个包含节点子树中所有匹配元素节点的 NodeList,如果没有找到匹配,则返回一个空的 NodeList。

注意: 返回的 NodeListquerySelectorAll()不是 live 的,这意味着 DOM 的变化不会反映在集合中。这与其他返回活动节点列表的 DOM 查询方法不同。

"live" 表示在 DOM 运行时添加的节点,并且可以在新添加的节点上工作
2021-03-16 21:39:18
+1 用于指出活动节点列表的区别。根据您打算如何使用结果,这是一个非常重要的区别,需要注意。
2021-04-09 21:39:18

关于区别,querySelectorAll之间的结果有一个重要的地方getElementsByClassName:返回值不同。querySelectorAll将返回一个静态集合,同时getElementsByClassName返回一个实时集合。如果将结果存储在变量中供以后使用,这可能会导致混淆:

  • 生成的变量querySelectorAll将包含在调用方法时满足选择器的元素
  • 生成的变量getElementsByClassName将包含使用时满足选择器的元素(这可能与调用方法的那一刻不同)。

例如,请注意即使您没有重新分配变量aux1aux2,它们在更新类后也会包含不同的值:

// storing all the elements with class "blue" using the two methods
var aux1 = document.querySelectorAll(".blue");
var aux2 = document.getElementsByClassName("blue");

// write the number of elements in each array (values match)
console.log("Number of elements with querySelectorAll = " + aux1.length);
console.log("Number of elements with getElementsByClassName = " + aux2.length);

// change one element's class to "blue"
document.getElementById("div1").className = "blue";

// write the number of elements in each array (values differ)
console.log("Number of elements with querySelectorAll = " + aux1.length);
console.log("Number of elements with getElementsByClassName = " + aux2.length);
.red { color:red; }
.green { color:green; }
.blue { color:blue; }
<div id="div0" class="blue">Blue</div>
<div id="div1" class="red">Red</div>
<div id="div2" class="green">Green</div>

顺便提一下 - 所有旧的 DOM api 都返回一个节点列表document.getElementsByNamedocument.getElementsByTagNameNS或者document.getElementsByTagName将表现出相同的行为。
2021-03-30 21:39:18
一些分析说 querySelector 比 getElementById 花费更多的时间,比如这里dimlucas.com/index.php/2016/09/17/...如果我们考虑访问时间呢?从 getElementById 获取的活动节点是否比从 querySelector 获取的静态节点花费更多时间?
2021-04-02 21:39:18
@Ericdocument.getElementById()不返回活动节点。它比document.querySelector('#id_here')可能更快,因为querySelector必须首先解析 CSS 选择器。
2021-04-02 21:39:18
@RBT 我会提到这些较旧的 DOM API 不返回 NodeList 对象,它们返回 HTMLCollections。
2021-04-03 21:39:18

我来到此页面纯粹是为了找出在性能方面使用的更好方法 - 即更快:

querySelector / querySelectorAll or getElementsByClassName

我发现了这个:https : //jsperf.com/getelementsbyclassname-vs-queryselectorall/18

它对上面的 2 x 示例运行了一个测试,此外它还对 jQuery 的等效选择器进行了测试。我的测试结果如下:

getElementsByClassName = 1,138,018 operations / sec - <<< clear winner
querySelectorAll = 39,033 operations / sec
jquery select = 381,648 operations / sec
哇,这是一个巨大的差异,感谢您查找它。显然querySelectorAll在幕后需要额外的工作(包括解析选择器表达式,考虑伪元素等),而getElementsByClassName只是递归对象遍历。
2021-03-16 21:39:18