Clipboard API:全新的异步复制、粘贴API

4,264 阅读4分钟

Clipboard API:全新的异步复制、粘贴API。它提供(如果用户授予权限)对系统剪贴板内容的读取和写入访问权限。 Clipboard API 可用于在 Web 应用程序中实现剪切、复制和粘贴功能,只需调用writeText方法就可以成功复制一段文本。此 API 旨在取代使用 document.execCommand() 访问剪贴板。

document.execCommand() 访问剪贴板是同步的,只能读写 DOM

系统剪贴板通过全局 navigator.clipboard 属性公开。

所有 Clipboard API 方法都是异步操作的; 它们返回一个 Promise,一旦剪贴板访问完成,该 Promise 就会被解析。 如果剪贴板访问被拒绝,则抛出错误。 接下来让我们一起学习 Clipboard API 的方法。

复制:将内容写入剪切板

writeText()

将文本写入系统剪贴板,可以调用 writeText()。此时文本已复制成功,可以使用 Ctrl+V 得到复制的内容。

async function copyText(text) {
  try {
    await navigator.clipboard.writeText(text)
    console.log('页面内容已复制到剪贴板 ')
  } catch (e) {
    console.log(`复制文案失败: ${e}`)
  }
}

write()

实际上,writeText() 只是通用 write() 方法的一种简写,它还允许您将图像复制到剪贴板。 图像复制成功后,需通过 read() 方法读取。

write() 方法接收参数为 ClipboardItem 对象数组。 创建一个新的 ClipboardItem 对象,需以 MIME 类型为键,以 Blob 为值。

目前未实现对多个 ClipboardItems 的支持。但支持多键值对的数据。

  const data = new ClipboardItem({
    // [MIME]: Blob
    'image/png': Blob,
    'text/plain': Blob
  })
  // 支持 
  await navigator.clipboard.write([data]);
  // 不支持
  await navigator.clipboard.write([data, data]);
 

注意:由于Clipboard API 还属于实验性 API ,所以在 Safari 浏览器中表现有所不同.

  1. 必须在https环境才能调试使用。否则报错。NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
  2. ClipbordItem 的 blob 属性值需要返回一个Promise。
const data = new ClipboardItem({
  'text/plain': new Promise((resolve) => {
    resolve(new Blob(['example text'], { type: 'text/plain' }))
  })
})

获取图像的 Blob 数据:

  1. 可以使用 fetch() 从服务器请求图像,然后在响应上调用 blob()
  2. 也可以将图像绘制到 canvas 上并调用 canvas 的 toBlob() 方法。
// 复制图片和文本
async function copy() {
  const image = await fetch('example.png');
  const text = new Blob(['Cute sleeping kitten'], {type: 'text/plain'});
  // 复制多个,也可以单独复制图片
  const item = new ClipboardItem({
    'text/plain': text,
    'image/png': image.blob()
  });
    
  try {
    await navigator.clipboard.write([item]);
    console.log('页面内容已复制到剪贴板 ')
  } catch (e) {
    console.log(`复制失败: ${e}`)
  }
}  

Firefox 不支持 write() 方法

粘贴:从剪切板读取数据

readText()

要从剪贴板读取文本,可以调用 readText()

async function getClipboardText() {
  try {
    const text = await navigator.clipboard.readText();
    console.log(`粘贴内容: ${text}`);
    // 一些操作
  } catch (e) {
    console.error(`从剪切板获取数据失败: ${e}`);
  }
}

Firefox 仅在扩展程序中支持 readText() 方法

read()

read() 返回 ClipboardItems 对象的列表。要从剪贴板读取图像,可通过遍历 ClipboardItems 列表来获取。

async function getClipboardContents () {
  try {
    const clipboardItems = await navigator.clipboard.read() // clipboardItems列表
    for (const clipboardItem of clipboardItems) {
      // types: ClipboardItem 中可用的 MIME 类型数组。
      for (const type of clipboardItem.types) { 
        // 返回使用请求的 MIME 类型的 Blob 解析的 Promise。
        // 如果未找到 MIME 类型,则返回错误。
        const blob = await clipboardItem.getType(type) 
        if (type === 'text/plain') { // 文本
            // Blob 接口中的 text() 方法返回一个 Promise.
            // 该 Promise 使用包含 Blob 内容的字符串进行解析,解释为 UTF-8。
            const text = await blob.text()
            console.log(text)
          } else if (type === 'image/png') { // 图片
            const url = URL.createObjectURL(blob)
            console.log(url)
          }
      }
    }
  } catch (e) {
    console.error(`粘贴失败: ${e}` )
  }
}

Firefox 不支持 read() 方法

监听事件

Clipboard API 继承自 EventTarget (一个 DOM 接口,由可以接收事件、并且可以创建侦听器的对象实现),因此它也有监听事件。测试还未发现如何监听事件,目前可以使用之前的 document.addEventListener 监听。

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  // 多内容或者图像内容
  getClipboardContents()
  
  // 当只有文本时
  if (navigator.clipboard) {
    text = await navigator.clipboard.readText();
  }
  else {
    text = e.clipboardData.getData('text/plain'); // 早期写法
  }
});

权限策略集成

安全策略

为了确保应用程序不能滥用api,当行为不符合要求时,这些api可能会造成糟糕的用户体验,一些api只能在用户处于“活跃交互”状态时使用,这意味着用户当前正在与web页面交互,或者已经与该页面至少交互过一次。浏览器限制对敏感api的访问,如弹出窗口、全屏或振动api,以防止恶意脚本滥用这些功能。

安全策略

与许多新 API 一样,剪贴板 API 仅支持通过 HTTPS 提供服务的页面。 为帮助防止滥用,仅当页面是当前标签页时才允许访问剪贴板。 当前标签页中的页面无需请求许可即可写入剪贴板,但从剪贴板读取始终需要许可。

非安全域名下(localhost除外) navigator.clipboard 为 undefined。应该在https环境下使用。

iframe 中使用 API

要在 iframe 中使用 API,您需要使用权限策略启用它。该策略定义了一种机制,允许有选择地启用和禁用各种浏览器功能和 API。 具体来说,您需要根据应用程序的需要传递剪贴板读取或剪贴板写入之一或两者。

<iframe
    src="index.html"
    allow="clipboard-read; clipboard-write"
>
</iframe>

是否开启权限

复制和粘贴权限已添加到权限 Permission API。您可以通过以下方式查看复制和粘贴权限。

async function getPermission () {
  const queryOpts = { name: 'clipboard-read', allowWithoutGesture: false }
  const permissionStatus = await navigator.permissions.query(queryOpts)
  // 返回请求权限的状态; “授予”、“拒绝”或“提示”之一。
  console.log(permissionStatus.state) 
  // 监听权限变更
  permissionStatus.onchange = () => {
    console.log(permissionStatus.state);
  };
}

Safari 暂不支持 Permission API

兼容性

目前浏览器已基本支持 Clipboard API。 Safari 在 13.1 版中已经支持;Firefox 仅支持文本,且在扩展模式才支持readText(); Google 浏览器中,图像支持仅限于 PNG。 如果您对使用 API 感兴趣,请在仔细查阅浏览器支持表

clipboard.png

总结

全新的 Clipboard API 功能很吸引人。只是当前还是处于实验性阶段,浏览器兼容性和限制还是有点糟糕。希望 Safari 和 Chrome 的写法保持统一。不然又得if else 或者 try catch

通过以上内容我们简单了解了 Clipboard API 的使用方法,如有表述不准确的地方,请大家指正。

参考

  1. async-clipboard
  2. MDN Clipboard API
  3. Blob
  4. 安全策略