Cách dán hình ảnh

Harry Theodoulou
Harry Theodoulou

Phong cách hiện đại

Sử dụng API Bảng nhớ tạm không đồng bộ

Để đọc hình ảnh từ bảng nhớ tạm của người dùng theo cách lập trình, tức là sau khi nhấp vào nút, bạn có thể sử dụng phương thức read() của API Bảng nhớ tạm không đồng bộ. Nếu chưa được cấp quyền đọc từ bảng nhớ tạm, thì lệnh gọi đến navigator.clipboard.read() sẽ yêu cầu quyền đọc trong lệnh gọi đầu tiên của phương thức đó.

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

Hỗ trợ trình duyệt

  • 86
  • 79
  • 13,1

Nguồn

Cách cổ điển

Theo dõi sự kiện paste

Cách cổ điển để dán hình ảnh là sử dụng API Bảng tổng hợp (đồng bộ). API này cho phép bạn truy cập vào trường clipboardData trong Tài liệu: sự kiện paste. Tuy nhiên, phương pháp này có một số hạn chế, chẳng hạn như vì nó đồng bộ nên việc dán một lượng lớn dữ liệu có thể chặn trang.

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

Hỗ trợ trình duyệt

  • 66
  • 79
  • 63
  • 13,1

Nguồn

Nâng cao dần dần

Đối với các trình duyệt không hỗ trợ API Bảng nhớ tạm Async, người dùng không thể truy cập bảng nhớ tạm của người dùng theo phương thức lập trình (ví dụ: khi nhấp vào nút). Do đó, đối với việc truy cập vào bảng nhớ tạm của người dùng trên sự kiện paste, bạn có thể sử dụng API Bảng nhớ tạm không đồng bộ hoá và sử dụng API Bảng nhớ tạm (đồng bộ).

Các đối tượng ClipboardItem đến từ navigator.clipboard.read có trường types là một mảng và các đối tượng File đến từ event.clipboardData.files có trường type là một chuỗi. Bạn có thể kiểm tra có điều kiện từng trường type hoặc types để tìm hình ảnh trong bảng nhớ tạm:

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

Tài liệu đọc thêm

  • API bảng nhớ tạm trên MDN
  • Bỏ chặn quyền truy cập vào bảng nhớ tạm trên web.dev

Bản minh họa

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