现在很多网站,尤其是技术性强的论坛、博客等,用户都可能有大量复制需求。但用户手动复制在内容不止一屏时很不方便,且一些网站的设计可能导致用户无法正确复制内容。因此,就需要使用脚本复制内容,实现点击按钮自动复制的效果。
本文大部分属性及方法参考MDN开发者文档,并有对应页面的链接,可供参考。
注:末尾附录有无注释的源代码,可直接在项目中使用。
显然,最方便的方法便是直接设置剪贴版的数据。在IE中暴露了 window.clipboardData 属性,可获取到一个表示剪贴版数据的对象。
这个对象的类型,至少在符合规范的新版本中是DataTransfer。在剪贴版这一例中,用到的该借口的函数有三个:
getData(type)
,获取指定 type
类型的数据值。setData(type,data)
,设置指定 type
类型的数据值为value
。clearData([type])
,清空指定 type
类型的数据。如果不提供参数,则清空所有数据。要注意该type
不是通常意义的key
,它的取值是固定的,如 text/plain
(可直接使用 text
), text/html
, text/uri-list
等,显然是指 MIME 类型。
于是,简单地使用以下代码便可自动复制内容:
window.clipboardData.setData("text",data);
当然,这很不安全。因此,只有IE支持它。
显然,上述操作剪贴版的方式使得用户在不同位置的数据暴露给未知的第三方。所以大多数浏览器禁止了这一操作。但是,至少单纯地复制选中区域风险不大,这通常是被允许的。
但是,这就要求系统将选区调整到某个包含所需文本的区域,然后再设法触发复制操作。如果自动复制功能只是复制现成的元素,并且内容是连续的话(目前只有Firefox允许多个区域同时选中,并且复制的内容无法保证正确),直接将选区调整到该段区域即可。
但大多数情况下我们需要像 setData
方法那样复制特定文本,还要防止各种元素被错误复制。因此,就需要创建一个临时的文本节点,从而达到复制的效果。
基本的复制函数如下(下附无注释完整版):
function copy(data) {
var sel = window.getSelection(); //获取Selection对象
var range = document.createRange(); //创建Range对象
var node = document.createTextNode(data); //创建文本节点,并指定内容
document.body.appendChild(node); //加入body末尾(否则无法选中)
range.selectNode(node); //选中文本节点
sel.removeAllRanges(); //删除原先选区
sel.addRange(range); //将区域加入选区
document.execCommand("copy"); //execCommand执行复制操作
document.body.removeChild(node); //删除临时节点
}
copy("该文本将会被复制");
其中,execCommand
是用于对文档执行特定命令的方法。使用 copy
命令可指定复制当前选中区域。
另外,注意 cut
命令在非IE浏览器不能用于删除不可编辑元素,因此应使用 copy
+ 删除元素确保元素被从文档删除。
该函数的意义十分清晰:
Range
区域;body
末尾,然后设置区域包括该节点;当然,该版本还未优化兼容性,并且会导致原有选区清除。因此,笔者在附录中新增了一个完整版本。
其中,Selection 是目前主流浏览器(IE 9+)都支持的,用于表示选择区域的接口。当前选区对象可使用 window.getSelection()
方法获取。
比较重要的 Selection 属性和方法有:
anchorNode
focusNode
,获取选区锚点(按住开始选中的点)及焦点(松开结束选中的点)各自所在的节点。注意锚点不一定在焦点之前。anchorOffset
focusOffset
,获取选区锚点及焦点在所在节点的偏移量。如果把文本节点中每个字符看作一个子节点,则该值可看作边界点在所在节点的第几个子节点之后。isCollapsed
,返回选区是否折叠(即锚点与焦点是否位于同一点)。折叠的选区可以用于表示插入光标(Caret)的位置!getRangeAt(index)
,获取指定下标的 Range 的引用。还有一个重要的 Range 对象,它表示文档的某一区域,Selection 就是用它处理选区。顺便要注意,一个 Selection 可能有多个 Range,也就是用户按住Ctrl键选中多个区域时(目前除Firefox外的浏览器都禁止多个选区)。
Range 的属性中,有 Range.startContainer
Range.startOffset
Range.endContainer
Range.endOffset
属性,显然和刚刚 Selection 对应的属性意义相同,此处不赘述。
重要的部分方法有:
setStart(node,offset)
setEnd(node,offset)
,将区域的起点/终点设在 node
节点内,偏移量为 offset
处。selectNode(node)
,使区域包含 node
节点及其内容。insertNode(node)
,在区域之前插入 node
节点,并包括该新增节点。因此,灵活使用这些方法,就能实现简单的内容自动复制。当然,IE9以下必须使用 document.selection
处理,这在DHTML文档中有详细说明,此处不再赘述。
完整的复制函数如下(兼容IE 4+)。
function copy(data){
if(window.clipboardData){
window.clipboardData.setData("text",data);
}
else if(document.execCommand){
if(document.selection){
var sel = document.selection,srange = sel.createRange();
var range = document.createTextRange();
var node = document.createElement("span");
var temp = document.body.createTextNode(data);
node.style.display = "none";
node.appendChild(temp);
document.body.appendChild(node);
range.moveToElementText(node);
range.select();
document.execCommand("copy");
srange.select();
document.body.removeChild(node);
}
else if(window.getSelection){
var sel = window.getSelection(),srange = [];
var range = document.createRange();
var node = document.body.createTextNode(data);
for(var i=0; i<sel.rangeCount; i++) {
srange[i]=sel.getRangeAt(i);
}
document.body.appendChild(node);
range.selectNode(node);
sel.removeAllRanges();
sel.addRange(range);
document.execCommand("copy");
document.body.removeChild(node);
sel.removeAllRanges();
for(var i in srange) {
sel.addRange(srange[i]);
}
}
}
}