DEV Community

Cover image for The Subtly Different Results of Query Selectors
Angus Bower-Brown
Angus Bower-Brown

Posted on • Edited on

The Subtly Different Results of Query Selectors

A Distinctly non-technical introduction

(Skip To Content)

Recently, I was talking with my instructor about what exactly to blog about. My past experience with blogging was pretty limited and every topic that crossed my mind seemed either too large or small a problem to address in a single post.

I was grateful then, when Demetrio (my instructor) gave me some solid advice on what to explore: a technical problem you’re struggling to overcome or an idea you don’t understand.

It’s no secret that writing out concepts in your own words is a great way to make them clear and find out exactly where the holes in your understanding are. By being honest about what problems you’re actually struggling with, you also get a documentation of your learning process which could be invaluable when returning to your posts at later point.

With that in mind, what I’m writing about in this post is a relatively small part of JavaScript that I’ve personally struggled to grasp: the different results of different query selectors. Those differences are subtle but go into the heart of what a webpage exactly is and how it relates to the code that defines it, so I think blogging about them will be a fantastic exercise for my learning journey.

Query Selectors

When interacting with the DOM with our JavaScript, we often start by “grabbing” the element we want to affect with a query selector.

So, for the following stretch of code in an HTML document:

    <ul id = list>
      <li class="list-entry">Thing 1</li>
      <li class="list-entry">Thing 2</li>
      <li class="list-entry">Thing 3</li>
    </ul>

Enter fullscreen mode Exit fullscreen mode

I could grab the li elements in a number of different ways.

Three possible examples are:

  • using the getElementsByClassName() method

  • using the querySelectorAll() method

  • grab the containing ul element and access its childNodes property

All of these techniques would allow me to select what I need to on the DOM, but they would also return them to me in subtly different ways.

Using the first method document.getElementsByClassName('list-entry') returns an HTML Collection:

returned HTML Collection

Using the second document.querySelectorAll(".list-entry") returns a static Node List:

returned NodeList

Using the third document.getElementById('list').childNodes returns a live Node List with a different number of things within:

returned live node list

This post is about the differences between these three results, whether those differences are important and whether or not they might affect the way I use each selector.

To begin that process, we'll outline the difference between an Element and a Node.

Elements and Nodes

Put simply, nodes are the objects that make up the Document Object Model. There are many different kinds of node and elements are simply one of those kinds that have access to methods that other kinds don't. Not all nodes are elements but all elements are nodes.

Venn diagram

Having loosely outlined the distinction between these two objects, I want to go into why that distinction is important for understanding the different results of query selectors.

HTML Collections and Node Lists

The main difference between these two collections is what they can contain:

  • HTML Collections contain only elements

  • Node Lists can contain any kind of node

This accounts for the different number of objects returned by the childNodes property of my ul element:

returned live node list

and the document.querySelectorAll() method:

returned NodeList

While the document.querySelectorAll() method searched for all nodes with the class name of 'list-item' and returned only the elements I was after, the childNodes property returned all of the nodes in contained within my ul element which included the non-element nodes such as 'text'.

I can demonstrate that this further by adding a comment to my list:

    <ul id = list>
      <li class="list-entry">Thing 1</li>
      <li class="list-entry">Thing 2</li>
      <li class="list-entry">Thing 3</li>
      <!-- Our Comment -->
    </ul>

Enter fullscreen mode Exit fullscreen mode

Accessing the childNodes property of our ul again, I get:
Image description

The number of nodes has changed, whilst the number of elements are exactly the same.

So, when creating a nodeList, for the vast majority of cases I'll only want to handle the elements on my DOM and so the document.querySelectorAll() method on a class is a more precise way to grab the nodes I want.

If I ever want to grab some non-element nodes, I can do so with the childNodes property of the containing node.

-

So, that answers how best to construct a Node List to suit my goals, but what about HTML Collections? Is there a difference between using one of those as opposed to a Node List?

Well, while the nodes contained in both are exactly the same- there is still a difference in the HTML Collection returned by document.getElementsByClassName() and the Node List returned by document.querySelectorAll(): the HTML Collection is live and the Node List is static.

Live and Static Collections

The difference between live and static collections is simple enough to state plainly:

  • Live collections update their values whenever the DOM is changed

  • Static collections often don't unless the method is performed again.

But despite being simple enough to articulate, I personally found it a tricky concept to grasp so, to explore it properly in practice, I'll start a basic DOM manipulation by defining both an HTML Collection and a Node List:

Defining the two Collections

So far, things are as expected; the two collections have the same number of objects within.

I'll now add a new element to the DOM with the following:

Image description

If I now recheck the values of my HTMLCollection and nodeList variables, the difference between live and static collections is made clear:

Image description

The HTML Collection has automatically updated its containing nodes to include the added li, whereas the Node List has not.

If I wanted the Node List to update its contents, I would have to call the querySelectorAll() method again.

Knowing this, I can now make the judgement as to which selector would be best to use:

  • If I want my collection to be responsive to changes on the DOM, I should create a live HTML Collection

  • If I want my collection to change itself only when redefined I should create a static Node List

  • Live HTML Collections can be good for web apps that require collections to listen to changing elements on the DOM, but they can also lead to potentially confusing bugs.

  • Static Node Lists can be great for knowing exactly what nodes your grabbing and when you want to update your list.

  • Live collections are more dynamic, static collections are simpler to use.

In Closing

I haven't covered everything that separates a Node list from an HTML Collection (there's a difference in the methods that work with each for example), but I feel like I have way more a feeling now on why I would use one selector over another.

Now I’m on the other side of this piece- I also feel like I haven't appreciated how valuable a part of the course these blog posts can be. The clarity this has given me over a funny and precise little part of web-dev has made it something I think I'll return to in a heartbeat for niggly problems I don't quite get.

There’s never a shortage of those.

Thanks for reading, here's to many more!

Top comments (0)