You're catching the error properly, but (as you've identified) Firefox simply isn't throwing an error.
This is because Fiefox doesn't choke on JSONification of DOM objects, where other browsers do:
JSON.stringify(document.getElementById("header"))
In Chrome and Safari, this line results in an error (because in WebKit/Blink, cyclic DOM objects like siblings exist directly on each DOM object), while in Firefox with harmlessly produces the string "{}
".
This is because Firefox's DOM objects do not have any of their own enumerable properties:
Object.keys(document.getElementById("header"))
> []
In WebKit/Blink browsers, this line provides an array of property names as strings, because DOM object have their own properties. JSON.stringify
only captures an object's own properties, rather than prototype properties.
Bonus Info: More Than You Wanted to Know About the DOM
In Firefox, DOM objects mostly don't have their own properties; instead, property access is delegated up the prototype chain to the HTMLElement.prototype
, Element.prototype
, or Node.prototype
(or the element's immediate prototype, like HTMLDivElement.prototype
or HTMLAnchorElement.prototype
).
You might wonder: if accessing a property on a DOM element results in prototype access, how can DOM elements have different property values? Don't all DOM elements have more or less the same prototype chain?
The trick here is that the prototype properties don't have values, they are getter functions. For example, when you ask for firstChild
of a HTMLDivElement
, the JavaScript engine takes the following steps:
- Look for the
firstChild
property on the object itself. It's not there.
- Look for the
firstChild
property on the object's prototype.
- Continue up the prototype chain until we find
firstChild
on Node.prototype
.
Node.prototype.firstChild
is defined by an accessor property descriptor, meaning that property access results in the execution of a get
function.
- The
this
value during the execution of the getter function is the particular DOM element whose firstChild
value you asked for/ Firefox uses that this
value to do some under-the-hood lookup of the DOM element's first child.
Thus, when you do:
var val = document.getElementById("header").firstChild;
you're really doing:
var elm = document.getElementById("header");
var nodeProto = elm.__proto__.__proto__.__proto__.__proto__;
var propDescriptor = Object.getOwnPropertyDescriptor(nodeProto, "firstChild");
var getterFunc = propDescriptor.get;
var val = getterFunc.call(elm); // invoke the getter with `this` set to `elm`
Or (less readably):
var val = Object.getOwnPropertyDescriptor(document.getElementById("header").__proto__.__proto__.__proto__.__proto__, "firstChild").get.call(document.getElementById("header"))