Created
March 22, 2024 06:37
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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