解锁剪贴板访问
1、document.execCommand() 方法说明
在 IE10 以前我们可以使用 document.execCommand()
来实现文本的复制和粘贴操作,但是这种粘贴板是同步操作的。
这样页面在复制较少内容的时候页面不会出现卡顿的情况,但是当你的复制粘贴的内容中带有图片、文本和外部链接的时候,浏览器会对复制的内容进行清理和图片的解码操作,这样就会导致磁盘或者网络的阻塞,从而导致页面产生卡顿情况。
document.execCommand()
的权限采用松散的定义,这样导致了在不同浏览器之间存在兼容性的问题。所以现在这个比较新的浏览器中都已废弃这个 API。
2、Clipboard 说明
clipboard
接口实现了 Clipboard API
,如果用户授予了相应的权限后就能够使用系统剪贴板的读写操作。
所有的 clipboard api
的方法都是异步的,即返回的都是 Promise
对象,在剪贴板访问成功后就可以进行相应操作,但是剪贴板拒绝返回的话,promise 就会返回错误信息回来。
clipboard 相关的方法有如下 4 个:
-
read()
从剪贴板读取数据(文本和图片),返回一个Promise
对象。在检索到数据后,就会返回一个clipboardItem
对象数组。 -
readText()
从剪贴板中读取文本数据,返回一个Promise
对象。在检索到数据后就会返回一个字符串。 -
write()
写入任何数据到粘贴板中,是一个异步操作,当操作完成后会返回一个Promise
对象。这个方法需要事先把需要复制的内容转为blob
,并且传入的参数是ClipboardItem
的实例对象数组。ClipboardItem
主要是存放 bolob 数据。 -
writeText()
写入一个文本到粘贴板中,是一个异步操作,当操作完成后会返回一个Promise
对象。
注意:火狐浏览器虽然支持 Clipboard
这个 API,但是它提供的方法只有writeText()
。
3、writeText() / write() 实例演示
const _copyBtn = document.getElementById("copy_btn");
async function copyPageInfo() {
const _textArea = document.getElementById("copy_box");
try {
await navigator.clipboard.writeText(_textArea.value);
console.log("复制成功");
} catch (error) {
console.log(error);
}
}
_copyBtn.addEventListener("click", () => {
copyPageInfo();
});
const _copy_img_btn = document.getElementById("copy_img_btn");
async function copyPageImg() {
const _imgUrl = "./images/grid布局1.png";
const data = await fetch(_imgUrl);
const blob = await data.blob();
try {
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);
console.log("图片复制成功");
} catch (error) {
console.log(error);
}
}
_copy_img_btn.addEventListener("click", () => {
copyPageImg();
});
4、readText() / read() 实例演示
const _readText = document.getElementById("readText");
async function getTextData() {
try {
const text = await navigator.clipboard.readText();
console.log(text);
} catch (error) {
console.log(error);
}
}
_readText.addEventListener("click", () => {
getTextData();
});
const _readImg = document.getElementById("readImg");
async function getImgData() {
try {
const _clipboardItem = await navigator.clipboard.read();
for (const item of _clipboardItem) {
for (const type of item.types) {
const blob = await item.getType(type);
console.log(URL.createObjectURL(blob));
}
}
} catch (error) {
console.log(error);
}
}
_readImg.addEventListener("click", () => {
getImgData();
});
5、复制事件/张贴事件
5.1 复制事件
可以使用 copy 事件来进行复制事件的监听,因为 copy 包含 clipboardData 属性,其中的项目已经采用正确的格式,从而无需手动创建 Blob。
具体实例如下:
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);
}
});
5.2 张贴事件
可以使用 paste 事件来进行张贴事件的监听。具体实例代码如下:
document.addEventListener("paste", async (e) => {
e.preventDefault();
const text = await navigator.clipboard.readText();
console.log("Pasted text: ", text);
});
6、安全和权限
ClipBoard API 仅支持通过 HTTPS
提供的页面。为帮助防止滥用,仅当页面是活动选项卡时才允许访问剪贴板。活动选项卡中的页面无需请求许可即可写入剪贴板,但从剪贴板读取始终需要许可。
ClipBoard API 的异步特性真正派上用场的地方在于:如果尚未授予权限,尝试读取或写入剪贴板数据的操作会自动提示用户授予权限。由于 API 是基于承诺的,这是完全透明的,用户拒绝剪贴板权限会导致承诺拒绝,因此页面可以做出适当的响应。
当页面处于活动标签页时,会自动授予 clipboard-write 权限。必须请求 clipboard-read 权限,这在您尝试从剪贴板读取数据时需要执行。例如如下的授权验证代码:
async function check() {
const queryOpts = { name: "clipboard-read", allowWithoutGesture: false };
const permissionStatus = await navigator.permissions.query(queryOpts);
// 可以获取到 'granted', 'denied' or 'prompt':
console.log(permissionStatus.state);
// 监听粘贴板的状态改变
permissionStatus.onchange = () => {
console.log(permissionStatus.state);
};
}