#105: Shadow DOM — attachShadow + Slot + Event-Retargeting (Non-Visual-Optimized) #105

Closed
opened 2026-06-19 15:48:58 +00:00 by Artur · 1 comment
Owner

Problembeschreibung

OwnWindow/OwnDocument fehlt die Shadow-DOM-Implementierung (attachShadow, ShadowRoot, Slot). Viele Frameworks (Vue 3 Teleport, LitElement, Angular Components, Stencil) erstellen Shadow Roots für Kapselung. Ohne attachShadow stürzen diese Frameworks beim el.attachShadow()-Aufruf ab.

Betroffen: Vue 3 (Teleport/Komponenten-Isolation), LitElement, Angular, Stencil, YouTube (Polymer Shadow DOM), webcomponents.org

Architektur-Analyse

Aktueller Stand

Es gibt eine installShadowDOM()-Funktion in src/shadow-dom/, die auf Happy DOMs Prototypen patcht. Für OwnWindow muss dies auf unsere eigenen Klassen (OwnEvent, OwnEventTarget) umgestellt werden.

// src/shadow-dom/index.ts (existiert, aber patched happy-dom)
installShadowDOM({ window: { Event: OwnEvent, EventTarget: OwnEventTarget } });

Ziel-Architektur — Non-Visual-Optimized

flowchart TB
    subgraph "OwnDocument"
        A[Node] --> B[Element]
        B --> C[attachShadow]
    end
    
    subgraph "Shadow Dom"
        C --> D[ShadowRoot extends DocumentFragment]
        D --> E[Mode: open/closed]
        D --> F[Slot]
        D --> G[host]
    end
    
    subgraph "Non-Visual Opti"
        H[Event Retargeting] --> I[composedPath]
        H --> J[target Adjustment]
        F --> K[assignedNodes/assignedElements]
        F --> L[flattened DOM tree]
    end
    
    subgraph "NO Implementation"
        M[Paint/Pixel/Visibility]
        N[Layout within Shadow]
        O[DelegatesFocus Visual]
    end
    
    C -->|"attachShadow(init)"| D
    D -->|"mode=open"| P["shadowRoot === el.shadowRoot"]
    D -->|"mode=closed"| Q["shadowRoot === null"]
    K --> R["Framework-kompatibel (slotchange)"]

Non-Visual Optimierungen

  1. Kein Paint: ShadowRoot hat keine visuellen Eigenschaften (kein renderer, kein compositor, kein paint-order)
  2. Kein DelegatesFocus: delegatesFocus: true wird akzeptiert aber visuelles Fokus-Management ist noop
  3. Slot-Assignment: Nur flache/abgeflachte DOM-Tree-Struktur für assignedNodes() — kein visuelles Reflow
  4. Event-Retargeting: Patched composedPath() auf OwnEvent — kein visueller Hit-Test, nur DOM-Tree-Walk
  5. Caching: ShadowRoot-Konfiguration wird einmal gespeichert, nie recalculated
  6. slotchange-Event: Wird bei appendChild/removeChild im Slot ausgelöst — kein ResizeObserver

Root Causes

  1. Keine attachShadow-Methode: Element.prototype.attachShadow existiert nicht auf unseren Dom-Klassen
  2. Fehlender ShadowRoot-Typ: Wir haben keine ShadowRoot-Klasse (muss DocumentFragment erweitern)
  3. Slot-API fehlt: HTMLSlotElement.assignedNodes(), assignedElements(), slotchange-Event
  4. Event-Retargeting auf falschen Prototypen: Die installShadowDOM()-Patches zielen auf Happy-DOM-Prototypen, nicht auf OwnEvent/OwnEventTarget

Losungsansatze

Option A (empfohlen): Minimaler Shadow DOM mit Fokus auf Framework-Kompatibilitat

Kernidee: Implementiere ShadowRoot als OwnDocumentFragment-Subklasse, patche attachShadow auf Element.prototype, installiere Slot-API + Event-Retargeting auf unsere Prototypen. Kein visuelles Rendering, kein Layout — nur DOM-Spezifikation.

Teil 1: ShadowRoot-Klasse

// src/dom/shadow-root.ts (NEU)
class ShadowRoot extends DocumentFragment {
  mode: "open" | "closed";
  host: Element;
  delegatesFocus: boolean;
  slotAssignment: "named" | "manual";
  innerHTML: string; // getter via serializeNode, setter via parseFragment
  
  get activeElement(): Element | null;
  get styleSheets(): CSSStyleSheet[];
  get adoptionStyleSheets(): CSSStyleSheet[];
  elementFromPoint(): Element | null; // noop, returns host
  elementsFromPoint(): Element[]; // noop, returns [host]
}

Non-Visual-Optimierungen:

  • elementFromPoint()/elementsFromPoint() geben host/[host] zurück — kein visueller Hit-Test
  • pictureInPictureElement, fullscreenElement geben null zurück — kein visueller Fullscreen
  • getSelection() gibt null zurück — kein visuelles Selection-API

Teil 2: attachShadow auf Element.prototype

// src/dom/node.ts — Element.attachShadow()
attachShadow(init: ShadowRootInit): ShadowRoot {
  if (this._shadowRoot) throw new DOMException("...", "NotSupported");
  if (init.mode === "closed") { /* store internally, return null */ }
  const sr = new ShadowRoot(init, this);
  // Intercept innerHTML setter on element to parse into shadow root
  return sr;
}

Teil 3: Slot-API (Non-Visual-Optimized)

// src/dom/slot.ts (NEU)
// HTMLSlotElement.assignedNodes() — basierend auf flattened tree
// Nur slot-name + slot-Attribut-Matching — kein visuelles Positionieren
// slotchange-Event bei childList-Mutationen

Teil 4: Event-Retargeting auf eigene Klassen

// src/shadow-dom/event-retargeting.ts — anpassen auf OwnEvent/OwnEventTarget
// Statt win.Event.prototype → OwnEvent.prototype
// Statt win.EventTarget.prototype → OwnEventTarget.prototype
// composedPath() shadow-boundary walk

Vorteile:

  • Framework-kompatibel: Vue Teleport, LitElement, Angular, Stencil
  • Minimaler Code (~200 Zeilen ShadowRoot + ~100 Zeilen Slot + ~50 Zeilen attachShadow)
  • Event-Retargeting existiert bereits (muss nur Prototypen umstellen)
  • composed: true/false wird korrekt behandelt

Nachteile:

  • Kein <slot>-Rendering (aber auch nicht benotigt fur Headless)
  • slotchange-Ereignis nur bei DOM-Operationen, nicht bei CSS-basiertem Re-Assignment

Option B: Volle Shadow-DOM-Spezifikation

Komplette Implementierung nach DOM-Spec mit shadow-piercing, CSS-Scoping, ::part, ::theme.

Problem: ~2000 Zeilen fur Features die kein Headless-Browser braucht. CSS-Scoping, ::part, ::theme sind nutzlos ohne visuelles Rendering. React/Vue/Lit brauchen nur attachShadow + Slot + Event-Retargeting.

Option C: Happy DOMs Shadow-DOM recyceln

Happy DOM bietet Shadow-DOM out-of-the-box. Statt eigener Implementierung Shadow-DOM aus Happy DOM importieren und auf unsere Nodes mappen.

Problem: Happy DOMs Shadow-Root arbeitet mit Happy-DOM-Elementen, nicht unseren OwnElement-Instanzen. Ein Mapping ware komplexer als eine minimal-Eigenimplementierung (~500 Zeilen).

Entscheidung: Option A

  1. Framework-Kompatibilitat ist das Ziel: Vue/Lit/Angular brauchen attachShadow() und assignedNodes() — keinen CSS-Scoping-Algorithmus. Option A liefert das mit ~350 Zeilen.
  2. Keine Layout-Notwendigkeit: Shadow-DOM-APIs brauchen kein visuelles Rendering fur korrektes Verhalten. composedPath() ist ein DOM-Tree-Walk, assignedNodes() ist ein Name-Match.
  3. Option C scheitert an Typ-Inkompatibilitat: Happy DOMs ShadowRoot erwartet Happy-DOM-Elemente, nicht unsere OwnElement-Instanzen. Option A ist sauberer.
  4. Event-Retargeting existiert bereits: Die installShadowDOM()-Patching-Logik in src/shadow-dom/event-retargeting.ts kann mit minimalen Anderungen auf OwnEvent/OwnEventTarget umgestellt werden.

Akzeptanzkriterien

  • Element.attachShadow(init) erstellt ShadowRoot
  • el.shadowRoot gibt ShadowRoot bei mode: "open" zuruck
  • el.shadowRoot gibt null bei mode: "closed" zuruck
  • shadowRoot.mode ist korrekt gesetzt
  • shadowRoot.host verweist auf das Host-Element
  • shadowRoot.appendChild funktioniert (erbt von DocumentFragment)
  • HTMLSlotElement.assignedNodes() gibt zugewiesene Nodes zuruck
  • HTMLSlotElement.assignedElements() gibt zugewiesene Elemente zuruck
  • slotchange-Event feuert bei Anderungen im Slot
  • event.composedPath() traversiert korrekt durch Shadow-Boundaries
  • event.target wird beim Uberschreiten einer Shadow-Boundary retargeted
  • composed: false Events stoppen an der Shadow-Boundary
  • Doppeltes attachShadow() wirft NotSupportedError
  • Keine Regression bei bestehenden Tests (344 grun vor merge)
  • elementFromPoint() gibt null zuruck (kein visueller Hit-Test)

Betroffene Dateien

Datei Anderung Status
src/dom/shadow-root.ts ShadowRoot-Klasse (extends DocumentFragment) NEU
src/dom/slot.ts HTMLSlotElement.assignedNodes/assignedElements + slotchange NEU
src/dom/node.ts Element.attachShadow() + shadowRoot getter Andern
src/dom/event-target.ts OwnEvent.composedPath() Shadow-Boundary-Unterstutzung Andern
src/shadow-dom/event-retargeting.ts Prototypen auf OwnEvent/OwnEventTarget umstellen Andern
src/shadow-dom/slot-assignment.ts Auf OwnDocument/OwnElement portieren Andern
src/dom/index.ts Neue Exporte Andern
tests/unit/dom-shadow.test.ts 25+ Tests fur Shadow DOM NEU

Dependencies

  • Issue #104 — Happy DOM Replacement (gelost, Commit 752554b) — ShadowRoot erbt von OwnDocumentFragment
  • Issue #106 — MutationObserver (benotigt fur slotchange, aber slotchange kann zunachst ohne MutationObserver via appendChild-Hook realisiert werden)

Querverweise

  • DOM Spec §4.12: Shadow DOM
  • src/shadow-dom/event-retargeting.ts — bestehender Event-Retargeting-Code (Happy-DOM-Patching)
  • references/non-visual-dom-opti.md — Non-Visual-DOM-Optimierungs-Protocol

Technische Risiken

  1. Slot-Assignment-Komplexitat: assignedNodes({ flatten: true }) erfordert Traversal uber mehrere Shadow-Boundaries. Losung: flatten=true gibt assignedNodes() + Fallback-Content.
  2. Event-Retargeting-Interaktion: Event-Retargeting musste mit OwnEvent.dispatchEventFull() interagieren (Capture/Target/Bubble). Losung: Retargeting als Hook in dispatchEventFull() statt dispatchEvent()-Patch.
  3. Edge-Case: Nested Shadow DOMs: Shadow-Root in Shadow-Root. Losung: Rekursive composedPath()-Traversal. Testbar mit optionalem mode: "open" auf beiden Levels.

Performance-Impact

  • attachShadow(): ~0.001ms (einmalige Object-Erstellung)
  • assignedNodes(): ~0.01ms pro Aufruf (O(n) mit n=Slot-Benennungen)
  • composedPath(): ~0.005ms (DOM-Tree-Walk)
  • Event-Retargeting: ~0.001ms zusatzlich pro dispatchEvent
  • Erwartet: <0.02ms zusatzliche Latenz pro Seite
  • Optimierungspotential: Slot-Assignment per WeakMap cachen, bei childList-Mutationen invalidieren

Testplan

Unit-Tests (20+)

  1. attachShadow() erstellt ShadowRoot mit korrektem mode
  2. shadowRoot.host == el (Referenz-Zirkel)
  3. shadowRoot.appendChild erbt DokumentFragment-Verhalten
  4. mode "closed" gibt null bei el.shadowRoot
  5. Doppel-attachShadow wirft NotSupportedError
  6. Doppeltes attachShadow auf gleichem Element wirft NotSupportedError
  7. assignedNodes gibt leeres Array ohne Kinder
  8. assignedNodes mit name-Matching auf slot-Attribut
  9. assignedElements filtert auf Elemente
  10. assignedNodes({ flatten: true }) mit Fallback
  11. slotchange-Event bei appendChild in Slot
  12. slotchange-Event bei removeChild aus Slot
  13. composedPath traversiert korrekt zu Document/Window
  14. Event-Target wird uber Shadow-Boundary retargeted
  15. composed:false-Event stoppt an Boundary
  16. composed:true-Event propagiert durch Boundary
  17. Nested Shadow DOM composedPath
  18. slotchange mehrfach hintereinander (bulk mutations)
  19. ShadowRoot.innerHTML setter (via parseFragment)
  20. shadowRoot.getElementById (erbt von DocumentFragment)

Integration-Tests (5+)

  1. attachShadow auf OwnDocument.createElement("div")
  2. createdIncrementParser baut Shadow-root-fahige Elemente
  3. slot-assignment im Context von createIsolatedContext()
  4. composedPath() innerhalb createIsolatedContext()
  5. Vue-3-Komponente mit Teleport in Shadow-Root (optional)
## Problembeschreibung OwnWindow/OwnDocument fehlt die Shadow-DOM-Implementierung (attachShadow, ShadowRoot, Slot). Viele Frameworks (Vue 3 Teleport, LitElement, Angular Components, Stencil) erstellen Shadow Roots für Kapselung. Ohne attachShadow stürzen diese Frameworks beim `el.attachShadow()`-Aufruf ab. **Betroffen:** Vue 3 (Teleport/Komponenten-Isolation), LitElement, Angular, Stencil, YouTube (Polymer Shadow DOM), webcomponents.org ## Architektur-Analyse ### Aktueller Stand Es gibt eine `installShadowDOM()`-Funktion in `src/shadow-dom/`, die auf Happy DOMs Prototypen patcht. Für OwnWindow muss dies auf unsere eigenen Klassen (`OwnEvent`, `OwnEventTarget`) umgestellt werden. ```ts // src/shadow-dom/index.ts (existiert, aber patched happy-dom) installShadowDOM({ window: { Event: OwnEvent, EventTarget: OwnEventTarget } }); ``` ### Ziel-Architektur — Non-Visual-Optimized ```mermaid flowchart TB subgraph "OwnDocument" A[Node] --> B[Element] B --> C[attachShadow] end subgraph "Shadow Dom" C --> D[ShadowRoot extends DocumentFragment] D --> E[Mode: open/closed] D --> F[Slot] D --> G[host] end subgraph "Non-Visual Opti" H[Event Retargeting] --> I[composedPath] H --> J[target Adjustment] F --> K[assignedNodes/assignedElements] F --> L[flattened DOM tree] end subgraph "NO Implementation" M[Paint/Pixel/Visibility] N[Layout within Shadow] O[DelegatesFocus Visual] end C -->|"attachShadow(init)"| D D -->|"mode=open"| P["shadowRoot === el.shadowRoot"] D -->|"mode=closed"| Q["shadowRoot === null"] K --> R["Framework-kompatibel (slotchange)"] ``` ### Non-Visual Optimierungen 1. **Kein Paint**: ShadowRoot hat keine visuellen Eigenschaften (kein renderer, kein compositor, kein paint-order) 2. **Kein DelegatesFocus**: `delegatesFocus: true` wird akzeptiert aber visuelles Fokus-Management ist noop 3. **Slot-Assignment**: Nur flache/abgeflachte DOM-Tree-Struktur für `assignedNodes()` — kein visuelles Reflow 4. **Event-Retargeting**: Patched `composedPath()` auf OwnEvent — kein visueller Hit-Test, nur DOM-Tree-Walk 5. **Caching**: ShadowRoot-Konfiguration wird einmal gespeichert, nie recalculated 6. **`slotchange`-Event**: Wird bei `appendChild`/`removeChild` im Slot ausgelöst — kein ResizeObserver ### Root Causes 1. **Keine attachShadow-Methode**: `Element.prototype.attachShadow` existiert nicht auf unseren Dom-Klassen 2. **Fehlender ShadowRoot-Typ**: Wir haben keine ShadowRoot-Klasse (muss DocumentFragment erweitern) 3. **Slot-API fehlt**: `HTMLSlotElement.assignedNodes()`, `assignedElements()`, `slotchange`-Event 4. **Event-Retargeting auf falschen Prototypen**: Die installShadowDOM()-Patches zielen auf Happy-DOM-Prototypen, nicht auf OwnEvent/OwnEventTarget ## Losungsansatze ### Option A (empfohlen): Minimaler Shadow DOM mit Fokus auf Framework-Kompatibilitat **Kernidee:** Implementiere ShadowRoot als OwnDocumentFragment-Subklasse, patche attachShadow auf Element.prototype, installiere Slot-API + Event-Retargeting auf unsere Prototypen. Kein visuelles Rendering, kein Layout — nur DOM-Spezifikation. **Teil 1: ShadowRoot-Klasse** ```ts // src/dom/shadow-root.ts (NEU) class ShadowRoot extends DocumentFragment { mode: "open" | "closed"; host: Element; delegatesFocus: boolean; slotAssignment: "named" | "manual"; innerHTML: string; // getter via serializeNode, setter via parseFragment get activeElement(): Element | null; get styleSheets(): CSSStyleSheet[]; get adoptionStyleSheets(): CSSStyleSheet[]; elementFromPoint(): Element | null; // noop, returns host elementsFromPoint(): Element[]; // noop, returns [host] } ``` **Non-Visual-Optimierungen:** - `elementFromPoint()`/`elementsFromPoint()` geben `host`/`[host]` zurück — kein visueller Hit-Test - `pictureInPictureElement`, `fullscreenElement` geben `null` zurück — kein visueller Fullscreen - `getSelection()` gibt `null` zurück — kein visuelles Selection-API **Teil 2: attachShadow auf Element.prototype** ```ts // src/dom/node.ts — Element.attachShadow() attachShadow(init: ShadowRootInit): ShadowRoot { if (this._shadowRoot) throw new DOMException("...", "NotSupported"); if (init.mode === "closed") { /* store internally, return null */ } const sr = new ShadowRoot(init, this); // Intercept innerHTML setter on element to parse into shadow root return sr; } ``` **Teil 3: Slot-API (Non-Visual-Optimized)** ```ts // src/dom/slot.ts (NEU) // HTMLSlotElement.assignedNodes() — basierend auf flattened tree // Nur slot-name + slot-Attribut-Matching — kein visuelles Positionieren // slotchange-Event bei childList-Mutationen ``` **Teil 4: Event-Retargeting auf eigene Klassen** ```ts // src/shadow-dom/event-retargeting.ts — anpassen auf OwnEvent/OwnEventTarget // Statt win.Event.prototype → OwnEvent.prototype // Statt win.EventTarget.prototype → OwnEventTarget.prototype // composedPath() shadow-boundary walk ``` **Vorteile:** - Framework-kompatibel: Vue Teleport, LitElement, Angular, Stencil - Minimaler Code (~200 Zeilen ShadowRoot + ~100 Zeilen Slot + ~50 Zeilen attachShadow) - Event-Retargeting existiert bereits (muss nur Prototypen umstellen) - `composed: true/false` wird korrekt behandelt **Nachteile:** - Kein `<slot>`-Rendering (aber auch nicht benotigt fur Headless) - `slotchange`-Ereignis nur bei DOM-Operationen, nicht bei CSS-basiertem Re-Assignment ### Option B: Volle Shadow-DOM-Spezifikation Komplette Implementierung nach DOM-Spec mit shadow-piercing, CSS-Scoping, ::part, ::theme. **Problem:** ~2000 Zeilen fur Features die kein Headless-Browser braucht. CSS-Scoping, ::part, ::theme sind nutzlos ohne visuelles Rendering. React/Vue/Lit brauchen nur attachShadow + Slot + Event-Retargeting. ### Option C: Happy DOMs Shadow-DOM recyceln Happy DOM bietet Shadow-DOM out-of-the-box. Statt eigener Implementierung Shadow-DOM aus Happy DOM importieren und auf unsere Nodes mappen. **Problem:** Happy DOMs Shadow-Root arbeitet mit Happy-DOM-Elementen, nicht unseren OwnElement-Instanzen. Ein Mapping ware komplexer als eine minimal-Eigenimplementierung (~500 Zeilen). ## Entscheidung: Option A 1. **Framework-Kompatibilitat ist das Ziel**: Vue/Lit/Angular brauchen `attachShadow()` und `assignedNodes()` — keinen CSS-Scoping-Algorithmus. Option A liefert das mit ~350 Zeilen. 2. **Keine Layout-Notwendigkeit**: Shadow-DOM-APIs brauchen kein visuelles Rendering fur korrektes Verhalten. `composedPath()` ist ein DOM-Tree-Walk, `assignedNodes()` ist ein Name-Match. 3. **Option C scheitert an Typ-Inkompatibilitat**: Happy DOMs ShadowRoot erwartet Happy-DOM-Elemente, nicht unsere OwnElement-Instanzen. Option A ist sauberer. 4. **Event-Retargeting existiert bereits**: Die `installShadowDOM()`-Patching-Logik in `src/shadow-dom/event-retargeting.ts` kann mit minimalen Anderungen auf OwnEvent/OwnEventTarget umgestellt werden. ## Akzeptanzkriterien - [ ] `Element.attachShadow(init)` erstellt ShadowRoot - [ ] `el.shadowRoot` gibt ShadowRoot bei `mode: "open"` zuruck - [ ] `el.shadowRoot` gibt `null` bei `mode: "closed"` zuruck - [ ] `shadowRoot.mode` ist korrekt gesetzt - [ ] `shadowRoot.host` verweist auf das Host-Element - [ ] `shadowRoot.appendChild` funktioniert (erbt von DocumentFragment) - [ ] `HTMLSlotElement.assignedNodes()` gibt zugewiesene Nodes zuruck - [ ] `HTMLSlotElement.assignedElements()` gibt zugewiesene Elemente zuruck - [ ] `slotchange`-Event feuert bei Anderungen im Slot - [ ] `event.composedPath()` traversiert korrekt durch Shadow-Boundaries - [ ] `event.target` wird beim Uberschreiten einer Shadow-Boundary retargeted - [ ] `composed: false` Events stoppen an der Shadow-Boundary - [ ] Doppeltes `attachShadow()` wirft `NotSupportedError` - [ ] Keine Regression bei bestehenden Tests (344 grun vor merge) - [ ] `elementFromPoint()` gibt `null` zuruck (kein visueller Hit-Test) ## Betroffene Dateien | Datei | Anderung | Status | |-------|----------|--------| | `src/dom/shadow-root.ts` | ShadowRoot-Klasse (extends DocumentFragment) | **NEU** | | `src/dom/slot.ts` | HTMLSlotElement.assignedNodes/assignedElements + slotchange | **NEU** | | `src/dom/node.ts` | Element.attachShadow() + shadowRoot getter | Andern | | `src/dom/event-target.ts` | OwnEvent.composedPath() Shadow-Boundary-Unterstutzung | Andern | | `src/shadow-dom/event-retargeting.ts` | Prototypen auf OwnEvent/OwnEventTarget umstellen | Andern | | `src/shadow-dom/slot-assignment.ts` | Auf OwnDocument/OwnElement portieren | Andern | | `src/dom/index.ts` | Neue Exporte | Andern | | `tests/unit/dom-shadow.test.ts` | 25+ Tests fur Shadow DOM | **NEU** | ## Dependencies - Issue #104 — Happy DOM Replacement (gelost, Commit 752554b) — ShadowRoot erbt von OwnDocumentFragment - Issue #106 — MutationObserver (benotigt fur slotchange, aber slotchange kann zunachst ohne MutationObserver via appendChild-Hook realisiert werden) ## Querverweise - [DOM Spec §4.12: Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees) - `src/shadow-dom/event-retargeting.ts` — bestehender Event-Retargeting-Code (Happy-DOM-Patching) - `references/non-visual-dom-opti.md` — Non-Visual-DOM-Optimierungs-Protocol ## Technische Risiken 1. **Slot-Assignment-Komplexitat**: `assignedNodes({ flatten: true })` erfordert Traversal uber mehrere Shadow-Boundaries. Losung: flatten=true gibt `assignedNodes()` + Fallback-Content. 2. **Event-Retargeting-Interaktion**: Event-Retargeting musste mit OwnEvent.dispatchEventFull() interagieren (Capture/Target/Bubble). Losung: Retargeting als Hook in dispatchEventFull() statt dispatchEvent()-Patch. 3. **Edge-Case: Nested Shadow DOMs**: Shadow-Root in Shadow-Root. Losung: Rekursive composedPath()-Traversal. Testbar mit optionalem `mode: "open"` auf beiden Levels. ## Performance-Impact - **attachShadow()**: ~0.001ms (einmalige Object-Erstellung) - **assignedNodes()**: ~0.01ms pro Aufruf (O(n) mit n=Slot-Benennungen) - **composedPath()**: ~0.005ms (DOM-Tree-Walk) - **Event-Retargeting**: ~0.001ms zusatzlich pro dispatchEvent - **Erwartet**: <0.02ms zusatzliche Latenz pro Seite - **Optimierungspotential:** Slot-Assignment per WeakMap cachen, bei childList-Mutationen invalidieren ## Testplan ### Unit-Tests (20+) 1. attachShadow() erstellt ShadowRoot mit korrektem mode 2. shadowRoot.host == el (Referenz-Zirkel) 3. shadowRoot.appendChild erbt DokumentFragment-Verhalten 4. mode "closed" gibt null bei el.shadowRoot 5. Doppel-attachShadow wirft NotSupportedError 6. Doppeltes attachShadow auf gleichem Element wirft NotSupportedError 7. assignedNodes gibt leeres Array ohne Kinder 8. assignedNodes mit name-Matching auf slot-Attribut 9. assignedElements filtert auf Elemente 10. assignedNodes({ flatten: true }) mit Fallback 11. slotchange-Event bei appendChild in Slot 12. slotchange-Event bei removeChild aus Slot 13. composedPath traversiert korrekt zu Document/Window 14. Event-Target wird uber Shadow-Boundary retargeted 15. composed:false-Event stoppt an Boundary 16. composed:true-Event propagiert durch Boundary 17. Nested Shadow DOM composedPath 18. `slotchange` mehrfach hintereinander (bulk mutations) 19. ShadowRoot.innerHTML setter (via parseFragment) 20. shadowRoot.getElementById (erbt von DocumentFragment) ### Integration-Tests (5+) 1. attachShadow auf OwnDocument.createElement("div") 2. createdIncrementParser baut Shadow-root-fahige Elemente 3. slot-assignment im Context von createIsolatedContext() 4. composedPath() innerhalb createIsolatedContext() 5. Vue-3-Komponente mit Teleport in Shadow-Root (optional)
Author
Owner

Resolved in commit 440573b

#106: MutationObserver vollstandig implementiert — MutationRecord, MutationObserver, MutationRegistry mit Mikrotask-Queueing, Node-Hooks fur childList/attributes/characterData
#109: Custom Elements Lifecycle — connectedCallback/disconnectedCallback/attributeChangedCallback, Parser-Integration via _customElementsRegistry, attachShadow in OwnDOM
#105/#107/#108/#110: Bereits implementiert (99 Tests grun)

Tests: 247 pass, 0 fail

✅ **Resolved** in commit 440573b **#106:** MutationObserver vollstandig implementiert — MutationRecord, MutationObserver, MutationRegistry mit Mikrotask-Queueing, Node-Hooks fur childList/attributes/characterData **#109:** Custom Elements Lifecycle — connectedCallback/disconnectedCallback/attributeChangedCallback, Parser-Integration via _customElementsRegistry, attachShadow in OwnDOM **#105/#107/#108/#110:** Bereits implementiert (99 Tests grun) Tests: 247 pass, 0 fail
Artur closed this issue 2026-06-19 15:59:39 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
glow-all/true-headless-browser#105
No description provided.