querySelectorAll() vs getElementsByClassName

Published March 8, 2019
.* :☆゚

querySelectorAll() & getElementsByClassName() are both common JavaScript methods that retrieve elements that match any given selectors. They are both incredibly useful for manipulating elements on the page, but confusion can arise when it seems you can use them interchangeably, sometimes with no noticeable difference.

While they can sometimes be used in place of each other, both methods actually return a different kind of output which can greatly affect your code runs. This is especially noticeable when dynamic elements come into play.

For those that are learning the basics of JavaScript, knowing the difference between these two methods is helpful in writing cleaner, more meaningful code that stands up to a whole lot more use-cases. It’ll also help you to know what to look out for in the future when debugging, or using other similar methods such as querySelector() or getElementbyID().


What’s the deal?

querySelectorAll() retrieves a list of elements from the document based on your given selector, and returns a static NodeList object.

getElementsByClassName() retrieves a list of elements from the document based on an element’s class name, and returns a live HTML collection of elements.

Because querySelectorAll() returns a list that is static from the moment it is called, its list of items cannot be updated thereafter even if changes are made to the DOM dynamically. Contrast this to getElementsByClassName(), which returns matching sets of elements at any given moment it is called. If you are making changes to the DOM on the fly, the list returned by getElementsByClassName() will be updated dynamically.

MDN states on static NodeLists:

…any changes in the DOM does not affect the content of the collection.

These differences are made more apparent when these methods are used to manipulate dynamically created content, as you’ll see below.


Example

For example, say I want to show an instagram feed on my page, with its images dynamically loaded into its parent element, #instagram-feed.

 <div id="instagram-feed"></div>

Typical steps to achieve this will likely involve writing a javascript function that appends a number of instagram posts to #instagram-feed. When the function is called, #instagram-feedis populated with images and it’ll look something like this:

 <div id="instagram-feed">
    <img src="example.jpg" class="ig-post"> //appended item
    <img src="example.jpg" class="ig-post"> //appended item
    <img src="example.jpg" class="ig-post"> //appended item
    <img src="example.jpg" class="ig-post"> //appended item
    <img src="example.jpg" class="ig-post"> //appended item
 </div>

Now, say I wanted to select all elements matching .ig-post, and add a class ‘loaded’ after the images are appended.

I might try doing something like this:

function getIgPosts() {
  ... //logic to retrieve ig posts goes here
}
getIgPosts(); //call the function

var igPosts = document.querySelectorAll('.ig-post'); //select the appended posts

for (var i = 0; i < igPosts.length; i++) {
  igPosts[i].classList.add('loaded'); //add the class
}

However if I check the browser console, ig-posts won’t have the class ‘loaded’. This is because querySelectorAll() will reference the DOM before these items are appended, and never updated thereafter. Therefore, it’s list will never have the appended instagram posts to attach a class to.

If you try the same method again but with getElementsByClassName instead, you’ll find the NodeList updates as soon as the images are appended.

function getIgPosts() {
  ... //logic to retrieve ig posts goes here
}
getIgPosts(); //call the function

var igPosts = document.getElementsByClassName('.ig-post'); //select the appended posts

for (var i = 0; i < igPosts.length; i++) {
  igPosts[i].classList.add('loaded'); //add the class
}

Remember, using getElementsByClassName() returns a list of items that is dynamically updated as the DOM is updated. This means that when #instagram-feed is updated with appended posts, getElementsByClassName() is able to select the appended posts as soon as it appears.

  • Sidenote: There is a way to use querySelectorAll() to return a static list of dynamically created elements, and that is to use it as part of a callback function. If you don’t know what a callback is, don’t worry! It’s not super important in the context of this post, however you can read this article which I personally think is one of the best articles on callback functions I have found.

Which is better to use?

‘Better’ is subjective, and really depends on how you want to manipulate the elements on your page.

It important to note, you are able to use more complex selectors with querySelectorAll() and combine tags, ids, classes, and pseudo elements together to select a group of elements like the following:

var links = document.querySelectorAll('a[href^="https://"]');

For a more thorough and complex element query, querySelectorAll() does the job well, with the caveat being the list is static. (and for more great examples using querySelectorAll(), check out this article.

When using getElementsByClassName() you are only able to specify a class:

var items = document.getElementsByClassName('item');

or multiple classes:

var items = document.getElementsByClassName('item gallery-item');

Lastly, performance-wise I honestly don’t think you’ll notice too much of a difference between using the two, however you can visit this link and make up your own mind on what to use.


Looping over the objects

There are several ways you can iterate over the returned object with both querySelectorAll() and getElementsByClassName(), many of which are documented here.

My go to methods:

For vanilla JS, I just use your typical for loop:

var galleryItems = document.getElementsByClassName('gallery-item');

for (var i = 0; i < galleryItems.length; i++) {
    console.log(galleryItems[i]);
}

If you are using something like Babel or Typescript to process your JavaScript, you can use a foreach loop which is what I personally use all the time as it is the easiest to remember.

var galleryItems = document.querySelectorAll('.gallery-item');

galleryItems.forEach(function(x) {
  return x['type'];
});