在Chrome上document.execCommand('copy')无法工作

20
只有在Chrome浏览器中,document.execCommand('copy')返回true但不会复制文本,而是清空剪贴板。
我找不到任何人遇到同样的问题,虽然有很多类似的问题,但除非确实是重复的,否则请不要将此标记为重复。
  • selection.addRange()之前调用selection.removeAllRanges()
  • selection.getRangeAt(0).cloneContents()返回包含正确文本的片段
  • 文本区域中的文本似乎没有被选择
  • 如果在document.execCommand('copy')之前调用textarea.select(),则文本将出现选定状态并复制到剪贴板。我不想这样做,因为它会使文本区域获得焦点并导致页面滚动。
  • 在Chrome 61和63上进行测试,MacOS操作系统
  • 在Safari中工作正常
以下是我的代码(在单击事件侦听器中使用):
https://codepen.io/jakecr/pen/XVXvKz
var textarea = document.createElement('textarea');
textarea.textContent = 'coppied text';
document.body.appendChild(textarea);

var selection = document.getSelection();
var range = document.createRange();
range.selectNodeContents(textarea);
selection.removeAllRanges();
selection.addRange(range);

// DOESN'T WORK WITHOUT THIS
// textarea.select();

console.log(selection.getRangeAt(0).cloneContents());
console.log('copy success', document.execCommand('copy'));
3个回答

34

我不太清楚这里实际上发生了什么......

似乎在你的文本框中应该使用 valuetextContent 属性之间存在不匹配。
Chrome 似乎总是使用 value,而 Firefox 使用 textContent

btn.onclick = e => {
  const txt = document.createElement('textarea');
  document.body.appendChild(txt);
  txt.value = 'from value'; // chrome uses this
  txt.textContent = 'from textContent'; // FF uses this
  var sel = getSelection();
  var range = document.createRange();
  range.selectNode(txt);
  sel.removeAllRanges();
  sel.addRange(range);
  if(document.execCommand('copy')){
    console.log('copied');
  }
  document.body.removeChild(txt);
}
<button id="btn">Copy!</button>
<textarea>You can paste here

</textarea>

由于Chrome浏览器不查看textContent属性,所以Range#selectNodeContents在该浏览器上将无法选择任何内容...

但是,在这种情况下,您可以使用Range#selectNode,在此情况下应该返回相同的结果,并解决此问题。

document.getElementById('btn').addEventListener('click', function(){
  var textarea = document.createElement('textarea');
  textarea.textContent = 'copied text';
  document.body.appendChild(textarea);

  var selection = document.getSelection();
  var range = document.createRange();
//  range.selectNodeContents(textarea);
  range.selectNode(textarea);
  selection.removeAllRanges();
  selection.addRange(range);

  console.log('copy success', document.execCommand('copy'));
  selection.removeAllRanges();

  document.body.removeChild(textarea);
  
})
<button id="btn">copy</button>
<textarea>You can paste here</textarea>


34

对于2020年阅读此问题的人,如果您在使用document.execCommand('copy')时遇到问题,您可以尝试使用Clipboard API。

根据Mozilla

浏览器扩展可以与系统剪贴板交互的方法有两种:Document.execCommand()方法和现代的异步Clipboard API。

同样根据Mozilladocument.execCommand()现已过时:

此功能已过时。虽然某些浏览器仍可使用,但不建议使用它,因为它可能随时被移除。请尽量避免使用它。

使用Clipboard API,向剪贴板写入文本特别容易:

const textToCopy = 'Hello there!'
navigator.clipboard.writeText(textToCopy)
  .then(() => { alert(`Copied!`) })
  .catch((error) => { alert(`Copy failed! ${error}`) })

更多信息:

Mozilla关于两个剪贴板系统的讨论

Google关于两个剪贴板系统的讨论

另一个关于Clipboard API的好讨论

CanIUse


2
谢谢!我一直在到处寻找这个! - Paulo Hgo
那么使用原生JavaScript没有集中的方法来做到这一点吗? - leopinzon
谢谢!我是这样实现的:onClick='navigator.clipboard.writeText("CopyMe")' - Ken Palmer
今天,在2021年5月,这是在当前版本的EDGE中唯一有效的方法。 - Sarah Trees
1
不幸的是,这对于其他内容类型(如text/html)无效。 - Dorian Marchal
安全原因 - Doerthous

2
我发现你无法动态插入输入框,插入一些文本,然后将其复制到剪贴板。但我成功地从现有的输入标签中复制了文本。"最初的回答"

以上代码动态插入了一个输入字段,对我而言它起作用了。 - Jake
1
我正在添加一个隐藏的输入字段,但我应该修改我发现的内容。最终我所做的是附加一个可见的输入字段,放入内容,execCommand('copy'),然后隐藏输入元素。 - voodoologic
1
这对我帮助很大。我有一个隐藏的 div 中的输入,我在动态更改其内容。必须在进行选择时将 div 设置为可见,否则它不起作用。 - Dr_Derp
传统的方法(也就是hack)动态创建DOM输入元素并从中复制,仍然在许多情况下对我们有效,但是我们发现在某些UI框架中(可能是@Dr_Derp所描述的),其他事件或焦点/时间似乎会干扰,还与我们附加元素的位置有关。navigator.clipboard API在这里为我们节省了不少麻烦,因为我们只支持最近的浏览器。 - storsoc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接