Why is document.all falsy?
Asked Answered
R

5

82

document.all is a non-primitive object in the DOM that is falsy.

For example, this code doesn't do anything:

if (document.all) {
    alert("hello");
}

Can someone explain why this is?

Recognizance answered 27/4, 2012 at 11:53 Comment(6)
Modern browsers don't implement this outdated thing any more. It's a IE "standard", Opera also "shims" it.Rhapsodic
@Nanne the question is: can someone explain, why the code does nothing. If it isn't implemented, if will be false and nothing will happen. So I do think, it is answer.Files
But the question also stated that we are dealing with a non-null object? Maybe I read that wrong, but I assumed that means in the test it was there, but just didn't trigger?Rectangle
@Nanne: OK, I understood the question now. I've added another answer.Rhapsodic
There's a good tl;dr explanation in Kyle Simpson's book about this: github.com/getify/You-Dont-Know-JS/blob/master/… (scroll down to the 'Why?!' heading).Hickok
Chrome 75 in 2019 still has document.all == null. Why? What is the point of still maintaining this?Endogen
B
131

Disclaimer: I’m the guy who tweeted the question that led to this thread :) It was a question I would ask and answer in my Front-Trends talk. I wrote that tweet 5 minutes before going on stage.


The question I was asking is the following.

The ECMAScript spec defines ToBoolean() as follows:

ToBoolean(condition), slide from my Front-Trends 2012 talk

As you can see, all non-primitive objects (i.e. all objects that aren’t a boolean, a number, a string, undefined, or null) are truthy as per the spec. However, in the DOM, there is one exception to this — a DOM object that is falsy. Do you know which one that is?

The answer is document.all. The HTML spec says:

The all attribute must return an HTMLAllCollection rooted at the Document node, whose filter matches all elements.

The object returned for all has several unusual behaviors:

The user agent must act as if the ToBoolean() operator in JavaScript converts the object returned for all to the false value.

The user agent must act as if, for the purposes of the == and != operators in JavaScript, the object returned for all is equal to the undefined value.

The user agent must act such that the typeof operator in JavaScript returns the string 'undefined' when applied to the object returned for all.

These requirements are a willful violation of the JavaScript specification current at the time of writing (ECMAScript edition 5). The JavaScript specification requires that the ToBoolean() operator convert all objects to the true value, and does not have provisions for objects acting as if they were undefined for the purposes of certain operators. This violation is motivated by a desire for compatibility with two classes of legacy content: one that uses the presence of document.all as a way to detect legacy user agents, and one that only supports those legacy user agents and uses the document.all object without testing for its presence first.

So, document.all is the only official exception to this ECMAScript rule. (In Opera, document.attachEvent etc. are falsy too, but that’s not specced anywhere.)

The above text explains why this was done. But here’s an example code snippet that’s very common on old web pages, and that will illustrate this further:

if (document.all) {
  // code that uses `document.all`, for ancient browsers
} else if (document.getElementById) {
  // code that uses `document.getElementById`, for “modern” browsers
}

Basically, for a long time document.all was used in this way to detect old browsers. Because document.all is tested first though, more modern browsers that offer both properties, would still end up in the document.all code path. In modern browsers, we’d prefer to use document.getElementById, of course, but since most browsers still have document.all (for other backwards compatibility reasons) the else would never be accessed if document.all was truthy. Had the code been written differently, this wouldn’t be a problem:

if (document.getElementById) {
  // code that uses `document.getElementById`, for “modern” browsers
} else if (document.all) {
  // code that uses `document.all`, for ancient browsers
}

But sadly, a lot of existing code does it the other way around.

The simplest fix for this problem is to simply make document.all be falsy in browsers that still mimic it.

Biernat answered 1/5, 2012 at 7:35 Comment(5)
Quite involved answer for an obsolete feature.Santo
@adrian Welcome to the Web, where everything is complicated because of legacy features :)Biernat
But in Oct'17 they had this notice removed, though the behavior remains the same up until this very day...Euchre
And so we have mariusschulz.com/blog/… smhAbominate
Interesting. I think this was because document.all was not ever implemented by Netscape. You had to use document.getElementById. And for IE you had to use document.all. Therefore there was no 'newer' or 'older' way to do it, they were just competing browsers, so there was no particular reason to have document.getElementById condition first. At some point (possibly when Chrome came along?) document.all started working again, but only kind of since you could no longer test for it...Umiak
L
24

ES2019 Update

There is now an [[IsHTMLDDA]] internal slot for objects:

An [[IsHTMLDDA]] internal slot may exist on implementation-defined objects. Objects with an [[IsHTMLDDA]] internal slot behave like undefined in the ToBoolean and Abstract Equality Comparison abstract operations and when used as an operand for the typeof operator.

The HTML Standard has also been updated to add that internal slot for objects that implement the HTMLAllCollection interface:

Objects that implement the HTMLAllCollection interface are legacy platform objects with an additonal [[Call]] internal method described in the section below. They also have an [[IsHTMLDDA]] internal slot.


The reason for this madness is specified in this note in the HTML Standard:

These special behaviors are motivated by a desire for compatibility with two classes of legacy content: one that uses the presence of document.all as a way to detect legacy user agents, and one that only supports those legacy user agents and uses the document.all object without testing for its presence first.

So basically the standard wants to be compatible with these two types of code:

  • Code that checks if it is running inside Internet Explorer to use its non-standard features, like document.all and Activex;

    if (document.all) {
        useActiveXStuff();
    }
    
  • Code that assumes it's running inside Internet Explorer and uses document.all.

    document.all["my-button"].onclick = function () {
        alert("hi");
    };
    
Labialized answered 25/5, 2020 at 15:15 Comment(1)
Seems like DDA stands for “Document Dot All”Gendarmerie
R
8

Modern browsers don't implement this outdated thing any more. It was introduced by IE, but most of the others "shim" it to be compatible.

To make browser detection possible (back in the old days you could tell IE apart from NN by testing for document.all) while supporting document.all syntax, other browsers made the "weird" implementation that typeof document.all returns undefined.

Opera> document.all
// prints the array-like object
Opera> typeof document.all
"undefined"
Opera> Boolean(document.all)
false

Before FF dropped support for it, it also showed weird behaviour as stated in this message. You may find more internals in Mozilla bug #412247.

There is also a very long thread in the W3C mailing list archive, beginning with http://lists.w3.org/Archives/Public/public-html/2009Jun/0546.html

Rhapsodic answered 27/4, 2012 at 12:24 Comment(0)
O
8

In short, it's to make BOTH of these code samples work. Browsers have to do this so that old web pages will continue to work.

Sample 1

// Internet Explorer
if (document.all) {
    useActiveX()
}
// Netscape Navigator
else {
    useOldButStillWorkingCode()
}

Sample 2

document.all.output.innerHTML = 'Hello, world!'
Oday answered 20/5, 2015 at 19:0 Comment(0)
G
5

The other answers already give a good explanation of why document.all behaves the way it does.

However, I was very curious to know more about the historical perspective. Where does document.all come from and why do modern browsers support it to this day?

So, I did some research to learn more about it, and here's what I found.

document.all was originally introduced in Internet Explorer 4. It's main use was to access elements by ID, like this:

var element = document.all[id]

Later, the W3C standardized document.getElementById as a way to get elements by their ID.

However, since IE had the biggest market share for many years, many websites just kept using document.all without testing for it.

Some of these websites were quite popular and they would break in browsers other than IE.

Therefore, there started being discussions about adding support for document.all in other browsers so that websites using document.all would work in those browsers.

Just to give you some examples of what was being discussed, here are two discussions from bugzilla:

So, in the end, other browsers started implementing document.all.

However, since websites were using document.all to detect IE using if statements like this one:

if (document.all) {
  // Use proprietary Internet Explorer APIs
}

To prevent other browsers from being misdetected as Internet Explorer, the W3C standardized document.all as a falsy object that behaves like undefined.

So, as of 2023, document.all is still supported in all major browsers. Why you might ask? Probably because they want old websites to work.

I actually made a 2 minute YouTube video about document.all and its history, so if you're curious about that, check it out: https://youtu.be/KFasyUpmoss

document.all

Gravestone answered 12/3, 2023 at 23:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.