Checking support...
Custom Attributes are "Custom Elements, but for attributes". You subclass
Attr, register it under a hyphenated name, and every matching
attribute on a connected element is upgraded in place to an instance of your
class, with lifecycle callbacks mirroring custom elements. Where a custom
element models what an element is, a custom attribute models a
behaviour an element has, and can be applied to any element: HTML,
SVG, MathML, or another custom element.
class PersistValue extends Attr {
connectedCallback() {
const stored = localStorage.getItem(this.value);
if (stored !== null) this.ownerElement.value = stored;
this.ownerElement.addEventListener("input", this);
}
disconnectedCallback() {
this.ownerElement.removeEventListener("input", this);
}
attributeChangedCallback(oldValue, newValue) {
if (oldValue !== null) localStorage.removeItem(oldValue);
}
handleEvent() {
localStorage.setItem(this.value, this.ownerElement.value);
}
}
customAttributes.define("persist-value", PersistValue);
<input name="email" persist-value="email-draft" />
<textarea name="bio" persist-value="bio-draft"></textarea>
el.getAttributeNode("persist-value") and
el.attributes["persist-value"] return the
PersistValue instance, so methods added to the class live on the
attribute node and never clash with element API.
| Callback | Runs when |
|---|---|
constructor() | the attribute is upgraded, constructed directly, or created by document.createAttribute() |
attributeChangedCallback(oldValue, newValue) | the value is added, changed or removed; once on upgrade with its initial value |
connectedCallback() | added to a connected element, or its element is inserted into a document |
disconnectedCallback() | removed from a connected element, or its element is removed |
connectedMoveCallback() | its element is moved with moveBefore() |
adoptedCallback(oldDocument, newDocument) | its element is adopted into another document |
import "custom-attributes-polyfill";
Or apply it manually:
import { isSupported, apply } from "custom-attributes-polyfill/fn";
if (!isSupported()) apply();
The polyfill covers window.customAttributes,
CustomAttributeRegistry (including scoped registries for shadow
trees), the Attr constructor,
document.createAttribute(), and the
customAttributeRegistry option on createElement(),
importNode(), attachShadow() and
setHTMLUnsafe().
define(), upgrade(), initialize() and createAttribute() are synchronous.disconnectedCallback/connectedCallback pair fires.document, shadow roots, and documents passed to initialize() are observed.cloneNode() does not copy it.See the README for the full list.