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

javascript cut/copy/paste to clipboard: how did Google solve it?

本文关键字:何解决 解决 复制 剪切 javascript 剪贴板 谷歌      更新时间:2023-09-26

是的,这个问题已经被问了一次又一次:如何使用javascript从系统剪贴板复制和粘贴到系统剪贴板?到目前为止,我只找到了部分解决方案和技巧。过去人们之所以经常问这个问题,是因为仍然没有有效的解决方案。然而,我看到Google Docs现在实际上已经为键盘事件和按钮提供了一个有效的解决方案。所以,这是可能的,但他们是如何做到的?软件沙拉文章,用JavaScript访问系统剪贴板–圣杯,给出了一个很好的问题概述(但它已经有几年的历史了)。

简而言之:

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

  • 您可以通过Flash或Java Applet使用一些破解来将某些内容复制到系统剪贴板,而无需用户批准。

  • 您可以使用clipboardData.setData(IE)和execCommand(其他浏览器)的"真实"解决方案,这取决于用户的批准。

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

我知道这个问题很久以前就发布了,但我需要检查谷歌是如何做到的,所以也许有人会觉得这很有用。

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

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();
});

键盘快捷键的实时示例:http://jsfiddle.net/tyk9U/

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

您可以使用非常简单的清单文件为自己创建这样的应用程序(详细信息请点击此处https://developer.chrome.com/apps/first_app):

{
    "manifest_version": 2,
    "name": "App name",
    "description": "App description",
    "version": "1.0",
    "app": {
        "urls": [
            "http://your.app.url.here/"
        ],
        "launch": {
            "web_url": "http://your.app.url.here/"
        }
    },
    "icons": {
        "128": "x-128.png"
    },
    "permissions": [
        "clipboardRead",
        "clipboardWrite"
    ]
}

此处的"权限"字段启用剪贴板操作。

现在,当您启用此功能时,您可以执行document.execCommand('copy'),它将工作(将返回true)。但这并不是全部——chrome中的document.execCommand('copy')触发复制事件,您可以使用用于捕获键盘剪贴板快捷键的相同代码来捕获它。这是谷歌现在做的。

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

注意:这个答案在编写时是准确的,并且正确地回答了OP的问题。然而,从那时起,技术就在发展;如果您有兴趣在web应用程序中支持复制和粘贴,请参阅本页上的其他最新答案。—ruakh]


然而,我看到Google Docs现在实际上已经为键盘事件和按钮提供了一个有效的解决方案。

不,不是。不是。对于键盘事件,谷歌文档什么都不做;它只是没有阻止浏览器的默认复制和粘贴功能;因此,用户可以自由复制和粘贴,而不会受到谷歌文档的阻碍。对于按钮,谷歌文档不支持系统剪贴板,但它自己的"网络剪贴板"完全在谷歌文档中。您不能使用工具栏按钮复制文本以粘贴到计算机上的其他程序中,也不能粘贴从计算机上的另一个程序复制的文本。

有关这方面的更多信息,请参阅"在谷歌文档中复制和粘贴"。(这是面向用户的,而不是面向开发人员的,但它很好地明确了什么是受支持的,什么是不受支持的。)

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

完整的演示可以在这里找到:http://jsfiddle.net/rve7d/

在尝试创建这个演示时,我发现Mateusz W的回答非常有用,但他没有考虑对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.');
}

附言:我需要这个功能用于我们的自定义电子表格视图组件,并在途中分析了谷歌电子表格的源代码,所以我的解决方案基本上符合他们的解决方案。

谷歌使用了一种非常简单但很酷的方法。通过使用firebug,您将知道加载的html代码有一个大小为1的文本区域。谷歌文档所做的是,当用户选择文本并按下ctrl+c时,它会捕获事件,并通过某种技术获取文档容器中选择的文本,并将文本区域的值设置为该内容。然后聚焦并选择文本区域。现在它释放ctrl+c事件。但现在文本是在文本区域中选择的,所以当事件发生时,浏览器会复制文本区域中的文本,因此我们会得到复制的文本

<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>