协慌网

登录 贡献 社区

如何使用 JavaScript 复制到剪贴板?

将文本复制到剪贴板的最佳方法是什么? (多浏览器)

我努力了:

function copyToClipboard(text) {
    if (window.clipboardData) { // Internet Explorer
        window.clipboardData.setData("Text", text);
    } else {  
        unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");  
        const clipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);  
        clipboardHelper.copyString(text);
    }
}

但在 Internet Explorer 中,它会出现语法错误。在 Firefox 中,它表示unsafeWindow is not defined

没有闪存的好技巧: Trello 如何访问用户的剪贴板?

答案

概观

有 3 个主要的浏览器 API 可用于复制到剪贴板:

  1. 异步剪贴板 API [navigator.clipboard.writeText]
    • Chrome 66 中提供以文本为中心的部分(2018 年 3 月)
    • Access 是异步的并且使用JavaScript Promises ,可以编写,因此安全用户提示(如果显示)不会中断页面​​中的 JavaScript。
    • 可以直接从变量将文本复制到剪贴板。
    • 仅在通过 HTTPS 提供的页面上受支持。
    • 在 Chrome 中,活动标签中的 66 页可以在没有权限提示的情况下写入剪贴板。
  2. document.execCommand('copy')
    • 大多数浏览器都支持 2015 年 4 月〜(请参阅下面的浏览器支持)。
    • 访问是同步的,即在页面中停止 JavaScript 直到完成,包括显示和用户与任何安全提示交互。
    • 从 DOM 读取文本并将其放在剪贴板上。
    • 在测试期间〜2015 年 4 月,只有 Internet Explorer 被注意为在写入剪贴板时显示权限提示。
  3. 覆盖复制事件
    • 请参阅覆盖复制事件的剪贴板 API 文档。
    • 允许您修改任何复制事件在剪贴板上显示的内容,可以包括纯文本以外的其他格式的数据。
    • 这里没有涉及,因为它没有直接回答这个问题。

一般发展说明

在控制台中测试代码时,不要指望与剪贴板相关的命令可以正常工作。通常,页面必须是活动的(Async Clipboard API)或需要用户交互(例如用户单击)以允许( document.execCommand('copy') )访问剪贴板,请参阅下面的详细信息。

异步 + 后备

由于浏览器支持新的 Async Clipboard API,您可能希望回退到document.execCommand('copy')方法以获得良好的浏览器覆盖率。

这是一个简单的例子:

function fallbackCopyTextToClipboard(text) {
  var textArea = document.createElement("textarea");
  textArea.value = text;
  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    var successful = document.execCommand('copy');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Fallback: Copying text command was ' + msg);
  } catch (err) {
    console.error('Fallback: Oops, unable to copy', err);
  }

  document.body.removeChild(textArea);
}
function copyTextToClipboard(text) {
  if (!navigator.clipboard) {
    fallbackCopyTextToClipboard(text);
    return;
  }
  navigator.clipboard.writeText(text).then(function() {
    console.log('Async: Copying to clipboard was successful!');
  }, function(err) {
    console.error('Async: Could not copy text: ', err);
  });
}

var copyBobBtn = document.querySelector('.js-copy-bob-btn'),
  copyJaneBtn = document.querySelector('.js-copy-jane-btn');

copyBobBtn.addEventListener('click', function(event) {
  copyTextToClipboard('Bob');
});


copyJaneBtn.addEventListener('click', function(event) {
  copyTextToClipboard('Jane');
});
<div style="display:inline-block; vertical-align:top;">
  <button class="js-copy-bob-btn">Set clipboard to BOB</button><br /><br />
  <button class="js-copy-jane-btn">Set clipboard to JANE</button>
</div>
<div style="display:inline-block;">
  <textarea class="js-test-textarea" cols="35" rows="4">Try pasting into here to see what you have on your clipboard:
  
  </textarea>
</div>

请注意,此片段在 StackOverflow 的嵌入式预览中效果不佳,您可以在此处尝试: https ://codepen.io/DeanMarkTaylor/pen/RMRaJX?edit = 1011

异步剪贴板 API

请注意,可以通过 Chrome 66 中的权限 API“请求权限” 并测试对剪贴板的访问权限。

var text = "Example text to appear on clipboard";
navigator.clipboard.writeText(text).then(function() {
  console.log('Async: Copying to clipboard was successful!');
}, function(err) {
  console.error('Async: Could not copy text: ', err);
});

document.execCommand( '复制')

本文的其余部分介绍了document.execCommand('copy') API 的细微差别和细节。

浏览器支持

JavaScript document.execCommand('copy')支持已经增长,请参阅下面的链接以获取浏览器更新:

简单的例子

var copyTextareaBtn = document.querySelector('.js-textareacopybtn');

copyTextareaBtn.addEventListener('click', function(event) {
  var copyTextarea = document.querySelector('.js-copytextarea');
  copyTextarea.focus();
  copyTextarea.select();

  try {
    var successful = document.execCommand('copy');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Copying text command was ' + msg);
  } catch (err) {
    console.log('Oops, unable to copy');
  }
});
<p>
  <button class="js-textareacopybtn" style="vertical-align:top;">Copy Textarea</button>
  <textarea class="js-copytextarea">Hello I'm some text</textarea>
</p>

复杂示例:复制到剪贴板而不显示输入

如果屏幕上有可见的textareainput元素,上面的简单示例效果很好。

在某些情况下,您可能希望将文本复制到剪贴板而不显示input / textarea元素。这是解决此问题的一种方法(基本上是插入元素,复制到剪贴板,删除元素):

使用 Google Chrome 44,Firefox 42.0a1 和 IE 11.0.8600.17814 进行测试。

function copyTextToClipboard(text) {
  var textArea = document.createElement("textarea");

  //
  // *** This styling is an extra step which is likely not required. ***
  //
  // Why is it here? To ensure:
  // 1. the element is able to have focus and selection.
  // 2. if element was to flash render it has minimal visual impact.
  // 3. less flakyness with selection and copying which **might** occur if
  //    the textarea element is not visible.
  //
  // The likelihood is the element won't even render, not even a flash,
  // so some of these are just precautions. However in IE the element
  // is visible whilst the popup box asking the user for permission for
  // the web page to copy to the clipboard.
  //

  // Place in top-left corner of screen regardless of scroll position.
  textArea.style.position = 'fixed';
  textArea.style.top = 0;
  textArea.style.left = 0;

  // Ensure it has a small width and height. Setting to 1px / 1em
  // doesn't work as this gives a negative w/h on some browsers.
  textArea.style.width = '2em';
  textArea.style.height = '2em';

  // We don't need padding, reducing the size if it does flash render.
  textArea.style.padding = 0;

  // Clean up any borders.
  textArea.style.border = 'none';
  textArea.style.outline = 'none';
  textArea.style.boxShadow = 'none';

  // Avoid flash of white box if rendered for any reason.
  textArea.style.background = 'transparent';


  textArea.value = text;

  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    var successful = document.execCommand('copy');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Copying text command was ' + msg);
  } catch (err) {
    console.log('Oops, unable to copy');
  }

  document.body.removeChild(textArea);
}


var copyBobBtn = document.querySelector('.js-copy-bob-btn'),
  copyJaneBtn = document.querySelector('.js-copy-jane-btn');

copyBobBtn.addEventListener('click', function(event) {
  copyTextToClipboard('Bob');
});


copyJaneBtn.addEventListener('click', function(event) {
  copyTextToClipboard('Jane');
});
<div style="display:inline-block; vertical-align:top;">
  <button class="js-copy-bob-btn">Set clipboard to BOB</button><br /><br />
  <button class="js-copy-jane-btn">Set clipboard to JANE</button>
</div>
<div style="display:inline-block;">
  <textarea class="js-test-textarea" cols="35" rows="4">Try pasting into here to see what you have on your clipboard:
  
  </textarea>
</div>

补充笔记

仅在用户执行操作时有效

所有document.execCommand('copy')调用必须作为用户操作的直接结果发生,例如单击事件处理程序。这是一种防止在用户剪贴板不期望时弄乱用户剪贴板的措施。

有关详细信息,请参阅此处Google Developers 帖子

剪贴板 API

注意完整的剪贴板 API 草案规范可以在这里找到: https//w3c.github.io/clipboard-apis/

是否支持?

  • 如果命令 “受浏览器支持”, document.queryCommandSupported('copy')应该返回true
  • 如果现在调用document.execCommand('copy')成功,则document.queryCommandEnabled('copy')返回true 。检查以确保从用户启动的线程调用该命令并满足其他要求。

但是,作为浏览器兼容性问题的示例,如果从用户启动的线程调用该命令,则从 2015 年 4 月到 10 月的 Google Chrome 仅从document.queryCommandSupported('copy')返回true

请注意以下兼容性细节

浏览器兼容性细节

虽然简单地调用document.execCommand('copy')包装在一个try / catch块中,并且由于用户单击而被调用,但它将获得最大的兼容性,下面有一些附带条款:

document.execCommanddocument.queryCommandSupporteddocument.queryCommandEnabled任何调用都应该包含在try / catch块中。

不同的浏览器实现和浏览器版本在调用时抛出不同类型的异常而不是返回false

不同的浏览器实现仍在不断变化, 剪贴板 API仍处于草稿状态,因此请记住进行测试。

自动复制到剪贴板可能很危险,因此大多数浏览器(IE 除外)都非常困难。就个人而言,我使用以下简单的技巧:

function copyToClipboard(text) {
  window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
}

将向用户显示提示框,其中已选择要复制的文本。现在按Ctrl + CEnter (关闭框)就足够了 - 瞧!

现在剪贴板复制操作是 SAFE,因为用户手动完成(但是以非常简单的方式)。当然,适用于所有浏览器。

<button id="demo" onclick="copyToClipboard(document.getElementById('demo').innerHTML)">This is what I want to copy</button>

<script>
  function copyToClipboard(text) {
    window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
  }
</script>

以下方法适用于 Chrome,Firefox,Internet Explorer 和 Edge 以及最新版本的 Safari(在 2016 年 10 月发布的版本 10 中添加了复制支持)。

  • 创建 textarea 并将其内容设置为要复制到剪贴板的文本。
  • 将 textarea 附加到 DOM。
  • 选择 textarea 中的文本。
  • 调用 document.execCommand(“copy”)
  • 从 dom 中删除 textarea。

注意:您将看不到 textarea,因为它是在 Javascript 代码的同一个同步调用中添加和删除的。

如果您自己实现这一点,需要注意的一些事项:

  • 出于安全原因,这只能从事件处理程序(如 click)调用(就像打开窗口一样)。
  • IE 将在第一次更新剪贴板时显示权限对话框。
  • IE 和 Edge 将在 textarea 聚焦时滚动。
  • execCommand()可能会抛出某些情况。
  • 除非您使用 textarea,否则可能会吞下新行和制表符。 (大多数文章似乎建议使用 div)
  • 显示 IE 对话框时,textarea 将可见,您需要隐藏它,或使用 IE 特定的 clipboardData api。
  • 在 IE 系统中,管理员可以禁用剪贴板 API。

以下功能应尽可能干净地处理以下所有问题。如果您发现任何问题或有任何改进建议,请发表评论。

// Copies a string to the clipboard. Must be called from within an 
// event handler such as click. May return false if it failed, but
// this is not always possible. Browser support for Chrome 43+, 
// Firefox 42+, Safari 10+, Edge and IE 10+.
// IE: The clipboard feature may be disabled by an administrator. By
// default a prompt is shown the first time the clipboard is 
// used (per session).
function copyToClipboard(text) {
    if (window.clipboardData && window.clipboardData.setData) {
        // IE specific code path to prevent textarea being shown while dialog is visible.
        return clipboardData.setData("Text", text); 

    } else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
        var textarea = document.createElement("textarea");
        textarea.textContent = text;
        textarea.style.position = "fixed";  // Prevent scrolling to bottom of page in MS Edge.
        document.body.appendChild(textarea);
        textarea.select();
        try {
            return document.execCommand("copy");  // Security exception may be thrown by some browsers.
        } catch (ex) {
            console.warn("Copy to clipboard failed.", ex);
            return false;
        } finally {
            document.body.removeChild(textarea);
        }
    }
}

https://jsfiddle.net/fx6a6n6x/