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 returns HTMLCollection not a single Element. 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 an id those are found as well. This could be desirable, although document.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 get HTMLCollection, not Element. Some risk, but manageable, especially if you know your ids and stick to document.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__ and toString, 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!