Как вставлять изображения

Современный способ

Использование API асинхронного буфера обмена

Чтобы прочитать изображения из буфера обмена пользователя программным способом, то есть после нажатия кнопки, можно использовать метод read() API Async Clipboard . Если разрешение на чтение из буфера обмена еще не предоставлено, вызов navigator.clipboard.read() запросит его при первом вызове метода.

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

Поддержка браузера

  • 86
  • 79
  • 13.1

Источник

Классический способ

Слушайте событие paste

Классический способ вставки изображений — использование (синхронного) API буфера обмена , который дает вам доступ к полю clipboardData внутри события Document: paste . Однако этот метод имеет ограничения, например, поскольку он синхронный, вставка больших объемов данных может заблокировать страницу.

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

Поддержка браузера

  • 66
  • 79
  • 63
  • 13.1

Источник

Прогрессивное улучшение

Для браузеров, которые не поддерживают API Async Clipboard, невозможно программно получить доступ к буферу обмена пользователя (например, по нажатию кнопки). Таким образом, для доступа к буферу обмена пользователя в событии paste вы можете использовать API асинхронного буфера обмена и вернуться к (синхронному) API буфера обмена.

Объекты ClipboardItem , поступающие из navigator.clipboard.read , имеют поле types , которое представляет собой массив, а объекты File , поступающие из event.clipboardData.files , имеют поле type , которое представляет собой строку. Вы можете условно проверить каждое из полей type или types изображений в буфере обмена:

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

дальнейшее чтение

  • API буфера обмена на MDN
  • Разблокировка доступа к буферу обмена на web.dev

Демо

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