Een shadow root is een ingekapselde DOM-substructuur die is gekoppeld aan een hostelement: stijlen binnen de shadow root lekken niet naar buiten en paginastijlen lekken niet naar binnen (behalve overgenomen property's). Templates bouwen de boomstructuur efficiënt op. Slots projecteren Light DOM-content erin.
Een schaduwroot toevoegen
this.attachShadow({ mode: 'open', delegatesFocus: true });
delegatesFocus: true zorgt dat de focus van de host wordt verplaatst naar het eerste focusbare element in de schaduwstructuur, stuurt :focus-stijlen naar de host en stuurt op formuliergerelateerde aangepaste elementen (static formAssociated = true) <label>-klikken door naar de interne bediening. Stel dit in als de component focusbare bedieningselementen omvat. Gebruik in bijna alle gevallen mode: 'open', zodat ontwikkelaarstools, paginascripts en serverhydratatie element.shadowRoot kunnen bereiken (zie Web Components Orientation).
Templates: de boom efficiënt samenstellen
Definieer een <template> eenmalig (modulebereik of een statisch veld) en kloon deze per instantie. Een geparseerd fragment klonen is veel sneller dan een innerHTML-tekenreeks opnieuw parseren bij elke constructie.
const template = document.createElement('template');
template.innerHTML = `
<div class="card">
<slot name="header"></slot>
<div class="body"><slot></slot></div>
<slot name="footer"></slot>
</div>
`;
class MyCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
// MANDATORY: deep-clone the template content so each instance gets its own nodes.
this.shadowRoot.append(template.content.cloneNode(true));
}
}
customElements.define('my-card', MyCard);
Houd templates overzichtelijk. Diep geneste 'div-soep' en veel niveaus van geneste schaduwroots voegen beide meetbare stijlresolutie en indelingskosten toe, een grote prestatie-impact op schaal. Geef de voorkeur aan een platte structuur en semantische elementen.
Slots: de delen die mensen verrassen
Standaard- en benoemde slots werken zoals gedocumenteerd. Dit is het gedrag dat problemen veroorzaakt:
- Witruimte telt. Tekstknooppunten tussen elementen (waaronder pure witruimte) worden toegewezen aan de standaardruimte, dus een standaardruimte is zelden echt leeg.
- De DOM-volgorde blijft behouden, niet de ruimtevolgorde. Geprojecteerde nodes behouden hun oorspronkelijke Light DOM-volgorde voor
:first-child/:last-child-doeleinden, ongeacht welke sleuf ze vullen. Een::slotted(*:first-child)-regel target het eerste onderliggende DOM-element, niet het eerste onderliggende element van die ruimte. De regel komt daarom vaak niet overeen met wat u verwacht. slotchangeis het signaal voor de upgradetiming. Deze wordt geactiveerd als toegewezen nodes veranderen (waaronder de eerste toewijzing) en is de betrouwbare manier om te weten dat Light DOM-onderliggende elementen zijn aangekomen na een upgrade, wanneer ze misschien niet bestonden tijdensconnectedCallback.
this.shadowRoot.querySelector('slot[name="header"]')
.addEventListener('slotchange', (e) => {
const nodes = e.target.assignedElements();
this.toggleAttribute('has-header', nodes.length > 0);
});
Stylesheets laden in een shadow root
| Methode | Voordelen | Nadelen |
|---|---|---|
<style> in de template |
Eenvoudig, volledig ingekapseld, werkt zonder JS. | Wordt per instantie gedupliceerd, waardoor het DOM op schaal wordt opgeblazen. |
<link rel="stylesheet"> |
Extern bestand dat in het cachegeheugen kan worden opgeslagen. | Extra verzoek; risico op een flits van niet-opgemaakte content. |
adoptedStyleSheets |
Eén gedeelde CSSStyleSheet voor alle instanties; het snelst op schaal. |
Vereist JS; samengestelde spreadsheets ondersteunen @import niet. |
Voor veel instanties van één component heeft een gedeelde samengestelde stylesheet de voorkeur:
const sheet = new CSSStyleSheet();
sheet.replaceSync(`:host { display: block; }`);
class MyCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
// The same sheet object is reused by every instance; no per-instance parse.
this.shadowRoot.adoptedStyleSheets = [sheet];
}
}