Do You Know About document.all?
If you're doing any direct interaction with elements in the DOM, then you are certainly familiar with document.getElementById('myId')
. It's good enough, but I've always wanted something cleaner and more direct. Something like document.forms
where you have direct access to named form elements, like document.forms.myForm
but for elements with an id. This technically exists and it's called document.all
.
Hang on a second...
Some folks in the 40-50 age range are rolling their eyes right now and preparing to leave a passionate comment about how old and awful document.all
is and how you should never use it. And they're kind of right, but only kind of. Let me first just explain what this is and why it's been deprecated, then I'll suggest a nice alternative.
Behold the ancient secret to DOM lookups - all
!
The all
property of document
is an instance of HTMLAllCollection
. The HTML spec says:
[T]he elements represented by the collection of an HTMLAllCollection object consist of all the descendant elements of the root Document.
In other words, it contains a reference to every element on the page including <html>
. Open the browser console, type document.all
and hit enter. There it all is. I think that's pretty cool!
Anyway, this once proprietary API was a carry-over from IE4 and is considered legacy and its use is bad practice. MDN even says it's deprecated and, as far as I can tell, doesn't even bother to document the HTMLAllCollection
interface. That's a real bummer for folks who find document.getElementById('myId')
to be more verbose and clanky than they'd like. Again, document.forms
is what we'd like for all elements and that's what makes document.all
so desirable. Check this out:
<h1 id="welcomeMessage" hidden>Welcome!</h1>
<script>
document.all.welcomeMessage.hidden = false;
</script>
That works 100% across all current versions of every major browser! Even the dev tools will autocomplete it for you.
So what's the problem? Well, there are a few things that make its use a little risky:
Philosophically this was never supposed to be part of the HTML spec. There's apparently a lot of old internet browser-wars history or something here, but do we care? No! On the surface, it's a better experience than
document.getElementById()
so it begs to be used.If more than one element has the same
id
then it returnsHTMLCollection
not a singleElement
. That's certainly manageable since you absolutely should make sure your element ids are unique at any given point, so to me this is not a problem. The tradeoff is worth it.If you intentionally or unintentionally access a property with a name that matches an element with a
name
attribute not anid
those are found as well. This could be desirable, althoughdocument.forms.myForm.elements.myInput
is probably where you want to go, but the real risk is checkbox groups. Those groups share a common name and so you will getHTMLCollection
, notElement
. Some risk, but manageable, especially if you know your ids and stick todocument.forms
for accessing form controls.If by some chance your element ids are the same as an inherited property of
all
, which are things like__proto__
andtoString
, then you will get a reference to one of those objects and not the element. I find this very manageable. The list of properties is short and known and unlikely to be anything you'd want to use as an id anyway. Although, some apps do not control all the ids used on the page. Anyway, here's the list of ids you can't use:item namedItem length constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__ __proto__
All in all, I don't find document.all
nearly as broken and bad as some people say. I think it's worth saving and so I opened an issue with the WHATWG in hopes of revising interest in fixing all
or introducing a similar property.
What's wrong with getElementById()?
Nothing technically, but it's clunky. In projects that do direct DOM access, there are usually references like this one scattered everywhere (or hopefully initialized together at the top of a script):
const welcomeMessage = document.getElementById('welcomeMessage');
This has been true for pretty much ever. Even during the jQuery era this was quite common. The big hang-up is having to pass the id string to the getter. You don't want to repeat that string over and over again and putting that string in a variable is silly because you might as well just do the lookup and assign that to the variable, hence the popularity of this pattern.
A nice alternative
So for little projects and things I own, I'd happily go against the grain and use document.all
. Not a problem at all for simple projects. My code will be cleaner and more enjoyable as a result.
However, there is an alternative approach that I think I might just use from now on. A simple Proxy gets me the document.all
experience with no risk:
// Get any element with an id as a property on this object
const elements = new Proxy({}, {
get(target, elementId) {
if (!target[elementId]) {
target[elementId] = document.getElementById(elementId);
}
return target[elementId];
}
});
// HTML: <h1 id="welcomeMessage" hidden>Welcome!</h1>
elements.welcomeMessage.hidden = false;
Have you ever used document.all
? What about something like that Proxy trick above? I'm curious to learn what other people have tried besides getElementById()
. Let me know in the comments!