Verwerk de click van de interne knop en verstuur vervolgens een benoemde CustomEvent vanaf de host van de component. De pagina die de component gebruikt, luistert op die host en leest event.detail uit. Deze handleiding bouwt een complete lokale browserdemo op een Mac en legt uit welke eventopties elke listener nodig heeft.
Definieer welke melding de gebruiker nodig heeft
Bij een gewone JavaScript-webcomponent is de gebruiker de pagina of omvattende component die je aangepaste element gebruikt. Hier is de concrete taak om die gebruiker te laten reageren wanneer iemand een interne knop activeert, zonder de shadow DOM van de component te doorzoeken.
Het voorbeeld gebruikt een synthetische tegel met het label Tile A. De openbare gebeurtenis heet tile-activate en bevat een itemId en een activeringswaarde count. CustomEvent biedt het veld detail voor deze toepassingsgegevens; de naam van de gebeurtenis is hoofdlettergevoelig. De verzendende code en de listener moeten exact dezelfde naam gebruiken. Documentatie van de CustomEvent-constructor.
Beschouw deze keuzes als een kleine interfaceovereenkomst:
- Naam:
tile-activate. - Betekenis: de interne knop is geactiveerd en de teller is bijgewerkt.
- Gegevens:
detail.itemIdidentificeert de tegel;detail.countbevat de bijgewerkte tellerstand. - Herkomst: de host van het aangepaste element verstuurt de melding.
- Bezorging: directe gebruikers en listeners op bovenliggende elementen kunnen de melding ontvangen, ook over de shadow-grenzen van omvattende componenten heen.
Dit zijn ontwerpkeuzes voor dit voorbeeld. Een nuttige gebeurtenis beschrijft de handeling die de gebruiker belangrijk vindt. Zo kan de component zijn interne opmaak later wijzigen en toch dezelfde meldingsinterface behouden.
Bouw het complete voorbeeld en open het op je Mac
Je hebt een actuele desktopbrowser en een teksteditor nodig. Voor dit voorbeeld in één bestand hoef je niets te installeren of te bouwen. Het gebruikt een zelfstandig aangepast element dat HTMLElement uitbreidt, volgens het platformpatroon dat wordt beschreven in de sectie over aangepaste elementen in de HTML-standaard.
Kies in Teksteditor Archief > Nieuw en vervolgens Opmaak > Maak platte tekst. Plak het volledige document hieronder. Kies Archief > Bewaar, geef het de naam component-click.html en selecteer Gebruik .html als daarom wordt gevraagd. Dit zijn de door Apple beschreven stappen om in Teksteditor een HTML-bestand te maken. Werken met HTML-documenten in Teksteditor op de Mac.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Component click notification</title>
</head>
<body>
<h1>Component click notification</h1>
<section id="demo">
<action-tile id="tile" item-id="tile-a"></action-tile>
<p id="direct-status">Direct listener: waiting</p>
<p id="parent-status">Parent listener: waiting</p>
</section>
<script>
class ActionTile extends HTMLElement {
constructor() {
super();
this._count = 0;
this._button = null;
this._root = this.attachShadow({ mode: "open" });
this._handleClick = () => {
this._count += 1;
this.dispatchEvent(new CustomEvent("tile-activate", {
detail: {
itemId: this.getAttribute("item-id"),
count: this._count
},
bubbles: true,
composed: true
}));
};
}
connectedCallback() {
if (!this._button) {
this._button = document.createElement("button");
this._button.type = "button";
this._button.textContent = "Activate Tile A";
this._root.append(this._button);
}
this._button.addEventListener("click", this._handleClick);
}
disconnectedCallback() {
this._button.removeEventListener("click", this._handleClick);
}
}
customElements.define("action-tile", ActionTile);
const tile = document.getElementById("tile");
const demo = document.getElementById("demo");
const directStatus = document.getElementById("direct-status");
const parentStatus = document.getElementById("parent-status");
tile.addEventListener("tile-activate", (event) => {
const { itemId, count } = event.detail;
directStatus.textContent =
`Direct listener: ${itemId}, activation ${count}`;
});
demo.addEventListener("tile-activate", (event) => {
const { itemId, count } = event.detail;
parentStatus.textContent =
`Parent listener: ${itemId}, activation ${count}`;
});
</script>
</body>
</html>
Open het opgeslagen bestand in je browser en klik op Activate Tile A. Het verwachte eerste resultaat is Direct listener: tile-a, activation 1 en Parent listener: tile-a, activation 1. Bij elke volgende activering nemen beide weergegeven aantallen met één toe. Dit zijn verwachte resultaten van het voorbeeld, geen gerapporteerde browser-testresultaten.
De belangrijke volgorde is zichtbaar in de code: werk de componentstatus bij, maak de melding aan, verstuur die vanaf this en laat de gebruiker zijn eigen interface bijwerken. dispatchEvent() voert toepasselijke listeners synchroon uit voordat de functie terugkeert, zodat gebruikers tijdens hun handlers de bijgewerkte teller zien. Documentatie van dispatchEvent.
Kies de juiste opties voor bubbelen en shadow-grenzen
Het verzenddoel en de locatie van de listener bepalen welke opties van belang zijn. Een listener die rechtstreeks aan de host is gekoppeld, ontvangt een gebeurtenis die op die host wordt verstuurd, ook als bubbles false is. Een gewone listener op een bovenliggend element heeft bubbelen nodig. Om een shadow-grens over te steken is ook composed vereist. De standaardwaarden voor bubbles, composed en cancelable zijn allemaal false. DOM-standaard: Event-interface.
De volgende beslissingstabel is afgeleid van die propagatieregels. Er wordt uitgegaan van gewone listeners die zonder capture zijn geregistreerd en van handlers die de propagatie niet stoppen.
| Waar de aangepaste gebeurtenis begint | Waar de gebruiker luistert | Configuratie voor die route |
|---|---|---|
| Host van de component | Dezelfde host | Geen van beide vlaggen hoeft true te zijn |
| Host van de component | Gewoon bovenliggend element in dezelfde boom | bubbles: true |
| Interne shadow-knop | Gewoon bovenliggend element buiten die shadow-root | bubbles: true, composed: true |
| Host van de component binnen een andere shadow-boom | Gewoon bovenliggend element buiten de omvattende shadow-root | bubbles: true, composed: true |
In de demo verstuurt this.dispatchEvent(...) de melding vanaf de host. De melding hoeft daarom niet uit de eigen shadow-root van de component te komen. Met composed: true kan de melding ook een shadow-grens van een omvattende component passeren als deze tegel later wordt genest. Die bredere reikwijdte moet een bewuste keuze zijn voor de openbare interface. Documentatie van Event.composed.
Wijzig voor een gerichte proef alleen bubbles: true in bubbles: false, sla het bestand op en laad de pagina opnieuw. Na activering hoort de directe status te worden bijgewerkt, terwijl de bovenliggende status Parent listener: waiting blijft. Zet bubbelen daarna weer aan. Als je alleen composed in false verandert, blijven beide listeners in dit document werken, omdat geen van beide routes een shadow-grens passeert.
Behoud het gedrag bij initialisatie en opnieuw verbinden
Gebeurtenisbezorging kan aanvankelijk goed lijken, maar dubbele meldingen opleveren nadat een component is verplaatst of opnieuw verbonden. Houd de verantwoordelijkheden voor aanmaken, verbinden en opruimen expliciet.
De constructor roept eerst super() aan, initialiseert de status en koppelt een shadow-root. Het renderen gebeurt in connectedCallback() en wordt met de bestaande knopverwijzing beveiligd, zodat het maar één keer wordt uitgevoerd. Deze keuzes volgen de vereisten voor constructors in de HTML-standaard en de richtlijn om initialisatie die niet opnieuw mag plaatsvinden te beveiligen wanneer verbindingscallbacks opnieuw worden uitgevoerd. Vereisten voor constructors en reacties van aangepaste elementen.
De component registreert zijn interne kliklistener wanneer hij wordt verbonden en verwijdert die wanneer hij wordt losgekoppeld. Bij opnieuw verbinden gebruikt hij de bestaande knop en behoudt hij _count. Verbindings- en loskoppelcallbacks zijn levenscyclus-haken die worden aangeroepen wanneer elementen het document binnenkomen of verlaten. Aangepaste elementen gebruiken.
De opgeslagen functie _handleClick is van belang: bij verwijderen moet dezelfde functie worden opgegeven als bij registratie, met dezelfde capture-instelling. Een nieuwe arrowfunctie aanmaken in de aanroep voor het verwijderen zou niet verwijzen naar de geregistreerde listener. Documentatie van removeEventListener.
Als je in een grotere component listeners toevoegt aan window, document of een ander extern doel, zorg dan voor even expliciete opruiming van die listeners. Houd levenscycluswijzigingen gescheiden van de betekenis van de melding, zodat het opnieuw verbinden van het element niet per ongeluk een gebruikersactie aankondigt.
Ontbrekende of herhaalde meldingen opsporen
Gebruik de twee zichtbare statusregels om de fout te vinden voordat je eventvlaggen wijzigt. Zo kun je onderscheid maken tussen een probleem met de melding van de component en een probleem met de bezorging aan bovenliggende elementen.
- Geen van beide statussen verandert: vergelijk de gebeurtenisnaam bij het versturen en registreren en controleer vervolgens of de definitie van het aangepaste element en de knophandler worden uitgevoerd. In dit document worden de listeners van de gebruiker geregistreerd voordat iemand de knop activeert.
- Alleen de directe status verandert: controleer
bubbles. Kijk ook of een tussenliggende handler de propagatie stopt. - De bezorging mislukt alleen wanneer het element in een andere component is genest: bepaal welke shadow-grens de route kruist en controleer
composed. - De aantallen springen na opnieuw verbinden: controleer of listeners herhaaldelijk worden geregistreerd, vooral als er elke keer dat het element wordt verbonden nieuwe anonieme functies worden gemaakt.
Herhaalde anonieme functies zijn afzonderlijke listenerobjecten, ook als hun broncode identiek is. Een vaste functieverwijzing maakt registratie en opruiming eenvoudiger te begrijpen. addEventListener() onderscheidt ook de capture-optie van de listener, die bepaalt wanneer deze wordt uitgevoerd. Documentatie van addEventListener.
Een native click kan al buiten een shadow-boom terechtkomen, omdat door de browser verstuurde klikgebeurtenissen composed zijn. Een aparte aangepaste gebeurtenis is nuttig wanneer de gebruiker een benoemde componentactie en een vastgelegde gegevensinhoud nodig heeft. Verwerk niet beide meldingen als dezelfde toepassingsactie, tenzij die duplicatie opzettelijk is. Documentatie van Event.composed.
Houd ten slotte in het ontwerp van je gebeurtenissen de melding en toestemming om verder te gaan uit elkaar. Dit voorbeeld verhoogt de teller vóór het versturen en maakt de gebeurtenis niet annuleerbaar. Een gebruiker die preventDefault() aanroept, kan die update niet ongedaan maken. Voor een annuleerbaar verzoek is een expliciete implementatie nodig die de retourwaarde van dispatchEvent() controleert voordat de gevraagde actie wordt uitgevoerd. Retourwaarde van dispatchEvent.
Voor deze taak is de volledige overeenkomst klein: één handler voor de interne knop werkt de status bij, één benoemde aangepaste gebeurtenis wordt vanaf de host verstuurd en gebruikers lezen de gedocumenteerde gegevensinhoud uit. Zorg eerst dat de directe listener werkt en schakel daarna de propagatieroutes in die je omvattende interface daadwerkelijk nodig heeft.
Bronnen
- HTML-standaard — Aangepaste elementen — WHATWG.
- DOM-standaard — WHATWG.
- CustomEvent: constructor CustomEvent() — MDN Web Docs.
- EventTarget: methode dispatchEvent() — MDN Web Docs.
- Event: eigenschap composed — MDN Web Docs.
- Aangepaste elementen gebruiken — MDN Web Docs.
- EventTarget: methode addEventListener() — MDN Web Docs.
- EventTarget: methode removeEventListener() — MDN Web Docs.
- Werken met HTML-documenten in Teksteditor op de Mac — Apple.