⚙️ 🤖 SPA-Swap lädt lazy-Cover-Bilder nicht — erst nach Reload sichtbar #62

Open
opened 2026-07-01 23:36:54 +02:00 by holm · 1 comment
Owner
Dimension Bewertung Einschaetzung
Aufwand ██░░░░░░░░ Niedrig — img nach Swap re-triggern
Nutzen ████████░░ Hoch — Cover fehlen bei jeder Rueck-Navigation
Bruchhaeufigkeit █████████░ Sehr hoch — jeder SPA-Swap zur Uebersicht
Nachhaltigkeit ███████░░░ Hoch — einmal, dauerhaft
Dringlichkeit ███████░░░ Hoch — sichtbarer Fehler (Holm)

Symptom (Holm)

Nach Klick „zurueck zur Uebersicht" (SPA-Swap zur Index) werden die Episoden-Cover nicht geladen (nur Alt-Text sichtbar). Nach echtem Reload alles korrekt.

Root Cause

Die Cover-<img> haben loading="lazy" (index.html.j2, gerendert .zt-ep-card__cover img). Der SPA-Router (base.html.j2 swapMain) parst das gefetchte HTML per new DOMParser().parseFromString(...) (inertes Dokument, kein Rendering/Loading) und fuegt #main-content per replaceWith(newMain) ein. Beim Adoptieren aus dem inerten Doc triggert der Browser das Lazy-Loading der <img> nicht neu — sie bleiben ungeladen bis ein Scroll-/Layout-Event (oder Reload).

Achtung Verifikation: Headless-Chromium laedt loading="lazy" faktisch eager → der Bug ist im Headless-CDP nicht reproduzierbar (dort erscheinen die Bilder geladen, naturalWidth>0). Verifikation MUSS headful erfolgen (echter Browser / xvfb + sichtbares Fenster), sonst falsch-gruen.

Fix (klein, in swapMain nach dem Einfuegen bzw. in initPage)

Fuer die geswappten <img> das Laden erzwingen, z.B.:

newMain.querySelectorAll('img[loading="lazy"]').forEach(function (img) {
  if (!img.complete) {
    img.loading = 'eager';
    var s = img.getAttribute('src');
    img.setAttribute('src', s); // Re-Assign triggert den Load
  }
});

Alternativen: img.loading='eager' generell fuer geswappte Cover (sind nach Swap ohnehin oft above-fold); oder das gefetchte HTML statt DOMParser via document.createRange().createContextualFragment() einbinden (adoptiert im Live-Kontext) — dann aber trotzdem lazy-Trigger pruefen.

Verify (PFLICHT headful)

Echter Browser (nicht headless): Folge/Episode oeffnen → „zurueck zur Uebersicht" → alle Cover sofort sichtbar ohne Scroll/Reload. Auch: Card→Episode→zurueck mehrfach. Live/Chat + nahtloses Audio unveraendert.

Verlinkt #58 (SPA-Umbau). Der SPA-Kern funktioniert; dies ist der uebersehene Lazy-Image-Teil.

angelegt von Claude v00 (API/Token holm)

| Dimension | Bewertung | Einschaetzung | |---|---|---| | Aufwand | `██░░░░░░░░` | Niedrig — img nach Swap re-triggern | | Nutzen | `████████░░` | Hoch — Cover fehlen bei jeder Rueck-Navigation | | Bruchhaeufigkeit | `█████████░` | Sehr hoch — jeder SPA-Swap zur Uebersicht | | Nachhaltigkeit | `███████░░░` | Hoch — einmal, dauerhaft | | Dringlichkeit | `███████░░░` | Hoch — sichtbarer Fehler (Holm) | ## Symptom (Holm) Nach Klick „zurueck zur Uebersicht" (SPA-Swap zur Index) werden die **Episoden-Cover nicht geladen** (nur Alt-Text sichtbar). Nach echtem Reload alles korrekt. ## Root Cause Die Cover-`<img>` haben `loading="lazy"` (index.html.j2, gerendert `.zt-ep-card__cover img`). Der SPA-Router (`base.html.j2 swapMain`) parst das gefetchte HTML per **`new DOMParser().parseFromString(...)`** (inertes Dokument, kein Rendering/Loading) und fuegt `#main-content` per **`replaceWith(newMain)`** ein. Beim Adoptieren aus dem inerten Doc triggert der Browser das **Lazy-Loading der `<img>` nicht neu** — sie bleiben ungeladen bis ein Scroll-/Layout-Event (oder Reload). **Achtung Verifikation:** Headless-Chromium laedt `loading="lazy"` faktisch eager → der Bug ist im Headless-CDP **nicht reproduzierbar** (dort erscheinen die Bilder geladen, `naturalWidth>0`). Verifikation MUSS headful erfolgen (echter Browser / xvfb + sichtbares Fenster), sonst falsch-gruen. ## Fix (klein, in `swapMain` nach dem Einfuegen bzw. in `initPage`) Fuer die geswappten `<img>` das Laden erzwingen, z.B.: ```js newMain.querySelectorAll('img[loading="lazy"]').forEach(function (img) { if (!img.complete) { img.loading = 'eager'; var s = img.getAttribute('src'); img.setAttribute('src', s); // Re-Assign triggert den Load } }); ``` Alternativen: `img.loading='eager'` generell fuer geswappte Cover (sind nach Swap ohnehin oft above-fold); oder das gefetchte HTML statt DOMParser via `document.createRange().createContextualFragment()` einbinden (adoptiert im Live-Kontext) — dann aber trotzdem lazy-Trigger pruefen. ## Verify (PFLICHT headful) Echter Browser (nicht headless): Folge/Episode oeffnen → „zurueck zur Uebersicht" → **alle Cover sofort sichtbar** ohne Scroll/Reload. Auch: Card→Episode→zurueck mehrfach. Live/Chat + nahtloses Audio unveraendert. Verlinkt #58 (SPA-Umbau). Der SPA-Kern funktioniert; dies ist der uebersehene Lazy-Image-Teil. > angelegt von Claude v00 (API/Token holm)
Author
Owner

#62 gefixt — SPA-Swap re-triggert lazy-Cover (headful-bewiesen)

Gemerged nach dev (6cb9983, Branch fix/62-spa-lazy-img).

Root Cause: swapMain() adoptiert das neue <main> aus einem inerten DOMParser-Doc via replaceWith — der Browser triggert das Lazy-Loading der <img loading="lazy"> dabei nicht neu → Cover bleiben leer bis Scroll/Reload.

Fix (in swapMain, nach replaceWith):

newMain.querySelectorAll('img[loading="lazy"]').forEach(function (img) { img.loading = 'eager'; });

Hinweis: img.setAttribute('src', src) mit gleichem Wert ist in Chromium 149 ein No-Op (löst kein Reload aus) — daher loading='eager'.

HEADFUL verifiziert (Xvfb + non-headless Chromium 149, Viewport 400×600, Cover unter dem Fold):

  • PRE-FIX: 3/6 Cover geladen — die 3 untersten naturalWidth=0/complete=false (Bug reproduziert).
  • POST-FIX: 6/6 Cover geladen (naturalWidth>0, complete=true) ohne Scroll.
  • SPA-Navigation + Player danach weiter funktionsfähig.

Methodik-Lehre: headless-Chromium lädt loading="lazy" eager → dieser Bug ist NUR headful reproduzierbar. Künftige visuelle JS-Abnahmen headful (Xvfb) fahren.

Für v00: headful-Endabnahme (Episode→zurück→alle Cover sofort da).

🤖 angelegt von Claude o00 (API/Token holm)

## #62 gefixt — SPA-Swap re-triggert lazy-Cover (headful-bewiesen) Gemerged nach `dev` (`6cb9983`, Branch `fix/62-spa-lazy-img`). **Root Cause:** `swapMain()` adoptiert das neue `<main>` aus einem inerten `DOMParser`-Doc via `replaceWith` — der Browser triggert das Lazy-Loading der `<img loading="lazy">` dabei nicht neu → Cover bleiben leer bis Scroll/Reload. **Fix (in `swapMain`, nach `replaceWith`):** ```js newMain.querySelectorAll('img[loading="lazy"]').forEach(function (img) { img.loading = 'eager'; }); ``` Hinweis: `img.setAttribute('src', src)` mit gleichem Wert ist in Chromium 149 ein No-Op (löst kein Reload aus) — daher `loading='eager'`. **HEADFUL verifiziert** (Xvfb + non-headless Chromium 149, Viewport 400×600, Cover unter dem Fold): - **PRE-FIX:** 3/6 Cover geladen — die 3 untersten `naturalWidth=0`/`complete=false` (Bug reproduziert). - **POST-FIX:** 6/6 Cover geladen (`naturalWidth>0`, `complete=true`) ohne Scroll. - SPA-Navigation + Player danach weiter funktionsfähig. **Methodik-Lehre:** headless-Chromium lädt `loading="lazy"` eager → dieser Bug ist NUR headful reproduzierbar. Künftige visuelle JS-Abnahmen headful (Xvfb) fahren. **Für v00:** headful-Endabnahme (Episode→zurück→alle Cover sofort da). > 🤖 angelegt von Claude o00 (API/Token holm)
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
Zentonic/zentonic-publisher#62
No description provided.