So, here is something weird.
Yesterday I was refactoring some code and came across something akin to this:
const logo = document.getElementById("brand")
brand.style.top = 0
At no point in the codebase was a variable named brand
defined. And so any sane observer would expect that the preceding snippet would result in a runtime error -- assuming there was not something like a linter available that would have picked up on it (and the fact that logo
was being defined but never used) first.
I started having one of those moments that we all dread: I was looking at some code that could not possibly have ever worked. Yet, there it was. Working. I sank into my chair, not sure to make of what I was seeing.
In fact, the only other place I could see the word "brand" in the entire application was in the HTML document upon which this Javascript was being executed (yes, some websites still send HTML over the wire).
So I changed it:
<div id="brand_foo"> ... </div>
And then, lo and behold, the code instantly started causing a runtime error. Wait. No way... The browser is surely not pre-defining a global variable for each DOM element with an ID attribute. Right?
Wrong! It does. I've looked at this code in most major browsers and mobile devices and not one of them complained.
Don't believe me? You can try it yourself right now. Press F12 (or otherwise Right Click -> Inspect), find an element with an ID, and then evaluate that ID at the console REPL.
What about IDs such as "brand-name" that don't conform to Javascript's grammar? Surely they are ignored? Nope:
window["brand-name"] // --> <div id="brand-name">
I have no idea how well known this is. I certainly did not know about it but, admittedly, I haven't spent a lot of time in the browser in many years. I've reached out to all of the frontend developers I know and none of them knew about this either.
As it turns out, the HTML specification actually documents this functionality. Although it seems major browsers are not implementing it uniformly at all. If you define more than one element with the same ID (you evil person), then Chrome will actually return an array of Element
objects whilst Firefox returns the first element it saw. And neither browsers define global variables based on the name
attribute on elements like img
and a
.
Should I make use of this?
No. Please, no. Imagine how confusing this could get if you were dealing with many DOM elements in a non-trivial codebase and none of them were being defined anywhere visible to the programmer. Or imagine if you go all-in and then one day a major browser decides this is insanity and disables it!
The only place I can see this being of use would be in the REPL whilst debugging. But, still, it's kind of cool in a never-do-this kind of way.
Header image originally from http://www.classichorrorcampaign.com
Latest comments (36)
I knew about this for years, didn't realize it wasn't common knowledge.
Didn't know Chrome returns an array until your article.
That is what's truly insane. They literally added extra logic to process invalid HTML differently, instead of assuming valid HTML and bailing after first match.
Which means that doing
new Vue({el: app})
new Vue({el: '#app'})
will suddenly start failing (and only in Chrome) when someone makes an inner component that has an unfortunate#app
id as well.Your concern is one of the focal points of Shadow DOM specification.
Related is the IDs for
<form>
elements: if you use<input id="foobar">
you can access foobar from DOM via form.foobar. This is OK until you use<button id="submit">
, then all codes relying onform.submit()
function does not work anymore (prototype function overshadowed by object property).I stumped on the "feature" a while ago. I don't know if this behaviour is documented anywhere in the DOM specification. Might as well if somebody is working on a linter for HTML/DOM-related stuff, add a rule to disallow id which is names from Element or Window object.
I wouldn't believe it until I tried it.
I tried it. It worked.
I now want to never touch code again :D.. it was scary
it´s like angular ;)
I knew this from long long ago, but I thought that it is only happens on Internet Explorer. As I have just quickly tested, it seems that Chrome, Firefox, and Safari do the same.
Holy christ!
I agree that you shouldn't have variables referenced to the ID in the HTML, while from my past experience when you work on more massive/complex apps it turns up that NO IDs are the best IDs. But from what you had mentioned in the post, we have Angular 2+ template reference variables which are pretty much following the same practice as ID to window.variable just in a scope of a component.
Erm, you realise that you're talking about scoped variables, right? The very opposite of global variables…
Yup that is why I had mentioned that they are following the practice of how ID generate variables (as globals) to document, just here in the scope of the components.
The point was if you don't check the template for IDs (assuming those global vars will be used in code) , you will find them used but never defined in the JS part which might get you confused at start. While the same thing in Angular, if you don't check the template, you can find variables in your component which aren't defined there. In both cases you will have to look in the template to find the references.
I hope it is more clear now :)
Not really. In standard HTML, id attributes are made into global variables. In angular, component-scoped variables in the typescript/JavaScript are made available to the template HTML via template directives. They're two completely different things.
Try keep in strict mode: w3schools.com/js/js_strict.asp
Okay, so I'm a bit confused when you say, "yes, some websites still send HTML over the wire". I thought ALL web servers send back HTML when an http request is received for a particular website. I'm still learning guys, so if I'm wrong please help me. Thank you
The HTML in modern web apps is just a static resource like images. So when you load a web app, you fetch the HTML (that defines the layouts basically) together with all assets linked and the browser starts executing the JS code. But the data themselves are fetched into the app in the form of JSON and then inserted into the document via templating.
For example, an email web app, when you log into it, fetches you the all HTML of all the UI, but the emails themselves come in via AJAX and JSON and injected into the DOM by JS code.
No, don't sweat it - that is a good question.
It was meant as a subtle jab at the explosion in popularity of frontend frameworks, in which one sends Javascript over the wire and then creates the HTML elements at runtime in the web browser. For example, a React app typically sends Javascript over HTTP and then the web browser executes it to produce the HTML.
So my comment was kind of saying: "Some people still do it the 'old' way"
Since it's still a browser doing the rendering, it needs at least a
script
tag to run the JavaScript(not sure if only having a script tag in an HTML file would be valid HTML5). And usually some more but minimal HTML is still delivered to the browser.Sometimes devs are not aware of it, because this file rarely changes in the code base, or it is "hidden" because the build tool applies some default template.
Yep, absolutely. To clarify, the comment is referring to the HTML elements which make up the visible UI of a particular app. Images, titles, tables, lists, etc, etc. :)
Lol thank you for that. That actually sounds cool! I have to play around with that.
This is specified the WHATWG spec, html.spec.whatwg.org/#named-access...
Note that they explicitly point out that this is a bad idea to use in your code:
Did a random benchmark, link to benchmark
and the result is,
Whenever you see something like this in HTML / JS / CSS, I recommend you close your eyes immediately, then read a few passages from JavaScript: The Good Parts or Eloquent JavaScript and pretend the bad thing doesn't exist.
This is old as hell... also this is the reason why you should not define an ID for styling instead of a class for elements which you will never reffrrence. Using more unnecessary IDs spends more RAM and makes your source less optimised and less performant.
I never see that before D:
So JavaScript = voodoo magic