阅读 1575

剪贴板复制粘贴操作汇总

剪贴板

在前端日常开发中,对剪贴板相关的操作相对较少。但针对某些特定的应用场景,又是必不可少会用到的。比如:富文本编辑器开发、ctrl + v 上传图片、支付宝红包口令自动复制(典型被玩坏儿了的用途)

剪贴板操作途径

剪贴板事件

操作剪贴板,最基础的方式为监听剪贴板事件。出于兼容性考虑,一般都只在剪贴板事件处理函数中访问 clipboardData ,非 IE 浏览器读数据只能在 onpaste 事件时可用。要使用剪贴板事件读写数据,就必须得用户主动使用快捷键或者右键菜单。所以监听板事件的主要应用场景是用户触发后对数据进行拦截处理。

document.execCommand

document.execCommand() 方法用于操纵可编辑区域的内容,例如最常用的 'copy' 命令可将选择区域的内容复制到剪贴板中,配合 window.getSelection() 方法就可以实现自动选择内容区域并写入内容到剪贴板。clipboard.js 库及红包口令自动复制主要就通过此方法实现的。

此方法也有一些问题:

  • Chrome \ Safari 不支持 'paste' 命令
  • Chrome 不能直接在后台调用 'copy' 命令,需用户点击触发
  • IE 可直接在后台调用和点击触发 'copy' 命令,但会出现权限提示

Asynchronous Clipboard API

看过上面的介绍,你会发现,操作剪贴板处处受限,而且迂回曲折,更困难的是有些功能是无法实现的。但不可否认,造成这一困惑很大一部分原因是安全考虑。

试想一下,当你浏览网页,点了下页面或者按钮,就复制了支付宝红包口令,会怎样?呃,还能接受?那么 'rm -rf /' 呢?(我用 Windows,我怕啥

而做为 Clipboard API 新增内容的 Asynchronous Clipboard API 及 Clipboard Permissions API 就是为了解决上述问题而来的,专治各种迂回曲折及安全风险。截至目前(2018.03),Chrome 66 实现了部分功能,预测未来会有变化,本文不做详细介绍。暂归纳特点如下:

  • HTTPS 下才能调用
  • 页面处于活动选项卡才能调用
  • 基于 Promise,便于异步操作和错误处理
  • 实现了编程方式的粘贴(读)和复制(写)
  • 粘贴(读)和复制(写)权限已添加到 Permissions API 中 ,操作需要用户授权
  • Clipboard Permissions API 目前仅适用于 Asynchronous Clipboard API。根据规范,未来会添加到所有 Clipboard API 中

处理粘贴 ( paste )

对于粘贴,目前广泛支持的仅有快捷键及右键菜单触发,而 IE 支持的编程方式使用场景不多,这里不做介绍。

纯文本或带格式文本

对纯文本的拦截

El.addEventListener('paste', event => {
    event.preventDefault();
    let clipboardData = event.clipboardData || window.clipboardData, // IE 兼容
        plainText = clipboardData.getData('text');  // 无格式文本
    
    // ... 对 plainText 进行一系列处理操作
    
    document.execCommand('insertText', false, plainText);  // 插入无格式文本
    document.execCommand('paste', false, plainText);  // IE 兼容
})
复制代码

如需对有格式文本进一步处理,可用以下方法转换为 DOM 结构,再进行操作。(IE 浏览器无法获得 'text/html' 所以无法实现下面的功能)

    /* 仍在上述事件处理函数中,下面均省略 */
    let plainHTML = clipboardData.getData('text/html'),   // 有格式文本
        domContainer = document.createElement('div');
    domContainer.innerHTML = plainHTML;
    
    // ... 对 domContainer 进行一系列处理操作
    
    El.innerHTML = domContainer.innerHTML; // 使用 innerHTML 方式整体插入
    El.appendChild(nodeOfDomContainer); // 只插入其中某个节点
复制代码

图片

如 DOM 结构中含有图片标签,可考虑上传图片至服务器中,然后将其 src 替换为服务器返回的地址。

如复制的是单个图片(例如 word 中复制过来的),可以使用下述方法得到图片的 DataURL,再上传。(IE 浏览器粘贴图片会自动生成带 DataURL 的图片标签,所以不需要下述方法)

    let items = clipboardData.items;
    for (let i = 0; i < items.length; i++) {
        let item = items[i];
        if (/image/.test(item.type)) {
            let file = item.getAsFile(), // 得到文件对象
                reader = new FileReader();
            reader.onload = function() {
                upload(reader.result) // 上传图片,伪代码
            }
            reader.readAsDataURL(file); // 读取为 DataURL / Base64
        }
    }
复制代码

处理复制 ( copy )

快捷键及右键菜单触发

常见应用场景是拦截复制操作,对复制内容加工后再写入剪贴板,例如在知乎复制大段文本后加入版权信息。

El.addEventListener('copy', event => {
    event.preventDefault();
    let clipboardData = event.clipboardData || window.clipboardData,
        text = window.getSelection().toString();
    
    // ... 对 text 进行一系列处理操作
    
    clipboardData.setData('text/plain', text); // 将处理好的 text 写入剪贴板
    clipboardData.setData('text', text); // IE 兼容
})
复制代码

编程式复制

虽是编程式复制,但无法做到完全自动复制,需要用户先触发 点击 事件。clipboard.js 库及红包口令自动复制(在 document 上监听 click 事件)主要就通过此方法实现的。

固定内容

Button.addEventListener('click', event => {
    let sometext = '红包码 xxxxxx',
        hiddenInput = document.createElement('input');
    hiddenInput.value = sometext;
    hiddenInput.setAttribute('readonly', '');
    hiddenInput.style.position = 'absolute';
    hiddenInput.style.left = '-9999px';
    document.body.appendChild(hiddenInput);
    hiddenInput.select();
    hiddenInput.setSelectionRange(0, hiddenInput.value.length); // ios
    document.execCommand('copy');
    document.body.removeChild(hiddenInput);
})
复制代码

复制某个元素内的文本

Button.addEventListener('click', event => {
    let someElement = document.getElementById('someelement'),
        selection = window.getSelection(),
        range = document.createRange();
    range.selectNodeContents(element);
    selection.removeAllRanges();
    selection.addRange(range);
    document.execCommand('copy');
})
复制代码

可以发现,目前对剪贴板的操作真是千奇百怪,希望新的 API 快快成为现实吧(做梦)!

参考

[1] Clipboard API and events | w3c

[2] clipboard.js | Github

[3] 利用 javascript 实现富文本编辑器 | 掘金

[4] 富文本编辑器初探 | 掘金

[5] JavaScript复制内容到剪贴板 | 掘金