剪贴板读写(复制、粘贴等)操作

浏览器中与系统剪贴板交互一共有两套方案:已经废弃的 Document.execCommand(),以及现行标准异步 Clipboard API

查看剪贴板读写 DEMO

document.execCommand()

document.execCommand() 用于对当前文档执行编辑类指令。复制、剪切操作作用于页面选中的内容,可以是输入框选中文本,或是 contenteditable 可编辑区域。

document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)

方法返回布尔值,代表该指令是否执行成功。

  • aCommandName:命令名字符串,常用:copycutpastedeleteforwardDeleteselectAll
  • aShowDefaultUI:布尔值,是否展示原生交互界面,Firefox 并未实现该参数,通常填 false
  • aValueArgument:部分指令需要的额外参数。
// copy、cut 在部分浏览器依旧可以运行;paste 通过脚本调用通常会被浏览器安全策略拦截
document.execCommand('copy')
document.execCommand('cut')
document.execCommand('paste')

已废弃document.execCommand() 已经从 Web 标准中移除。浏览器为兼容旧项目还保留部分实现,不保证长期可用。新项目优先使用 Clipboard API。

复制操作 copy

document.execCommand('copy') 将页面选中的文本写入系统剪贴板。在用户点击这类真实用户事件回调内调用,通常不需要额外权限。

const inputDom = document.querySelector('#input')
inputDom.select()
document.execCommand('copy')

该指令只能复制页面上被选中的内容。如果希望复制任意指定字符串,需要临时创建 DOM 元素完成选中文本,执行复制后再移除元素:

function fnExecCommandCopy(str) {
    let inputDom = document.createElement("input")
    document.body.appendChild(inputDom)
    inputDom.value = str
    inputDom.focus()
    inputDom.select()
    if (document.execCommand("copy")) {
        console.log("复制成功")
    } else {
        console.log("不支持复制")
    }
    inputDom.blur()
    document.body.removeChild(inputDom)
}

含换行的文本用 textareainput 更稳妥。

注意事项

  • 复制逻辑尽量放在用户触发的事件回调(click 等),纯脚本自动执行复制,多数浏览器会拒绝。
  • execCommand 是同步执行;复制大数据量会阻塞页面渲染。如果浏览器弹出权限确认框,页面会卡住等待用户操作完成。

剪切操作 cut

document.execCommand('cut') 将选中内容写入剪贴板,同时删除页面选中的内容。

const inputDom = document.querySelector('#input')
inputDom.select()
document.execCommand('cut')

粘贴操作 paste

document.execCommand('paste') 读取剪贴板内容,并写入当前获得焦点的可编辑元素中。

const inputDom = document.querySelector('#input')
inputDom.focus()
document.execCommand('paste')

出于安全策略,现行浏览器普遍阻止脚本直接调用 paste,该方式大多数场景下无法生效。

Clipboard API

Clipboard 接口是现行标准,通过 navigator.clipboard 获取实例。仅在安全上下文(HTTPS、localhost)下可用,用来替代废弃的 execCommand

使用要点:

  • 先做能力判断 if (navigator.clipboard),低版本浏览器可以回退 execCommand
  • 生产环境 HTTPS 才可以正常调用;本地开发 localhost 允许访问。
  • 权限模型基于 Permissions API,权限标识符:clipboard-read(读剪贴板)、clipboard-write(写剪贴板)。Firefox、Safari 对这两个名字的 query 支持不完整,查不到就直接调用读写并用 catch 处理失败。
  • 写入剪贴板: 在用户真实交互(点击等)回调中,多数浏览器直接允许;Safari 等浏览器限制更严格,同样依赖用户手势,且页面通常需要处于前台、拥有焦点。
  • 读取剪贴板: Chromium 通常会弹窗向用户索要授权。
  • 在开发者工具控制台直接运行代码会因权限上下文不同而失败:控制台属于另一个执行上下文,不继承网页权限。

Permissions API

Permissions API 用来查询权限状态。网页侧真正通用的是 query();剪贴板权限不能靠脚本主动 request() 弹出授权,只有实际读写剪贴板时才会询问用户。

  • query():查询某个 API 当前权限状态。
  • request()revoke():曾经出现在草案里,现行浏览器基本不实现;网页标准里也没有 requestAll()

查询剪贴板读取权限示例(Chromium;其他引擎可能不认这个 name):

navigator.permissions.query({name:'clipboard-read'}).then(function(result) {
    if (result.state === 'granted') {
        console.log('granted 已授权')
    } else if (result.state === 'prompt') {
        console.log('prompt 需要弹窗询问用户')
    } else {
        console.log('denied 拒绝授权')
    }
})

浏览器扩展程序:可以直接在 manifest.json 声明 clipboardReadclipboardWrite 权限。网页脚本不能通过 JavaScript 主动唤起授权弹窗。

writeText() 写入纯文本

专门写入字符串文本,返回 Promise。成功 resolve,权限拒绝、报错会进入 catch。

navigator.clipboard.writeText("霁光浮瓦碧参差。").then((res) => {
    console.log('writeText:写入成功!')
}).catch((err) => {
    console.error('写入失败', err)
})

write() 写入多类型数据(文本、图片等二进制)

write() 支持写入多种 MIME 类型,参数是 ClipboardItem 对象数组。

注意:ClipboardItem 的属性值需要传入 Blob 对象,部分浏览器(尤其是 Safari)要求传入 Promise。

navigator.clipboard.write([
    new ClipboardItem({
        "text/plain": new Blob(['要复制的文本'], { type: "text/plain" })
    })
]).then((res) => {
    console.log('write:写入成功!')
}).catch((err) => {
    console.error('写入失败', err)
})

同时写入文本与图片示例:

(async function(){
    const image = await fetch('tupian_27781.png').then(response => response.blob())
    const text = new Blob(['一夕轻雷落万丝,\n霁光浮瓦碧参差。\n有情芍药含春泪,\n无力蔷薇卧晓枝。'], {type: 'text/plain'})
    const data = new ClipboardItem({
        'text/plain': text,
        'image/png': image
    })
    await navigator.clipboard.write([data])
    console.log('write:写入成功!')
})()

readText() 读取剪贴板纯文本

返回 Promise,成功拿到剪贴板文本。浏览器会触发权限询问。

navigator.clipboard.readText().then((res) => {
    console.log(`readText:${res}`)
}).catch((err) => {
    console.error('读取失败', err)
})

read() 读取多格式数据(文本、图片)

返回 ClipboardItem 数组,可以处理图片、富文本等多种剪贴板内容。通过 item.getType(type) 获取对应 MIME 的 Blob。

navigator.clipboard.read().then(data => {
    data.forEach(item => {
        item.types.forEach(async (type) => {
            let readRes = await item.getType(type)
            switch(type) {
                case 'text/plain': {
                    let tx = await readRes.text()
                    console.log(tx)
                    break
                }
                case 'image/png': {
                    console.log(URL.createObjectURL(readRes))
                    break
                }
                default: {
                    console.log('未处理的数据类型!')
                    break
                }
            }
        })
    })
}).catch(err => {
    console.error('无法读取剪贴板: ', err)
})

剪贴板事件

浏览器存在一组剪贴板事件:快捷键 CtrlCmdCXV,右键菜单复制粘贴,以及 execCommand 指令都会触发。注意:异步 Clipboard API 的 writeTextreadTextwriteread 不会触发这一套事件。

事件清单:

  • copy:用户发起复制时触发(可拦截并改写将要写入剪贴板的数据)
  • cut:用户发起剪切时触发
  • paste:用户发起粘贴时触发
  • beforecopybeforecutbeforepaste:复制、剪切、粘贴之前触发,属于非标准事件,各浏览器实现不一致,新项目不要依赖。

注册监听:

document.addEventListener('copy', (event) => {});
document.addEventListener('cut', (event) => {});
document.addEventListener('paste', (event) => {});

事件对象 event.clipboardData 是一个 DataTransfer 对象,可以读取、修改本次剪贴板的数据。调用 event.preventDefault() 可以阻止浏览器默认剪贴行为。

示例:拦截 copy 事件,改写复制到剪贴板的内容:

document.addEventListener('copy', (event) => {
    event.preventDefault()
    event.clipboardData.setData('text/plain', 'COPY ME!!!')
    event.clipboardData.setData('text/html', '<p>COPY ME!!!</p>')
})

clipboardData 常用方法:

  • setData(type, data):设置指定 MIME 类型的剪贴板数据。
  • getData(type):读取指定类型数据。
  • clearData([type]):清除剪贴板数据;不传参数清除全部。
  • items:类数组,存放剪贴项集合,通常只有一项。

参考资料

MDN Clipboard

MDN:扩展与剪贴板

MDN Permissions

MDN DataTransfer

© lizhao all right reserved,powered by Gitbook文件修订时间: 2026-09-01 23:02:38

results matching ""

    No results matching ""