最近有需求,需要通过点击按钮,将卡密复制到粘贴板。一开始想着要 Android/iOS 提供能力,但 baidu 一下发现,js 原来可以通过 document.execCommand 实现。
1、通过创建 input 元素
- 说明
这种方式是最简单、最易懂的。
- 原理
1.创建一个 input 元素,给 input 元素赋值,添加到 body 元素下
2.选中 input 元素中的内容
3.执行复制到粘贴板操作
4.移除 input 元素
- 存在的问题
调试好提测自信满满,没想到还是发现问题了,在 PC 端开发、调试确实不会出现问题。
但是 iOS 会拉起键盘;复制后执行 input.blur() 键盘就会闪一下(拉起 -> 收起)。
于是放弃了这种方式。
- 实现代码
function copyInput(value) {
// 1.创建一个 input 元素,给 input 元素赋值,添加到 body 元素下
const input = document.createElement('input')
input.value = value
document.body.appendChild(input)
// 2.选中 input 元素中的内容
input.select()
// 3.执行复制到粘贴板操作
if (document.execCommand('Copy')) {
document.execCommand('Copy')
} else {
alert('复制失败')
}
// 4.移除 input 元素
document.body.removeChild(input)
}
2、通过选中范围
- 说明
这样就不会有拉起键盘的问题了。刚好红宝书看了范围(篇幅挺大),不过没想到什么应用场景,就粗略的过了一遍。
- 原理
1.创建元素,对其赋值,设置 userSelect 为 auto,添加到 body 元素下
2.创建范围,将范围覆盖待复制的节点
3.选择指定范围
4.执行复制操作
5.移除选中操作,移除待复制元素
- 存在的问题
如果项目中全局设置了 user-select: none; 那就需要将其设置为 auto, 要不然会阻止选取。我就是因为这个查了很久。(谁知道全局设置了这个属性🤡)
- 实现代码
function copyRange(value){
const copyEl = document.createElement('p')
copyEl.innerText = value
copyEl.style.userSelect = 'auto'
document.body.appendChild(copyEl)
const range = document.createRange()
range.selectNode(copyEl)
const selection = window.getSelection()
selection.addRange(range)
if (document.execCommand) {
document.execCommand('copy')
} else {
alert('复制失败')
}
selection.removeAllRanges()
document.body.removeChild(copyEl)
}
在 Vue 实例中使用
官方文档:v2.cn.vuejs.org/v2/guide/cu…
<template>
<div>
<button
v-copy:value="msg"
v-copy:success="onCopySuccess"
v-copy:fail="onCopyFail"
>复制</button>
</div>
</template>
<script>
export default {
data() {
return {
msg: 'cardKey888'
}
},
directives: {
copy: function (el, binding) {
// 暂时只想到添加在 window 上,不知道还有没有更好的方式
if (binding.arg === "success") el.successCallback = binding.value
else if (binding.arg === "fail") el.failCallback = binding.value
else {
el.onclick = function () {
const copyEl = document.createElement("p")
copyEl.innerText = binding.value
document.body.appendChild(copyEl)
const range = document.createRange()
range.selectNode(copyEl)
const selection = window.getSelection()
selection.addRange(range)
if (document.execCommand) {
document.execCommand('copy')
el.successCallback()
} else {
alert('复制失败')
el.failCallback()
}
selection.removeAllRanges()
document.body.removeChild(copyEl)
}
}
}
},
methods: {
onCopySuccess() {
console.log('onCopySuccess')
},
onCopyFail() {
console.log('onCopyFail')
}
}
}
</script>
在 main.js 中全局注册
Vue.directive('copy',
function(el, binding) {
if (binding.arg === 'success') {
el.successCallback = binding.value
} else if (binding.arg === 'fail') {
el.failCallback = binding.value
} else {
el.onclick = function() {
const copyEl = document.createElement('p')
copyEl.innerText = binding.value
copyEl.style.userSelect = 'auto' // 防止无法选中区域
document.body.appendChild(copyEl)
const range = document.createRange()
range.selectNode(copyEl)
const selection = window.getSelection()
selection.addRange(range)
if (document.execCommand) {
document.execCommand('copy')
el.successCallback()
} else {
el.failCallback()
}
selection.removeAllRanges()
document.body.removeChild(copyEl)
}
}
}
)
3、使用 vue 插件
1 下载依赖
npm install vue-clipboard2
2 在 main.js 中
import VueClipboard from 'vue-clipboard2'
Vue.use(VueClipboard)
3 使用
<button
v-clipboard:copy="'testcopy'"
v-clipboard:success="onCopy"
v-clipboard:error="onError"
>复制卡密</button>
总结
如果是 PC 端,用哪种都可以;
如果需要兼容移动端,最好还是使用通过选中范围实现,也可以在 vue 中使用 vue-clipboard2。
最好还是将复制指令抽离,在 main.js 中注册,以便后续使用。