折腾一下剪切板

304 阅读3分钟

点击复制数据

document.execCommand方法

实际上就是模拟浏览器选中复制的过程。

选中某个区域的内容

借助HTMLInputElement.select()的能力,可以选中input(type='text')或textarea元素内输入的所有文本内容。

const textarea = document.createElement('textarea');
// textarea.value为你要复制的内容
textarea.value = "哦吼";
// 调整样式,千万不要让人看见
textarea.style.cssText = 'position: fixed; z-index: -10000; opacity: 0; pointer-events: none';
document.body.appendChild(textarea);
// 选中它
textarea.select();

创建一个不让用户看见的文本输入框,并且将复制的内容填入到输入框内,调用select选中所有内容。

执行复制命令

执行document.execCommand('copy'),整个复制的完整代码如下:

function copy(content) {
    const textarea = document.createElement('textarea');
    textarea.value = content;
    textarea.style.cssText = 'position: fixed; z-index: -10000; opacity: 0; pointer-events: none';
    document.body.appendChild(textarea);
    textarea.select();
    // 执行复制命令
    const result = document.execCommand('copy');
    if (result) {
        console.log('复制成功');
    } else {
        console.error('复制失败');
    }
    document.body.removeChild(textarea);
    return result;
}

可见,document.execCommand('copy')是个同步的过程,我们可以通过结果来判断复制的成功与否。顺便补充,粘贴剪切板内容即document.execCommand('paste')了,粘贴前需要先聚焦(focus)最终粘贴的目标元素。

封装成自定义指令 v-copy

诉求是用户点击复制按钮,复制某段内容到剪切板内——这里完全可以通过指令的形式实现。

function copy(event) {
    const textarea = document.createElement('textarea');
    textarea.value = event.target.$value;
    textarea.style.cssText = 'position: fixed; z-index: -10000; opacity: 0; pointer-events: none';
    document.body.appendChild(textarea);
    textarea.select();
    // 执行复制命令
    const result = document.execCommand('copy');
    if (result) {
        console.log('复制成功');
    } else {
        console.error('复制失败');
    }
    document.body.removeChild(textarea);
}
Vue.directive('copy', {
    bind(el, { value }) {
        el.$value = value;
        el.addEventListener('click', copy);
    },
    componentUpdated(el, { value }) {
        el.$value = value;
    },
    unbind(el) {
        el.removeEventListener('click', copy);
    },
});

在Vue模板内只需要很简单的指令调用就可以了。

<button v-copy="data">点击复制</button>

But坏消息,document.execCommand在MDN官网上显示已废弃:

image.png

Clipboard

Clipboard API提供了响应剪贴板命令(剪切、复制和粘贴)与异步读写系统剪贴板的能力。

async function copy(content) {
    if (!navigator.clipboard) {
        // 使用document.execCommand方法
        return;
    }
    try {
        await navigator.clipboard.writeText(content);
        console.log('复制成功');
    } catch(error) {
        console.error('复制失败:', error);
    }
}

相比document.execCommand方法来说,Clipboard API的实现更优雅、合理些。因为相对较新,规范也还处在Working Draft状态,难免存在些兼容问题。

Clipboard API

writeText(string) 将文本内容写入剪切板,然后返回Promise对象。

write(dataTransfer) 将任意数据(比如文本、图片)写入剪切板,然后返回Promise对象。

readText() 从剪切板读取文本内容,然后返回Promise对象。

read() 从剪切板读取文本、图片等内容,然后返回Promise对象。

检查Clipboard权限

从权限Permissions API获取权限之后,才能访问剪贴板内容;如果用户没有授予权限,则不允许读取或更改剪贴板内容。

比如readText()读取剪切板内容时,如果用户没有授予过权限,系统弹出权限对话框,询问用户是否允许脚本读取剪切板数据。如果用户拒绝,readText()读取内容失败(reject)。 image.png

Clipboard API的读写能力对应clipboard-readclipboard-write权限,Permissions API允许获取对应的权限状态,可用于提示用户是否有权限进行对应操作。

await navigator.permissions.query({name: 'clipboard-read'});
// result: {state: 'granted'}
// granted-同意,prompt-询问,denied-拒绝

禁止复制

CSS

user-select禁止用户进行文本选择。

body { user-select: none; }

JavaScript

有交互就会有事件,用户复制内容到剪切板时,会触发copy事件,取消它的默认操作即可。

document.body.addEventListener('copy', event => {
    event.preventDefault();
    // 或者
    // return false;
})

不管是CSS还是JavaScript,都仅仅只是阻止用户在页面上复制而已。打得开调试工具的,照样复制无误。

改动复制内容

比如,从掘金上复制一段内容,复制的内容会附带上作者、链接、来源等信息,比如:

作者:vickiZheng7
链接:https://juejin.cn/post/6844903982297513991
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

只要阻止copy事件的默认行为,然后修改剪切板的内容,就可以实现这种效果了。

document.body.addEventListener('copy', event => {
    event.preventDefault();
    let selection = document.getSelection().toString();
    if (selection.length > 50) {
        selection += '\n' + '来源:xxx';
    }
    event.clipboardData.setData('text/plain', selection);
})

示例中调用了事件对象event.clipboardDatasetData(type, data)方法,修改了剪切板的内容。

你还可以通过event.clipboardData.getData(type)获取剪切板数据,event.clipboardData.clearData([type])清除剪切板数据。

以上。

参考链接

document.execCommand

剪贴板操作 Clipboard API 教程

前端er怎样操作复制粘贴

Async Clipboard API: Accessing the clipboard using JavaScript