Lettura selettiva del formato: un valore predefinito migliore per l'API Async Clipboard

Prashant Singh
Prashant Singh

Data di pubblicazione: 3 luglio 2026

Se hai mai utilizzato un editor di documenti, un'app per fogli di lavoro o qualsiasi app in cui l'incollatura di contenuti dagli appunti è un'interazione principale, sai che l'aspettativa dell'utente è che l'incollatura sia istantanea.

Purtroppo, in molti casi, le app web leggono più dati degli appunti di quelli di cui hanno bisogno, il che può rendere l'incollatura dei contenuti più lenta del dovuto, sprecare cicli della CPU e utilizzare la memoria per i dati di cui l'app non ha mai bisogno.

La lettura selettiva del formato risolve questo problema di prestazioni modificando l' API Async Clipboard per soddisfare meglio le aspettative degli utenti e fornire agli sviluppatori ciò di cui hanno bisogno: leggere solo i formati di cui l'app ha effettivamente bisogno.

Il problema: leggere in anticipo tutti i formati di dati

Quando un utente copia da qualsiasi app, ad esempio Excel, Documenti Google o Photoshop, l'app di solito scrive diverse rappresentazioni degli stessi dati negli appunti di sistema: testo normale, HTML, immagine e, a volte, formati personalizzati. L'app web che legge i dati (quando l'utente incolla dagli appunti) raramente ha bisogno di tutte queste rappresentazioni. Un editor di fogli di lavoro potrebbe volere HTML. Un editor di testo potrebbe voler esporre un comando "Incolla come testo normale". Un editor di codice potrebbe voler solo il testo e nient'altro.

Finora, navigator.clipboard.read() recuperava ogni rappresentazione disponibile dei dati dagli appunti di sistema, eseguiva la sanificazione (che può richiedere tempo, soprattutto con payload HTML o immagine di grandi dimensioni) e conservava i byte in memoria, anche per i formati che l'app non avrebbe mai utilizzato. Più grande è il payload inutilizzato, più tempo l'utente doveva attendere e più memoria consumava la scheda, proprio quando la reattività dell'incollatura è più importante.

La soluzione: enumerare solo, poi leggere on demand

A partire da Chrome ed Edge 149, ClipboardItem gli oggetti restituiti dal metodo navigator.clipboard.read() descrivono solo cosa c'è negli appunti, anziché fornire subito l'accesso ai dati. I byte effettivi per un determinato formato vengono letti dagli appunti solo quando chiami ClipboardItem.getType(mimeType). La superficie dell'API è invariata. Lo stesso codice che hai scritto ieri funziona ancora oggi, offrendoti vantaggi immediati in termini di prestazioni:

// Enumerate the available MIME types.
// No payload is fetched yet.
const items = await navigator.clipboard.read();

for (const item of items) {
  if (item.types.includes('text/html')) {
    // Only read the HTML data.
    // The data is fetched (and sanitized) here only.
     const blob = await item.getType('text/html');
     useThePastedHtml(await blob.text());
     break;
   }

   // text/plain, image/png, or any other custom formats
   // stored in the clipboard are never read.
}

Un altro vantaggio in termini di prestazioni è che la memorizzazione nella cache è automatica. La chiamata di getType('text/html') due volte sullo stesso ClipboardItem legge gli appunti di sistema una sola volta e restituisce il Blobmemorizzato nella cache alla seconda chiamata.

I vantaggi

Latenza. Il lavoro che doveva essere svolto quando veniva chiamato navigator.clipboard.read(), come la copia di byte tra processi, la sanificazione di HTML o la decodifica delle immagini, ora viene eseguito solo per i formati di cui l'app ha effettivamente bisogno. Le app che leggono un solo formato ottengono la maggior parte del risparmio. Più grandi sono i formati che salti, maggiore è il guadagno. HTML è il formato che beneficia maggiormente perché la sanificazione HTML è costosa.

Memoria. In precedenza, la chiamata di navigator.clipboard.read() quando gli appunti contenevano un'immagine di 50 MB significava che 50 MB dovevano essere utilizzati nella memoria del renderer della pagina web fino al rilascio di ClipboardItem, anche se l'app aveva bisogno solo della rappresentazione text/plain dei dati. Ora, solo i formati che l'app legge quando chiama ClipboardItem.getType() utilizzano la memoria nel processo di rendering. Per le app in cui l'incollatura è comune, come editor di documenti, compositori di email o strumenti di progettazione, questo riduce significativamente l'utilizzo della memoria durante l'incollatura, soprattutto nelle sessioni con più schede in cui diversi editor potrebbero leggere gli appunti in parallelo.

Nessuna attivazione. Tutti i siti web usufruiscono automaticamente di questi vantaggi. Non devi eseguire la migrazione del codice o il codice di gating dietro la logica di rilevamento delle funzionalità.

Una modifica del comportamento da tenere presente

La lettura successiva dei dati introduce una leggera modifica del comportamento: un oggetto ClipboardItem non è più uno snapshot bloccato degli appunti al momento della risoluzione della chiamata a navigator.clipboard.read().

Quando viene chiamato read() e restituisce un ClipboardItem, questo elemento è solo un handle lazy per i dati negli appunti di sistema. Se i contenuti degli appunti cambiano tra la chiamata a read() e una chiamata successiva a getType(), la chiamata verrà rifiutata con un InvalidStateError. Questo vale anche per un formato che hai letto correttamente in precedenza sullo stesso ClipboardItem.

// Read from the clipboard.
const items = await navigator.clipboard.read();
// Get the first ClipboardItem.
const item = items[0];

// Actually fetch the data now, which works fine.
const text = await item.getType('text/plain');

// The user writes something new to the clipboard
// by copying from another app, or your own code writes to
// the clipboard again.
await navigator.clipboard.writeText('something new');

// Fetching the data from the earlier ClipboardItem
// rejects with InvalidStateError, because the contents
// of the clipboard changed.
const html = await item.getType('text/html');

Questo accade perché la restituzione di dati che non riflettono più gli appunti sarebbe fonte di confusione e porterebbe a problemi difficili da eseguire il debug.

In pratica, la modifica è invisibile alla maggior parte del codice, perché le app in genere chiamano read() e getType() in sequenza all'interno dello stesso gestore di incollatura. Tuttavia, potresti voler controllare i casi in cui il codice mantiene un ClipboardItem tra i limiti asincroni.

Ecco un esempio in cui questa modifica potrebbe diventare un problema: memorizzazione nella cache degli oggetti ClipboardItem su window, visualizzazione in un'interfaccia utente della cronologia degli appunti o attesa dell'interazione dell'utente prima di chiamare getType(). Per evitare problemi con uno scenario di questo tipo:

  • Esegui il wrapping delle chiamate getType() nei blocchi try / catch e tratta InvalidStateError come un segnale che indica che gli appunti sono stati spostati e che il codice deve essere letto di nuovo.
  • Se devi conservare i dati degli appunti, copia il Blob nella tua struttura non appena esegui getType(). Non trattare ClipboardItem come spazio di archiviazione a lungo termine.
  • Utilizza l' clipboardChange evento per invalidare in modo proattivo gli handle obsoleti.

Standard e supporto del browser

La lettura selettiva del formato è una modifica della specifica delle API Clipboard guidata dal gruppo di lavoro Web Editing e dal team della piattaforma web Microsoft Edge.

Anche Safari supporta questo comportamento e Firefox ha indicato una posizione standard positiva e l'intenzione di allinearsi.

Un miglioramento silenzioso, ma significativo, delle prestazioni e dell'interoperabilità per l'API Clipboard, con Chromium che ora converge verso lo stesso modello.