What do querySelectorAll and getElementsBy* methods return?
Asked Answered
P

12

205

Do getElementsByClassName (and similar functions like getElementsByTagName and querySelectorAll) work the same as getElementById or do they return an array of elements?

The reason I ask is because I am trying to change the style of all elements using getElementsByClassName. See below.

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

//works
document.getElementById('myIdElement').style.size = '100px';
Piling answered 21/5, 2012 at 23:17 Comment(3)
The clue is, very much, in the name: getElementsByClassName() implies a plural, whereas getElementById() implies a singular element item.Holzer
I get that, it just didn't make sense to me that you can't change all the elements with that class name using the code above instead of having to loop through an array. jquery way is much better, i was just curious about the js wayPiling
Might be useful too: #3872047Shellacking
N
198

Your getElementById code works since IDs have to be unique and thus the function always returns exactly one element (or null if none was found).

However, the methods getElementsByClassName, getElementsByName, getElementsByTagName, and getElementsByTagNameNS return an iterable collection of elements.

The method names provide the hint: getElement implies singular, whereas getElements implies plural.

The method querySelector also returns a single element, and querySelectorAll returns an iterable collection.

The iterable collection can either be a NodeList or an HTMLCollection.

getElementsByName and querySelectorAll are both specified to return a NodeList; the other getElementsBy* methods are specified to return an HTMLCollection, but please note that some browser versions implement this differently.

Both of these collection types don’t offer the same properties that Elements, Nodes, or similar types offer; that’s why reading style off of document.getElements() fails. In other words: a NodeList or an HTMLCollection doesn’t have a style; only an Element has a style.


These “array-like” collections are lists that contain zero or more elements, which you need to iterate over, in order to access them. While you can iterate over them similarly to an array, note that they are different from Arrays.

In modern browsers, you can convert these iterables to a proper Array with Array.from; then you can use forEach and other Array methods, e.g. iteration methods:

Array.from(document.getElementsByClassName("myElement"))
  .forEach((element) => element.style.size = "100px");

In old browsers that don’t support Array.from or the iteration methods, you can still use Array.prototype.slice.call. Then you can iterate over it like you would with a real array:

var elements = Array.prototype.slice
    .call(document.getElementsByClassName("myElement"));

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

You can also iterate over the NodeList or HTMLCollection itself, but be aware that in most circumstances, these collections are live (MDN docs, DOM spec), i.e. they are updated as the DOM changes. So if you insert or remove elements as you loop, make sure to not accidentally skip over some elements or create an infinite loop. MDN documentation should always note if a method returns a live collection or a static one.

For example, a NodeList offers some iteration methods such as forEach in modern browsers:

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

A simple for loop can also be used:

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

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

Aside: .childNodes yields a live NodeList and .children yields a live HTMLCollection, so these two getters also need to be handled carefully.


There are some libraries like jQuery which make DOM querying a bit shorter and create a layer of abstraction over “one element” and “a collection of elements”:

$(".myElement").css("size", "100px");
Nominee answered 21/5, 2012 at 23:18 Comment(6)
Does that also apply to <iframe> which is also part of your domainGay
It's 2018... Just create a wrapper function for querySelectorAll() and you can have nice short code without a large, old-school dependency. qSA(".myElement").forEach(el => el.style.size = "100px") Maybe have the wrapper receive a callback. qSA(".myElement", el => el.style.size = "100px")Saprophagous
"If you prefer something shorter, consider adding a huge library to your project" I know 2012 was a different time, but even then I would've found that bit to be ludicrous.Sisile
"Iterate over it like you would with a real array… Careful, getElementsByClassName returns a live NodeList that might be unexpectedly modified during the loop, e.g. if the classname that they were selected by is removed. ;-)Burkle
@Burkle I’ve now mentioned this in an edit.Karylkarylin
The reference to jQuery should probably be removed from this answer, for various reasons: it behaves significantly differently from native DOM methods, it’s not directly relevant to the question asked, and it requires loading a library that is too big just to shorten one or two function calls. The last concern has been valid almost a decade ago, but is even more relevant today as jQuery is losing relevance. Granted, some browsers may cache jQuery internally, but do we really want new devs to adopt the practice of loading a huge library just to use a small subset from it?Karylkarylin
M
26

You are using a array as an object, the difference between getElementbyId and getElementsByClassName is that:

  • getElementbyId will return an Element object or null if no element with the ID is found
  • getElementsByClassName will return a live HTMLCollection, possibly of length 0 if no matching elements are found

getElementsByClassName

The getElementsByClassName(classNames) method takes a string that contains an unordered set of unique space-separated tokens representing classes. When called, the method must return a live NodeList object containing all the elements in the document that have all the classes specified in that argument, having obtained the classes by splitting a string on spaces. If there are no tokens specified in the argument, then the method must return an empty NodeList.

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

getElementById

The getElementById() method accesses the first element with the specified id.

https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById

in your code the lines:

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

will NOT work as expected, because the getElementByClassName will return an array, and the array will NOT have the style property, you can access each element by iterating through them.

That's why the function getElementById worked for you, this function will return the direct object. Therefore you will be able to access the style property.

Malt answered 15/2, 2016 at 2:53 Comment(6)
Note that the whatwg specs that are being implemented by the browsers do differ from the w3c ones here, the former (and hence current browsers) return an HTMLCollection for getElementsByClassName, not a NodeList. Minor, but may confuse some.Entrance
@Kaiido—the practical difference being…? To my understanding, a NodeList is a generic collection of DOM elements and is available in any DOM, not just an HTML DOM (e.g. an XML DOM), whereas an HTMLCollection is for HTML DOMs (obviously). The only difference I can see is the namedItem method of an HTMLCollection.Burkle
PS Nit pick: link for the WHATWG HTML Living Standard and the W3C HTML 5.2 standard. Spoilt by choice. ;-) Makes no difference to the point you raised though.Burkle
@Burkle NodeList has many methods that are not accessible on HTMLCollection.Entrance
@Kaiido—sure, but forEach isn't specified as part of the interface for either collection or NodeList by W3C or WHATWG, it's specified separately, e.g. as a property of generic collections in the Web IDL specification so should apply to both collections and NodeLists (though I accept your point that the collection returned by getElementsByClassName doesn't have a forEach method). I guess the bottom line is that there's enough of a story for a good answer to tell it. :-)Burkle
@Burkle I don't get your point really... I said it's "minor", though I had to make the correction because of your edit, and then to point at the discrepancy my own edit made between the quote used by Alvaro Joao, and the edited sentence. Yes, NodeList do have ArrayLike methods because their <Node> attr is iterableEntrance
A
16

ES6 provides Array.from() method, which creates a new Array instance from an array-like or iterable object.

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>

As you can see inside the code snippet, after using Array.from() function you are then able to manipulate over each element.


The same solution using **`jQuery`**.

$('.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>
Agamogenesis answered 21/2, 2017 at 23:7 Comment(0)
E
13

The following description is taken from this page:

The getElementsByClassName() method returns a collection of all elements in the document with the specified class name, as a NodeList object.

The NodeList object represents a collection of nodes. The nodes can be accessed by index numbers. The index starts at 0.

Tip: You can use the length property of the NodeList object to determine the number of elements with a specified class name, then you can loop through all elements and extract the info you want.

So, as a parameter getElementsByClassName would accept a class name.

If this is your HTML body:

<div id="first" class="menuItem"></div>
<div id="second" class="menuItem"></div>
<div id="third" class="menuItem"></div>
<div id="footer"></div>

then var menuItems = document.getElementsByClassName('menuItem') would return a collection (not an array) of the 3 upper <div>s, as they match the given class name.

You can then iterate over this nodes (<div>s in this case) collection with:

for (var menuItemIndex = 0 ; menuItemIndex < menuItems.length ; menuItemIndex ++) {
   var currentMenuItem = menuItems[menuItemIndex];
   // do stuff with currentMenuItem as a node.
}

Please refer to this post for more on differences between elements and nodes.

Engen answered 7/1, 2016 at 9:14 Comment(0)
W
8

In Other Words

  • document.querySelector() selects only the first one element of the specified selector. So it doesn't spit out an array, it's a single value. Similar to document.getElementById() which fetches ID-elements only, since IDs have to be unique.

  • document.querySelectorAll() selects all elements with the specified selector and returns them in an array. Similar to document.getElementsByClassName() for classes and document.getElementsByTagName() tags only.


Why use querySelector?

It's used merely for the sole purpose of ease and brevity.


Why use getElement/sBy?*

Faster performance.


Why this performance difference?

Both ways of selection has the purpose of creating a NodeList for further use. querySelectors generates a static NodeList with the selectors thus it must be first created from scratch.
getElement/sBy* immediately adapts the existing live NodeList of the current DOM.

So, when to use which method it's up to you/your project/your device.


Infos

Demo of all methods
NodeList Documentation
Performance Test

Withdraw answered 23/10, 2017 at 11:41 Comment(0)
A
7

You could get a single element by running

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

but it's going to work for the first element with class .myElement.

If you would like apply this for all elements with the class I suggest you to use

document.querySelectorAll('.myElement').forEach(function(element) {
    element.style.size = '100px';
});
Aguayo answered 2/7, 2017 at 19:29 Comment(1)
super old school solution: [].forEach.call(document.getElementsByClassName('myClass'), function (el) { el.style.size = '100px'; });Heel
K
5

It returns Array-like list.

You make that an Array as example

var el = getElementsByClassName("elem");
el = Array.prototype.slice.call(el); //this line
el[0].appendChild(otherElem);  
Kassandrakassaraba answered 17/6, 2016 at 3:21 Comment(0)
A
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
Antefix answered 25/11, 2018 at 4:6 Comment(0)
S
2

With any browser supporting ES5+ (any browser basically above IE8) you can use the Array.prototype.forEach method.

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

caniuse source

Shafting answered 20/4, 2015 at 16:3 Comment(0)
N
1

So I was told that this is a duplicate from my question and I should delete mine, which I will do so I can keep the forum clean and keep the right to make questions.

As I think mine and this question are really different I will point out the answer to mine, so I will complete the knowledge in this page and the information will not be lost.

Question

  1. I have a code in the snippet that has a document.getElementsByClassName("close")[0], what the [0] is doing?

  2. I never seen a square brackets being used in getElementsByClassName for what purpose is it used for?

  3. Also, how can I convert it to jQuery?

Answer

  1. The code in the snippet has a [0] it is actually being used as a array and as it is a 0 it is referring to the first time the appointed class is being used.

  2. Same thing above.

  3. I couldn't really do it and no one answered it. In the part of the code that is refering to event. target I can not use $("#myModal") instead of document.getElementById("myModal"), I think they should equivalent, but in this case the jQuery form substituting the standard one will not result in the desired effect.

    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>

update

It seems I can't really delete mine question and people are unsatisfied with it, I really don't know what I should do.

Nylon answered 25/12, 2021 at 18:47 Comment(0)
H
0

Super old school solution:

        [].forEach.call(document.getElementsByClassName('myClass'), function (el) {
            el.style.size = '100px';
        });
Heel answered 15/12, 2021 at 21:32 Comment(0)
L
-2

An answer for Drenzii's specific case...

You could make a function that will work for any of the word elements and pass in the number of the one you want to transform, like:

// Binds `wordButtons` to an (array-like) HTMLCollection of buttons
const wordButtons = document.getElementsByClassName("word");

// Applies the `slantWord` function to the first word button
slantWord(1);

// Defines the `slantWord` function
function slantWord(wordNumber) {
  const index = wordNumber - 1; // Collection index is zero-based
  wordButtons[index].style.transform = "rotate(7deg)"; // Transforms the specified button
}
<div class="wordGameContainer">
  <button class="word word1">WORD 1</button>
  <button class="word word2">WORD 2</button>
  <button class="word word3">WORD 3</button>
  <button class="word word4">WORD 4</button>
</div>

<div>
  <button onclick="moveWord()" class="playButton">PLAY</button>
</div>
Larocca answered 11/7, 2020 at 1:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.