Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Created November 29, 2023 23:17
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/428abdeca224c37014d27fc420906d88 to your computer and use it in GitHub Desktop.
Save dfkaye/428abdeca224c37014d27fc420906d88 to your computer and use it in GitHub Desktop.
onpropertychange signal v.4 -- redone form element sketch that works for arrays and objects
// 18 september 2023
// onpropertychange signal v.4
// cont'd from https://gist.github.com/dfkaye/4e06eb749f878662e31a6cb019fbd650
// re-done in less than 30 minutes, this implementation makes more sense and
// works for arrays and objects...
// 19 September 2023
// dispatch propertychange event in deleteProperty handler
// add sync logic and reposition Reflect.set() calls in each handler
// Fails on element nodes (see end tests).
function P(data) {
var target = new EventTarget;
// These are merged on the data in order to expose them through the proxy.
var API = {
addEventListener(name, h) { return target.addEventListener(name, h); },
removeEventListener(name, h) {
if (name == "propertychange" && h === this.onpropertychange) {
return delete this.onpropertychange;
}
return target.removeEventListener(name, h);
},
dispatchEvent(event) { return target.dispatchEvent(event); },
toJSON() { return data; },
toString() { return JSON.stringify(data); },
// onpropertychange: null,
api() { return API; }
};
// Don't reset the prototype as in v.2, just add the api methods onto the data
// and call it good. They are not serialized as JSON anyway. Maybe make that
// the representation on any "query".
data = Object.assign(Object(data), API);
// sync target properties with data properties
function sync({ data, target }) {
var API = data.api();
Object.getOwnPropertyNames(data).forEach(function (k) {
if (!(k in API)) {
target[k] = data[k];
}
});
Object.getOwnPropertyNames(target).forEach(function (k) {
if (!(k in data)) {
delete target[k];
}
});
}
// proxy the data
var handler = {
defineProperty(data, key, descriptor) {
var {value} = descriptor;
var previous = Reflect.get(data,key);
if (key === 'onpropertychange') {
if (value == null) {
return handler.deleteProperty(data, key);
}
target.removeEventListener('propertychange', previous);
target.addEventListener('propertychange', value);
}
Reflect.set(data, key, value);
sync({ data, target });
var event = new CustomEvent("propertychange", {
detail: { propertyName: key, previous, value }
});
return target.dispatchEvent(event);
},
deleteProperty(data, key) {
var value = Reflect.get(data, key);
if (key === 'onpropertychange') {
target.removeEventListener('propertychange', value);
}
Reflect.deleteProperty(data, key);
sync({ data, target });
// add onpropertydelete???
var event = new CustomEvent("propertychange", {
detail: { propertyName: key, deletedValue: value }
});
return target.dispatchEvent(event);
}
};
return new Proxy(data, handler);
}
/* test it out */
var a = P([]);
function f(e) {
console.warn("onpropertychange\n", e.target, e.detail);
}
a.onpropertychange = f;
function h(e) {
console.log("h\n", e.target, e.detail)
}
a.addEventListener("propertychange", h);
a[0] = '11';
a.push(99);
console.log(a.length, 1 in a);
a.length = 1;
console.log(a.length, 1 in a);
delete a[0];
console.log( JSON.stringify(a), a.toString(), a.toJSON() );
a.name = 'array';
a.length = 0;
console.log( JSON.stringify(a), a.toString(), a.toJSON() );
console.dir( a.api() );
var o = P({name: "object"});
o.addEventListener("propertychange", h);
o.onpropertychange = f;
console.log( o.toString() );
o.name = 'test';
o.onpropertychange = null;
console.log( o.toString() );
console.dir( o.api() );
/* fails element nodes */
var n = P(Object.assign(document.createElement("input"), {
defaultValue: "show me"
}));
console.log(n.getAttribute("value"));
// TypeError: 'getAttribute' called on an object that does not implement
// interface Element.
console.log(n.outerHTML);
// TypeError: 'get outerHTML' called on an object that does not implement
// interface Element.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment