When coding in VanillaJS, I usually create shortcuts for document.querySelector
and document.querySelectorAll
. I also like to declare D
as a shortcut to document
:
const D = document
const $ = D.querySelector.bind(D)
const $$ = (selector, startNode = D) => [...startNode.querySelectorAll(selector)]
It's also good to know that the $
and $$
functions are already built-in aka Command Line API when you open the JS console in Devtools.
With that dollar functions, you can already use a syntax that is similar to jQuery:
<button id="button">click me!</button>
$('#button').onclick = () => {
alert('You clicked me!')
}
If you want to play with multiple elements, the $$
shortcut to document.querySelectorAll
comes into play.
<button> button 1 </button>
<button> button 2 </button>
<button> button 3 </button>
$$('button').map(btn => {
btn.style.backgroundColor = 'red'
})
When it comes to event handling, having an on
method can be useful:
Array.prototype.on = function(type, listener, options) {
this.map(el => {
if (el instanceof Element) {
el.addEventListener(type, listener, options)
}
})
return this // for chaining
}
This way, you can register multiple event handlers on multiple elements at once:
$$('button').on('click', e => {
const btn = e.target
alert('You clicked ' + btn.textContent)
}).on('mouseover', e => {
const btn = e.target
btn.style.backgroundColor = 'red'
}).on('mouseout', e => {
const btn = e.target
btn.style.backgroundColor = 'blue'
})
Top comments (15)
Instead of
startNode||D
I would recommend default arguments – it's exactly the reason they were added to the languageGood point :)
Another option is to attach the
on
method plusArray.prototype.map
to theNodeList
prototype instead:This keeps the Array prototype clean =).
Short but sweet post, Lea! 🙌
Found it while researching our story on vanilla JS. Also been working on an open source list of paid & free quality resources for devs to learn vanilla JS--open for PRs. Maybe I should add an "article" section with content such as yours?
Hi Franck, thank you! I love your list of resources for Vanilla JS. Feel free to add it as you like :)
Hi, ignoring with which document you're working is a quick way to bang your head to the wall. Always keep a reference to which document you're in:
A shorthand for document, why not:
Everything's nice now, you need to work off-line ?
In this case, another option is to define another shortcut for documents other than
document
. For example, you can move the $ function definition inside a function that takes adocument
as a parameter:I like that you monkey-patch
Array#on
- but is there ever an instance that aNodeList
returned fromquerySelector
wouldn't return anElement
?What I'm trying to ask is:
Is
if (el instanceof Element)
a necessary check?The reason I did the check is that
NodeList
gets converted into anArray
via the$$
function.NodeList
s are read-only and always containElement
elements, so it is safe to calladdEventListener
on them without the check.Array
s are not read-only and the array elements are not safe from reassignment.Great tips
Great article!
I am not sure how I feel about altering built-in prototypes, I generally like to keep those clean. But the jquery-like syntax and the ability to chain event listeners without having to actually use jQuery is super sweet! 👌
We should avoid mutating the Array prototype though. That's why MooTools SmooshGate happened. Good post though, short and simple
Love it!
Definitely going to incorporate this in my snippet library.
Another Lea happens to have made something similar: blissfuljs.com/
Yes, Bliss.js by Lea Verou also provide the
$
and$$
shortcuts forquerySelector
andquerySelectorAll
, plus more functions that make it a fully-fledged, powerful and lightweight framework.