DEV Community

Cover image for DOM elements with ID's are global variables

DOM elements with ID's are global variables

Andrew Buntine on January 12, 2017

So, here is something weird. Yesterday I was refactoring some code and came across something akin to this: const logo = document.getElementById...
Collapse
 
realkaylent profile image
Kaylen Travis Pillay

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

Collapse
 
buntine profile image
Andrew Buntine • Edited

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"

Collapse
 
karfau profile image
Christian Bewernitz • Edited

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.

Thread Thread
 
buntine profile image
Andrew Buntine

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

Collapse
 
realkaylent profile image
Kaylen Travis Pillay

Lol thank you for that. That actually sounds cool! I have to play around with that.

Collapse
 
danp profile image
danp

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.

Collapse
 
loderunner profile image
Charles Francoise

Nicely spotted! Although we're supposed to think of "global" variables as Bad™, I think there's a definite case to be made why this is actually Not So Bad™.

An DOM element with an ID, by definition, can be referenced from any context. Semantically, it is pretty akin to a "global variable". Also, one of the guarantees of DOM elements with IDs is that they have a speedy lookup (O(1) if possible).

With these assumptions, it makes sense that a naive implementation would store them somewhere in the global scope. Would it have been better to store them in a member object, such as window.elementsWithIds? Probably, but quite marginally. And as Rachel Carvalho pointed out, not necessarily worth breaking legacy applications.

Great catch, though. I think all front-end developers should be aware of this, if only to avoid mind-bending bugs like the one you mentioned and to understand the inner workings of the target machine (aka "The Browser").

--

EDIT: I'm quite new at Javascript so I'm bound to make some errors.

My previous argument doesn't hold in face of this little snippet.

<html>
<body>
<div id="crypto">Crypto</div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
> document.getElementById('crypto')
<div id=​"crypto">​Crypto​</div>​

> window.crypto
Crypto {subtle: SubtleCrypto}
Enter fullscreen mode Exit fullscreen mode

Naming your element after a built-in Window property does not overwrite the property, but you can still perform a lookup by ID on the document. So there's obviously a better mechanism already in place.

The fact that IDs create global objects is probably only to maintain backwards compatibility, as Rachel pointed out.

Collapse
 
stephenhand profile image
Stephen Hand

Dom elements referenced by ID are global variables and have the same drawbacks as other global variables. I've seen more than supposedly 'reuseable component' break as soon as you tried to add a second instance of it to a page because it's referencing its internal DOM elements by ID...

Collapse
 
babailiica profile image
100 кила minion 🌈

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.

Collapse
 
yoshevski profile image
Yoshevski

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.

Collapse
 
ashleyjsheridan profile image
Ashley Sheridan

Erm, you realise that you're talking about scoped variables, right? The very opposite of global variables…

Collapse
 
yoshevski profile image
Yoshevski • Edited

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

Thread Thread
 
ashleyjsheridan profile image
Ashley Sheridan

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.

Collapse
 
maximeeuziere profile image
xem

Hi,

I studied this behavior on my blog, if you're interested:

xem.github.io/articles/#implicitge...

Collapse
 
rachelcarvalho profile image
Rachel Carvalho

People who have had contact with front-end code for long enough will remember that this is how people used to write Javascript 🤢. I'm talking about way back when websites were developed for IE 6 and did not run well on the new Firefox browser because of so much non standard html, css and js there was.

Collapse
 
qm3ster profile image
Mihail Malo

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.

Collapse
 
entrptaher profile image
Md Abu Taher

Did a random benchmark, link to benchmark

and the result is,
perfs

Collapse
 
tkesgar profile image
Ted Kesgar • Edited

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 on form.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.

Collapse
 
russellbeattie profile image
Russell Beattie

As someone who's been doing web development from the beginning of browsers, this isn't a surprise, as that's how it was done, and HTML is very backwards compatible. But what I didn't know (because I've long since stopped using this 'feature') is how Chrome treats elements with the same ID! Wow, that's weird. I just checked, and it returns an HTMLCollection, which is array-like, but not actually an array. And according to the MDN docs, Opera (I assume the old one) returns a NodeList.

I guess the different implementations is a good thing - if browser engines all acted the same, developers would end up using this old method regularly, but as it is, the best way to be cross-browser is by using an explicit getElementById instead.

Collapse
 
thebohman profile image
Christopher Bohman

The best part about knowing this? I've done some things in a dev environment expecting an error to be thrown.. and then it doesn't. Then I get confused. Then I doubt my own existence. When there's an explanation, less of this happens.

Collapse
 
andreujuanc profile image
Juan C. Andreu

Saw this behavior in cordova-plugin-splashscreen where they used the element's id without any kind of check, assuming it will exist.

Problem was my application removed that element and their code didn't check for it's existence.

Good post.

Collapse
 
nathanhinchey profile image
Nathan Hinchey

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:

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 document.getElementById() or document.querySelector().

Collapse
 
eljayadobe profile image
Eljay-Adobe

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.

Collapse
 
errietta profile image
Erry Kostala

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

Collapse
 
tomas2387 profile image
Tomás Prado Bley

You just found the Pandora box nobody have seen! lol.
Anyway, This is quite ugly and I hope nobody uses it. I don't see any benefits, only to make a nice place for bugs to hide.

Collapse
 
klabranche profile image
Kevin LaBranche

I've been doing web dev for a long time and didn't know about this. lol on myself but not something I plan on using either.

Collapse
 
rutinerad profile image
Samuel Lindblom

Yep, I knew about it, the outcome of the exact same "looking at some code that could not possibly have ever worked [...], not sure to make of what I was seeing".

Collapse
 
jpablosd profile image
Juan Pablo Soto

it´s like angular ;)

Collapse
 
gregorgonzalez profile image
Gregor Gonzalez

I never see that before D:

So JavaScript = voodoo magic

Collapse
 
atrandafir profile image
Alexandru Trandafir

I did noticed this some time ago too!! :D

Collapse
 
gautamkrishnar profile image
Gautam Krishna R

Holy christ!

Collapse
 
sudiptosen profile image
Sudipto Sen

Your concern is one of the focal points of Shadow DOM specification.

Collapse
 
maxart2501 profile image
Massimo Artizzu

Yes, that's actually a very old behaviour. As old as Internet Explorer... 5, maybe?
It's been kept just for compatibility reasons. Just don't ever, ever rely on that, because it's really bad :(

Collapse
 
sandhilt profile image
Bruno Ochotorena

Try keep in strict mode: w3schools.com/js/js_strict.asp

Collapse
 
djangotricks profile image
Aidas Bendoraitis

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.