JS67 复制到剪贴板
想要实现这样一个需求,给定一个输入框和按钮,点击按钮时会将输入框的内容复制到剪贴板,方便用户操作。该如何实现呢?
Clipboard API
Clipboard API的接口提供了一种读写操作系统剪贴板的方式,它有四个方法,分别是:
read
,从剪贴板读取数据,比如图片readText
,从剪贴板读取文本write
,写入数据(比如图片)到操作系统剪贴板writeText
,写入文本到剪贴板
我的需求是实现将文字写入到剪贴板,使用writeText
方法即可:
1 | const promise = navigator.clipboard.writeText(newClipText) |
clipboard
是挂在navigator
对象下的,接受一个字符串作为参数,返回一个Promise。如果写入成功就会被resolve
,如果没有权限则会被reject
很简单,但是它的兼容性不怎么样:
可以看出,IE、Edge、Safari全军覆没,即便FireFox和Chrome也都是很新的版本才支持,所以在生产环境上这个API肯定是不能完全胜任的。
document.execCommand
这个方法可以用来操作可编辑区域(比如鼠标选择的文本)的内容,实现富文本功能时大多会用到这个方法:
1 | bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument) |
返回值是一个Boolean,如果是false
代表操作不被支持。MDN上提示:注意不要用这个返回值去校验浏览器的兼容性(不太明白原理,如此验证会导致性能问题还是报错?既然不用来验证,返回值还有什么用?)
它有三个参数:
(1)第一个参数是一个字符串,是要执行的命令的名称。能够执行的命令非常多,不仅仅包括我们后面会用到的将选中内容复制到剪贴板的copy
方法,还有例如fontSize
、insertImage
、cut
等方法,具体可以参考命令列表;
(2)第二个参数是一个Boolean,表示是否展示用户界面,一般为false
,因为Mozilla没有实现这个功能
(3)第三个参数是给一些命令使用的额外的参数,默认为null
想要用它实现复制输入框内容,必须有可编辑区域,比较常用的就是input
(或者是contenteditable
为true
的区域),同时必须将可编辑区域中的内容,如果使用的容器是input
,那么需要让input
先focus
,然后通过input
的select
方法选中输入框中的内容(如果是contenteditable
为true
时则需要使用window.getSelection
获得用户选择的文本范围或光标位置)
在我们的需求中,可以利用现成的input
来当做容器,但是如果是作为通用的方法,如果用户双击一段文本后复制,那么就需要自己动手创建一个input
来实现。但是这个input
应该是隐藏的,不应该被用户发现,但是如果使用dispaly: none
是不行的,这样是没有办法获取到input
中的文本(用type="hidden"
也不行),所以我采用的是绝对定位,把input
定位到屏幕外,眼不见心不烦。
结合上面的Clipboard API,实现了一个公共方法(其中的Message
是一个用来提示的方法)
1 | /** |
document.execCommand
的兼容性还是相当不错的:
所以上面的方法基本上可以满足生产环境的要求了。
clipboard.js
实际上有现成的库可以使用,那就是clipboard.js
,官网在这里,Github的Star数和NPM的下载数都很高,并且压缩后体积只有3KB,直接使用应该是没有什么问题的。
它可以从一个元素复制、剪切文本,也可以从性中来复制文本,比如在Vue中我们可以直接使用在一个Button元素上,通过v-bind
绑定一个响应式数据在data-clipboard-text
属性上,就可以实现点击Button进行复制的功能
1 | <button class="btn" :data-clipboard-text="copyValue">Copy</button> |
它还有对应的成功或者失败的回调函数:
1 | var clipboard = new ClipboardJS('.btn'); |
更多的细节和高级用法可以参考它的官网。
它主要使用就是Selection
和execeCommand
这两个API,所以兼容性也是很不错的:
它的源码也不是很复杂,可以阅读这篇文章对源码的解读,其实自己去读一下也是可以的。
总结
所以,如果需要实现的功能不太复杂,那么上面自己封装的函数应该可以满足需求,如果需要频繁使用或者需要一些高级功能,那么可以直接使用clipboard.js
。