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'
})
Latest comments (15)
We should avoid mutating the Array prototype though. That's why MooTools SmooshGate happened. Good post though, short and simple
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! π
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 :)
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.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: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.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 =).
Love it!
Definitely going to incorporate this in my snippet library.
Great tips