Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Created November 29, 2023 23:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfkaye/1d9a55b8fe6659df7ba7c04edbd24bf7 to your computer and use it in GitHub Desktop.
Save dfkaye/1d9a55b8fe6659df7ba7c04edbd24bf7 to your computer and use it in GitHub Desktop.
onpropertychange signal v.6 - more problems between descriptors and proxies
// 21 September 2023
// onpropertychange signal v.6 already
// cont'd from https://gist.github.com/dfkaye/dae6958adf813735d9154be23a0ebb09
// Still not happy but we have better support across types of objects (only
// element, text, object, and array so far).
// After all that, Object.defineProperty on property names is enough to ensure
// related property updates, e.g., {textContent, nodeValue, data, wholeText}.
// We still need the proxy to get, set, and delete properties, so we need to
// assign the incoming object's properties to a bridge object that then
// delegates to the object. The trick to making objects, arrays, elements, and
// text reactive is in the iteration manner. If the item is not an instanceof
// EventTarget, then assign add, remove, dispatch, etc., to the proxied object
// so we can assign onpropertychange as a method and add it to listeners,
// dispatch events on it, and remove it.
// Things about handlers:
//
// If set is defined, then handler.set() is called when a new property is added,
// and defineProperty is never called, so handler.set() is where the action is.
// Setting onpropertychange to null is handled by handler.set(), while deleting
// is still handled by handler.deleteProperty().
// Problems:
//
// 1. Array.push() no longer breaks, but the new value at the array index is not
// reported in either handler, and the new array length change is not reported.
// 2. This implementation does not add the EventTarget interface methods to the
// non-EventTargets correctly.
function N(node) {
var handler = {
get(target, key) { return Reflect.get(target, key); },
set(target, key, value) {
var oldValue = Reflect.get(target, key);
console.error({key, value, oldValue});
if (key == 'onpropertychange') {
// remove add from node
console.warn("%csetting", "font-weight: bold;", key);
}
return Reflect.set(target, key, value);
},
defineProperty(target, key, descriptor) {
if (key == 'onpropertychange') {
// remove add from node
console.warn("%cdefining", "font-weight: bold;", key);
}
return Reflect.set(target, key, descriptor.value);
},
deleteProperty(target, key) {
if (key == 'onpropertychange') {
// remove from node
console.warn("%cdeleting", "font-weight: bold;", key);
}
return Reflect.deleteProperty(target, key);
}
};
var o = { toJSON() { return node } };
function assign(name) {
if (typeof node[name] == "function") {
(function(f) {
Object.defineProperty(o, name, {
value: function () {
return f.apply(node, Array.from(arguments));
}
});
})(node[name]);
}
else {
(function(name) {
Object.defineProperty(o, name, {
get() { return Reflect.get(node, name); },
set(value) { return Reflect.set(node, name, value); }
});
})(name);
}
}
Array.isArray(node)
// Array properties inherited but not enumerable.
? Object.getOwnPropertyNames(Object.getPrototypeOf(node)).forEach(assign)
// Properties in objects, elements, and text are enumerable.
: (function () { for (var name in node) { assign(name) } })();
//return node;
//return o;
return new Proxy(o, handler)
}
function test(item) {
console.group(item.constructor.name)
var element = item instanceof Element;
var text = item instanceof Text;
console.warn(element ? item.outerHTML : item.nodeValue);
item.textContent = 'text';
console.log(text ? item.data : item.innerText)
console.warn(element ? item.outerHTML : item.nodeValue);
var s = N(item);
s.textContent = 'updated';
console.log(text ? s.wholeText : s.innerText);
console.warn(element ? s.outerHTML : s.nodeValue);
s.textContent = 'updated again';
console.log(text ? s.wholeText : s.innerText);
console.warn(element ? s.outerHTML : s.nodeValue);
s.fake = 'fake';
console.log(s.fake);
element && s.setAttribute("name", "*******");
console.warn(element ? s.outerHTML : s.nodeValue);
s.hello = function (msg) { return msg + ", " + this.textContent };
console.warn(s.hello("hello"));
console.log(s.toString());
console.log(JSON.stringify(s));
// Fixed
// TypeError: s.push is not a function
// However, the new value at the array index is not reported, and the new
// array length change is not reported...
Array.isArray(item) && console.log(s.length);
Array.isArray(item) && console.log(s.push(77));
Array.isArray(item) && console.log(s.at(-1));
Array.isArray(item) && console.log(s.length);
// element only...
element && s.addEventListener("click", function (e) {
console.error("yes I got clicked", e);
});
element && s.dispatchEvent(new CustomEvent("click", {}));
s.onpropertychange = function (e) {
console.log(e);
};
s.onpropertychange = null;
delete s.onpropertychange;
console.groupEnd(item.constructor.name)
}
// fixtures
var section = document.createElement("section");
var text = new Text("some text");
section.replaceChildren(text);
// run
test(section);
test(text);
test({ });
test(['abc']);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment