La forma moderna
Usa la API de Async Clipboard
Para leer imágenes desde el portapapeles del usuario de manera programática, es decir, después de hacer clic en un botón, puedes usar el método read()
de la API de Async Clipboard. Si aún no se otorgó permiso para leer desde el portapapeles, la llamada a navigator.clipboard.read()
lo solicitará en la primera llamada al método.
const pasteButton = document.querySelector('#paste-button');
pasteButton.addEventListener('click', async () => {
try {
const clipboardItems = await navigator.clipboard.read();
for (const clipboardItem of clipboardItems) {
const imageTypes = clipboardItem.types.find(type => type.startsWith('image/'))
for (const imageType of imageTypes) {
const blob = await clipboardItem.getType(imageType);
// Do something with the image blob.
}
}
} catch (err) {
console.error(err.name, err.message);
}
});
La forma clásica
Escucha el evento paste
La forma clásica de pegar imágenes es usar la API de Clipboard (síncrono), que te brinda acceso al campo clipboardData
dentro del evento Documento: paste
. Sin embargo, este método tiene limitaciones; por ejemplo, debido a que es síncrono, pegar grandes cantidades de datos puede bloquear la página.
document.addEventListener('paste', async (e) => {
e.preventDefault();
for (const clipboardItem of e.clipboardData.files) {
if (clipboardItem.type.startsWith('image/')) {
// Do something with the image file.
}
}
});
Mejora progresiva
En el caso de los navegadores que no admiten la API de Async Clipboard, es imposible acceder al portapapeles del usuario de manera programática (por ejemplo, cuando se hace clic en un botón). Por lo tanto, para acceder al portapapeles de un usuario en un evento paste
, puedes usar la API de Async Clipboard y recurrir a la API de portapapeles (síncrono).
Los objetos ClipboardItem
que provienen de navigator.clipboard.read
tienen un campo types
que es un array, y los objetos File
que provienen de event.clipboardData.files
tienen un campo type
que es una string. Puedes verificar condicionalmente cada uno de los campos type
o types
para las imágenes del portapapeles:
document.addEventListener('paste', async (e) => {
e.preventDefault();
const clipboardItems = typeof navigator?.clipboard?.read === 'function' ? await navigator.clipboard.read() : e.clipboardData.files;
for (const clipboardItem of clipboardItems) {
let blob;
if (clipboardItem.type?.startsWith('image/')) {
// For files from `e.clipboardData.files`.
blob = clipboardItem
// Do something with the blob.
} else {
// For files from `navigator.clipboard.read()`.
const imageTypes = clipboardItem.types?.filter(type => type.startsWith('image/'))
for (const imageType of imageTypes) {
blob = await clipboardItem.getType(imageType);
// Do something with the blob.
}
}
}
});
Lecturas adicionales
- API de Clipboard en MDN
- Desbloquea el acceso al portapapeles en web.dev
Demostración
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🎉</text></svg>"
/>
<title>How to paste images</title>
</head>
<body>
<h1>How to paste images</h1>
<p>Hit <kbd>⌘</kbd> + <kbd>v</kbd> (for macOS) or <kbd>ctrl</kbd> + <kbd>v</kbd>
(for other operating systems) to paste images anywhere in this page.
</p>
</body>
</html>
CSS
:root {
color-scheme: dark light;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 1rem;
font-family: system-ui, sans-serif;
}
button {
display: block;
}
JS
document.addEventListener('paste', async (e) => {
e.preventDefault();
const clipboardItems = typeof navigator?.clipboard?.read === 'function' ? await navigator.clipboard.read() : e.clipboardData.files;
for (const clipboardItem of clipboardItems) {
let blob;
if (clipboardItem.type?.startsWith('image/')) {
// For files from `e.clipboardData.files`.
blob = clipboardItem
// Do something with the blob.
appendImage(blob);
} else {
// For files from `navigator.clipboard.read()`.
const imageTypes = clipboardItem.types?.filter(type => type.startsWith('image/'))
for (const imageType of imageTypes) {
blob = await clipboardItem.getType(imageType);
// Do something with the blob.
appendImage(blob);
}
}
}
});
const appendImage = (blob) => {
const img = document.createElement('img');
img.src = URL.createObjectURL(blob);
document.body.append(img);
};