Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.



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/RxGgNR


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().


Elysia 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


Element IDs are automatically attached to the window object

  <script>
  document.addEventListener('DOMContentLoaded', () => {
    window.thing.innerHTML = 'Hello, World!';
  });
  </script>
  <div id="thing"></div>


That's (much) slower and has surprising edge cases: https://news.ycombinator.com/item?id=32997636


    <div id="parent"></div>
Whoops.


Does caching help? If so, shouldn't getElementById do the same thing under the hood?


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.


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!


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.


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


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!


Nah, i rather skip brittle :) But what's not brittle in JS? Elm? (TS to some extend)


This 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


Should that be `Object.create(null)` to avoid problems with `<div id="toString"/>`?


A 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.


Why cache? I expect that getElementById will be efficient enough on its own.




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: