JavaScript Clipboard API
Impara la moderna Clipboard API asincrona di JavaScript — copia e leggi testo con navigator.clipboard, gestisci dati ricchi con ClipboardItem, contesto sicuro e requisiti di autorizzazione.
La moderna Clipboard API, esposta tramite navigator.clipboard, è il modo asincrono e basato su promise per leggere e scrivere negli appunti di sistema. Sostituisce il vecchio approccio sincrono document.execCommand('copy') con metodi che restituiscono promise, quindi si abbinano naturalmente ad async/await. L'API è distinta — ma spesso usata insieme — dagli eventi clipboard cut, copy e paste: gli eventi ti permettono di intercettare le azioni dell'utente, mentre navigator.clipboard permette al tuo codice di avviare direttamente le operazioni sugli appunti.
Scrivere Testo negli Appunti
Il compito più comune è copiare testo. navigator.clipboard.writeText(text) accetta una string, la scrive negli appunti e restituisce una Promise che si risolve quando la scrittura ha successo e viene rifiutata in caso di errore.
Poiché restituisce una promise, il modo più pulito per usarla è all'interno di una funzione async con try...catch, così puoi fornire un feedback all'utente in entrambi i casi:
<button id="copyBtn">Copy</button>
<span id="status"></span>
<script>
const button = document.getElementById('copyBtn');
const status = document.getElementById('status');
button.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText('Hello from W3docs!');
status.textContent = 'Copied!';
} catch (err) {
status.textContent = 'Copy failed';
console.error('Clipboard write failed:', err);
}
});
</script>L'await mette in pausa fino a quando la scrittura negli appunti non si conclude. Se l'utente ha negato l'autorizzazione o la pagina non è in un contesto sicuro, la promise viene rifiutata e il blocco catch viene eseguito — ecco perché non dovresti mai dare per scontato che la copia sia riuscita.
Leggere Testo dagli Appunti
Per leggere il testo attuale degli appunti, chiama navigator.clipboard.readText(). Anche questa restituisce una promise, questa volta risolvendo con il contenuto testuale degli appunti:
<button id="pasteBtn">Read clipboard</button>
<p id="output"></p>
<script>
const button = document.getElementById('pasteBtn');
const output = document.getElementById('output');
button.addEventListener('click', async () => {
try {
const text = await navigator.clipboard.readText();
output.textContent = `Clipboard contains: ${text}`;
} catch (err) {
output.textContent = 'Could not read clipboard';
console.error('Clipboard read failed:', err);
}
});
</script>Leggere è molto più delicato che scrivere, perché espone ciò che l'utente ha copiato — possibilmente una password o altri dati privati. Per questo motivo i browser proteggono readText() in modo più rigido: richiede un gesto esplicito dell'utente, e alcuni browser mostrano una richiesta di autorizzazione la prima volta, o consentono la lettura solo quando la scheda della pagina è in primo piano.
Requisiti e Avvertenze
La Clipboard API ha diverse regole che, se ignorate, portano a rifiuti silenziosi. Tieni a mente questi aspetti ogni volta che la utilizzi.
La Clipboard API funziona solo in un contesto sicuro — ovvero HTTPS, oppure localhost durante lo sviluppo. Su una pagina http:// semplice, navigator.clipboard è solitamente undefined. Le chiamate richiedono generalmente anche un gesto dell'utente come un clic o la pressione di un tasto, quindi attivarle al caricamento della pagina fallirà. Le Permissions API governano le autorizzazioni clipboard-read e clipboard-write, e le letture potrebbero richiedere il consenso dell'utente. Poiché qualsiasi chiamata può essere rifiutata — autorizzazione negata, documento non in primo piano, o browser non supportato — racchiudi sempre le chiamate alla clipboard in try...catch.
La pagina deve anche avere il focus per molte operazioni sugli appunti. Se chiami readText() da, ad esempio, un setTimeout mentre l'utente ha cambiato scheda, aspettati un rifiuto con un errore "document is not focused". Nota anche che una lettura dagli appunti avviene solo dopo che l'utente ha messo a fuoco e interagito con la tua pagina.
Copiare Dati Ricchi con ClipboardItem
Il testo è il caso semplice. Per copiare dati non testuali — immagini, HTML, o più formati contemporaneamente — usa navigator.clipboard.write(), che accetta un array di object ClipboardItem. Ogni ClipboardItem associa tipi MIME ai loro dati (tipicamente un Blob).
L'esempio seguente recupera un'immagine, la racchiude in un ClipboardItem tramite il Blob risultante e la copia:
async function copyImage(url) {
try {
const response = await fetch(url);
const blob = await response.blob();
const item = new ClipboardItem({ [blob.type]: blob });
await navigator.clipboard.write([item]);
console.log('Image copied to clipboard');
} catch (err) {
console.error('Failed to copy image:', err);
}
}La chiave all'interno del costruttore ClipboardItem è il tipo MIME (qui blob.type, ad esempio 'image/png'), e il valore è il dato per quel tipo. Un singolo elemento può contenere più rappresentazioni — ad esempio sia 'text/plain' che 'text/html' — permettendo all'applicazione di destinazione di scegliere quella migliore.
La lettura di dati ricchi rispecchia questo con navigator.clipboard.read(), che si risolve in un array di object ClipboardItem che puoi esaminare per tipo:
async function readClipboardItems() {
const items = await navigator.clipboard.read();
for (const item of items) {
for (const type of item.types) {
const blob = await item.getType(type);
console.log(`Found ${type}`, blob);
}
}
}Il supporto del browser per i metodi con dati ricchi (write e read con ClipboardItem) è più limitato e meno uniforme rispetto ai metodi per il testo. I tipi MIME per le immagini variano in particolare da browser a browser — image/png è il più affidabile. Esegui il rilevamento delle funzionalità con if ('write' in navigator.clipboard) e torna a copiare testo o un URL quando i dati ricchi non sono disponibili.
Il Fallback Legacy
Prima dell'API asincrona, copiare significava selezionare un elemento e chiamare document.execCommand('copy'). Quel metodo è ora deprecato, ma funziona ancora nei browser più vecchi, quindi è utile esclusivamente come fallback. Il pattern tipico selezionava un <textarea> nascosto, poi eseguiva il comando:
function copyTextFallback(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy'); // deprecated
document.body.removeChild(textarea);
}Il codice moderno dovrebbe preferire l'API asincrona e ricorrere al fallback solo quando navigator.clipboard non è disponibile:
async function copyText(text) {
if (navigator.clipboard) {
await navigator.clipboard.writeText(text);
} else {
copyTextFallback(text);
}
}Utilizzi nel Mondo Reale
La Clipboard API alimenta molte interazioni piccole ma preziose che vedi ogni giorno:
- Pulsanti "Copia codice" sulle pagine di documentazione, così i lettori possono prendere uno snippet senza selezionarlo manualmente.
- Pulsanti "Copia link da condividere" che mettono un URL negli appunti per incollarlo in chat o email.
- Copia di output generato, come una password, una chiave API, o una citazione formattata.
Qualunque cosa tu copi, fornisci un feedback visibile. Un pulsante che copia silenziosamente lascia gli utenti incerti se l'operazione sia riuscita. Cambia l'etichetta in "Copiato!", mostra un breve toast, o aggiorna un elemento di stato adiacente — questo è anche un vantaggio per l'accessibilità, poiché gli utenti di screen reader non ricevono alcun segnale sugli appunti dal browser stesso.