BOM – Clipboard API
前言
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 (e.g. input, textarea, contenteditable) 或者全局 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。