js复制到粘贴板

314 阅读2分钟

最近有需求,需要通过点击按钮,将卡密复制到粘贴板。一开始想着要 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 中注册,以便后续使用。