/* to syntax-sugar above */ function defineCE({tag,attr,html,update,onload, ...rest}) { customElements.define(tag, class extends HTMLElement { static get observedAttributes() { return attr }; attributeChangedCallback (attrName, oldValue, newValue) { // first time, it triggers before element is initialized... if (this.update) this.update(); } connectedCallback() { this.innerHTML = html.replace('',this.innerHTML); this.update = ()=>Object.entries(update||{}).forEach( ([selector,updater])=>{ updater( this.querySelector(selector), (attrName)=>eval(this.getAttribute(attrName)) ) }); Object.entries(rest).forEach( ([k,v])=>this[k] = v); if (onload) { onload.call(this) } eval(this.getAttribute('onload')) this.update(); } }); } /* example */ defineCE({ tag: "counter-ce", html:/*html*/` `, update: { 'span' : (el,attr)=>el.innerHTML = attr('value'), } })