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