Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Lazy image loading never defers: IntersectionObserver runs before the placeholder is sized

Aperta Adatta ai principianti
#9,438 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

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

0. Needs triage bug

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

  1. Enable the Photos app, so images carry photos-size metadata. Upload the images after enabling it.
  2. Create a document containing ~6 images of a few MB each, tall enough that most are several screens below the fold.
  3. In devtools, throttle the network to roughly 150 ms RTT / 10 Mbps.
  4. Open the document with the Network tab recording.
  5. 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 on main)
  • 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

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di nextcloud/text

Tutte le issue di nextcloud/text

Issue simili

Altre issue su JavaScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.