Last active
August 18, 2023 19:29
-
-
Save olanod/eb16c9fc9f8f141560cf988ef0bf5872 to your computer and use it in GitHub Desktop.
Custom element boiler plate
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
const p = new DOMParser(); | |
const tagFn = fn => (strings, ...parts) => fn(parts | |
.reduce((tpl, value, i) => `${tpl}${strings[i]}${value}`, '') | |
.concat(strings[parts.length])) | |
const html = tagFn(s => new DOMParser() | |
.parseFromString(`<template>${s}</template>`, 'text/html') | |
.querySelector('template')) | |
const css = tagFn(s => { | |
let style = new CSSStyleSheet() | |
style.replaceSync(s) | |
return style | |
}) | |
const baseStyle = css` | |
:host { | |
--my-color: #123; | |
} | |
/* use ids all you want as they are scoped to the shadow dom */ | |
#the-thing {} | |
` | |
const template = html` | |
<div id="the-thing"> | |
<slot>slots can have default content</slot> | |
</div> | |
` | |
/** | |
* Boiler plate of a custom element that can be a form associated element | |
*/ | |
export class MyElement extends HTMLElement { | |
static tag = "my-element"; | |
static userStyles = []; | |
static observedAttributes = []; | |
static formAssociated = true; | |
#internals; | |
// using the $ prefix as convention for dom elements | |
#$root; | |
// perhaps some helpers? | |
#$ = selector => this.#$root.querySelector(selector) | |
#$theThing; | |
// good place for event handlers | |
#onThingClicked = e => {} | |
constructor() { | |
super(); | |
this.#$root = this.attachShadow({ mode: 'closed'/*, delagatesFocus: true */}); | |
this.#$root.append(template.content.cloneNode(true)) | |
this.#$root.adoptedStyleSheets = [baseStyle, ...this.constructor.userStyles] | |
this.#$theThing = this.#$('#the-thing') | |
// for form associated element | |
if ('ElementInternals' in window && | |
'setFormValue' in window.ElementInternals.prototype) { | |
this.#internals = this.attachInternals(); | |
this.#internals.setFormValue(this.value); | |
} | |
} | |
connectedCallback() { | |
this.#$theThing.addEventListener('click', this.#onThingClicked) | |
} | |
// implement all this if we want the element to be a "form associated element" | |
// a.k.a. behave like a native input in a form | |
get value() { return '' } | |
get form() { return this.#internals.form; } | |
get name() { return this.getAttribute('name'); } | |
get type() { return this.localName; } | |
get validity() { return this.#internals.validity; } | |
get validationMessage() { return this.#internals.validationMessage; } | |
get willValidate() { return this.#internals.willValidate; } | |
checkValidity() { return this.#internals.checkValidity(); } | |
reportValidity() { return this.#internals.reportValidity(); } | |
} | |
customElements.define(MyElement.tag, MyElement); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment