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:
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.