Click To Copy 的实现 [2/2] JavaScript实现复制内容到前切板
在过去的几年,浏览器都一般使用document.execCommand
来进行剪切板操作。诚然,有这样一个单独且被广泛支持的方式去完整的复制和粘贴内容到Web应用是很帮的,但是,这种方式是有一定代价的:剪切板访问是同步的,并且,由于通常是操作用于input元素,因此只能进行DOM的读写操作。
这些问题,如果是在复制粘贴少量字节的文本内容时,同步访问是不会有多大影响的。但是同步访问会导致在很多情况下造成页面阻塞。在内容被安全粘贴之前,还需要时间去净化,解码。如果浏览器需要加载自粘贴内容的外链资源,那么有可能会因为磁盘读取和网络问题带来页面阻塞。试想,如果混入权限,询问用户能否在访问剪切板的时候阻塞页面会怎么样呢?
同时,在使用document.execCommand
进行剪切板访问操作的时候,交互的权限定义是非常松散的,并且会因为浏览器的不同有所区别。因此,想象,如果专用的剪切板API解决了页面阻塞和权限问题会是什么样呢?
这就是 Async Clipboard API,(异步前切板API),该方案在Chrome66中被退出,提供了很好的权限模型定义,并且由于异步特点不会阻塞页面。
复制:写入文本至剪切板(Copy: writing text to the clipboard)
通过调用writeText()
可以实现复制文本到剪切板,由于该API是异步执行的,writeText()
方法会返回一个Promise用于处理resolved或者rejected,这取决于传入的文本是否被正常传入。
navigator.clipboard.writeText('Text to be copied')
.then(() => {
console.log('Text copied to clipboard');
})
.catch(err => {
// This can happen if the user denies clipboard permissions:
console.error('Could not copy text: ', err);
});
类似的,你可以将该过程写做一个异步函数( async function)
async function copyPageUrl() {
try {
await navigator.clipboard.writeText(location.href);
console.log('Page URL copied to clipboard');
} catch (err) {
console.error('Failed to copy: ', err);
}
}
粘贴:从剪切板读出文本(Paste:reading)
这个过程和复制很想,你可以通过调用readText()
来剪切剪切板中的文本。并且等待返回的Promise来resolve,或者reject。
navigator.clipboard.readText()
.then(text => {
console.log('Pasted content: ', text);
})
.catch(err => {
console.error('Failed to read clipboard contents: ', err);
});
为了保持一致,这是等效的异步函数:
async function getClipboardContents() {
try {
const text = await navigator.clipboard.readText();
console.log('Pasted content: ', text);
} catch (err) {
console.error('Failed to read clipboard contents: ', err);
}
}
处理粘贴事件(Handling paste events)
已经计划引入一个新事件来检测剪贴板的变化,但是目前最好使用“粘贴”事件。 它与用于读取剪贴板文本的新异步方法很好地结合在一起:
document.addEventListener('paste', event => {
event.preventDefault();
navigator.clipboard.readText().then(text => {
console.log('Pasted text: ', text);
});
});
安全和权限(Security and permissions)
剪贴板访问一直是浏览器的安全隐患。没有适当的权限,页面可以静默的复制所有的恶意内容到用户的剪切板,在用户粘贴的时候,将产生灾难性的结果。想象一个网页静默的复制 rm -rf /
或者减压炸弹图片(decompression bomb image)到你的剪切板。
让网页不受限制地访问剪贴板就更麻烦了。
用户通常会将密码和个人信息等敏感信息复制到剪贴板上,这样任何页面都可以在用户不知情的情况下读取这些信息。
和很多新的API一样,navigator.clipboard
仅仅支持通过HTTPS协议部署的页面,这样能够有效阻止滥用,仅当页面是活动选项卡时才允许剪贴板访问,处于活动选项卡中的页面可以在不请求权限的情况下写入剪贴板,但从剪贴板读取始终需要权限。
为了使事情变得简单,将两个新的复制和粘贴权限添加到了权限API(Permissions API)
当页面为活动选项卡时,将自动向页面授予剪贴板写入权限。 必须请求剪贴板读取权限,您可以通过尝试从剪贴板读取数据来完成。
{ name: 'clipboard-read' }
{ name: 'clipboard-write' }
与使用Permissions API进行的所有操作一样,可以检查您的应用是否具有与剪贴板进行交互的权限:
navigator.permissions.query({
name: 'clipboard-read'
}).then(permissionStatus => {
// Will be 'granted', 'denied' or 'prompt':
console.log(permissionStatus.state);
// Listen for changes to the permission state
permissionStatus.onchange = () => {
console.log(permissionStatus.state);
};
});
向后看(Looking back)
在介绍Async Clipboard API
之前,各浏览器有一些不同的复制粘贴操作的混合实现。
在大多数浏览器中,浏览器自身的复制粘贴能够通过使用document.execCommand('copy')
和document.execCommad('paste')
来触发,如果被复制的文本是一个不在DOM中的字符串。我必须注入并选择它:参考[Click To Copy 的实现 [1/2] JavaScript实现复制内容到前切板]
检测和备用方案(Detection and fallback)
检测浏览器是否支持Async Clipboard API 操作 paste,如果不支持则采用替代方案,以防止报错。
document.addEventListener('paste', async e => {
let text;
if (navigator.clipboard) {
text = await navigator.clipboard.readText()
}
else {
text = e.clipboardData.getData('text/plain');
}
console.log('Got pasted text: ', text);
});