在过去的几年中,浏览器已用于
document.execCommand()
剪贴板交互。尽管得到了广泛的支持,但这种剪切和粘贴方法还是要付出代价的:剪贴板访问是同步的,并且只能读写 DOM。
这对于一小段文字来说很好,但是在很多情况下,阻止页面进行剪贴板传输是一种糟糕的体验。在内容可以安全粘贴之前,可能需要耗时的清理或图像解码。浏览器可能需要从粘贴的文档中加载或内联链接资源。在磁盘或网络上等待时会阻塞页面。想象一下,将权限添加到混合中,要求浏览器在请求剪贴板访问时阻止页面。同时,document.execCommand()
用于剪贴板交互的权限
被松散定义,并且在浏览器之间有所不同。
在 异步剪贴板 API 解决了这些问题,提供了一个良好定义的权限模型不阻止页面。Safari 最近宣布 在 13.1 版中支持它。这样,主要的浏览器就具有基本的支持水平。在撰写本文时,Firefox 仅支持文本。在某些浏览器中,图像支持仅限于 PNG。如果你对使用 API感兴趣, 请 在继续之前查阅浏览器支持表。
异步剪贴板 API 仅限于处理文本和图像。Chrome 84 引入了一项实验性功能,该功能使剪贴板可以处理任意数据类型。
复制:将数据写入到剪贴板
writeText()
要将文本复制到剪贴板,请调用writeText()
。由于此 API 是异步的,因此该writeText()
函数将返回一个 Promise,该 Promise
将根据传递的文本是否被成功复制来解决或拒绝:
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);
}
}
write()
实际上,writeText()
这只是通用write()
方法的一种便捷方法,它还允许你将图像复制到剪贴板。像一样writeText()
,它是异步的并返回
Promise。
要将图像写入剪贴板,你需要将图像作为 blob
。一种实现方法是使用fetch()
,从服务器请求图像,然后调用 blob()
响应。
由于多种原因,可能不希望或不可能从服务器请求图像。幸运的是,你还可以将图像绘制到画布上并调用 canvas 的 toBlob()
方法。
接下来,将ClipboardItem
对象数组作为参数传递给write()
方法。目前,你一次只能传送一张图片,但我们希望将来增加对多张图片的支持。ClipboardItem
将图像的
MIME 类型作为键并将 Blob 作为值的对象。对于从fetch()
或获取的 Blob 对象canvas.toBlob()
,该blob.type
属性自动包含图像的正确
MIME 类型。
try {
const imgURL = '/images/generic/file.png';
const data = await fetch(imgURL);
const blob = await data.blob();
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob
})
]);
console.log('Image copied.');
} catch (err) {
console.error(err.name, err.message);
}
复制事件
在用户启动剪贴板副本的情况下,非文本数据将作为 Blob 提供给你。该 copy
事件 包括一个clipboardData
属性,该属性具有已采用正确格式的项目,从而无需手动创建 Blob。呼叫preventDefault()
以防止默认行为因你自己的逻辑而异,然后将内容复制到剪贴板。在此示例中未涵盖的是当不支持剪贴板
API 时如何回退到较早的 API。
document.addEventListener('copy', async (e) => {
e.preventDefault();
try {
let clipboardItems = [];
for (const item of e.clipboardData.items) {
if (!item.type.startsWith('image/')) {
continue;
}
clipboardItems.push(
new ClipboardItem({
[item.type]: item,
})
);
await navigator.clipboard.write(clipboardItems);
console.log('Image copied.');
}
} catch (err) {
console.error(err.name, err.message);
}
});
粘贴:从剪贴板中读取数据
readText()
要从剪贴板中读取文本,请致电navigator.clipboard.readText()
并等待返回的 Promise 解决:
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);
}
}
read()
该navigator.clipboard.read()
方法也是异步的,并返回 Promise。要从剪贴板读取图像,请获取 ClipboardItem
对象列表 ,然后对其进行迭代。
每个都ClipboardItem
可以将其内容保留为不同的类型,因此你需要再次使用for...of
循环来遍历类型列表。对于每种类型,请getType()
以当前类型为参数调用该方法以获得相应的
Blob。和以前一样,此代码不与图像绑定,并且可以与其他将来的文件类型一起使用。
async function getClipboardContents() {
try {
const clipboardItems = await navigator.clipboard.read();
for (const clipboardItem of clipboardItems) {
for (const type of clipboardItem.types) {
const blob = await clipboardItem.getType(type);
console.log(URL.createObjectURL(blob));
}
}
} catch (err) {
console.error(err.name, err.message);
}
}
paste 事件
如前所述,已经计划引入事件以与 Clipboard API 一起使用,但是现在你可以使用现有paste
事件。它与用于读取剪贴板文本的新异步方法很好地配合使用。与copy
活动一样,不要忘记致电preventDefault()
。
document.addEventListener('paste', async (e) => {
e.preventDefault();
const text = await navigator.clipboard.readText();
console.log('Pasted text: ', text);
});
处理多种文件类型
大多数实现将多种数据格式放在剪贴板上,以便进行单个剪切或复制操作。这样做的原因有两个:作为应用程序开发人员,你无法了解用户要将文本或图像复制到的应用程序的功能,并且许多应用程序都支持将结构化数据粘贴为纯文本。这是通过 “编辑” 菜单项呈现给用户的,该菜单项的名称为 “粘贴和匹配样式” 或 “不格式化”。
以下示例显示了如何执行此操作。此示例用于fetch()
获取图像数据。
function copy() {
const image = await fetch('kitten.png');
const text = new Blob(['Cute sleeping kitten'], {type: 'text/plain'});
const item = new ClipboardItem({
'text/plain': text,
'image/png': image
});
await navigator.clipboard.write([item]);
}
安全性和权限
剪贴板访问一直是浏览器的安全隐患。没有适当的权限,页面可能会以静默方式将所有形式的恶意内容复制到用户的剪贴板,粘贴时会产生灾难性的结果。
使网页不受限制地对剪贴板具有读取权限,这会更加麻烦。用户通常将诸如密码和个人详细信息之类的敏感信息复制到剪贴板,然后在用户不知情的情况下,任何页面都可以读取这些信息。
与许多新 API 一样,剪贴板 API 仅支持通过 HTTPS 服务的页面。为防止滥用,仅当页面为活动选项卡时才允许剪贴板访问。活动选项卡中的页面可以在不请求权限的情况下写入剪贴板,但是从剪贴板中读取始终需要权限。
复制和粘贴的权限已添加到 Permissions
API 中。clipboard-write
当页面是活动选项卡时,权限会自动授予它们。clipboard-read
必须请求该权限,你可以通过尝试从剪贴板读取数据来执行此操作。下面的代码显示了后者:
const queryOpts = { name: 'clipboard-read', allowWithoutGesture: false };
const permissionStatus = await navigator.permissions.query(queryOpts);
// Will be 'granted', 'denied' or 'prompt':
console.log(permissionStatus.state);
// Listen for changes to the permission state
permissionStatus.onchange = () => {
console.log(permissionStatus.state);
};
你还可以使用该allowWithoutGesture
选项控制是否需要用户手势来调用剪切或粘贴。该值的默认值因浏览器而异,因此应始终将其包括在内。
这就是剪贴板 API 的异步特性真正派上用场的地方:尝试读取或写入剪贴板数据会自动提示用户获得许可(如果尚未授予许可)。由于 API 基于承诺,因此这是完全透明的,并且用户拒绝剪贴板权限会导致承诺被拒绝,因此页面可以适当地响应。
由于 Chrome 仅在页面为活动标签时才允许剪贴板访问,因此你会发现,如果直接粘贴到 DevTools 中,此处的某些示例将不会运行,因为 DevTools 本身就是活动标签。有一个技巧:使用延迟剪贴板访问setTimeout()
,然后在调用函数之前快速在页面内部单击以将其聚焦:
setTimeout(async () => {
const text = await navigator.clipboard.readText();
console.log(text);
}, 2000);
特征检测
要在支持所有浏览器的同时使用 Async Clipboard API,请测试 navigator.clipboard
并退回到较早的方法。例如,这是实现粘贴以包括其他浏览器的方式。
document.addEventListener('paste', async (e) => {
e.preventDefault();
let text;
if (navigator.clipboard) {
text = await navigator.clipboard.readText();
}
else {
text = e.clipboardData.getData('text/plain');
}
console.log('Got pasted text: ', text);
});
这还不是全部。在 Async Clipboard API 之前,Web 浏览器混合了多种复制和粘贴实现。在大多数浏览器中,可以使用document.execCommand('copy')
和触发浏览器自身的复制和粘贴 document.execCommand('paste')
。如果要复制的文本是 DOM 中不存在的字符串,则必须将其注入 DOM 中并进行选择:
button.addEventListener('click', (e) => {
const input = document.createElement('input');
document.body.appendChild(input);
input.value = text;
input.focus();
input.select();
const result = document.execCommand('copy');
if (result === 'unsuccessful') {
console.error('Failed to copy text.');
}
});
![]()
我们在微信上24小时期待你的声音
解答:网站优化,网站建设,搜索引擎优化,APP 开发,小程序开发非常感谢您有耐心的读完这篇文章:"在浏览器中取消阻止剪贴板访问",此文章仅为提供更多信息供用户参考使用或为学习交流的方便。如果对您有帮助,请收藏我们的网址:https://www.91webs.cn。