Clipboard API,“船新”的浏览器与系统剪贴板交互的异步 API。

小小的碎碎念

恰巧前段时间要做 复制到剪贴板 这个功能,就去查了一下实现方式,大致都是 document.execCommand 来实现。使用的时候没多注意这个 API 已经废弃,之后发现了,觉得既然废弃就应该有替代的。于是再继续查了查,原来确实是有替代,就是今天的主角 Clipboard API

接下来就是通过比对 document.execCommandClipboard API 的使用方法来介绍这两种方式的区别,来认识 Clipboard API

写入系统剪贴板

有两种方式向系统剪贴板写入数据。document.execCommand 触发 cutcopy 来写入;另一种方式就是使用 Clipboard APIwriteText()write() 写入数据。

使用 document.execCommand

bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)

返回值为布尔值,如果是 false 则表示操作不被支持或未被启用。

document.execCommand 是一个非常强大的 API,但我们这里只用到了它的 cutcopypaste

const copy = () => {
const copyEle = document.querySelector('#copy') as HTMLInputElement
copyEle.select()
const success = document.execCommand('copy')
if (success) {
alert('已将文本复制到剪贴板')
} else {
alert('复制失败')
}
}

<input value="这是一个copy测试" id="copy" />
<button onClick={copy}>点击复制</button>

使用 Clipboard API

相较 document.execCommandClipboard API 更加灵活,它不仅能够将当前选择复制到剪贴板,还能够直接指定要放入剪贴板的信息。

不同于 document.execCommand,Clipboard API 需要申请 clipboardReadclipboardWrite 权限。

我们可以通过 navigator.permissions.query() 来查询权限:

navigator.permissions.query({ name: 'clipboard-write' }).then(res => {
if (res.state === 'granted' || res.state === 'prompt') {
// write your code
}
})

更新剪贴板使用方法

const updateClipboard = (newText) => {
navigator.clipboard.writeText(newText).then(() => {
// successfully set
}, () => {
// failed
})
}

不同浏览器的差异

  • Chrome
    • 可以在所有执行上下中写入系统剪贴板
    • 不需要权限
  • Firefox
    • version 51 以上才开始支持 “clipboardWrite” 权限

具体的兼容性

  • write

    caniuse-write

  • writeText

    caniuse-writeText

读取系统剪贴板

document.execCommand 提供了 paste 指令,让用户能够粘贴内容。也可以使用 Clipboard APIread()readText()

使用 document.execCommand

const paste = () => {
const pasteText = document.querySelector('#output') as HTMLInputElement
pasteText.focus()
const success = document.execCommand('paste')
}

<input id="output" type="text"/>
<button onClick={paste}>粘贴</button>

使用 Clipboard API

在获得了 “clipboard-read” 权限之后,剪贴板的数据读取

navigator.clipboard.readText().then(text => {
document.querySelector('#output').innerText = text
})

这个代码片段从剪贴板提取文本并且用该文本替换 ID 为 "outbox" 的元素的当前内容。

具体的兼容性

  • read

    caniuse-read

  • readText

    caniuse-readText

Clipboard Event

Clipboard API 还可以让我们响应 复制剪切粘贴 等事件。只需要给元素增加对 copycutpaste 事件的监听,就能够拦截到事件之后进行自定义的处理。

Clipboard API 中定义了一个 ClipboardEvent,而这个 ClipboardEvent 有一个属性 ClipboardData (目前还是一个实验性的属性),顾名思义这是剪贴板数据。

ClipboardData 属性是一个 DataTransfer 对象,有两个方法:

  • setData(format, data):当用户进行复制和剪贴时,可以通过这个方法来写入。参数 format: DOMString 表示数据的类型,参数 data: DOMString 表示数据。
  • getData(format):可以在用户进行粘贴时处理数据。

拦截 copycut 事件

通过拦截用户的 copycut 事件,可以做到更改用户的剪贴板数据

const handleCopy = e => {
const selection = document.getSelection()
e.clipboardData?.setData('text/plain', `你的剪贴板数据被我更改了哦~~${selection}`)
e.preventDefault()
}

document.querySelector('#copy').addEventListener('copy', proxyClipboardCopy)

利用这个方法,我们可以做到在用户复制特定元素的内容时,在复制内容中加入你想加入的信息,比如: 版权信息

拦截 paste 事件

利用拦截用户的 paste 事件,我们可以做到更多的事情,比如富文本编辑中的粘贴图片之后自动上传或者进行 base64 转换之类的操作。

<textarea id="input" placeholder="请开始你的表演" />

const handlePaste = (e) => {
const input = document.querySelector('#input')
input.addEventListener('paste', () => {
const text = e.clipboardData.getData('text/plain')
console.log(text)
e.preventDefault()
})
}

总结

说一个小知识 document.execCommand 这个 API 本身也不是标准 API,这是 IE 的私有 API,只是被其他浏览器做了兼容支持。而这个 API 被废弃的原因主要是安全问题,它不需要任何权限就能执行一些敏感操作,还有就是这是一个同步方法,而且是操作 DOM 对象的,会阻塞页面渲染和脚本执行。

可能就是要解决以上的两个问题,Clipboard API 被设计成了需要相对应的权限才能使用的异步方法。

目前各个浏览器对 Clipboard API 的支持不相同,尤其是在能够复制更复杂内容的 writeread 上,Chrome 实现了而 Firefox 却还没有实现。而且具体的规范和标准也还处在工作草案阶段。相较于已经被废弃的 execCommandClipboard API 之后会发展成啥样也是蛮值得期待的。

评论