最近遇到个需求,需要在页面上点击一个复制按钮,然后复制对应的文本信息到剪贴板。脑子里完全是懵懵的,一般的处理都是页面的逻辑处理,还没有涉及过这方面的。
如何复制一段文本?当我们复制的时候,是先用鼠标选中某一段文字,然后按下ctl+c进行复制,那JavaScript如何操作浏览器呢?
JavaScript要进行复制功能,基本的语法和逻辑操作是绝对没法操作的,只能定位到JavaScript的DOM模型和浏览器环境,浏览器环境主要是对window对象以及其他浏览器操作,所以我们还是看看DOM模型是否提供了对应的接口可以使用。
DOM是JavaScript操作网页的接口,全称为“Document Object Model”,它的作用是将网页转换为一个JavaScript对象,从而可以使用脚本进行各种操作。
document.designMode
不得不说下document.designMode
,designMode控制这个文档是否可编辑,有效值为on/off
。这个属性的默认值为off,如果设置为on,那么这个页面中的所有元素都可编辑,可以按f12调出开发者工具,在console里面输入:
1 | document.designMode = 'on'; |
运行后你会发现,你可以随意的编辑这个页面上的所有内容,感觉是整个页面都类似于一个ide编辑器了。
document.execCommand
当一个html文档切换到设计模式designMode
时,文档对象暴露execCommand
方法,该方法允许运行命令来操作可编辑区域的内容。语法:
1 | bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument); |
返回值如果返回false
表示操作不被支持或未启用。参数:
- aCommandName:命令名称,可以是
backColor
,或者copy
,或者其他的。 - aShowDefaultUI:是否展示用户界面,如果为true,那么在页面上可以看到选中的区域。
- aValueArgument:一些命令需要额外的参数,比如
insertImage
需要提供插入image的url
这个很接近我们的目标了,我们是不是可以让JavaScript来控制选区,然后复制选中区域呢?
可以使用window.getSelection()
来获得用户选择的文本范围或光标所在的位置。可以测试下这个函数,在页面中选择一个区域,然后按f12调出开发者工具,在console里面输入:
1 | window.getSelection().toString(); |
可以看到这里输出了我们刚刚选择的区域的内容。
又进一步了,我们需要的是点击按钮后自动选择我们需要的内容,而不是用户来手动选择~那我们需要细看window.getSelection()
这个方法。
window.getSelection()
window.getSelection()
返回一个Selection
对象。Selection
对象表示用户选择的文本范围或插入符号的位置,它代表页面中的文本选区。可以跨越多个元素,文本选区由用户拖拽鼠标经过文字而产生。要获取Selection对象,直接调用window.getSelection()
即可。
Selection
对象所对应的是用户所选择的ranges
区域,俗称“拖蓝”。具体Selection
对象的用法可参照这个MDN。
我们只挑选出我们需要用到的:
- addRange() 将一个区域对象
range
加入到选区 - removeRange() 将一个区域对象从选区中移除
- removeAllRanges() 将所有的区域都从选区中移除
好了,然后我们需要做的是如何去搞一个选区对象Range
。
range
Range
表示包含节点和部分文本节点的文档片段。Range
可以用Document
对象的createRange
方法创建,也可以用Selection
对象的getRangeAt
方法获得。具体Range
的介绍可以参照这个MDN文档。我们照样选择我们使用的方法selectNodeContents()
,设定包含某个节点内容的Range
。
实现
ok,我们获得了所有的技术,现在需要开始实现这个功能了,首先我们创建三个变量:
1 | // 我们要复制的内容包含在这个节点里面 |
然后我们用选区对象Range
选择我们需要的内容所在的元素节点,然后将Range
交给Selection
对象,创造复制的文本内容:
1 | range.selectNodeContents(myEle); |
然后调用document.execCommand()
方法来复制到剪贴板:
1 | if (document.execCommand) { |
这里先判断document.execCommand
方法是否存在,如果存在就调用。
但是经过测试发现,有时候第一次起作用第二次就不起作用了,而且如果鼠标点击太快,就把按钮的文字选中然后复制上去,这就很蛋疼了。
我们还需要做些操作:
- 在复制操作创建选区之前,确保没有其他选区,也就是需要移除掉所有选择的内容,使用
selection.removeAllRanges();
- 对复制命令做异常控制,以防止在不支持的浏览器里面造成程序错误
- 对复制按钮进行无法选择设置,可以用css:
user-select:none;
最后完整的方法:
1 | function copyText = function(id) { |
2019.12.06日更新
在做复制功能的时候,我们需要在页面上放一个用来选中复制内容的元素,但是这个元素并不需要在页面上展示,所以我们会将元素设置为不可见,比如这样:
1 | <div class="copy-area" style="display:none;"> |
但是,在display:none;
之后,发现我们的copy功能失效了。原因是,元素不可见导致没法选中,所以复制的内容是空的。
其实,我们只需要让节点本质上是可见的,但是肉眼是看不见的即可。😯
可以将元素的节点高度设置为0,然后透明:
1 | <div class="copy-area" style="height: 0; opacity: 1"></div> |
这样,元素在页面上看不见,也不影响我们的页面,也可以正常进行复制,ok~