应用场景
关于拷贝粘贴的场景,大致上可划分以下两种
- 浏览器内(网页内和网页间)拷贝粘贴
- 系统中的任意进程与浏览器之间互相拷贝粘贴
限制
分析了应用场景之后,我们需要确定操作系统和浏览器的一些限制
- 是否有跨源(cross-origin)限制,即不同源的网页之间是否可以互相拷贝数据
- 是否被浏览器纳入网站权限控制列表中(因为需要访问系统资源,即剪切板)
- 能拷贝的最大字节数是多少
- 浏览器网页与系统其他进程之间、浏览器网页内、浏览器网页间的拷贝粘贴是否有区别
技术原理
拷贝粘贴的实质是系统剪切板 I/O(输入/输出),剪切板是操作系统内核维护的一块内存缓冲区,操作系统规定了这块内存缓冲区的最大大小,因此任何进程都不能拷贝超过这个大小的数据,浏览器可能会对它的访问增加进一步的限制,比如最大拷贝字节数,访问网站白名单,只能在用户触发的事件回调函数中访问等,这些额外的限制可能会导致在大量数据拷贝、跨站拷贝粘贴以及非用户事件回调等场景访问受限的问题,因此针对这些受限场景,应采取合适的解决方案或给予用户明确的提示
浏览器 API
浏览器目前主要提供了两种 API 用于支持剪切板 I/O
Document.execCommand()
document.execCommand 的主要用途是实现富文本编辑功能,它定义了一系列操作命令用来实现插入、删除、编辑文本、超链接、表格、列表、图片等内容,调整内容的样式、大小、布局,拷贝、剪切、粘贴内容,撤销与重做等功能,因此并没有提供直接访问系统剪切板的接口,我们需要按照它所定义的富文本编辑的模式间接达到访问剪切板的目的
拷贝/剪切
由于 document.execCommand 是针对富文本编辑设计的 API,它所定义的命令的操作对象是 DOM 元素,拷贝/粘贴也不例外,因此我们首先需要将待拷贝的数据转换成 DOM 元素,然后选中它,才能成功执行拷贝操作(document.execCommand("copy")
)
例如:
<p>
Email me at
<a class="js-emaillink" href="mailto:matt@example.co.uk">
matt@example.co.uk
</a>
</p>
<p>
<button class="js-emailcopybtn">
<img src="./images/copy-icon.png" />
</button>
</p>
var copyEmailBtn = document.querySelector(".js-emailcopybtn");
// 拷贝操作需要在事件回调函数内,否则不生效
copyEmailBtn.addEventListener("click", function (event) {
// 选择要拷贝的文档元素
var emailLink = document.querySelector(".js-emaillink");
var range = document.createRange();
range.selectNode(emailLink);
window.getSelection().addRange(range);
try {
// 现在执行拷贝操作
var successful = document.execCommand("copy"); // 可能会抛出 SecurityError 异常
var msg = successful ? "successful" : "unsuccessful";
console.log("Copy email command was " + msg);
} catch (err) {
console.log("Oops, unable to copy");
}
// 取消选择
window.getSelection().removeAllRanges();
});
以上示例首先利用 DOM Range 和 Selection API 选中了 <a>
标签,然后执行拷贝操作,成功拷贝了内容 matt@example.co.uk
,注意由于浏览器的安全策略,execCommand 可能会抛出异常,同时应检查它的返回值,如果调用失败,应给予用户合理的提示
针对 input
和 textarea
表单元素,我们也可以利用 HTMLInputElement.select() API 进行内容选择
注意:剪切只针对
input
、textarea
表单元素、可编辑文档或元素生效,因为它要从选择的 DOM 上删除拷贝的内容
粘贴
只有选择(激活)了富文本编辑模式下(Document.designMode/HTMLElement.contentEditable)的文档或内容,才能成功执行粘贴操作(document.execCommand("paste")
),被粘贴的内容将被浏览器直接插入到插入点所指定的区域,我们再从 DOM 中提取刚刚粘贴的内容
例如:
function paste() {
var pasteText = document.querySelector("#output");
pasteText.focus();
document.execCommand("paste");
console.log(pasteText.textContent);
}
document.querySelector("#paste").addEventListener("click", paste);
兼容性
Document.execCommand()
Document API: execCommand: copy command
测试地址:https://codepen.io/chrisdavidmills/full/gzYjag/
Clipboard API and events
Asynchronous Clipboard API
利用 navigator.clipboard
API 直接异步读写剪切板内容
Clipboard Events
利用 paste
事件和 clipboardData
事件属性读取剪切板内容
兼容性
Asynchronous Clipboard API
Clipboard Events