Cómo pegar imágenes

Harry Theodoulou
Harry Theodoulou

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);
  }
});

Navegadores compatibles

  • 86
  • 79
  • 13.1

Origen

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.
    }
  }
});

Navegadores compatibles

  • 66
  • 79
  • 63
  • 13.1

Origen

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);
};