Lazy image loading never defers: IntersectionObserver runs before the placeholder is sized
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 76/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- javascript
- Ambito
- frontend
Direzione di ricerca
Leggi src/nodes/ImageView.vue, in particolare mounted() e loadAttachmentMetadata(). L’issue indica che la soluzione proposta consiste nell’attendere il caricamento dei metadati prima di creare IntersectionObserver. Riproduci il problema con diverse immagini e verifica che inizialmente vengano caricate solo quelle vicine al viewport. Poi scorri per confermare che vengano caricate le restanti.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe the bug
Every image in a document is downloaded as soon as the document opens, regardless of whether it is on screen. On a remote server this makes a document with a handful of images take many seconds to become usable.
Text already implements lazy loading (see #4519). This bug asked for the fix and was closed in May 2025. So the IntersectionObserver is there in ImageView.vue. But it never gets the chance to defer anything.
In src/nodes/ImageView.vue (line 303 on main), mounted() calls loadAttachmentMetadata() without awaiting it:
mounted() {
this.attachmentPromise = this.$attachmentResolver.resolve(this.src)
this.loadAttachmentMetadata() // not awaited
...
this.$nextTick(() => { /* IntersectionObserver created here */ })
The placeholder's height comes from the photos-size metadata that this call resolves and the observer judges visibility from that height. Because it is not awaited, the observer runs while every figure is still a 16px stub stacked inside the viewport. Because of this, all of them count as visible and every image is loaded.
The fix is to use async mounted() and await this.loadAttachmentMetadata(). How we measured it is below.
Related: without the Photos app there is no photos-size metadata at all, so no placeholder is sized and lazy loading cannot work regardless of this bug. The code anticipates this ("bump up to warn when the Photos dependency is gone"), but the degradation currently is silent.
To Reproduce
- Enable the Photos app, so images carry
photos-sizemetadata. Upload the images after enabling it. - Create a document containing ~6 images of a few MB each, tall enough that most are several screens below the fold.
- In devtools, throttle the network to roughly 150 ms RTT / 10 Mbps.
- Open the document with the Network tab recording.
- Every image is requested immediately, including ones far below the fold.
Expected behavior
The code requests only the images at or near the viewport when the note is opened. The rest load as they are scrolled into view. This is my understand of what IntersectionObserver in ImageView.vue is written to accomplish.
Screenshots
Not applicable because the useful evidence is the timing, in the Logs section below.
Server details:
- Nextcloud version: 35.0.0 (35.0.0.10)
- PHP Version: 8.4.26
- Database: PostgreSQL 18.6
- Text app version: 9.0.0 (the same
mounted()is present onmain) - Photos app version: 8.0.0
Client details:
- OS: macOS
- Browser: Chromium (headless, via Playwright, for reproducible timing)
- Browser version: 153.0.8010.12
- Device: desktop
Logs
Nextcloud log (data/nextcloud.log)
Nothing relevant — this is entirely client-side.
Browser log
Network tab, document with six 2.5 MB images, 150 ms RTT / 10 Mbps:
BEFORE (stock Text 9.0.0)
- 6 image requests, all starting together, 15.15 MB total
- document ready: 14,384 ms
AFTER (await the metadata before creating the observer)
- 2 image requests, 5.05 MB total
- document ready: 6,426 ms
- scrolling through the rest: all 6 fetched and rendered, 0 failures
Polling each figure's height against the request start times shows the race:
- 790 ms 6 figures, heights [16,16,16,16,16,16] <- all inside the viewport
- 792 ms all six image requests start <- observer fires for all
- 804 ms 6 figures, heights [513,513,513,513,513,513] <- 4 of 6 now below the fold
The sizes that would have deferred four of the six arrive 12 ms after the decision has been made and the observers disconnected.
Diagnosis and measurements were produced with the help of Claude Code.
- Lingua principale
- JavaScript
- Stelle
- 665
- Fork
- 133
- Merge medio
- 1g 17h
- PR unite (30g)
- 172
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di nextcloud/text
-
0. Needs triage bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 67/100
I maintainer di solito rispondono entro 1 giorno
-
folder description not shows when using folder tree to navigateForse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Apertabug feature: folder description
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
1. to develop design call discuss discussion ux
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 58/100
nextcloud/text#9334 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Deleting a file from the editor's own "⋯" menu fails with 423 Locked: Text's own lock blocks itApertabug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 56/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di nextcloud/text
Issue simili
-
Pills' meter dots fade on bright and deep pillsForse già presa @wassim-azi l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
wassim-azi/agy-powerline#168 ·
I maintainer di solito rispondono entro 1 giorno
-
accounts bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
I maintainer di solito rispondono entro 1 giorno
-
area:docs effort:S impact:S kind:docs source:critic stage:ready
Difficoltà 1/5 1-3 ore Idoneità per principianti 91/100
Jason-Vaughan/TangleClaw#2693 ·
I maintainer di solito rispondono entro 1 giorno
-
infrastructure P2 security
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
-
Energy flow: add an "Other" row so the consumers add up to the consumption totalForse già presa @github-actions l’ha presa oggi. Apertaenhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
evcc-io/evcc#35512 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno