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

我试过:

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.

A nice trick without using Flash: How does Trello access the user's clipboard?

推荐答案

Overview

有三种主要浏览器API可用于复制到剪贴板:

  1. Async 剪贴板API [navigator.clipboard.writeText]

    • 以文本为中心的部分有Chrome 66 (March 2018)
    • Access是异步的,使用JavaScript Promises,可以编写,这样安全用户提示(如果显示)就不会中断页面中的JavaScript.
    • 文本可以直接从变量复制到剪贴板.
    • 仅在通过HTTPS提供服务的页面上受支持.
    • 在Chrome 66页面中,非活动选项卡可以在没有权限提示的情况下写入剪贴板.
  2. ?

    • 截至2015年4月,大多数浏览器都支持此功能(请参见下面的浏览器支持).
    • 访问是同步的,即停止页面中的JavaScript,直到完成,包括显示和用户与任何安全提示交互.
    • 文本从DOM中读取并放在剪贴板上.
    • 在2015年4月的测试期间,仅注意到Internet Explorer在写入剪贴板时显示权限提示.
  3. 覆盖复制事件

    • See 剪贴板API documentation on 覆盖复制事件.
    • 允许您修改任何复制事件中出现在剪贴板上的内容,可以包括纯文本以外的其他数据格式.
    • 这里没有介绍,因为它没有直接回答问题.

General development notes

在控制台中测试代码时,不要期望剪贴板相关的命令起作用.通常,页面需要处于活动状态(异步剪贴板API)或需要用户交互(例如,用户单击)才能允许(document.execCommand('copy'))访问剪贴板.有关详细信息,请参见下文.

IMPORTANT (noted here 2020/02/20)

请注意,由于本文最初是由deprecation of permissions in cross-origin IFRAMEs和其他IFRAME "sandboxing"编写的,因此嵌入式演示"运行代码片段"按钮和"codesen.io示例"无法在某些浏览器(包括Chrome和Microsoft Edge)上运行.

要开发和创建自己的网页,请通过HTTPS连接提供该网页,以进行测试和开发.

以下是演示代码工作的测试/演示页面: https://deanmarktaylor.github.io/clipboard-test/

Async + Fallback

由于浏览器对新的Async 剪贴板API的支持程度很高,您可能希望使用document.execCommand('copy')方法来获得良好的浏览器覆盖率.

下面是一个简单的例子(可能不适用于本网站,请阅读上面的"重要"注释):

function fallbackCopyTextToClipboard(text) {
  var textArea = document.createElement("textarea");
  textArea.value = text;
  
  // Avoid scrolling to bottom
  textArea.style.top = "0";
  textArea.style.left = "0";
  textArea.style.position = "fixed";

  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>

(codesen.io示例可能不起作用,请阅读上面的"重要"说明) 请注意,此代码片段在Stack Overflow的嵌入式预览中不能很好地工作,您可以在此处try :https://codepen.io/DeanMarkTaylor/pen/RMRaJX?editors=1011

Async 剪贴板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('copy')

这篇文章的睡觉深入了document.execCommand('copy')API的细微差别和细节.

浏览器支持

对JavaScript document.execCommand('copy')的支持有所增加,请参见下面的链接以获取浏览器更新:(102)?

简单的例子

(可能无法嵌入本网站,请阅读上面的"重要"注释)

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元素.这是解决此问题的方法的一个示例(主要是插入元素、复制到剪贴板、删除元素):

使用谷歌Chrome 44、Firefox 42.0a1和Internet Explorer 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 the 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
  // Internet Explorer the element is visible whilst the popup
  // box asking the user for permission for the web page to
  // copy to the clipboard.
  //

  // Place in the 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 the 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 post here.

剪贴板API

注意:完整的剪贴板API草案规范可在以下位置找到:

它是否受支持?

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

然而,作为浏览器兼容性问题的一个例子,如果该命令是从用户启动的线程调用的,那么2015年4月~10月的Google Chrome仅从document.queryCommandSupported('copy')返回true.

请注意下面的兼容性细节.

浏览器兼容性详细信息

虽然简单地调用包装在try/catch挡路中的document.execCommand('copy')(由于用户单击而调用)将使您获得最兼容的使用,但以下有一些附带条件:

任何拨打document.execCommanddocument.queryCommandSupporteddocument.queryCommandEnabled时都应该用try/catch型挡路包装.

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

不同的浏览器实现仍在变化中,剪贴板API仍在草案中,因此请记住进行测试.

Javascript相关问答推荐

如何制作删除按钮以从列表中删除该项目所属的项目?

为什么我的第二个OnClick Isloading值在TEK查询Mutations 查询中不起作用?

使用redux-toolet DelivercThunks刷新访问令牌

如何访问Json返回的ASP.NET Core 6中的导航图像属性

在React中获取数据后,如何避免不必要的组件闪现1秒?

从WooCommerce Checkout Country字段重新排序国家,保持国家同步

当试图显示小部件时,使用者会出现JavaScript错误.

为什么!逗号和空格不会作为输出返回,如果它在参数上?

Chart.js 4.4.2,当悬停在一个数据点上时,如何在工具提示中拥有多个数据点/标签?

为什么按钮会随浮动属性一起移动?

我可以使用空手道用户界面来获取网页的当前滚动位置吗?

如何使用TypeScrip设置唯一属性?

Nextjs 13.4 Next-Auth 4.2登录(&Quot;凭据&,{});不工作

在VS代码上一次设置多个变量格式

如何在Java脚本中对列表中的特定元素进行排序?

将范围 Select 器添加到HighChart面积图

MongoDB通过数字或字符串过滤列表

我在哪里添加过滤器值到这个函数?

JavaScript:多个图像错误处理程序

更新文本区域行号