Gestore di eventi pesante: perché un clic richiede 500 ms e come risolverlo
Cosa succede
Quando un utente fa clic, il browser esegue il gestore di eventi fino in fondo prima di poter disegnare il frame successivo. Se il gestore fa centinaia di millisecondi di lavoro sincrono — filtrare un array grande, costruire DOM, fare il parsing di JSON — la pagina resta congelata esattamente per quel tempo. L'utente non vede nulla finché il gestore non ritorna.
È il problema di INP più comune e compare nella scomposizione dell'INP come una lunga fase di processing time.
Come riconoscerlo
- Il processing time domina la scomposizione dell'INP (input delay e presentation delay sono piccoli).
- In DevTools Performance, un task lungo parte proprio sull'evento ed è attribuito alla tua funzione gestore.
- La lentezza cresce con la dimensione dei dati: più righe/elementi, peggiore è la sensazione del clic.
Come risolverlo
- Aggiorna prima la UI (stato premuto, spinner), poi cedi il controllo al browser con scheduler.yield() (o setTimeout(0) come fallback) prima di fare il lavoro pesante.
- Suddividi i cicli grandi in blocchi, cedendo il controllo tra i blocchi così il browser può disegnare.
- Sposta il calcolo puro (parsing, filtraggio, diffing) in un Web Worker — il main thread invia solo l'input e riceve il risultato.
Problema
button.addEventListener("click", () => {
const results = filterRows(allRows) // 400 ms of sync work
renderTable(results) // user saw nothing until now
})
Soluzione
button.addEventListener("click", async () => {
button.classList.add("loading") // instant feedback, painted first
await scheduler.yield() // let the browser paint
const results = filterRows(allRows)
renderTable(results)
button.classList.remove("loading")
})
Scansiona il tuo sito gratis →