Created
July 19, 2020 20:16
-
-
Save jazzyjackson/40f3e857d6c66d2b1311a0a0bce4cd01 to your computer and use it in GitHub Desktop.
This file contains 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
/* | |
This provides a getter / setter / emitter API for modifying HTML attributes | |
To replace element.setAttribute('name','nobody') with element.props.name = 'nobody' | |
And proviede a hook into being notified of attributes that are changed with this API | |
element.onAttributeChanged = function() | |
*/ | |
// you have to pass the HTMLElement context to the function, | |
function updateAttribute(prop, newValue){ | |
let attributeChange = {attribute: prop, oldValue: this.getAttribute(prop)} | |
if(typeof newValue == "undefined"){ | |
this.removeAttribute(prop) | |
} else if(typeof newValue == "object"){ | |
this.setAttribute(prop, JSON.stringify(newValue)) | |
} else { | |
this.setAttribute(prop, newValue) | |
} | |
return Object.assign(attributeChange, {newValue: this.getAttribute(prop)}) | |
} | |
Object.defineProperties(HTMLElement.prototype, { | |
// following old style of attaching event listeners, set a function to element.onAttributeChanged = ({attribute, oldValue, newValue}) => {} | |
onAttributeChanged: { | |
set: function(data){ | |
// incoming data is expecting to be a function | |
if(typeof data != 'function') throw new Error("onAttributeChanged can only be set to a callback") | |
this.attributeChangedCallback = data | |
} | |
}, | |
props: { | |
get(){ | |
let props = Array.from(this.attributes, attr => ({ | |
[attr.name]: attr.value | |
})).reduce((a, n) => Object.assign(a, n),{}) // You would think you could do .reduce(Object.assign), but assign is variadic, and reduce passes the original array as the 4th argument to its callback, so you would get the original numeric keys in your result if you passed all 4 arguments of reduce to Object.assign. So, explicitely pass just 2 arguments, accumulator and next. | |
return new Proxy(props, { | |
set: (obj, prop, value) => { | |
let update = updateAttribute.call(this, prop, value) | |
if(typeof this.attributeChangedCallback == 'function'){ | |
this.attributeChangedCallback(update) | |
} | |
return true | |
}, | |
get: (target, name) => { | |
return this.getAttribute(name) | |
} | |
}) | |
}, | |
set(data){ | |
Object.keys(data || {}).forEach(key => { | |
let update = updateAttribute.call(this, key, data[key]) | |
if(typeof this.attributeChangedCallback == 'function'){ | |
this.attributeChangedCallback(update) | |
} | |
}) | |
} | |
}, | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment