[HN Gopher] Named element IDs can be referenced as JavaScript gl... ___________________________________________________________________ Named element IDs can be referenced as JavaScript globals Author : mmazzarolo Score : 128 points Date : 2022-09-27 14:30 UTC (8 hours ago) (HTM) web link (css-tricks.com) (TXT) w3m dump (css-tricks.com) | esprehn wrote: | The global scope polluter has pretty bad performance and interop | surprises, you shouldn't depend on it and instead use | getElementById even if it's a bit more verbose. | | It uses a property interceptor which is fairly slow in v8: | | https://source.chromium.org/chromium/chromium/src/+/main:out... | | to call this mess of security checks: | | https://source.chromium.org/chromium/chromium/src/+/main:thi... | | which has this interop surprise: | | https://source.chromium.org/chromium/chromium/src/+/main:thi... | | which in the end scans the document one element at a time looking | for a match here: | | https://source.chromium.org/chromium/chromium/src/+/main:thi... | | In contrast getElementById is just a HashMap lookup, only does | scanning if there's duplicates for that id, and never | surprisingly returns a list! | goatlover wrote: | Is there a reason to not use querySelector, since it's a lot | more flexible? One reason jQuery became so popular is because | the DOM was painful to use. Things like querySelector fix that. | dspillett wrote: | _> Is there a reason to not use querySelector_ | | getElement is slightly faster, but not by enough to care IIRC | so I use querySelector for consistency and it's flexibility. | | _> One reason jQuery became so popular is because the DOM | was painful_ | | I would say that is the key reason, with everything else | being collateral benefits. Assuming you combine element | selection, dealing with legacy incompatibilities, and | function chaining to reduce boilerplate code, under the same | banner of "making the DOM less painful". | asah wrote: | ...and portability. | masswerk wrote: | BTW, in my experience getElementById() is still fastest. | olliej wrote: | That's surprising, webkit+blink and I'm guessing gecko all | optimize the query selector cases. I assume it's the cost | of the NodeList (because NodeLists are live :-/) | eyelidlessness wrote: | In isolation definitely, but in real world code it might be | faster to use querySelector for branchy code if it doesn't | always use an id. As with everything, if it's not | performance-sensitive write the code that's easier for | humans to read, and if it is measure first. | devmor wrote: | The performance difference is negligible. Both methods can | return 70k-100k selections in 10ms. | masswerk wrote: | I had projects where this contributed to a visibly | perceivable difference. This may have involved SVG, | though. | esprehn wrote: | On what hardware? | | Also there's a performance cliff when you have a lot of | unique ids (or selectors in use from JS). | | When you hit the cache querySelector is primarily a | getElementById call and then some overhead to match the | selector a second time (which chrome should really | optimize): | | https://source.chromium.org/chromium/chromium/src/+/main: | thi... | | But if you have more than 256 selectors and ids in use: | | https://source.chromium.org/chromium/chromium/src/+/main: | thi... | | You'll start to hit the selector parser a lot more and | then querySelector will be a fair bit slower going | through the CSS parser. | toddmorey wrote: | I really wish in the source code it was actually named | globalScopePolluter() | grenoire wrote: | I discovered this the hard way and I am still really torn. The | entire window global object is just a minefield. | stonewareslord wrote: | I don't think this article is complete. It mentions no pollution, | which is true of _window_ and most HTML elements, but not always. | Check this out, you can set an img name to getElementById and now | document.getElementById is the image element! | | Here's a minimal example (https://jsfiddle.net/wc5dn9x2/): | <img id="asdf" name="getElementById" /> <script> | // The img object | console.log(document.getElementById); // | TypeError: document.getElementById is not a function :D | console.log(document.getElementById('asdf')); </script> | | I tried poking around for security vulnerabilities with this but | couldn't find any :( | | It seems that the names overwrite properties on document with | themselves only for these elements: embed form iframe img object | | Edit: Here's how I found this: https://jsfiddle.net/wc5dn9x2/1/ | jefftk wrote: | Note that this is with the name attribute, not the id attribute | the article is discussing. | samtho wrote: | This always reminded me of PHP's infamous register_globals. For | those unfamiliar, anything in the '$_REQUEST' array (which itself | comprises of $_POST, $_GET, and $_COOKIE merged together) is | added to the global scope. So if you made a request to | index.php?username=root, $username would contain "root" unless it | was explicitly initialized it before it was used. | angelmm wrote: | Now I'm worried of using IDs and finding issues with globals in | JavaScript. Seems to be a curious issue to be debugged. | mmastrac wrote: | Avoid globals at all costs - use IIFE [1] instead, wrapping | your function in parenthesis and invoking it right away. | | [1] https://developer.mozilla.org/en-US/docs/Glossary/IIFE | throw_m239339 wrote: | { let foo = 1 }; // foo is | undefined here | WorldMaker wrote: | It's 2022, you can use ES2015 modules now. We can leave IIFE | to the dustbin of the past. | an1sotropy wrote: | When, today, does it make more sense to organize things | around IIFEs and not ES6 modules? | recursive wrote: | If you have access to `let`, you can just put `let` | declarations into a block. No need for a function to | establish scope. | jbverschoor wrote: | And then get coworkers to remove it because they don't | understand that you can create scopes like that | Slackwise wrote: | If you read the article and the spec, you'll see that any | explicitly created variables will always take precedence over | automatic IDs, so any globals will always override these IDs. | croes wrote: | And that could be the problem if you try to access an element | by id but a variable has the same name. This renders this | option pretty useless. | angelmm wrote: | In the additional considerations section [1], they mention | about not consistent behaviors between browsers. Those are | the kind of issues that are quite difficult to debug. | | [1] https://css-tricks.com/named-element-ids-can-be- | referenced-a... | bhhaskin wrote: | You shouldn't be using IDs anyways. They are just bad for a lot | of reasons. You can only have one on a page and it reduces your | reusability. Use classes instead. | err4nt wrote: | ID's aren't bad, they're unique identifiers, and useful for | (deep) linking to specific pieces of content within | documents. Please use ID's as liberally as you please, and | use them for their proper use. | goatlover wrote: | Use ids when JS needs to reference unique elements. Use | classes for styling and accessing groups. | isleyaardvark wrote: | JS can do just as well with unique classnames, which avoids | issues with ids like those given in the article. | roberttod wrote: | For me, the disadvantage above any listed on the blog is that if | I saw this global variable referenced in some code (especially | old code, where some parts might be defunct), I would have | absolutely no idea where it came from, and I bet a lot of others | would struggle too. | pkrumins wrote: | This is my favorite HTML and JS feature! | twicetwice wrote: | iirc this doesn't work in Firefox? or at least it doesn't work | the same way as in Chrome. I developed a tiny home-cooked app[0] | that depended on this behavior using desktop Chrome which then | broke when I tried to use it on mobile Firefox. I then switched | it to using document.getElementById | | like I should have and everything worked fine. Like others in | this thread, I recommend not relying on this behavior. | | [0]: https://www.robinsloan.com/notes/home-cooked-app/ | croes wrote: | Isn't the opposite of | | >So, if a DOM element has an id that is already defined as a | global, it won't override the existing one. | | So, if a global has name of the id of a DOM element, it won't | override the existing one? | | Wouldn't it be clearer to say globals always before DOM ids? | eithed wrote: | >To add insult to the injury, named elements are accessible as | global variables only if the names contain nothing but letter. | | This doesn't seem to be true as shown within this fiddle: | https://jsfiddle.net/L785cpdo/1/ | | Bear in mind that only undefined elements will be declared this | way | mmazzarolo wrote: | Author here. That was a mistake on my part, it shouldn't have | slipped in :) I removed that section, thanks for reporting! | mmastrac wrote: | This has been a thing since the 90s. I really wish we'd done away | with it for any document that specifies itself as HTML5. | | It's great for hacking a tiny script together, however. | russellbeattie wrote: | Yep, same here. The only time I use this bit of knowledge | nowadays is in the console. If I see a tag has an ID, I save | myself a few characters by just referring to it as a variable | since I know it's already there anyways. | | IDs were the only way to get a reference to an element early on | if I'm remembering correctly. Or maybe the DOM API just wasn't | well known. All the examples and docs just used IDs, that I can | remember for sure. | [deleted] | esprehn wrote: | Yeah, HTML5 explicitly documented the compatible behaviors | between browsers to reach uniformity, which meant standardizing | a lot of weird stuff instead of trying to fix it. | | See for example this thread where Mozilla tried to not do this: | https://bugzilla.mozilla.org/show_bug.cgi?id=622491 | bjkchoy wrote: | I saw this "shortcut" used in code snippets, on online | JS/CSS/HTML editors like JSFiddle. It did not even occur to me | this was part of JS spec, I thought the editor was generating | code behind my back! | seba_dos1 wrote: | > It did not even occur to me this was part of JS spec, | | It has nothing to do with JS spec; it's part of the DOM as | defined by the HTML spec. | kiawe_fire wrote: | Seems like something that could have been made safer just by name | spacing it a bit better. | | Something like "window.elements.myDiv"? I wonder why the decision | to go straight to the root. | bobince wrote: | The Netscape of the 90s wasn't interested in making features | 'safe'. They were about throwing out features as quickly as | possible to see what would stick. | | The simplest possible syntax is to make named elements | available globally, and if that clashes with future additions | to the DOM API then well that's a problem for some future | idiots to worry about. | | as a strategy it worked pretty well, unfortunately | WorldMaker wrote: | As the article points out, this initiative was an 90s IE one | and the Gecko team (Firefox, post-Netscape) were against it. | akira2501 wrote: | You can make this yourself with Proxy. I get a lot of mileage | out of this: // proxy to simplify loading and | caching of getElementById calls const $id = new | Proxy({}, { // get element from cache, or from DOM | get: (tgt, k, r) => (tgt[k] || ((r = | document.getElementById(k)) && (tgt[k] = r))), | // prevent overwriting set: () => $throw(`Attempt | to overwrite id cache key!`) }); | | Now if you have <div id="something></div> | | You can just do $id.something.innerHTML = | 'inside!'; | dphnx wrote: | `document.all` can be used in this way: <div | id="foo"></div> <script> const { foo } = | document.all // do something with foo </script> | | Don't use it though, it's deprecated as well[1]. | | [1]: https://developer.mozilla.org/en- | US/docs/Web/API/Document/al... | genezeta wrote: | This is one of those things that pops up every year or two years. | Unfortunately, the person writing about the new discovered weird | trick almost always fails to precede the article with a big, red, | bold "Please don't ever do this". | svnpenn wrote: | and then someone always follows up with "Please don't ever do | this", without explaining WHY you should never do this: | | https://wikipedia.org/wiki/Wikipedia:Chesterton's_fence | pmoleri wrote: | Nice article, thanks for sharing it. | genezeta wrote: | It's has been explained enough times. It's just that looking | things up for yourself seems to have gone out of fashion. | pierrec wrote: | lol, I just searched "problem with referencing named | element ids as javascript globals": the first result is the | linked article and the second result is, you guessed it, | this thread with your comment on top. | scratcheee wrote: | >the person writing about the new discovered weird trick | almost always fails to precede the article with a big, red, | bold "Please don't ever do this" | | > It's has been explained enough times. It's just that | looking things up for yourself seems to have gone out of | fashion. | | It appears you've countered your own complaint. | spookthesunset wrote: | That doesn't help people who stumble upon this when | searching for the problem. All the "look it up" response | does is make sure the search results are a bunch of content | saying "look it up", which isn't really that helpful. | llanowarelves wrote: | That's a classic. | | Get my hopes up finding an old forum post asking my | question, hoping to find answers. All the answers are | "use Google/etc", which is how I got there. | nkozyra wrote: | It is explained fairly early in the article. | | This used to be done quite a lot in the early JS days | when scope was kind of thrown out the window (no pun) and | you just did whatever dirty thing you needed to in order | to make a page work. | FrontAid wrote: | The article already explains that thoroughly. | LelouBil wrote: | I discovered that with a couple of friends while in JavaScript | class. Every one of us was like "this is actually horrible". | dfabulich wrote: | I'm surprised to find that this trick still works even in the new | backwards-incompatible JavaScript Modules (using <script | type="module">), which enables "strict" mode and a number of | other strictness improvements by default. | | I believe it works because the global object ("globalThis") is | the Window in either case; this is why JavaScript Modules can | refer to "window" in the global scope without explicitly | importing it. <!DOCTYPE html><body> | <div id="cool">cool</div> <script> | console.log(this); // Window | console.log(globalThis); // Window | console.log("script", cool.innerHTML); // script cool | </script> <script type="module"> | console.log(this); // undefined | console.log(globalThis); // Window | console.log("module", cool.innerHTML); // module cool | </script> </body></html> | | This seems like a missed opportunity. JavaScript Modules should | have been required to "import {window} from 'dom'" or something, | clearing out its global namespace. | eyelidlessness wrote: | There is some effort to standardize something along these | lines. Well, some things which combined would achieve this. | It's too late to bake it into ESM, but I believe it'll be | possible with ShadowRealms[1] and/or SES[2], and Built-in | Modules (JS STL)[3]. | | 1: https://github.com/tc39/proposal-shadowrealm | | 2: https://github.com/tc39/proposal-ses | | 3: https://github.com/tc39/proposal-built-in-modules | FrontAid wrote: | Another similar gotcha is that the global-scoped `name` variable | _must_ be a string. See https://developer.mozilla.org/en- | US/docs/Web/API/Window/name for details. var | name = true; typeof name; // "string", not "boolean" | | Luckily, this is not true within ES modules which you probably | use most of the time anymway. | efdee wrote: | It takes a special kind of human to name variable "name" but | not have it be a string. | sanitycheck wrote: | I work with such humans! I was looking at that exact | situation a few moments ago. | Minor49er wrote: | I can imagine someone doing this if they were using "name" as | a verb | orangecat wrote: | Something like name = {first: "Jane", last: | "Doe"} | | isn't obviously unreasonable. Which actually sets name to the | string "[object Object]". | eyelidlessness wrote: | Falsehoods programming languages believe about names. | esprehn wrote: | That's not magic, it's just how property getter and setters | work on the global: <script> var | _value = "test value"; | Object.defineProperty(window, "testName", { | get: () => _value, set: (value) => { _value = | String(value) }, }); </script> | <script> var testName = {}; // prints | [object Object] string console.log(testName, typeof | testName); var name = {}; // prints | [object Object] string console.log(name, typeof | name); </script> | | the `var` doesn't create a new property since the getter and | setter already exist. | | Other properties have the same behavior, for example `status`. | | Note: there's also LegacyUnforgeable which has similar | behavior: https://webidl.spec.whatwg.org/#LegacyUnforgeable | | Even if you're not using modules, using an IIFE avoids all this | by making your variables local instead of having them | define/update properties on the global. | simlevesque wrote: | I've been doing JS for like fifteen years, this one I never | knew. Wow. | | I must have never used "name" as a name for a global variable | or just for ones that were strings. | monkpit wrote: | It hurts | SpaceL10n wrote: | I think it would hurt less with TypeScript global types. Just | need to know what IDs you'd expect to find in the DOM. | err4nt wrote: | the ID's in DOM will never conflict or cause an issue with | your own JS code. You can't reliably use 'named access on the | window object' (the name of this feature) because of this, so | it's never a problem, and also largely useless. | codedokode wrote: | It would make sense to disable this with new release of HTML, for | example if the author uses an HTML6 doctype. | debacle wrote: | I don't want to sound like I have an axe to grind (but I do), but | this is the kind of feature/wart that shows the age of the | HTML/CSS/JS stack. | | The whole thing is ripe for a redo. I know they get a lot of | hate, but of all the big players in this space I think FB is the | best equipped to do this in a way that doesn't ruin everything. I | just wonder if they have an incentive (maybe trying to break the | Google/MS hegemony on search?). | tfsh wrote: | Could you explain how rewriting one of the worlds most complex | and critical specifications would break of the Google/MS | hegemony on search? | debacle wrote: | Sorry, what I meant was: | | "If FB decided to try and break into search, then they might | decide to attack the HTML/CSS/JS stack." | | Not the other way around. | WallyFunk wrote: | > The whole thing is ripe for a redo | | Web developers have worked around quirks for as long as I can | remember. The stack has many warts, but we learn to adapt to | them. Like 90% of a web developer's job is working around | gotchas, and will continue that way. A 'redo' might not be | needed. Developers need something to moan about and need | something to keep them employed :) | goatlover wrote: | There's always WASM, and I think Zuck is more interested in VR | than trying to push a new web standard. | doliveira wrote: | I find it pretty funny that we humans have invented all these | transpilers and bundlers, invested probably billions of dollars | in JITs, just to keep writing JS | eptcyka wrote: | The best equipped to do this are Google/MS/Apple because they | actually control the source code of relevant contemporary | browsers. | debacle wrote: | I think that this is the case (right now) because of Apple's | stranglehold on the browser on iOS and the complex | relationship between Google/Apple. | | If FB could launch a browser on iOS that was in their walled | garden, not only would it quickly receive wide adoption but | it might become people's primary browser. | | Not that I necessarily think that's a good thing, mind you. | eptcyka wrote: | Why would it quickly receive any adoption? Of all of the | behemoths, I would trust FB the least here. Not that I | trust any of the other big players enough not to use | Firefox everywhere I can. | debacle wrote: | The word "trust" doesn't factor into ~90% of users' | decisions. | | If FB says "hey install this app," they will install it. | shadowgovt wrote: | > It is implemented differently in browsers | | In 2022, that alone is enough to wipe it from my toolbox as a web | developer. Ain't nobody got time for that. | | (... there are lots of other reasons it'd be bad practice to rely | on this as well, although it's nice for debugging when | available). | beebeepka wrote: | This was mostly useful back in the days when we had to manually | query dom during development and debugging. I've seen some pretty | horrible things but never have I seen this in a codebase, not | even in a commit | 7952 wrote: | I remember using it on the first Javascript I ever used around | 20 years ago. I naively assumed that the DOM was like state in | a more procedural language and this variable trick played into | that. | tambourine_man wrote: | *rigamorale | | Should read "rigamarole" | thunderbong wrote: | I've always thought it was 'rigmarole'! | | Today I learned, it's both! | | https://en.wiktionary.org/wiki/rigmarole | spdustin wrote: | *rigmarole, if we're being pedantic, but I suspect the | contemporary spelling "rigamarole" is gaining on the proper | spelling, and that's one of the wonderful/terrible things about | the English language. | [deleted] | mikessoft_gmail wrote: ___________________________________________________________________ (page generated 2022-09-27 23:00 UTC)