BOM – Clipboard API

兴杰發表於2024-11-24

前言

Clipboard API 就是和 copy and paste 相關的 BOM API。

Copy Text

我們經常能看見這樣的互動體驗

點選 Copy code 以後,下面的程式碼就會被 copy 起來。

等同於我們 select 那些 code 之後按 ctrl + c。

這個就是用 Clipboard API 實現的。

<button class="copy-code-btn">Copy code</button>
const copyCodeBtn = document.querySelector('.copy-code-btn')!;

copyCodeBtn.addEventListener('click', async () => {
  await window.navigator.clipboard.writeText(`console.log('hello world');`);
});

程式碼很簡單,我就不解釋了,直接看效果唄

Copy Image

除了 text 以外,想要 copy 圖片也可以。

<button class="copy-image-btn">Copy image</button>
const copyImageBtn = document.querySelector('.copy-image-btn')!;

copyImageBtn.addEventListener('click', async () => {
  // 1. fetch 一張圖
  const imageResponse = await fetch('/src/test-files/stooges-logo.png');
  // 2. 獲取圖的 blob
  const imageBlob = await imageResponse.blob();
  // 3. 把 blob 裝進 ClipboardItem
  const clipboardItem = new ClipboardItem({ [imageBlob.type]: imageBlob });
  // 4. 呼叫 write 方法,傳入 ClipboardItem
  await window.navigator.clipboard.write([clipboardItem]);
});

注1:圖片必須是 png 格式,其它的格式不一定支援。

注2:只能傳入一個 ClipboardItem,multiple 不一定支援。

效果

另外呢,blob 支援多種型別,比如 text/html

const htmlText = '<p>Hello, world!</p>';
const htmlTextBlob = new Blob([new TextEncoder().encode(htmlText)], {
  type: 'text/html',
});
const htmlTextClipboardItem = new ClipboardItem({ [htmlTextBlob.type]: htmlTextBlob });
await window.navigator.clipboard.write([htmlTextClipboardItem]);

這樣也是 ok 的。

Paste Text

能 copy 自然也能 paste。

<button class="paste-text-btn">Paste text</button>
const pasteTextBtn = document.querySelector('.paste-text-btn')!;
pasteTextBtn.addEventListener(
'click', async () => { const text = await window.navigator.clipboard.readText(); console.log('paste: ', text); });

呼叫 readText 方法就可以拿到當前 copy 著的 text 了。(注:遊覽器會先像使用者獲取許可權)

效果

Paste with different types

copy 的內容不僅僅是 text,也可以是圖片,或者 rich text (HTML string)。

我們可以透過 read 方法讀取內容,接著判斷型別,然後解析出不同的內容。

pasteTextBtn.addEventListener('click', async () => {
  // 1. read items
  const clipboardItems = await window.navigator.clipboard.read();
  // 2. get first item (因為 Chrome 不支援 multiple,所以只會有 1 個 item)
  const clipboardItem = clipboardItems[0];

  // 3. 檢視 item 的型別
  //    它是一個 string array
  //    假如是 html text,它會是 ['text/plain', 'text/html']
  //    假如是 html 的圖片,它會是 ['text/html', 'image/png']
  console.log('types', clipboardItem.types);
  // 4. 指定讀取的型別,讀出來是 blob,我們還需要 decode 成 string
  const textDecoder = new TextDecoder();
  console.log('plain text', textDecoder.decode(await (await clipboardItem.getType('text/plain')).arrayBuffer()));
  console.log('html text', textDecoder.decode(await (await clipboardItem.getType('text/html')).arrayBuffer()));
});

效果

在網站 select text,最終的內容型別會是 ['text/plain', 'text/html']。

text/plain 返回的是 "Hello World" 單純的 string。

text/html 返回的是 html string,還包括了樣式。

再看看 paste image 的效果

console.log('image blob', await clipboardItem.getType('image/png'));

注:雖然圖片本來是 jpeg 格式,但經過 copy paste 就變成了 png 格式,這是因為 Chrome 不支援 jpeg,只支援 png。

Copy & Paste Event

當使用者在網站內 ctrl + c 或者 right click + copy 就會觸發 'copy' 事件。(注:right click + copy image 在 Chrome 不會觸發 copy 事件...不知道為什麼🤔)

我們可以在 element 或者全域性 document 去監聽這個事件 (copy 事件會冒泡)

document.addEventListener('copy', async (event: ClipboardEvent) => {
  console.log(await window.navigator.clipboard.readText()); // 讀取 copy 的內容
  event.preventDefault(); // 阻止 copy 內容
  await window.navigator.clipboard.writeText(`can't copy this!`); // 改寫 copy 的內容
});

'paste' 事件也是如此

document.addEventListener('paste', async (event: ClipboardEvent) => {
  console.log(await window.navigator.clipboard.readText()); // 讀取 paste 的內容
  event.preventDefault(); // 阻止 paste 內容
});

ClipboardEvent 沒有什麼鳥用,主要還是操作 Clipboard API。

相關文章