A Distinctly non-technical introduction
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>
I could grab the li
elements in a number of different ways.
Three possible examples are:
using the
getElementsByClassName()
methodusing the
querySelectorAll()
methodgrab the containing
ul
element and access itschildNodes
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:
Using the second document.querySelectorAll(".list-entry")
returns a static Node List:
Using the third document.getElementById('list').childNodes
returns a live Node List with a different number of things within:
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.
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:
and the document.querySelectorAll()
method:
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>
Accessing the childNodes
property of our ul
again, I get:
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:
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:
If I now recheck the values of my HTMLCollection and nodeList variables, the difference between live and static collections is made clear:
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)