#108: Layout Engine — Element-Klassifikation + Default-Geometrie (Non-Visual) #108
Labels
No labels
bug
docs
feature
housekeeping
html-spec
performance
react-compat
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
glow-all/true-headless-browser#108
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problembeschreibung
offsetWidth,offsetHeight,clientWidth,clientHeight,getBoundingClientRect(),scrollTop,scrollLeftgeben aktuell fake/default-Werte zuruck, die nicht zwischen Elementtypen unterscheiden.Viele Websites und Frameworks lesen diese Werte:
el.getBoundingClientRect().top < window.innerHeightfur Image-Intersectioncontainer.scrollTop + container.clientHeight >= container.scrollHeightbutton.getBoundingClientRect()zum Positionierenelement.clientWidthfur responsive Layoutsel.clientHeight > 0fur VisibilityNON-VISUAL-OPTIMIERUNG: Wir brauchen KEINEN echten Layout-Engine (kein Reflow, kein Box-Model). Stattdessen liefern wir DEFAULT-Werte die "gut genug" fur JS-Checks sind, ohne jemals Pixel-gepixeltes Layout zu berechnen.
Architektur-Analyse
Aktueller Stand
Ziel-Architektur — Non-Visual-Layout
Non-Visual Optimierungen (Das Protokoll)
el.offsetParent !== nullnull(nie versteckt)el.clientHeight > 01(nie null)el.getBoundingClientRect().width1(nie 0)el.scrollTop > 00el.scrollHeight > el.clientHeightfalseel.scrollWidth > el.clientWidthfalserect.top < innerHeightel.offsetTop0el.getClientRects()[DOMRect](nie leer)Kernprinzip: Jede Zahl die JS lesen konnte, ist > 0 oder 0 — nie undefined/null. Jeder Rect, der abgefragt wird, ist im Viewport sichtbar.
Root Causes
getBoundingClientRect()gibt{0,0,0,0}fur ALLES — inklusive<body>und<html>die per Viewport simuliert werden mussendisplay: noneElemente mussen{0,0,0,0}zuruckgeben — aktuell immer{0,0,0,0}scrollTopwird nie gelesen (fur Infinite Scroll Checks)el.offsetParent !== nullfur SichtbarkeitLosungsansatze
Option A (empfohlen): Non-Visual-Layout per Default-Klassifikation
Kernidee: Ein Klassifikationssystem das jedem Element einen "Layout-Typ" zuweist (Block, Inline, Replaced, Document). Basierend auf Typ werden Default-Großen zuruckgegeben, ohne jemals Geometrie zu berechnen.
Teil 1: Element-Klassifikation
Teil 2: Layout-Default-Werte pro Klasse
Teil 3: Getters auf Element.prototype
Vorteile:
display: none→ 0,0,0,0 — korrektes Spec-VerhaltenclientHeight > 0→ true fur sichtbare ElementegetBoundingClientRectist immer im Viewport → IntersectionObserver-Simulation ok<html>/<body>haben Viewport-Große wie im echten BrowserNachteile:
scrollHeight == clientHeight→ Infinite-Scroll erkennt nie Scroll-OverflowoffsetLeft == 10(hartcodiert) — keine echte XY-PositionierungOption B: Text-Breite-Schatzung
Word-Count-basierte Großen-Schatzung:
offsetWidth = wordCount * 8 + padding(approx).Problem: Schatzung ist immer falsch (Font-Metrics fehlen). 50% Schatzfehler = gleiche Qualitat wie Option A, aber 100x aufwandiger. Die Praktikabilitat fur Headless ist identisch zu A.
Option C: Vollstandiger Layout-Engine
CSS-Box-Model mit Block-Formatting-Context, Inline-Formatting, Positioning.
Problem: Eine echte Layout-Engine ware ~5000 LOC und musste alle CSS-Features abdecken (Flexbox, Grid, Float, Position, Table). Fur Headless-Scraping ist dies Overkill — Frameworks checken nur
> 0oder!== null, sie brauchen keine Pixel-genauen Werte.Entscheidung: Option A
> 0,!== null,in viewport— nie Pixel-genaue Werte. Option A liefert korrekte binare Ergebnisse.<img>mussoffsetWidth > 0haben,<body>muss Viewport-Position haben. Option A klassifiziert korrekt.display: noneHandhabung: Option A erkenntdisplay: nonevia getComputedStyle und gibt{0,0,0,0}— korrektes Spec-Verhalten.Akzeptanzkriterien
document.documentElement.offsetHeight== Viewport-Hohe (768)document.body.offsetHeight== Viewport-Hoheel.offsetWidth> 0 fur sichtbare Block-Elementeel.offsetWidth== 0 beidisplay: noneel.getBoundingClientRect().width> 0 fur sichtbare Elementeel.getBoundingClientRect().width== 0 beidisplay: none<img>hat offsetWidth = 150 (Replaced-Element Default)<span>hat offsetWidth = 50 (Inline-Element Default)getClientRects()gibt nicht-leeres Array zuruckel.scrollTop== 0,el.scrollLeft== 0 (nie gescrollt)el.scrollWidth == el.clientWidth(nie overflow)el.clientHeight == el.offsetHeight(kein border)el.offsetParentgibtnullzuruck (nie position:absolute/relative/fixed)Betroffene Dateien
src/css/element-layout.tssrc/dom/node.tssrc/dom/window.tstests/unit/dom-layout.test.tsDependencies
display: noneDetection)Technische Risiken
display: noneErkennung: Muss via getComputedStyle funktionieren — zirkulare Abhangigkeit zwischen Layout (benotigt display:none) und CSSOM (benotigt Layout nicht). Losung:display: nonewird direkt ausel.style.displaygelesen, nicht via getComputedStyle.offsetParent-Semantik:offsetParentistnullfurposition: fixed,display: none,<body>,<html>. Unsernull-Ruckgabewert ist korrekt fur die meisten Framework-Checks.getClientRects()fur inline-Elemente: Sollte ein Array von DOMRects pro Textzeile zuruckgeben. Da wir kein Text-Wrapping haben, geben wir[el.getBoundingClientRect()]zuruck — korrekt fur die meisten Anwendungsfalle.Performance-Impact
_layoutClassCache auf Element, invalidiert beistyle.display-AnderungTestplan
Unit-Tests (20+)
Integration-Tests (3+)
el.offsetHeight > 0fur created Elemente✅ 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