Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Created March 22, 2024 06:37
Show Gist options
  • Save dfkaye/5280ff3ba40e2548909c29b9f4477d7c to your computer and use it in GitHub Desktop.
Save dfkaye/5280ff3ba40e2548909c29b9f4477d7c to your computer and use it in GitHub Desktop.
How to create a custom setAttribute method on a non-DOM object and reflect the attribute as a property.
// 3 December 2023
// How to create a custom setAttribute method on a non-DOM object
// and reflect the attribute as a property.
// 3 December
// Expanded by adding get and remove attribute methods.
// 21 March 2024
// Added hasAttribute method.
// Added logic to reflect a property as an attribute.
// Fixed property setting and deletion logic in set/remove attribute methods.
// This is not a complete solution or package. We only demonstrate how to
// create a custom attribute method and the corresponding property reflection.
// Uses Object.defineProperties() on one object, then defines a proxy for that
// object with a handler that calls its respective set/remove attribute methods.
// Tests use console.assert so that only failing tests report in the console.
var o = Object.defineProperties({}, {
attributes: {
value: document.createElement('body').attributes
},
hasAttribute: {
value: function hasAttribute(name) {
var attr = this.attributes.getNamedItem(name);
return !!attr;
}
},
getAttribute: {
value: function getAttribute(name) {
var attr = this.attributes.getNamedItem(name);
return Object(attr).value;
}
},
setAttribute: {
value: function setAttribute(name, value) {
var attr = document.createAttribute(name);
attr.value = value;
this.attributes.setNamedItem(attr);
// reflect attribute as a property...
if (this[name] !== value) {
this[name] = value;
}
}
},
removeAttribute: {
value: function removeAttribute(name) {
var attr = this.attributes.getNamedItem(name);
if (attr) {
// 21 March 2024
// order of deletions matters here
// must remove the attribute entry before removing the
// property; otherwise the proxy (below) fails to find
// target[name] - I am not sure why....
this.attributes.removeNamedItem(name);
delete this[name];
};
return attr;
}
}
});
/* test it out */
// should update attribute and reflect it as a property
o.setAttribute("one", "two");
console.assert(o.hasAttribute("one"), `should have attribute, "one"`);
console.assert(o.getAttribute("one") === "two", `attribute not updated, "one"`);
console.assert(o.one === "two", `property not reflected, "one"`);
console.assert(o.removeAttribute("one").name === "one", `should remove attribute, "one"`);
console.assert(!o.hasAttribute("one"), `should not have attribute, "one"`);
// should not overwrite the setAttribute property method when setting setAttribute as an attribute.
o.setAttribute("setAttribute", "overridden");
console.assert(o.getAttribute("setAttribute") === "overridden", `"setAttribute" attribute not updated`);
console.assert(typeof o.setAttribute == "function", `setAttribute fn should not be overridden`);
// 3 December 2023
// reflect property updates to attributes.
var p = new Proxy(o, {
get(target, key) {
return Reflect.get(target, key);
},
set(target, key, value) {
target.setAttribute(key, value);
return Reflect.set(target, key, value);
},
deleteProperty(target, key) {
target.removeAttribute(key);
return Reflect.deleteProperty(target, key);
}
});
// should add attribute when property is added
p.hello = 55;
console.assert(p.hello === 55, `should set property "hello"`);
console.assert(p.hasAttribute("hello"), `should have attribute "hello"`);
console.assert(p.getAttribute("hello") === "55", `should set attribute "hello"`);
// should remove attribute when property is deleted
delete p.hello;
console.assert(!("hello" in p), `should delete property "hello"`);
console.assert(!p.hasAttribute("hello"), `should not have attribute "hello"`);
console.assert(!p.getAttribute("hello"), `should remove attribute "hello"`);
// should delete property when attribute removed
p.property = 33;
console.assert(p.getAttribute("property") === "33", `should have attribute "property"`);
console.assert(p.removeAttribute("property").name === "property", `should remove attribute "property"`);
console.assert(!("property" in p), `should remove property "property"`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment