“复制到剪贴板”是一个极为常见的web功能。多年来,我们一直依赖一个略显“古老”的 API——document.execCommand('copy')。它曾是我们的得力助手,但现在,它已经被正式标记为废弃(Deprecated)。
document.execCommand为何被时代抛弃?
在拥抱新事物之前,我们有必要了解旧事物的缺陷。execCommand 主要有三大“原罪”:
- 同步执行:execCommand 是一个同步操作。这意味着在执行时,它会阻塞浏览器的主线程。如果处理的数据量很大或页面复杂,可能会导致页面瞬间卡顿,影响用户体验。
- 依赖 DOM:execCommand 只能操作当前文档中**被选中(selected)**的内容。为了复制一段任意文本,我们不得不采取一些非常“黑客”的手段:
- 动态创建一个隐藏的 <textarea> 或 <input> 元素。
- 将想要复制的文本放入这个元素。
- 用 JavaScript 选中该元素的全部内容。
- 调用 document.execCommand('copy')。
- 最后,移除这个临时创建的元素。
这一套流程繁琐、不直观,且容易出错。
- 权限模型不清晰:它对剪贴板的访问权限控制非常模糊,不同浏览器的实现和限制也存在差异,这带来了一定的安全隐患。
正是因为这些原因,W3C 决定将其废弃,并推出了一个为现代 Web 量身定做的解决方案。
新的主角:强大的 Clipboard API (navigator.clipboard)
Clipboard API 是一个异步的、基于 Promise 的现代接口,它彻底改变了我们与剪贴板交互的方式。
它的核心优势在于:
- 异步操作:所有操作都返回 Promise,不会阻塞主线程,对性能友好。
- 安全可靠:它整合了浏览器的权限系统(Permissions API)。在读取剪贴板内容时,需要明确获得用户的授权,并且大多数操作要求页面在**安全上下文(HTTPS)**下运行。
- 不依赖 DOM:你可以直接将字符串、图片等数据写入剪贴板,无需任何 DOM 元素作为中介。
- 功能更强大:除了纯文本,它还支持写入和读取更丰富的数据类型,例如图片。
实战演练:如何使用 Clipboard API
让我们来看几个最常见的场景。
场景一:复制文本到剪贴板
这是最基础的需求。使用
navigator.clipboard.writeText(),一切都变得异常简单。
HTML:
<input id="copy-input" type="text" value="这是要被复制的文本">
<button id="copy-btn">复制文本</button>
JavaScript:
const copyBtn = document.getElementById('copy-btn');
const copyInput = document.getElementById('copy-input');
copyBtn.addEventListener('click', async () => {
if (!navigator.clipboard) { // Clipboard API 不可用
alert('抱歉,您的浏览器不支持 Clipboard API');
return;
}
const textToCopy = copyInput.value;
try {
await navigator.clipboard.writeText(textToCopy);
alert('文本已成功复制到剪贴板!');
} catch (err) {
console.error('复制失败:', err);
alert('复制失败,请检查浏览器权限或使用 HTTPS 协议');
}
});
看,代码干净利落!我们不再需要任何隐藏的 <textarea>。async/await 和 try...catch 的组合完美地处理了异步流程和可能出现的错误(例如用户拒绝授权)。
场景二:从剪贴板读取文本
读取操作比写入更敏感,因此浏览器会向用户请求权限。注意: 出于安全考虑,readText() 和 read() 方法通常只能在用户主动交互(如点击事件)的回调中调用。
HTML:
<button id="paste-btn">粘贴内容</button>
<div id="paste-area" style="border: 1px solid #ccc; padding: 10px; min-height: 50px;"></div>
JavaScript:
const pasteBtn = document.getElementById('paste-btn');
const pasteArea = document.getElementById('paste-area');
pasteBtn.addEventListener('click', async () => {
if (!navigator.clipboard) {
alert('抱歉,您的浏览器不支持 Clipboard API');
return;
}
try {
// 浏览器会弹出权限请求对话框
const text = await navigator.clipboard.readText();
pasteArea.innerText = text;
alert('内容已成功粘贴!');
} catch (err) {
console.error('粘贴失败:', err);
alert('粘贴失败,请确保您已授予剪贴板读取权限。');
}
});
场景三:更进一步,复制图片
这是 execCommand 无法直接做到的。Clipboard API 通过 write() 方法和 ClipboardItem 对象,让复制非文本数据成为可能。
async function copyImageToClipboard(imageUrl) {
try {
// 1. 获取图片数据
const response = await fetch(imageUrl);
const blob = await response.blob(); // 将图片转换为 Blob 对象
// 2. 创建一个 ClipboardItem
const item = new ClipboardItem({
[blob.type]: blob
});
// 3. 写入剪贴板
await navigator.clipboard.write([item]);
console.log('图片已复制到剪贴板');
} catch (err) {
console.error('复制图片失败: ', err);
}
}
// 使用示例
// copyImageToClipboard('path/to/your/image.png');
截至目前,所有主流现代浏览器(Chrome, Firefox, Edge, Safari)都已支持 Clipboard API 的核心功能。