Web Components
Definition: Web Components sind ein Set von W3C-Browser-Standards, die zusammen die Erstellung wiederverwendbarer, gekapselter HTML-Elemente ohne externe Abhängigkeiten ermöglichen. Die drei Kernstandards: Custom Elements (eigene HTML-Tags wie <product-card> registrieren via customElements.define()), Shadow DOM (gekapseltes DOM und CSS innerhalb eines Elements) und HTML Templates (<template>- und <slot>-Elemente für deklarative Inhaltsstruktur). Web Components sind nativ in allen modernen Browsern ohne Polyfills verfügbar. Sie werden u. a. von Google (Lit), Salesforce (Lightning Web Components) und GitHub (github-elements) eingesetzt. Für Marketing-Teams relevant: Web Components können GTM-Tracking erschweren, da Shadow DOM Event-Bubbling und querySelector-Zugriff einschränkt.
Erläuterung
Web Components lösen das Problem der Code-Wiederverwendbarkeit auf Browser-Ebene: Statt Framework-spezifische React- oder Vue-Komponenten zu bauen, die nur im jeweiligen Ökosystem funktionieren, erstellt man einmal einen Custom Element der in React, Vue, Angular oder purem HTML funktioniert. Lit (von Google) ist die populärste Library für Web Components: ~5 KB, reduziert Boilerplate erheblich. Der Hauptvorteil ist Framework-Unabhängigkeit – besonders relevant für Design Systems die von mehreren Teams mit verschiedenen Stacks genutzt werden.
Custom Elements
- Registrierung: customElements.define('mein-element', MeinElement); zweites Argument: Klasse die HTMLElement erweitert; Elementname muss Bindestrich enthalten (Namespace-Schutz: <mein-element> statt <meinelement>); customElements.whenDefined('mein-element').then() für asynchrones Warten auf Definition; customElements.get('mein-element') prüft ob bereits registriert
- Lifecycle-Callbacks: connectedCallback(): Element in DOM eingefügt (→ Initialisierung, Event-Listener hinzufügen); disconnectedCallback(): Element aus DOM entfernt (→ Cleanup, Event-Listener entfernen); attributeChangedCallback(name, oldValue, newValue): observedAttributes-Attribut geändert; constructor(): Vor DOM-Einfügung, nur minimale Initialisierung; adoptedCallback(): Element in neues Dokument verschoben (selten)
- Observed Attributes: static get observedAttributes() { return ['color', 'size']; } definiert welche Attribute auf Änderungen überwacht werden; attributeChangedCallback nur für gelistete Attribute; Properties vs. Attributes unterscheiden: Attribute: HTML-Strings; Properties: JavaScript-Werte auf DOM-Objekt; komplexe Daten (Arrays/Objects) als Properties übergeben, nicht als Attribute
- Autonomous vs. Customized Built-in Elements: Autonomous: <mein-element> erweitert HTMLElement direkt; Customized Built-in: <button is="mein-button"> erweitert HTMLButtonElement; Safari unterstützt Customized Built-ins nicht ohne Polyfill; Autonomous Custom Elements: vollständige Browser-Unterstützung in Chrome/Firefox/Safari/Edge
Shadow DOM
- Attach Shadow Root: this.attachShadow({mode: 'open'}) im constructor; mode 'open': this.shadowRoot von außen zugänglich (element.shadowRoot); mode 'closed': kein externer Zugriff (null zurückgegeben); Shadow Root: separater DOM-Baum innerhalb des Elements; CSS und JavaScript des Shadow DOM beeinflusst äußeren DOM nicht
- CSS-Enkapsulation: CSS im Shadow DOM gilt nur innerhalb des Shadow Root; globales CSS aus Light DOM dringt nicht ein (außer CSS Custom Properties / Variables); :host-Selector: stylt das Custom Element selbst von innen; :host([active]): konditionelles Host-Styling; ::slotted(p): stylt in Slots projizierte Inhalte aus Light DOM
- CSS Custom Properties als Theming-API: --mein-element-color: blue; im äußeren CSS definiert; var(--mein-element-color, black) im Shadow DOM verwendet; ermöglicht Theming ohne Shadow-DOM-Kapselung zu brechen; Standard-Pattern für konfigurierbare Design-System-Komponenten; Part-Attribut: element::part(knopf) für direktes externes Styling ausgewählter Shadow-DOM-Elemente
- Event-Retargeting: Events aus Shadow DOM: target-Property zeigt beim Bubbling durch Shadow-DOM-Grenze auf das Custom Element statt das interne Element; event.composedPath(): zeigt alle Elemente im Event-Pfad einschließlich Shadow DOM; composed: true bei eigenem Event-Dispatch für Shadow-DOM-Durchquerung nötig
HTML Templates und Slots
- template-Element: <template id="karten-template">...</template>; Inhalt wird nicht gerendert oder ausgeführt bis er geklont wird; document.getElementById('karten-template').content.cloneNode(true); performanter als innerHTML-String-Parsing; für sich wiederholende DOM-Strukturen ideal
- Slot-System: <slot></slot>: Standard-Slot für allen projizierten Inhalt; <slot name="titel"></slot>: benannter Slot; Verwendung: <mein-element><h2 slot="titel">Überschrift</h2></mein-element>; projizierter Inhalt bleibt im Light DOM (nicht physisch in Shadow DOM verschoben – nur visuell projiziert); Fallback-Inhalt innerhalb <slot> wird gezeigt wenn kein Inhalt projiziert wird
Framework-Integration und Lit
- Lit Library: Google-entwickelte Basis-Library für Web Components; LitElement erweitert HTMLElement mit reaktivem Property-System; lit-html: Tagged Template Literals für effizientes DOM-Rendering; ~5 KB gzipped; Reaktivität: @property()-Decorator löst automatisch Rerender bei Property-Änderung aus; deklaratives Template-Rendering mit html`...`
- React-Integration: React 18: verbesserte Custom-Element-Unterstützung; React übergibt Props als Attribute (Strings) statt Properties (JS-Werte) für unbekannte Elemente; Workaround: ref verwenden um Properties direkt zu setzen; @lit/react: Wrapper-Utility für React-freundliche Custom Elements; React 19: vollständige Custom-Element-Unterstützung mit Property-Binding
- Angular und Vue: Angular: CUSTOM_ELEMENTS_SCHEMA in NgModule; verhindert Fehler für unbekannte Elemente; Vue: compilerOptions.isCustomElement: tag => tag.includes('-') für Custom-Element-Erkennung; beide Frameworks: Property-Binding statt Attribute-Binding für komplexe Daten verwenden
Tracking-Implikationen (GTM/Analytics)
- Shadow DOM und GTM-Click-Trigger: GTM-Click-Trigger basiert auf Event-Delegation auf document-Ebene; Events aus Shadow DOM: Retargeting zeigt Custom Element als target statt internen Button; GTM {{Click Element}}-Variable: zeigt Custom Element, nicht internes <button>; CSS-Selektoren für GTM-Trigger: am Custom Element ansetzen (z. B. product-card) statt internen Elementen
- querySelector-Einschränkungen: document.querySelector('shadow-element button') findet keine Elemente im Shadow DOM; open mode: element.shadowRoot.querySelector('button') funktioniert; Analytics-Skripte die document-wide suchen: finden Shadow-DOM-Inhalte nicht; Tag-Manager und Analytics-Tools die auf DOM-Traversal basieren: benötigen Anpassungen
- Eigene Events dispatchen: this.dispatchEvent(new CustomEvent('kauf-abgeschlossen', {detail: {produktId: 123}, bubbles: true, composed: true})); bubbles: true + composed: true: Event durchquert Shadow-DOM-Grenze und bubblet durch Light DOM; GTM kann auf Custom Events via Custom-Event-Trigger hören; empfohlen als saubere Analytics-Integration-Schnittstelle