Introduction:
Please bear in mind I know why my solution doesn't work, and my question is how to make it working, with current code or in a completely different way, if possible.
Also please bear in mind this is not a duplicate of In the Proxy handler, how to distiguish getting a property (var) vs calling a method? or How to get function arguments in a Proxy handler although might sound similar.
After all these precautions:
I'm trying to make a "selector", similar to the one known from jquery, but using es6' querySelectorAll and the Proxy object.
I want to use Proxy, because I want to then call mySelector('div').style = 'color:red' - in such a way, that I don't want to add 'style' property - I don't even want to know in advance such a thing exists - I want to use the existing style property (in HTMLElement)
I also want to trap method calls, like mySelector('div').setAttribute (...);
Current almost working solution:
Here, function yes
returns a Proxied Yes
object. In there I trap 'get' and 'set'. This works fine with:
yes('.allDivs').setAttribute('style','color:red');
yes('.allDivs').style = 'color:blue';
But this does not work:
yes('.allDivs').style.color = 'green';
Code:
Example HTML:
<div class = 'allDivs'>
blah1
</div>
<div class = 'allDivs'>
blah2
</div>
Javascript:
(function(window) {
class Yes {
constructor(selector){
this.length = 0;
this.nodes = [];
if(typeof selector === 'string' && selector !== '') {
this.nodes = Array.from(document.querySelectorAll(selector));
}
//This is we can access the object with [index]:
if (this.nodes.length) {
this.length = this.nodes.length;
for (var i = 0; i < this.nodes.length; i++) {
this[i] = this.nodes[i];
}
}
}
//methods:
forEach (elem) {this.nodes.forEach (elem);return this;}
//traps:
//target here is *we* - the Yes instance (this), and prop is (string) property being called, a property or method
get (target, prop) {
console.log('::get called, for prop: ', prop);
return function(...args) {
target.forEach((elem) => {
if(typeof elem[prop] === 'function'){
elem[prop](...args);
}
});
}
}
set (target, prop, value) {
console.log('::set called, for prop, val:: ', prop, value);
target.forEach((elem) => {
if(typeof elem[prop] !== 'function'){
elem[prop] = value;
}
});
}
}
/////////
//End of class Yes definition
function yes (selector){
let yInstance = new Yes(selector);
return new Proxy(yInstance, yInstance);
}
//
Yes.fn = Yes.prototype;
window.yes = yes;
//E.fn.forEach = function (elem) {this.nodes.forEach (elem);return this;}
})(window);
//tests:
//These work fine:
//yes('.allDivs').setAttribute('style','color:red');//OK
//yes('.allDivs').style = 'color:blue';//OK
//This does not:
yes('.allDivs').style.color = 'green';//NOT OK
Working code: https://jsfiddle.net/kpion/5wy9uaqL/
Now, I know why it doesn't work, JS calls 'get' because it wants to read the .style
property. I would love to know how to do what I want to accomplish.
Notice, that I can't just return one 'div' HTMLElement, because I want to make the changes in the whole array of elements (hence the loop in the get/set handlers).