- 需用户先主动触发一个交互动作,才有权限复制文本。注意这里不能取巧,比如我创建一个dom,然后调用click之类的dom原生方法,并监听click事件,在click事件回调写下面的代码,最后在把这个dom移除。实测是不行的,必须触发源是用户使用硬件与UI交互才行。也就是下面代码要执行在事件回调中。
const copyFunc = (str) => { navigator.clipboard.writeText(str); }
navigator.clipboard.writeText方法返回一个Promise对象实例。
如果使用iframe跨域的情况需要启用Permissions Policy,在创建iframe的dom对象时给allow属性赋值“clipboard-write”启用剪贴板写的权限, 需要读的权限则再加上“clipboard-read”。若已经挂载到domtree中并在html中被渲染后,再使用控制台更改allow属性是不行的,会被浏览器视为无权限。<iframe src="" width="100%" height="100%" allow="clipboard-read; clipboard-write"> ... </iframe>
-
模拟用户操作,调用document.execCommand("copy")的API,这里提供两种思路,一种是强制给ClipboardEvent对象强制写入值,但注意要阻止默认事件,否则会被复写。代码如下:
const copyFunc = (str) => { const inject = e => { e.preventDefault(); e.clipboardData && e.clipboardData.setData("text/plain", str); document.removeEventListener("copy", inject, false); } document.addEventListener("copy", inject, false); document.execCommand("copy"); }
另一种思路是创建一个文本选区,然后再执行document.execCommand("copy")复制文本,这个实现和用户实际操作非常接近了,以下提供两种方法。
这里可以用document.createRange方法来创建一个选区,代码如下:const copyFunc = (str) => { // 随便创建一个html元素dom对象,并挂载到domtree上 const oDom = document.createElement("span"); document.body.appendChild(oDom); oDom.textContent = str; // 先清除所有选区 const selection = window.getSelection(); selection?.removeAllRanges(); // 如果想先判断可以看selection对象上rangeCount属性 // 创建range选区对象,并建立选区 const range = document.createRange(); range.selectNode(oDom); // 把range对象挂载到浏览器的选区中 selection?.addRange(range); // 执行复制 document.execCommand("copy"); // 移除dom对象 document.body.removeChild(oDom); }
假如我们创建的dom是input元素,还可以取一个巧,调用dom的API——select方法,可以直接创建一个本文选区,可以少写一些代码。代码如下:
const copyFunc = (str) => { const oDom = document.createElement("input"); oDom.value = str; document.body.appendChild(oDom); oDom.select(); document.execCommand("copy"); document.body.removeChild(oDom); }
document.execCommand这个API目前浏览器还支持,但由于安全问题在新规范中这个API已经被移除了,兴许某天浏览器不再兼容也说不定。官方给出的代替方案就是本文的1号方法。
-
Chrome内核浏览器有个API——document.dispatchEvent,这个API可以模拟触发一个事件,现在我们将用它来代替document.execCommand方法。另w3c规范中提供了构造函数ClipboardEvent来创建一个剪贴板事件对象,这个规范已经在各大浏览器中实践了可以正常使用。如果模拟一个自定义的copy事件应该能实现吧:
const copyFunc = (str) => { const clipboardEvent = new ClipboardEvent("copy", { clipboardData: new DataTransfer("text/plain", str) }); document.dispatchEvent(clipboardEvent); }
实测不能,那如果仅模拟触发一个copy事件,再回调中用setData方法注入数据,也许能行?
const copyFunc = (str) => { const clipboardEvent = new ClipboardEvent("copy"); const inject = e => { e.clipboardData && e.clipboardData.setData("text/plain", str); document.removeEventListener("copy", inject, false); } document.addEventListener("copy", inject, false); document.dispatchEvent(clipboardEvent); }
事件实测是可以触发的,也可以打印出这个e参数;但实际这个e.clipboardData的值为null,所以压根不会执行后面的方法。再优化一下:
const copyFunc = (str) => { const clipboardEvent = new ClipboardEvent("copy", { clipboardData: new DataTransfer("text/plain", "") }); const inject = e => { // e.preventDefault() 这里已经当作默认事件处理了,加不加不影响 e.clipboardData && e.clipboardData.setData("text/plain", str); document.removeEventListener("copy", inject, false); } document.addEventListener("copy", inject, false); document.dispatchEvent(clipboardEvent); }
很遗憾,实测依然不行。具体原因未知,但思路应该没有问题,如果有知道怎么解决的还请告知。
以上。实现很简单,但管理各种库才是麻烦事!尽量少用。
JS封装复制文本内容到剪贴板的方法
于 2022-12-18 02:57:07 首次发布