能力 · API
Async Clipboard API
发布于
Async Clipboard API 即 navigator.clipboard,用 Promise 读写系统剪贴板,取代 document.execCommand('copy') 与 ('paste')。纯文本走 writeText() 和 readText();图片、HTML 与多格式载荷封装成 ClipboardItem,经 write() 和 read() 传输。
Chrome 66 提供了 writeText() 与 readText(),Chrome 76 补上 read()、write() 与 ClipboardItem;Edge 79 与 Samsung Internet 12.0 跟随 Chromium。Firefox 63 加入 writeText(),125 加入 readText(),127 加入 read()、write() 与 ClipboardItem。macOS 上的 Safari 13.1 与 iOS 上的 Safari 13.4 一次性提供四个方法(BCD api.Clipboard)。该 API 只存在于安全上下文的 Window 中:localhost 以外的 http:// 源上 navigator.clipboard 为 undefined,Chromium 在 worker 中的读取以 NotAllowedError 拒绝。
navigator.clipboard.readText()navigator.clipboard.read()navigator.clipboard.read(formats)navigator.clipboard.writeText(data)navigator.clipboard.write(data)
new ClipboardItem(items)new ClipboardItem(items, options)clipboardItem.getType(type)ClipboardItem.supports(type)readText() 返回 Promise<DOMString>;read() 返回 Promise<sequence<ClipboardItem>>,返回的条目只带类型名,字节在调用 getType() 时才读取;writeText() 与 write() 返回 Promise<undefined>。读取在 Firefox 125+ 与 Safari 13.1+ 需要瞬时用户激活,在 Chrome 中需要激活或已授予的 clipboard-read 权限。写入在 Firefox 63+ 与 Safari 13.1+ 需要瞬时激活,Chrome 自 107 起需要激活或 clipboard-write 权限(107 之前只看权限)。ClipboardItem.supports() 是静态同步方法,存在于 Chrome 121、Firefox 127 与 Safari 18.4。
三个方法参数加上 ClipboardItem 构造函数的两个参数,就是规范定义的全部输入。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
data(writeText) |
DOMString |
是 | 以 text/plain;charset=utf-8 blob 写入。Windows 上浏览器会先把 \n 换成 \r\n。 |
data(write) |
sequence<ClipboardItem> |
是 | Chrome 76+ 与 Firefox 127+ 只接受恰好一个条目;两个及以上的数组以 NotAllowedError 拒绝(BCD api.Clipboard.write)。 |
formats(read) |
ClipboardUnsanitizedFormats |
否 | 只有一个成员 unsanitized: sequence<DOMString>,请求原始而非净化后的字节。Chrome 122 只对 text/html 生效;其他类型会拒绝。 |
items(构造函数) |
record<DOMString, ClipboardItemData> |
是 | MIME 类型到 Promise<Blob or DOMString> 的映射(直接传 Blob 或字符串会被包装)。以 "web " 为前缀的键是自定义格式(Chrome 104)。 |
options.presentationStyle |
"unspecified"、"inline" 或 "attachment" |
否 | 默认 "unspecified",给粘贴目标的提示。Chrome 未实现;Firefox 127 与 Safari 13.1 在读取到的条目上暴露它。 |
type(getType、supports) |
DOMString |
是 | 一个 MIME 类型,可带 "web " 前缀。 |
ClipboardItem 暴露 types(其 MIME 类型字符串的冻结数组)与 presentationStyle。每个浏览器都必须接受的强制类型是 text/plain、text/html 与 image/png;image/svg+xml 属于可选类型,Chrome 124 提供。
除 ClipboardItem 构造函数和 getType() 的同步部分外,所有方法都返回被拒绝的 Promise 而不是直接抛出。
| 异常 | 条件 |
|---|---|
NotAllowedError |
read() 或 readText() 的「check clipboard read permission」失败(没有瞬时激活也没有已授予的 clipboard-read);write() 或 writeText() 的写入检查失败;blob 类型不在强制与可选类型之内;净化未能完成;某个表示的 Promise 被拒绝;read(formats) 指定的 unsanitized 类型不在可选未净化列表内;Chrome 与 Firefox 中传入多个 ClipboardItem;Chromium 中文档没有焦点;clipboard-read 或 clipboard-write 被 Permissions Policy 拒绝。 |
NotFoundError |
readText() 时剪贴板没有 text/plain 表示,或该表示解码失败。 |
TypeError |
new ClipboardItem() 的 items 为空、某个键无法解析为 MIME 类型、或同一类型出现两次;getType() 的 type 无法解析。 |
InvalidStateError |
对 read() 得到的条目调用 getType() 时系统剪贴板已变化,或该条目对应的系统条目已不存在。 |
DataError |
仅 Chromium:ClipboardItemData 的 Promise 无法读取或解码,或处理 paste 事件期间剪贴板内容发生变化。 |
Firefox 多加了一道规范未命名的浏览器层关卡:read() 与 readText() 会弹出用户必须接受的粘贴提示,只有剪贴板内容同源时才跳过(BCD api.Clipboard.read)。拒绝该提示会以 NotAllowedError 拒绝。
每个示例都只检测它真正需要的方法,并给出方法缺失时走的分支。写入示例要在 click 处理函数中运行,这样瞬时激活仍然有效。
复制文本,缺少支持时回退到隐藏 textarea
Section titled “复制文本,缺少支持时回退到隐藏 textarea”navigator.clipboard.writeText() 覆盖 Chrome 66+、Firefox 63+ 与 Safari 13.1+;更老的版本以及任何 http:// 页面需要已废弃的 document.execCommand('copy'),它在这些引擎中仍可用于纯文本。
async function copyText(text) { if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(text); return 'async'; } const area = document.createElement('textarea'); area.value = text; area.setAttribute('readonly', ''); area.style.position = 'fixed'; area.style.opacity = '0'; document.body.append(area); area.select(); const ok = document.execCommand('copy'); area.remove(); return ok ? 'legacy' : 'failed';}
document.querySelector('#copy').addEventListener('click', async () => { const result = await copyText(location.href); console.log(`copied via ${result}`);});函数返回实际走的路径,界面只在其中一条成功时才显示「已复制」;浏览器拒绝时 execCommand 返回 false 而不是抛出。
在一个条目里同时写入 PNG 与 HTML 回退
Section titled “在一个条目里同时写入 PNG 与 HTML 回退”一个 ClipboardItem 可以携带多种表示;粘贴目标挑选它能理解的最丰富的那种。有 ClipboardItem.supports() 时(Chrome 121、Firefox 127、Safari 18.4)先检查,没有时按「只有强制类型」处理。
async function copyChart(canvas, captionHtml) { if (!navigator.clipboard?.write || !('ClipboardItem' in window)) { return copyText(captionHtml.replace(/<[^>]+>/g, '')); } const png = new Promise((resolve) => canvas.toBlob(resolve, 'image/png')); const items = { 'image/png': png }; const htmlOk = typeof ClipboardItem.supports !== 'function' || ClipboardItem.supports('text/html'); if (htmlOk) { items['text/html'] = new Blob([captionHtml], { type: 'text/html' }); } try { await navigator.clipboard.write([new ClipboardItem(items)]); } catch (err) { if (err.name !== 'NotAllowedError') throw err; return copyText(captionHtml.replace(/<[^>]+>/g, '')); }}直接传入 toBlob 的 Promise 而不是先 await,浏览器就能在同一手势内启动写入、稍后再填入字节;Safari 13.1 是依赖这一顺序的引擎(web.dev,Unblocking clipboard access)。
不依赖 Chromium 专有权限查询读取剪贴板图片
Section titled “不依赖 Chromium 专有权限查询读取剪贴板图片”只有 Chromium 把 clipboard-read 当作 Permissions API 名称;navigator.permissions.query({ name: 'clipboard-read' }) 在 Firefox 与 Safari 中抛出 TypeError。跳过查询,在手势内直接调用 read(),按拒绝原因分支。
document.querySelector('#paste').addEventListener('click', async () => { if (!navigator.clipboard?.read) { document.querySelector('#paste-area').focus(); // 让用户按 Ctrl+V / Cmd+V return; } try { const items = await navigator.clipboard.read(); const item = items.find((i) => i.types.includes('image/png')); if (!item) { console.log('剪贴板上没有 PNG,可用类型:', items.flatMap((i) => i.types)); return; } const blob = await item.getType('image/png'); document.querySelector('#preview').src = URL.createObjectURL(blob); } catch (err) { if (err.name === 'NotAllowedError') { document.querySelector('#paste-area').focus(); return; } throw err; }});聚焦一个 contenteditable 或 textarea 并监听 paste 事件是可移植的回退:paste 事件的 clipboardData.files 在每个引擎里都能交出同一张 PNG,不需要任何权限。
- Web Share API,剪贴板写入是它的常用回退
- File System Access API,处理大到不适合经剪贴板中转的文件
- Clipboard API and events: write() method(w3.org)
- Unblocking clipboard access(web.dev)
- Async Clipboard API(webkit.org)
- Chrome Platform Status: Asynchronous Clipboard API(chromestatus.com)
规范
| 规范 | 状态 |
|---|---|
| Async Clipboard API(异步剪贴板) | W3C 草案 |
| Clipboard API and events: read() method | W3C |
| Clipboard API and events: write() method | W3C |
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Desktop) | 支持 | 76 | 高 | 来源 | 1 |
| Chrome (Android) | 支持 | 76 | 高 | 来源 | 23 |
| Edge (Desktop) | 支持 | 79 | 高 | 来源 | 45 |
| Firefox (Desktop) | 支持 | 127 | 高 | 来源 | 678 |
| Firefox (Android) | 支持 | 127 | 高 | 来源 | 9101112 |
| Safari (macOS) | 支持 | 13.1 | 高 | 来源 | — |
| Safari (iOS) | 支持 | 13.4 | 高 | 来源 | 13 |
| Samsung Internet | 支持 | 12.0 | 高 | 来源 | 1415 |
| WebView (Android) | 支持 | 76 | 高 | 来源 | 1617 |
- 用户必须授予 `clipboard-read` 权限。
- 用户必须授予 `clipboard-read` 权限。
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- 用户必须授予 `clipboard-read` 权限。
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- 此方法必须在用户手势事件的处理函数内调用。
- 读取剪贴板时会显示粘贴提示;若剪贴板内容来自同源,则不显示该提示。
- manifest 中声明了 `clipboardRead` 权限的 Web 扩展可以不经粘贴提示直接读取数据。Firefox 147 之前,没有该权限的扩展无法读取剪贴板数据。
- 此方法必须在用户手势事件的处理函数内调用。
- 读取剪贴板时会显示粘贴提示;若剪贴板内容来自同源,则不显示该提示。
- manifest 中声明了 `clipboardRead` 权限的 Web 扩展可以不经粘贴提示直接读取数据。Firefox 147 之前,没有该权限的扩展无法读取剪贴板数据。
- 由 browser-compat-data 镜像自 Firefox 的数据推导。
- 由 browser-compat-data 镜像自 Safari 的数据推导。
- 用户必须授予 `clipboard-read` 权限。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。
- 用户必须授予 `clipboard-read` 权限。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。