点击复制数据
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官网上显示已废弃:
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)。
Clipboard API的读写能力对应clipboard-read
和clipboard-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.clipboardData
的setData(type, data)
方法,修改了剪切板的内容。
你还可以通过event.clipboardData.getData(type)
获取剪切板数据,event.clipboardData.clearData([type])
清除剪切板数据。
以上。
参考链接
Async Clipboard API: Accessing the clipboard using JavaScript