跳转到内容

Async Clipboard API:读写剪贴板

一句话: Async Clipboard API——navigator.clipboard——让你以编程方式读写系统剪贴板。 写入纯文本无需权限;读取操作受到严格管控:Chromium 可能使用 clipboard-read 权限, 而 Firefox 和 Safari 则需要瞬态激活(transient activation)和浏览器自身的粘贴提示框 (不存储持久权限)。所有操作均需要安全上下文(HTTPS 或 localhost)。

try {
await navigator.clipboard.writeText('你好,剪贴板!');
} catch (err) {
console.error('剪贴板写入失败:', err);
}

writeText 要求:

  • 安全上下文(HTTPS 或 localhost)。
  • 具有用户激活或页面获得焦点(具体规则因浏览器而异,但在点击处理器中调用 始终安全)。
const blob = new Blob(['<b>你好</b>'], { type: 'text/html' });
const item = new ClipboardItem({ 'text/html': blob });
await navigator.clipboard.write([item]);

write() 接受 ClipboardItem 对象数组。每个 item 可携带多种 MIME 类型,让粘贴目标 选择它所理解的最丰富格式。

const text = await navigator.clipboard.readText();
const items = await navigator.clipboard.read();
for (const item of items) {
for (const type of item.types) {
const blob = await item.getType(type);
// 处理 blob
}
}

权限行为因浏览器系列差异显著。

Chromium 使用 clipboard-readclipboard-write 作为 Permissions API 的权限名称:

  • 写入writeText / write):在已聚焦、有用户激活的上下文中调用 writeText 时无需权限提示。对某些 MIME 类型(如图片)调用 write() 可能会触发 clipboard-write 权限提示。
  • 读取readText / read):需要 clipboard-read 权限;首次读取时浏览器 会显示提示。可以不触发提示查询当前状态:
// 仅限 Chromium —— clipboard-read 在 Firefox 或 Safari 中不是合法的权限名称
const { state } = await navigator.permissions.query({ name: 'clipboard-read' });
// state: 'granted' | 'denied' | 'prompt'

Firefox clipboard-readclipboard-write 识别为 Permissions API 权限 名称——navigator.permissions.query({ name: 'clipboard-read' }) 会被拒绝或返回无意义 的状态。Firefox 的行为是:

  • 写入:需要瞬时激活(用户手势,如点击)。
  • 读取:需要瞬时激活,并触发一次性浏览器粘贴确认对话框,用户必须确认;不存在 持久授权。

Safari 同样通过 Permissions API 暴露 clipboard-read/clipboard-write,其 模型与 Firefox 类似:

  • 写入:需要瞬时激活。
  • 读取:需要瞬时激活和用户发起的粘贴提示;不存储持久权限。

navigator.clipboard 仅在安全上下文(HTTPS 或 localhost)中可用。在普通 HTTP 下,该属性为 undefined

读写操作应在用户手势处理器内或文档已获得焦点时调用。各浏览器细节略有不同,但在 click 处理器中调用始终安全。在无用户激活的上下文中调用通常会导致 NotAllowedError

传统的 document.execCommand('copy')document.execCommand('paste') 在旧版浏览器 中可用,但已废弃,且不支持富内容。仅作最后手段使用:

function legacyCopy(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
textarea.remove();
}

当前各浏览器数据请参见 /compatibility/

决策问题 建议行为 理由
点击按钮时复制纯文本? 在点击处理器中调用 navigator.clipboard.writeText() 无权限提示;所有现代浏览器在 HTTPS 下均可用。
将图片或 HTML 复制到剪贴板? navigator.clipboard.write([new ClipboardItem(...)]) 支持多格式载荷;粘贴目标选择最佳类型。
读取剪贴板内容? 始终在用户手势处理器中调用。在 Chromium 中可先查询 clipboard-read 权限;在 Firefox 和 Safari 中依赖瞬时激活和浏览器自身的粘贴提示,不要使用 navigator.permissions.query 权限模型因浏览器系列而异;瞬时激活是可移植的基线。
需要支持旧版浏览器? 特性检测 navigator.clipboard;回退到 execCommand execCommand 已废弃但在旧引擎中仍可用。
在 Service Worker 或后台上下文中访问剪贴板? 不可能——剪贴板访问需要已聚焦的文档。 按规范和浏览器策略,剪贴板在后台上下文中不可用。
  • 通过 HTTPS 提供页面——普通 HTTP 下 navigator.clipboard 为 undefined。
  • 在用户手势处理器内或文档已获焦点时调用剪贴板方法。
  • 纯文本用 writeText;富内容用带 ClipboardItemwrite
  • 始终在用户手势处理器中调用读取方法。在 Chromium 中可选择先查询 clipboard-read 权限状态;在 Firefox 和 Safari 中依赖瞬时激活和浏览器粘贴提示——navigator.permissions.query({ name: 'clipboard-read' }) 不能跨浏览器系列移植。
  • 捕获所有剪贴板调用的错误——权限可能被拒绝或撤销。
  • 为不支持 Async Clipboard API 的浏览器提供 document.execCommand 回退。