Skip to content

Instantly share code, notes, and snippets.

@olanod
Last active August 18, 2023 19:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save olanod/eb16c9fc9f8f141560cf988ef0bf5872 to your computer and use it in GitHub Desktop.
Save olanod/eb16c9fc9f8f141560cf988ef0bf5872 to your computer and use it in GitHub Desktop.
Custom element boiler plate
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