akira2501
7 hours 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.
kibibu
6 hours ago
I 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/RxGgNRsli
5 hours ago
I 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().open-paren
7 hours ago
Element IDs are automatically attached to the window object
<script>
document.addEventListener('DOMContentLoaded', () => {
window.thing.innerHTML = 'Hello, World!';
});
</script>
<div id="thing"></div>
esprehn
7 hours ago
That's (much) slower and has surprising edge cases: https://news.ycombinator.com/item?id=32997636
akira2501
7 hours ago
<div id="parent"></div>
Whoops.jonathrg
6 hours ago
Does caching help? If so, shouldn't getElementById do the same thing under the hood?
akira2501
6 hours ago
It 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.
MrLeap
6 hours ago
This is awesome! Proxies can be so cool. I made this proxy many years ago for tracking stats/buffs/debuffs/gear for game things.
o11c
4 hours ago
Should that be `Object.create(null)` to avoid problems with `<div id="toString"/>`?
nattaylor
7 hours ago
If 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
7 hours ago
The 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
7 hours ago
Sorry, 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
6 hours ago
My 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
7 hours ago
Nah, i rather skip brittle :) But what's not brittle in JS? Elm? (TS to some extend)
mattlondon
6 hours ago
Why cache? I expect that getElementById will be efficient enough on its own.