Selecting elements (part 1)
Lets get warmed up! Before we can get productive, we need two small helpers that we'll be using in most components we'll build from here on. I'm talking about $
and $$
, which are wrappers around document.querySelector
and document.querySelectorAll
.
function $(selector, scope = document) { return scope.querySelector(selector);} function $$(selector, scope = document) { return Array.from(scope.querySelectorAll(selector));}
Why bother since they're near aliases of their native counterparts? One of the reasons we declare these is because the native functions names are so damn long. It's not about laziness, the verbosity of the native functions hurts readability.
Selecting a single element
$
selects a single occurrence of an element that matches a given selector.
function $(selector, scope = document) { return scope.querySelector(selector);} const map = $('[data-map]');
By default, $
looks for a matching element across the entire document. Use the optional scope
argument to look for an element inside another.
<div data-map> <div data-map-marker></div></div>
const map = $('[data-map]'); const marker = $('[data-map-marker]', map);
Selecting multiple elements
$$
selects all occurrences of an element that matches a given selector.
function $$(selector, scope = document) { return Array.from(scope.querySelectorAll(selector));} const imageGalleries = $$('[data-image-gallery]');
This one contains a bit more plumbing than $
. document.querySelectorAll
returns a NodeList
object, which doesn't provide any array functions like map
and forEach
(in most browsers at least). Before returing the result, the NodeList
gets transformed to an array, which is way more useful.
$$('[data-image-gallery]').forEach(imageGallery => { //});
Just like $$
it can accept a scope as its second argument.
<ul data-image-gallery> <li><img></li> <li><img></li></ul>
$$('[data-image-gallery]').forEach(imageGallery => { const images = $$('img', imageGallery); //});
A note on performance
Browsers also have more specific functions to target nodes like getElementById
and getElementsByClassName
. These are more than twice as fast as querySelector
, should we use those instead?
querySelector
can run over thousands times per millisecond. This is probably more than fast enough for your application. If you're writing low level framework code, it's a different story, but for application development, querySelector
is cheap enough.
In Chris Ferdinandi's words: It's not slow. It's just not as fast.
These selector functions will be one of the main building blocks for the vanilla JS components we'll be creating over the next few weeks.