Binding scalar data to HTML element – JavaScript Proxies

I may be guilty of a bit of an XY question here, but I’m doing this out of theoretical interest rather than any immediate need.

My goal is, ultimately, to build a lightweight framework that allows me to bind scalar data to a property of an HTML element such that the property updates “automagically” whenever the value changes.

I’m trying to come up with the most syntactically elegant way to do this, that puts IDE code-completion and developer convenience at the forefront.

I’m aware of Proxies, and I’ll get to that in a moment. My challenge is really in the syntactic sugar I’m trying to create, so come along for the ride – I can always fall back to many tried-and-true approaches but I’m hoping for something more magical.

Let’s assume I already have a nice little framework that wraps DOM Nodes with additional functionality, and can Proxy the DOM Node if needed.

The ultimate data-binding implementation from a syntax/API perspective would be:

let someVar = "foo"
let someElement = MyFramework.getById("some-div"); // returns a proxied HTMLElement with additional methods added
someElement.bind("innerText", someVar);

// later...
someVar = "bar"; // someElement.innerText automatically updates

Now, before you freak out, I’m aware that this will simply not work, as there’s no way to proxy a Primitive, so I’m happy to wrap this in a state object that CAN be proxied.

let state = MyFramework.createState(); // returns a proxy of the State object
state.someVar = "foo";
let someElement = MyFramework.getById("some-div");
someElement.bind("innerText", state.someVar);

This is where it gets tricky, because I actually need to access the object key “someVar” within my .bind() function, but I’m just getting the value of state.someVar.

I CAN set a proxy within my createState() function which can trap the getter and instead of returning the value, returns the property name like this:

MyFramework.createState = function(initialProps = {}){
  let proxy = new Proxy(initialProps, {
     get(target, prop, receiver){
        return prop;
     }
   }

   return proxy;
}

…but then I can’t really make use of the variable, because I don’t know how to make my “get” trap conditional – ie, only return the prop when being accessed from within the bind() function, otherwise just return Reflect(...arguments).

What I’m hoping to avoid is the following:

someElement.bind("innerText", state, "someVar");

Similarly,

someElement.bind("innerText", state, state.someVar)

just seems redundant and prone to user error.

Alternatively I’d be open to:

state.someVar.bind(someElement, "innerText");

but that won’t work (that I can think of) because someVar is still a scalar (primitive) and therefore can’t get any custom properties (like .bind() attached).

I was thinking I could intercept the first accessor (state.someVar), Proxy THAT and trap its get to handle the non-existent “bind” function, but again, without being able to do that contextually, the user would then never be able to use state.someVar in a simple scalar context, and we’d have to resort to something ugly like state.someVar.set("new val") and state.someVar.get(), and that defeats the purpose of my inquiry.

I think I’ve about exhausted my level of JS internals knowledge and wondering if there’s something obvious I’m missing.

And I should point out that if you are tempted to say “well, you’re asking about Y but really you should approach X differently” I appreciate that, but only if there really is NO way to achieve the syntactic elegance I’m looking for. Like I said, there are plenty of documented ways to achieve this – I’m trying to see if I can do it fancier.