If this is the first time you’re hearing about it, brace yourself! We can see it in action simply by adding an ID to an element in HTML:
Normally, we’d define a new variable using
getElementById("cool") to select that element:
var el = querySelector("#cool");
But we actually already have access to
#cool without that rigamorale:
id — or
window[ELEMENT_ID]. Again, this isn’t exactly “new” but it’s really uncommon to see.
As you may guess, accessing the global scope with named references isn’t the greatest idea. Some folks have come to call this the “global scope polluter.” We’ll get into why that is, but first…
This approach is outlined in the HTML specification, where it’s described as “named access on the
Internet Explorer was the first to implement the feature. All other browsers added it as well. Gecko was the only browser at the time to not support it directly in standards mode, opting instead to make it an experimental feature. There was hesitation to implement it at all, but it moved ahead in the name of browser compatibility (Gecko even tried to convince WebKit to move it out of standards mode) and eventually made it to standards mode in Firefox 14.
One thing that might not be well known is that browsers had to put in place a few precautionary measures — with varying degrees of success — to ensure generated globals don’t break the webpage. One such measure is…
Probably the most interesting part of this feature is that named element references don’t shadow existing global variables. So, if a DOM element has an
id that is already defined as a global, it won’t override the existing one. For example:
I won't override window.foo
And the opposite is true as well:
I will be overridden :(
This behavior is essential because it nullifies dangerous overrides such as
alertAPI. This safeguarding technique may very well be the why you — if you’re like me — are learning about this for the first time.
The case against named globals
Earlier, I said that using global named elements as references might not be the greatest idea. There are lots of reasons for that, which TJ VanToll has covered nicely over at his blog and I will summarize here:
- Accidental references are far too easy. A simple typo may very well wind up referencing a named global and give you unexpected results.
- It is implemented differently in browsers. For example, we should be able to access an anchor with an
— but some browsers (namely Safari and Firefox) return a
ReferenceErrorin the console.
- It might not return what you think. According to the spec, when there are multiple instances of the same named element in the DOM — say, two instances of — the browser should return an
HTMLCollectionwith an array of the instances. Firefox, however, only returns the first instance. Then again, the spec says we ought to use one instance of an
idin an element’s tree anyway. But doing so won’t stop a page from working or anything like that.
- Maybe there’s a performance cost? I mean, the browser’s gotta make that list of references and maintain it. A couple of folks ran tests in this StackOverflow thread, where named globals were actually more performant in one test and less performant in a more recent test.
Let’s say we chuck the criticisms against using named globals and use them anyway. It’s all good. But there are some things you might want to consider as you do.
As edge-case-y as it may sound, these types of global checks are a typical setup requirement for polyfills. Check out the following example where we set a cookie using the new
CookieStoreAPI, polyfilling it on browsers that don’t support it yet:
This code works perfectly fine in Chrome, but throws the following error in Safari.:
TypeError: cookieStore.set is not a function
Safari lacks support for the
CookieStoreAPI as of this writing. As a result, the polyfill is not applied because the
imgelement ID creates a global variable that clashes with the
That script grabs a reference to the input element and invokes
focus()on it. It works correctly. Still, we don’t know how long it will continue to work.
You see, the global variable we’re using to reference the input element will stop working as soon as browsers start supporting the
BarcodeDetectorAPI. At that point, the
window.BarcodeDetectorglobal will no longer be a reference to the input element and
.focus()will throw a “
window.BarcodeDetector.focusis not a function” error.
Bonus: Not all named elements generate global references
Want to hear something funny? To add insult to the injury, named elements are accessible as global variables only if the names contain nothing but letter. Browsers won’t create a global reference for an element with a ID that contains special characters and numbers, like
Let’s sum up how we got here:
- All major browsers automatically create global references to each DOM element with an
id(or, in some cases, a
- Accessing these elements through their global references is unreliable and potentially dangerous. Use
- Since global references are generated automatically, they may have some side effects on your code. That’s a good reason to avoid using the
idattribute unless you really need it.
As a general rule, relying on this will lead to brittle code. Which IDs end up mapping to this API can vary over time, as new features are added to the web platform, for example. Instead of this, use
I think the fact that the HTML spec itself recommends to staying away from this feature speaks for itself.