javascript剪切/复制/粘贴到剪贴板:谷歌是如何解决的?

javascript剪切/复制/粘贴到剪贴板:谷歌是如何解决的?,javascript,copy,clipboard,paste,Javascript,Copy,Clipboard,Paste,是的,这个问题一再被问到:如何使用javascript从系统剪贴板复制和粘贴到系统剪贴板?到目前为止,我只找到了部分解决方案和破解方法。 过去经常提出这一问题的原因是仍然没有有效的解决办法。然而,我看到谷歌文档实际上已经为键盘事件和按钮提供了一个可行的解决方案。所以,这是可能的,但他们是如何做到的呢?软件沙拉的文章,给出了一个很好的问题概述(但它是几年前) 简言之: 您可以使用键盘事件ctrl+x、ctrl+c、ctrl+v从具有准备好的数据的隐藏文本区域复制文本,或捕获隐藏字段中粘贴的文本,

是的,这个问题一再被问到:如何使用javascript从系统剪贴板复制和粘贴到系统剪贴板?到目前为止,我只找到了部分解决方案和破解方法。 过去经常提出这一问题的原因是仍然没有有效的解决办法。然而,我看到谷歌文档实际上已经为键盘事件和按钮提供了一个可行的解决方案。所以,这是可能的,但他们是如何做到的呢?软件沙拉的文章,给出了一个很好的问题概述(但它是几年前)

简言之:

  • 您可以使用键盘事件ctrl+x、ctrl+c、ctrl+v从具有准备好的数据的隐藏文本区域复制文本,或捕获隐藏字段中粘贴的文本,然后对其执行操作

  • 您可以通过Flash或Java小程序使用一些黑客技术将内容复制到系统剪贴板,而无需用户批准

  • 您可以将“真实”解决方案用于IE的clipboardData.setData和其他浏览器的execCommand,这取决于用户的批准


知道谷歌是如何解决剪贴板问题的吗?

[注意:这个答案在撰写时是准确的,并且正确地回答了OP的问题。但是,从那时起,技术不断发展;如果您对在web应用程序中支持复制和粘贴感兴趣,请参阅本页上其他更新的答案。-ruakh]


然而,我看到谷歌文档实际上已经为键盘事件和按钮提供了一个可行的解决方案

不,没有。不完全是。对于键盘事件,Google Docs不做任何事情;它只是不阻止浏览器的默认复制和粘贴功能;因此,用户可以自由复制和粘贴,而不需要Google Docs的阻碍。对于按钮,Google Docs不支持系统剪贴板,而是支持它自己的“web剪贴板”这完全在谷歌文档中。你不能使用工具栏按钮复制要粘贴到计算机上其他程序的文本,也不能粘贴从计算机上其他程序复制的文本


有关这方面的更多信息,请参阅。(这是面向用户的,而不是面向开发人员的,但它很好地说明了什么是支持的,什么是不支持的。)

谷歌使用了一种非常简单但很酷的方法。通过使用firebug,你会知道加载的html代码有一个大小为1的文本区域。谷歌文档的作用是,当用户选择文本并按下ctrl+c时,它捕获事件,并通过某种技术获取文档容器中选择的文本,并设置v文本区域对该内容的值。然后它聚焦并选择文本区域。现在它释放ctrl+c事件。但现在文本在文本区域中被选中,因此当事件重新发生时,浏览器将文本复制到文本区域,从而我们得到复制的文本。我知道这个问题很久以前就发布了,但我需要检查谷歌是如何做到的,所以也许有人会觉得这很有用

实际上谷歌也使用系统剪贴板,但这有点棘手。如果你使用键盘快捷键,你可以捕捉到复制/粘贴/剪切事件,例如窗口:

window.addEventListener('copy', function (ev) {
    console.log('copy event');
    // you can set clipboard data here, e.g.
    ev.clipboardData.setData('text/plain', 'some text pushed to clipboard');
    // you need to prevent default behaviour here, otherwise browser will overwrite your content with currently selected 
    ev.preventDefault();
});
键盘快捷键的实时示例:

不幸的是,这是键盘快捷键的唯一解决方案,上下文菜单也有问题,因为如果没有本机(受信任的)复制/剪切/粘贴事件,您就无法访问剪贴板数据。但google做了一个有趣的把戏。有API
document.execCommand()
允许您为contenteditable元素运行命令,还有一个命令“copy”,您可以通过
document.execCommand('copy')触发它
。但当您在Chrome控制台中尝试此操作时,它将返回
false
。我花了一点时间调查,结果发现他们安装了名为“Google Drive”的Chrome扩展(转到chrome://apps/ 你可以在那里看到)这将启用对域drive.google.com和docs.google.com的剪贴板访问。打开一些文档或电子表格并在控制台中键入
document.execCommand('copy')
-它将返回
true
。卸载扩展时,您将无法从上下文菜单中使用剪贴板操作

您可以使用非常简单的清单文件为自己创建此类应用程序(详细信息如下所示):

此处的“权限”字段启用剪贴板操作

现在,当您启用此功能后,您可以执行
document.execCommand('copy')
,它将正常工作(将返回
true
)。但这并不是一切-
document.execCommand('copy'))
在chrome中触发复制事件,您可以使用用于捕捉键盘剪贴板快捷键的相同代码捕捉该事件。这就是谷歌现在所做的


当然,此描述仅对Chrome有效。

作为其他人在本帖中已经发布的内容的补充,我创建了一个完整的示例,演示了键盘快捷方式(Mac OS X上的CTRL+C或CMD+C)以及触发复制操作的自定义按钮方式

完整的演示可以在这里找到:

我发现Mateusz W answer在尝试创建此演示时非常有用,但他没有考虑对IE的支持,IE的行为略有不同,并使用不同的数据类型作为第一个参数

if(window.clipboardData) {
    // use just 'Text' or 'Url' as a first param otherwise strange exception is thrown
    window.clipboardData.setData('Text', 'Text that will be copied to CB');        
} else if(ev.originalEvent.clipboardData) {
    ev.originalEvent.clipboardData.setData('text/plain', 'Text that will be copied to CB');      
} else {
    alert('Clipboard Data are not supported in this browser. Sorry.');
}
PS:我需要为我们的自定义电子表格视图组件提供此功能,并在途中分析了Google电子表格的源代码,以便我的解决方案基本上符合他们的解决方案。

复制:

<p>COPY : </p>
<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" value="clipboard" >clipboard</button></p>
<textarea rows="10" cols = "12"></textarea>
<p>CUT: </p>
<p><textarea class="js-cuttextarea">Hello I'm some text</textarea></p>
<p><button class="js-textareacutbtn" disable>Cut Textarea</button></p>
<script>
//copy clipboard
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');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Copy email command was ' + msg);
  } catch(err) {
    console.log('Oops, unable to copy');
  }
  // Снятие выделения - ВНИМАНИЕ: вы должны использовать
  // removeRange(range) когда это возможно
  window.getSelection().removeAllRanges();
});
//cut
var cutTextareaBtn = document.querySelector('.js-textareacutbtn');
cutTextareaBtn.addEventListener('click', function(event) {
  var cutTextarea = document.querySelector('.js-cuttextarea');
  cutTextarea.select();
  try {
    var successful = document.execCommand('cut');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Cutting text command was ' + msg);
  } catch(err) {
    console.log('Oops, unable to cut');
  }
});
</script>
发电子邮件给我

剪贴板

削减:

你好,我是一些短信

剪切文本区域

//复制剪贴板 var copyEmailBtn=document.querySelector('.js emailcopybtn'); copyEmailBtn.addEventListener('click',函数(事件){ // Выборка ссылки с электронной почтой var emailLink=document.querySelector('.js emailLink'); var range=document.createRange(); 范围。选择节点(电子邮件)
<p>COPY : </p>
<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" value="clipboard" >clipboard</button></p>
<textarea rows="10" cols = "12"></textarea>
<p>CUT: </p>
<p><textarea class="js-cuttextarea">Hello I'm some text</textarea></p>
<p><button class="js-textareacutbtn" disable>Cut Textarea</button></p>
<script>
//copy clipboard
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');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Copy email command was ' + msg);
  } catch(err) {
    console.log('Oops, unable to copy');
  }
  // Снятие выделения - ВНИМАНИЕ: вы должны использовать
  // removeRange(range) когда это возможно
  window.getSelection().removeAllRanges();
});
//cut
var cutTextareaBtn = document.querySelector('.js-textareacutbtn');
cutTextareaBtn.addEventListener('click', function(event) {
  var cutTextarea = document.querySelector('.js-cuttextarea');
  cutTextarea.select();
  try {
    var successful = document.execCommand('cut');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Cutting text command was ' + msg);
  } catch(err) {
    console.log('Oops, unable to cut');
  }
});
</script>