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