4 ms·
I've been using this for years: const $id = new Proxy({}, { // get element from cache, or from DOM get: (tgt, k, r) => (tgt[k] || ((r = doc
by akira2501 2y ago
I've been using this for years:
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 programming errors
set: () => $throw(`Attempt to overwrite id cache key!`)
});
It's nice to be able to refer to elements by property name, so:
<div id="thing"></div>
Is reachable with:
$id.thing
And, since the underlying structure is just an object, you can still enumerate it with Object.keys, which can sometimes be a useful debugging aid and general catalog of accessed elements.
Anyways.. Proxy is a wildly underappreciated and used class in JavaScript.
- nattaylor 2y agoIf you like brittle things, the id attribute is already made into an attribute on the window for legacy reasons Edit: My tone may have indicated that parent's solution was brittle. It's not!
- akira2501 2y agoThe id attribute can take on values that are already present or reserved in window. "fetch", "opener", etc.. The reason to have a separate system that correctly calls getElementById() is to avoid this issue. So, it's actually a _less_ brittle mechanism that doesn't rely on legacy mechanisms and lacks the surprises that come with that.
- nattaylor 2y agoSorry, I didn't mean to suggest your solution was brittle -- I actually quite like it and want to adopt it! But I do think the legacy browser behavior with the ID attribute as window properties is very brittle for the reasons you suggest
- akira2501 2y agoMy fault, I tend to "rapid fire" sometimes. Yours was next to another reply on the identical subject and that caused me to mistake the meaning of the comma in your sentence. Reading it more slowly, I see it now, and, thank you!
- cies 2y agoNah, i rather skip brittle :) But what's not brittle in JS? Elm? (TS to some extend)
- open-paren 2y agoElement IDs are automatically attached to the window object <script> document.addEventListener('DOMContentLoaded', () => { window.thing.innerHTML = 'Hello, World!'; }); </script> <div id="thing"></div>
- jonathrg 2y agoDoes caching help? If so, shouldn't getElementById do the same thing under the hood?
- akira2501 2y agoIt used to more in the past. The native call does usually include a cache and will use it optimistically; however, the strategies around invalidating this cache are varied and some surprisingly basic DOM actions can trigger it. Then browsers usually fallback to the full DOM query case. The cache eliminates this variability, which prior to flexbox, could be very useful in highly nested site designs particularly in mobile contexts.
- kibibu 2y agoI also put together a Proxy microlib a few years ago. I agree it's very powerful. You can use it like: $('.my-elements').className = 'Important'; $('.my-elements').style.color = 'Red'; $('.my-elements').classList.add('Another'); const $ = (() => { function listProxy(arr) { return new Proxy(arr, { set: (t, p, v, r) => { for(let x of t) { x[p] = v; } }, get: (t, p, r) => { if(t.length > 0 && t[0][p] instanceof Function) { return (...args) => { Array.prototype.map.call(t, (x) => x[p](args)) }; } else { return listProxy( Array.prototype.map.call(t, (x) => x[p]) ); } } }) } return (sel, root) => { if(root === undefined) root = document; return listProxy(root.querySelectorAll(sel)); } })(); demo: https://codepen.io/anon/pen/RxGgNR https://codepen.io/anon/pen/RxGgNR
- sli 2y agoI built a Proxy-based microlib for making fluent REST calls just on a lark a couple years ago. It was never anything production-ready, but it was so handy that I used it in all of my JS projects until I moved away from webdev. The API was basically: const api = new Proxy({route: baseRoute}, handler); // handler is the microlib's export const result = await api.get.some.route.invoke(); // GET {baseRoute}/some/route invoke() is just how it finally fires the call. I didn't feel like spending too much time making it automatic, the benefits just weren't large enough to justify compared to just calling invoke().
- dimava 2y agoElysia Eden [1] uses the same thing, except putting `get` to the place of `invoke` `api.some.route.get()` [1] https://elysiajs.com/eden/treaty/overview.html https://elysiajs.com/eden/treaty/overview.html
- mattlondon 2y agoWhy cache? I expect that getElementById will be efficient enough on its own.
- MrLeap 2y agoThis is awesome! Proxies can be so cool. I made this proxy many years ago for tracking stats/buffs/debuffs/gear for game things. https://github.com/MrLeap/Statsi https://github.com/MrLeap/Statsi
- o11c 2y agoShould that be `Object.create(null)` to avoid problems with `<div id="toString"/>`?
- akira2501 2y agoA call to `$id.toString` actually yields the native unbound function whether or not there is such an element on the page. So get is not invoked in this case.