复制内容到剪贴板

JavaScript 有两种方式可以让浏览器与系统剪贴板交互:Document.execCommand() 方法以及现代的异步的 Clipboard API。

document.execCommand()

document.execCommand() 方法允许运行命令来操纵可编辑内容区域(即元素的 contenteditable 属性值为true)的元素。

document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)

该方法返回布尔值,表示操作是否被支持或启用。

  • aCommandName: 字符串,命令的名称:copy、cut、delete、forwardDelete(删除光标所在位置的字符)、paste、selectAll(选中编辑区里的全部内容)等等。

  • aShowDefaultUI: 布尔值, 是否展示用户界面,一般为 false。(Mozilla 没有实现)。

  • aValueArgument: 一些命令需要的额外参数。

document.execCommand() 方法常用指令:

// 各种浏览器都支持:复制、剪切和粘贴这三个操作
document.execCommand('copy')
document.execCommand('cut')
document.execCommand('paste')

已废弃: document.execCommand() 特性已经过时了。 虽然它可能仍然在一些浏览器中工作,但它的使用是不鼓励的,因为它可能随时被删除。 尽量避免使用它。

复制操作

document.execCommand('copy') 命令用于将选中的文本内容写入剪贴板。该命令无需任何特别的权限就可以使用。

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

默认情况下,document.execCommand('copy') 只能将选中的文本内容写入到剪贴板。以下方法实现写入任意文本到剪贴板:

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)
}

注意: 复制操作最好放在事件监听函数里面,由用户触发(比如用户点击按钮)。如果脚本自主执行,某些浏览器可能会报错。

注意: document.execCommand('copy') 是同步操作,如果复制/粘贴大量数据,页面会出现卡顿。有些浏览器还会跳出提示框,要求用户许可,这时在用户做出选择前,页面会失去响应。

剪切操作

document.execCommand('cut') 命令用于将选中的文本内容写入剪贴板,同时删除选中的内容。

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

粘贴操作

document.execCommand('paste') 命令用于将剪贴板里面的文本内容取出,输出到当前的焦点元素中。

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

注意: 出于安全考虑,浏览器通常不允许 document.execCommand('paste') 访问剪贴板,因为它可能包含敏感信息。因此,该命令一般是不启效的。

Clipboard API

Clipboard 接口实现了 Clipboard API,如果用户授予了相应的权限,就能提供系统剪贴板的读写访问。Clipboard 对象是通过 navigator.clipboard 属性返回,所有剪贴板操作都通过这个对象进行。

该 API 被设计用来取代使用 document.execCommand() 的剪贴板访问方式。

使用 Clipboard API 需要注意以下问题:

  • 异步的 Clipboard API 方法是一个近期新增的规范,并且这个规范可能不适用于所有浏览器。

  • Chrome 浏览器规定,只有 HTTPS 协议的页面才能使用这个 API。不过,开发环境(localhost)允许使用非加密协议。

  • 调用时需要明确获得用户的许可。权限的具体实现使用了 Permissions API,跟剪贴板相关的有两个权限: clipboard-readclipboard-writeclipboard-write 自动授予脚本,而 clipboard-read 必须用户明确同意给予。也就是说,写入剪贴板,脚本可以自动完成,但是读取剪贴板时,浏览器会弹出一个对话框,询问用户是否同意读取。

  • **脚本读取的总是当前页面的剪贴板。**也就是说,如果把相关的代码粘贴到开发者工具中直接运行,可能会报错,因为这时的当前页面是开发者工具的窗口,而不是网页页面。

Permissions

Permissions API 提供以下方法:

  • query(): 返回给定 API 的用户权限状态。

  • request(): 请求使用给定 API 的权限。目前任何浏览器都不支持此功能。

  • requestAll(): 请求使用一组给定 API 的权限。目前任何浏览器都不支持此功能。

  • revoke(): 撤销当前在给定 API 上设置的权限。

以下是查看是否有 clipboard-read 权限的方法:

navigator.permissions.query({name:'clipboard-read'}).then(function(result) {
    if (result.state === 'granted') {
        console.log('granted')
        return
    }
    if (result.state === 'prompt') {
        console.log('prompt')
        return
    }
    console.log('fail')
})

遗憾的是,目前为止,浏览器还不支持 Permissions.request()Permissions.requestAll() 这两个申请权限的方法。我们可以尝试用 Chrome 插件来开放 Clipboard 权限。

复制:将文本写入剪贴板

使用 Clipboard API 的 writeText()write() 方法来将数据写入剪贴板。Clipboard API 比 execCommand 更加灵活,因为你不仅可以将当前选中的内容写入到剪贴板,还可以指定要写入剪贴板的内容。

**注意:**请先确认是否有 Permissions API 的 clipboardWrite 权限。因为这是一个新的 API,不同浏览器的具体要求不同。

writeText() 方法用于写入特定字符串到操作系统的剪贴板:

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

write() 方法用于将任意数据写入剪贴板,可以是文本数据,也可以是二进制数据,如图片。

需要注意的是: write() 方法的传参必需一个 ClipboardItem 实例。

navigator.clipboard.write([
    new ClipboardItem({
        "text/plain": new Blob(['要复制的文本'], { type: "text/plain" })
    })
]).then((res) => {
    console.log('write:写入成功!')
}).catch((err) => {
    console.log(err)
})
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
})
navigator.clipboard.write([data]).then(() => {
    console.log('write:写入成功!')
}).catch((err) => {
    console.log(err)
})

粘贴:从剪贴板中读取文本

使用 Clipboard API 的 readText()read() 方法可从剪贴板读取任意文本或二进制数据。

readText() 方法用于读取剪贴板的文本数据。注意:在尝试读取剪贴板的内容时,浏览器会提醒用户是否允许该请求:

navigator.clipboard.readText().then((res) => {
    console.log(`readText:${res}`)
}).catch((err) => {
    console.log(err)
})

read() 方法用于读取剪贴板的数据,与 readText() 的不同是,该方法可以读取剪贴板中的任意数据,如图片:

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)
})

查看剪贴板读写DEMO

剪贴板事件

剪贴板有3个操作: 复制、剪切、粘贴。这3个操作共对应下列 6 个剪贴板事件:

  • copy: 复制时触发;

  • cut: 剪切时触发;

  • paste: 粘贴时触发;

  • beforecopy: 复制前触发;

  • beforecut: 剪切前触发;

  • beforepaste: 粘贴前触发。

注意: 后三个事件,浏览器不一定支持。比如:Chrome 只支持 beforecopybeforecut,不支持 beforepaste

通常,我们只需要关注前三个事件即可:

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

这三个事件的触发条件:

  • 快捷键: ctrl+cctrl+xctrl+v 操作。

  • 鼠标右键菜单:复制、剪切、粘贴操作。

  • execCommand:copycutpaste 指令。

特别注意: 这三个事件在使用异步 Clipboard API 访问剪贴板时不会触发。

在剪贴板事件中,可以调用 event.preventDefault() 取消剪贴板操作,并且剪贴板 event 对象有一个 clipboardData 属性,它是一个 DataTransfer 对象。它允许我们覆盖将写入剪贴板的数据,让我们有机会以其他格式写入数据,例如 text/html

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

event.clipboardData 有以下属性和方法:

  • setData(type, data): 修改剪贴板数据,需要指定数据类型。

  • getData(type): 获取剪贴板数据,需要指定数据类型。

  • clearData([type]): 清除剪贴板数据,可以指定数据类型。如果不指定类型,将清除所有类型的数据。

  • items: 一个类似数组的对象,包含了所有剪贴项,不过通常只有一个剪贴项。

参考资料

MDN Clipboard

MDN Interact with the clipboard

MDN Permissions

MDN DataTransfer

异步剪贴板 API:使用 JavaScript 访问剪贴板

阮一峰 剪贴板操作 Clipboard API 教程

最后更新于