0%

javascript复制文本到剪贴板

最近遇到个需求,需要在页面上点击一个复制按钮,然后复制对应的文本信息到剪贴板。脑子里完全是懵懵的,一般的处理都是页面的逻辑处理,还没有涉及过这方面的。

如何复制一段文本?当我们复制的时候,是先用鼠标选中某一段文字,然后按下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
2
3
4
5
6
// 我们要复制的内容包含在这个节点里面
let myEle = document.getElementById(id);
// 创建一个选区对象:range
let range = document.createRange();
// 创建一个selection对象
let selection = window.getSelection();

然后我们用选区对象Range选择我们需要的内容所在的元素节点,然后将Range交给Selection对象,创造复制的文本内容:

1
2
range.selectNodeContents(myEle);
selection.addRange(range);

然后调用document.execCommand()方法来复制到剪贴板:

1
2
3
4
if (document.execCommand) {
// 复制选中的文字到剪贴板
document.execCommand('copy', false, null);
}

这里先判断document.execCommand方法是否存在,如果存在就调用。

但是经过测试发现,有时候第一次起作用第二次就不起作用了,而且如果鼠标点击太快,就把按钮的文字选中然后复制上去,这就很蛋疼了。

我们还需要做些操作:

  1. 在复制操作创建选区之前,确保没有其他选区,也就是需要移除掉所有选择的内容,使用selection.removeAllRanges();
  2. 对复制命令做异常控制,以防止在不支持的浏览器里面造成程序错误
  3. 对复制按钮进行无法选择设置,可以用css:user-select:none;

最后完整的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function copyText = function(id) {
let myEle = document.getElementById(id);
let range = document.createRange();
let selection = window.getSelection();

range.selectNodeContents(myEle);
selection.removeAllRanges(); // 先移除掉所有的选择区域
selection.addRange(range); // 添加元素的目标选择区域
try {
if (document.execCommand) {
// 复制选中的文字到剪贴板
document.execCommand('copy', false, null);
selection.removeAllRanges(); // 移除掉所有的选择区域
console.log('复制成功');

}
} catch (e) {
console.log('不支持复制命令:', e);
}
}

2019.12.06日更新

在做复制功能的时候,我们需要在页面上放一个用来选中复制内容的元素,但是这个元素并不需要在页面上展示,所以我们会将元素设置为不可见,比如这样:

1
2
3
<div class="copy-area" style="display:none;">
test
</div>

但是,在display:none;之后,发现我们的copy功能失效了。原因是,元素不可见导致没法选中,所以复制的内容是空的。

其实,我们只需要让节点本质上是可见的,但是肉眼是看不见的即可。😯

可以将元素的节点高度设置为0,然后透明:

1
<div class="copy-area" style="height: 0; opacity: 1"></div>

这样,元素在页面上看不见,也不影响我们的页面,也可以正常进行复制,ok~

码字辛苦,打赏个咖啡☕️可好?💘