Lab 02: Web Components
Overview
Step 1: Custom Elements Lifecycle
class MyButton extends HTMLElement {
// Declare which attributes trigger attributeChangedCallback
static get observedAttributes() {
return ['variant', 'disabled', 'size', 'label'];
}
constructor() {
super();
// ✓ Can: attach shadow DOM, set up initial state
// ✗ Can't: access children, inspect DOM, set attributes
this._shadow = this.attachShadow({ mode: 'open' });
}
// Element inserted into DOM
connectedCallback() {
this.render();
this._addEventListeners();
}
// Element removed from DOM
disconnectedCallback() {
this._removeEventListeners();
// Cancel subscriptions, timers, etc.
}
// Element moved to new document (e.g., into iframe)
adoptedCallback() {
// Rarely needed
}
// Observed attribute changed
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) return; // no-op
this.render(); // re-render on any attribute change
}
// Getters/setters for property ↔ attribute reflection
get variant() { return this.getAttribute('variant') || 'primary'; }
set variant(v) { this.setAttribute('variant', v); }
get disabled() { return this.hasAttribute('disabled'); }
set disabled(v) { v ? this.setAttribute('disabled', '') : this.removeAttribute('disabled'); }
render() {
this._shadow.innerHTML = `
<style>
:host { display: inline-flex; }
:host([disabled]) { opacity: 0.5; pointer-events: none; }
button { /* styles */ }
</style>
<button part="button" ?disabled="${this.disabled}">
<slot></slot>
</button>
`;
}
_addEventListeners() {
this._shadow.querySelector('button')?.addEventListener('click', this._handleClick);
}
_removeEventListeners() {
this._shadow.querySelector('button')?.removeEventListener('click', this._handleClick);
}
_handleClick = (e) => {
this.dispatchEvent(new CustomEvent('my-click', {
bubbles: true,
composed: true, // cross Shadow DOM boundary
detail: { variant: this.variant }
}));
}
}
customElements.define('my-button', MyButton);Step 2: Shadow DOM — open vs closed
Step 3: Template and Slot
Step 4: CSS Custom Properties Across Shadow DOM
Step 5: Custom Events and Element Communication
Step 6: Form-Associated Custom Elements
Step 7: Upgrading and whenDefined
Step 8: Capstone — jsdom Custom Element Demo
Summary
Feature
API
Purpose
Last updated
