logo
Published on

如何在JavaScript中将文本复制到剪贴板

Authors
  • Name
    Twitter

在现代Web开发中,能够将文本复制到剪贴板是一个常见的需求。本文将详细介绍三种在JavaScript中实现这一功能的方法,包括异步剪贴板API和document.execCommand('copy'),以及覆盖复制事件。

概述

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

  1. 异步剪贴板API [navigator.clipboard.writeText]

    • 该API的文本部分在Chrome 66(2018年3月)中可用。
    • 访问是异步的,使用JavaScript Promises,可以编写代码避免安全提示打断页面上的JavaScript执行。
    • 可以直接从变量中将文本复制到剪贴板。
    • 仅支持HTTPS页面。
    • 在Chrome 66中,非活动标签页可以在无权限提示的情况下写入剪贴板。
  2. document.execCommand('copy') (已废弃) 👎

    • 自2015年4月起的大多数浏览器都支持该方法(见下文的浏览器支持部分)。
    • 访问是同步的,即在完成之前会阻止页面上的JavaScript执行,包括显示和用户交互的安全提示。
    • 文本是从DOM中读取并放置到剪贴板上。
    • 在2015年4月的测试中,只有Internet Explorer需要在写入剪贴板时显示权限提示。
  3. 覆盖复制事件

    • 请参阅剪贴板API文档中的覆盖复制事件
    • 允许修改任何复制事件中出现在剪贴板上的内容,可以包含除纯文本以外的其他格式的数据。
    • 此方法不在本文的讨论范围内。

开发注意事项

在控制台测试代码时,不要期望与剪贴板相关的命令能正常工作。通常,需要页面处于活动状态(异步剪贴板API)或需要用户交互(例如点按事件才能允许document.execCommand('copy')访问剪贴板)。

重要提示(注:2020年2月20日)

请注意,自从本文最初撰写以来,跨域IFRAME中权限的废弃及其他IFRAME的“沙箱”措施导致嵌入的演示“运行代码片段”按钮和“codepen.io 示例”在某些浏览器(包括Chrome和Microsoft Edge)中无法正常工作。

为了开发,建议创建自己的网页,并通过HTTPS连接提供服务以进行测试和开发。

提供了一个演示页面来展示代码的工作效果:https://deanmarktaylor.github.io/clipboard-test/

异步剪贴板API与回退方法

由于异步剪贴板API的浏览器支持水平,您可能需要回退到document.execCommand('copy')方法以确保良好的浏览器覆盖率。

下面是一个简单的示例(在此站点嵌入时可能无法工作,请阅读上面的“重要提示”):

function fallbackCopyTextToClipboard(text) {
  var textArea = document.createElement('textarea')
  textArea.value = text

  // 避免滚动到底部
  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>

可以在这里尝试:https://codepen.io/DeanMarkTaylor/pen/RMRaJX?editors=1011

异步剪贴板API

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

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')的支持情况如下(已废弃)👎:

简单示例

(在此站点嵌入时可能无法工作,请阅读上面的“重要提示”)

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和Internet Explorer 11.0.8600.17814中测试。

(在此站点嵌入时可能无法工作,请阅读上面的“重要提示”)

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

  //
  // *** 这一步额外的样式设置可能不是必须的。 ***
  //
  // 为什么要这样做?为了确保:
  // 1. 该元素能够获得焦点和选择。
  // 2. 如果该元素闪烁呈现,它的视觉影响最小。
  // 3. 尽量减少选择和复制时可能出现的不稳定性 **可能**
  //    在元素不可见时发生。
  //
  // 可能这些元素不会呈现,甚至不会闪烁,所以有些只是预防措施。
  // 但是在Internet Explorer中,元素会在弹出框
  // 要求用户允许网页复制到剪贴板时显示。
  //

  // 无论滚动位置如何,放置在屏幕的左上角。
  textArea.style.position = 'fixed'
  textArea.style.top = 0
  textArea.style.left = 0

  // 确保它的宽度和高度很小。设置为1px / 1em
  // 在某些浏览器中无法工作。
  textArea.style.width = '2em'
  textArea.style.height = '2em'

  // 我们不需要填充,减少如果闪烁呈现时的大小。
  textArea.style.padding = 0

  // 清理任何边框。
  textArea.style.border = 'none'
  textArea.style.outline = 'none'
  textArea.style.boxShadow = 'none'

  // 如果由于任何原因呈现时避免白框闪烁。
  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')调用必须作为用户操作的直接结果,例如点击事件处理程序。这是为了防止未预期的操作干扰用户的剪贴板。

看到谷歌开发者的帖子了解更多信息。

剪贴板API

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

是否支持?

  • document.queryCommandSupported('copy')应该返回true,如果命令“受浏览器支持”。
  • document.queryCommandEnabled('copy')返回true,如果document.execCommand('copy')调用现在成功。

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

请查看下面的兼容性详情。

浏览器兼容性详情

尽管简单地将document.execCommand('copy')封装在一个try/catch块中,作为用户点击的结果调用可以获得最好的兼容性,使用以下方法有一些注意事项:

任何对document.execCommanddocument.queryCommandSupporteddocument.queryCommandEnabled的调用都应封装在一个try/catch块中。

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

不同的浏览器实现仍在变化,剪贴板API仍然是草案,请记得进行测试。