querySelectorAll和getElementsBy*方法返回什么?

204
< p > getElementsByClassName(以及类似的函数,如getElementsByTagNamequerySelectorAll)是否与getElementById相同,还是它们返回元素数组?

我问这个问题是因为我正在尝试使用getElementsByClassName更改所有元素的样式。请参见以下内容。

//doesn't work
document.getElementsByClassName('myElement').style.size = '100px';

//works
document.getElementById('myIdElement').style.size = '100px';

45
线索在名称中,getElementsByClassName() 暗示复数,而 getElementById() 暗示单个元素项。 - David Thomas
1
我明白了,只是对于使用上述代码无法更改所有具有该类名的元素而不必循环遍历数组这一点感到困惑。JQuery 的方式要好得多,我只是对 JS 的方式感到好奇。 - dmo
1
可能也会有用:https://dev59.com/Um865IYBdhLWcg3wUs_z - kapa
12个回答

197
你的 getElementById 代码有效,因为ID必须是唯一的,因此该函数始终返回一个元素(如果未找到则返回null)。
然而,方法getElementsByClassNamegetElementsByNamegetElementsByTagNamegetElementsByTagNameNS返回一个可迭代的元素集合。
方法名称提供了提示:getElement意味着单数,而getElements则意味着复数

方法querySelector也返回单个元素,而querySelectorAll返回可迭代集合。

可迭代集合可以是NodeListHTMLCollection

getElementsByNamequerySelectorAll都指定返回NodeList;其他getElementsBy*方法指定返回HTMLCollection,但请注意一些浏览器版本实现方式不同。

这两种集合类型都没有Elements、Nodes或类似类型所提供的属性;这就是为什么从document.getElements()读取style会失败。
换句话说:NodeListHTMLCollection没有style;只有Elementstyle


这些“类数组”集合是包含零个或多个元素的列表,您需要迭代它们以访问它们。 虽然您可以像迭代数组一样迭代它们,但请注意它们与Array不同。 在现代浏览器中,您可以使用Array.from将这些可迭代对象转换为真正的数组;然后您就可以使用forEach和其他 数组方法,例如迭代方法
Array.from(document.getElementsByClassName("myElement"))
  .forEach((element) => element.style.size = "100px");

在不支持Array.from或迭代方法的旧浏览器中,您仍然可以使用Array.prototype.slice.call。然后,您可以像处理真实数组一样对其进行迭代:
var elements = Array.prototype.slice
    .call(document.getElementsByClassName("myElement"));

for(var i = 0; i < elements.length; ++i){
  elements[i].style.size = "100px";
}

您还可以遍历NodeListHTMLCollection本身,但请注意,在大多数情况下,这些集合是实时的MDN文档, DOM规范),即它们会随着DOM的更改而更新。因此,如果您在循环过程中插入或删除元素,请确保不要意外跳过某些元素创建无限循环。MDN文档应始终注明方法返回的是实时集合还是静态集合。

例如,在现代浏览器中,NodeList提供了一些迭代方法,如forEach

document.querySelectorAll(".myElement")
  .forEach((element) => element.style.size = "100px");

一个简单的 for 循环也可以被使用:

var elements = document.getElementsByClassName("myElement");

for(var i = 0; i < elements.length; ++i){
  elements[i].style.size = "100px";
}

附注:.childNodes 返回一个 实时NodeList,而 .children 返回一个 实时HTMLCollection,因此这两个获取器也需要小心处理。


有一些类库,比如jQuery,可以使DOM查询变得更短,并在“一个元素”和“一组元素”之间创建了一层抽象:

$(".myElement").css("size", "100px");

2
这也适用于 <iframe> 吗?它也是您的域的一部分。 - Maduro
6
2018年了... 只需创建一个包装函数来调用 querySelectorAll(),就可以拥有简洁的代码而不需要依赖过时的大型库。qSA(".myElement").forEach(el => el.style.size = "100px") 也许可以让这个包装函数接受一个回调函数。qSA(".myElement", el => el.style.size = "100px") - user2437417
3
如果您希望简短一些,考虑将一个巨大的库添加到您的项目中。我知道2012年是不同的时代,但即使在那时,我也觉得那段话很荒唐。 - CoryCoolguy
4
“像处理真正的数组一样迭代它……”请注意,“getElementsByClassName”返回一个可能在循环期间意外修改的实时NodeList,例如如果选择它们的类名被删除。;-) - RobG
@RobG 我已经在编辑中提到了这一点。 - Sebastian Simon
1
这个答案中对jQuery的引用可能应该被移除,原因有很多:它的行为与本地DOM方法有显著不同,它与所问的问题没有直接关联,并且需要加载一个太大的库来缩短一两个函数调用。最后一个问题在近十年来一直存在,但随着jQuery的失去影响力,它变得更加重要。当然,一些浏览器可能会在内部缓存jQuery,但我们真的希望新的开发人员采用加载一个巨大的库来使用其中的一小部分的做法吗? - Sebastian Simon

26
您正在将数组用作对象,getElementbyIdgetElementsByClassName之间的区别在于:

  • getElementbyId会返回一个元素对象或者如果未找到具有该ID的元素,则返回null
  • getElementsByClassName将返回一个实时的 HTMLCollection,如果未找到匹配的元素,则可能为长度为0

getElementsByClassName

getElementsByClassName(classNames)方法接受一个包含无序唯一空格分隔标记组成的字符串。当调用该方法时,必须返回一个live的NodeList对象,其中包含在文档中具有指定参数中所有类的所有元素,通过在空格上拆分字符串来获取类。如果参数中未指定标记,则该方法必须返回一个空的NodeList。

https://www.w3.org/TR/2008/WD-html5-20080610/dom.html#getelementsbyclassname

getElementById

getElementById()方法访问具有指定ID的第一个元素。

https://developer.mozilla.org/zh-CN/docs/Web/API/Document/getElementById

在您的代码中,这一行:

1- document.getElementsByClassName('myElement').style.size = '100px';


应该改为:

1- document.getElementsByClassName('myElement')[0].style.fontSize = '100px';

不会按预期工作,因为getElementByClassName将返回一个数组,而该数组没有style属性,您可以通过遍历来访问每个element

这就是为什么getElementById函数对您起作用的原因,该函数将返回直接对象。因此,您将能够访问style属性。


请注意,浏览器正在实现的whatwg规范与w3c不同,前者(即当前浏览器)返回一个HTMLCollection而不是NodeList。这虽然只是一个小差异,但可能会引起一些混淆。 - Kaiido
1
@Kaiido - 实际区别是什么?据我所知,NodeList 是 DOM 元素的通用集合,在任何 DOM 中都可用,而不仅仅是 HTML DOM(例如 XML DOM),而 HTMLCollection 则是专门为 HTML DOM 设计的。我能看到的唯一区别是 HTMLCollectionnamedItem 方法。 - RobG
PS小细节:WHATWG HTML Living StandardW3C HTML 5.2标准的链接。真是眼花缭乱啊。;-) 不过这并不影响你提出的观点。 - RobG
1
@RobG NodeList拥有许多方法(https://developer.mozilla.org/en-US/docs/Web/API/NodeList#Methods),这些方法在HTMLCollection上不可用。 - Kaiido
@Kaiido - 当然,但是forEach并没有被W3C或WHATWG规定为集合或NodeList的接口的一部分,它是单独指定的,例如在Web IDL规范中作为通用集合的属性,因此应该适用于集合和NodeList(尽管我接受您的观点,即由getElementsByClassName返回的集合没有forEach方法)。我想最重要的是有足够的故事可以讲述一个好答案。 :-) - RobG
@RobG,我真的不明白你的意思...我说这只是“小问题”,尽管我不得不进行更正,因为你的编辑,然后指出我的编辑在Alvaro Joao引用的语句和编辑后的句子之间存在的差异。是的,NodeList具有ArrayLike方法,因为它们的<Node>属性是“可迭代的”。 - Kaiido

16

ES6 提供了 Array.from()方法,可以从类数组对象或可迭代对象中创建一个新的数组实例。

ES6提供了Array.from()方法,可以从类数组对象或可迭代对象中创建一个新的数组实例。

let boxes = document.getElementsByClassName('box');

setTimeout(() => {
  Array.from(boxes).forEach(v => v.style.background = 'green');
  console.log(Array.from(boxes));
}, 500);
.box {
  width: 50px;
  height: 50px;
  margin: 5px;
  background: blue;
  display: inline-block;
}
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>

通过使用Array.from()函数,可以在代码片段中看到,您随后可以对每个元素进行操作。


$('.box').css({'background':'green'});
.box {
  width: 50px;
  height: 50px;
  margin: 5px;
  background: blue;
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>


13
此页面中获取以下说明:
getElementsByClassName()方法返回具有指定类名的文档中所有元素的集合,作为NodeList对象。
NodeList对象表示节点集合。可以通过索引号访问节点。索引从0开始。
提示:您可以使用NodeList对象的length属性确定具有指定类名的元素数量,然后可以遍历所有元素并提取所需信息。
因此,作为参数,getElementsByClassName将接受一个类名。
如果这是您的HTML主体:
<div id="first" class="menuItem"></div>
<div id="second" class="menuItem"></div>
<div id="third" class="menuItem"></div>
<div id="footer"></div>

然后var menuItems = document.getElementsByClassName('menuItem')将返回一个集合(不是数组),包含3个与给定类名匹配的上面的<div>

你可以使用以下语句遍历这些节点(在本例中为<div>集合):

for (var menuItemIndex = 0 ; menuItemIndex < menuItems.length ; menuItemIndex ++) {
   var currentMenuItem = menuItems[menuItemIndex];
   // do stuff with currentMenuItem as a node.
}
请参考这篇文章,了解元素和节点之间的差异。

8

换句话说

  • document.querySelector()只选择指定选择器的第一个元素。因此它不会返回数组,而是单个值。与document.getElementById()类似,后者仅获取ID元素,因为ID必须是唯一的。

  • document.querySelectorAll()选择所有指定选择器的元素并将它们返回到数组中。与document.getElementsByClassName()选取类名和document.getElementsByTagName()仅选取标签相似。


为什么使用querySelector?

它仅用于简便和简洁的目的。


为什么使用getElement/sBy?*

更快的性能。


为什么有这种性能差异?

两种选择方式都旨在创建进一步使用的NodeListquerySelectors生成具有选择器的静态NodeList,因此必须从头开始创建。
getElement/sBy*立即适应当前DOM的现有动态NodeList。

所以,何时使用哪种方法取决于您/您的项目/您的设备。


信息

所有方法的演示
NodeList文档
性能测试


7
您可以通过运行以下代码获取单个元素:

document.querySelector('.myElement').style.size = '100px';

但是这只会对带有类名.myElement的第一个元素起作用。

如果您想将其应用于所有具有该类的元素,则建议使用

document.querySelectorAll('.myElement').forEach(function(element) {
    element.style.size = '100px';
});

超级老派的解决方案:[].forEach.call(document.getElementsByClassName('myClass'), function (el) { el.style.size = '100px'; }); - lenI

5
它返回类似数组的列表。
例如,您可以将其转换为数组。
var el = getElementsByClassName("elem");
el = Array.prototype.slice.call(el); //this line
el[0].appendChild(otherElem);  

5
/*
 * To hide all elements with the same class, 
 * use looping to reach each element with that class. 
 * In this case, looping is done recursively
 */

const hideAll = (className, i=0) => {
if(!document.getElementsByClassName(className)[i]){ //exits the loop when element of that id does not exist
  return; 
}

document.getElementsByClassName(className)[i].style.visibility = 'hidden'; //hide element
return hideAll(className, i+1) //loop for the next element
}

hideAll('appBanner') //the function call requires the class name

2

任何支持ES5+的浏览器(基本上包括IE8以上的浏览器)都可以使用Array.prototype.forEach方法。

Array.prototype.forEach.call(document.getElementsByClassName('answer'), function(el) {
    el.style.color= 'red';
});

caniuse 资源链接


1

有人告诉我这是我的问题的重复,我应该删除我的问题,为了保持论坛干净并保留提问的权利,我会这样做。

但我认为我的问题和这个问题真的很不同,所以我会指出我的答案,这样我就可以完善这个页面的知识,信息也不会丢失。

问题

  1. 我在片段中有一个代码 document.getElementsByClassName("close")[0][0] 是做什么用的?

  2. 我从未见过方括号被用于 getElementsByClassName,它的用途是什么?

  3. 此外,如何将其转换为 jQuery?

答案

  1. 片段中的代码有一个 [0],它实际上被用作数组,因为它是 0,它引用了指定类第一次使用的情况。

  2. 同上。

  3. 我无法真正做到这一点,也没有人回答。在代码部分中,它引用了 event.target,我不能使用 $("#myModal") 替代 document.getElementById("myModal"),我认为它们应该是等价的,但在这种情况下,用 jQuery 形式替换标准形式将不会产生期望的效果。

    window.onclick = function(event) {
      if (event.target == modal) {
        modal.style.display = "none";
      }
    }

var modal = document.getElementById("myModal");
var btn = document.getElementById("myBtn");
var span = document.getElementsByClassName("close")[0];

btn.onclick = function() {
  modal.style.display = "block";
}
span.onclick = function() {
  modal.style.display = "none";
}
window.onclick = function(event) {
  if (event.target == modal) {
    modal.style.display = "none";
  }
}
body {font-family: Arial, Helvetica, sans-serif;}

.modal {
  display: none;
  position: fixed;
  z-index: 1;
  padding-top: 100px;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgb(0,0,0);
  background-color: rgba(0,0,0,0.4);
}

.modal-content {
  background-color: #fefefe;
  margin: auto;
  padding: 20px;
  border: 1px solid #888;
  width: 80%;
}

.close {
  color: #aaaaaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}
<h2>Modal </h2>

<button id="myBtn">Open Modal</button>

<div id="myModal" class="modal">
  <div class="modal-content">
    <span class="close">&times;</span>
    <p>Some text in the Modal..</p>
  </div>
</div>

更新

看起来我真的无法删除我的问题,人们对此不满意,我真的不知道该怎么办。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接