JavaScript中如何复制到剪贴板?

4346

这仍然没有得到完美的支持。您可以在Can I use Clipboard API上查看支持情况,该网站详细介绍了W3C Clipboard APIs document的支持情况。 - mkobit
27个回答

3469

概述

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

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

    • 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年02月20日记录)

请注意,自此帖子最初编写以来,跨域 IFRAME 权限的弃用 和其他 IFRAME 的"沙盒化" 控制在某些浏览器(包括 Chrome 和 Microsoft Edge)中阻止了嵌入式演示的 "运行代码片段" 按钮和 "codepen.io 示例" 的正常工作。

为了进行开发,请创建您自己的网页,并通过 HTTPS 连接提供该页面以进行测试和开发。

这里是一个演示代码工作的测试/演示页面: https://deanmarktaylor.github.io/clipboard-test/

异步 + 回退

由于新的异步剪贴板 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>

异步剪贴板 API

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

请注意,此代码段在 Stack Overflow 的嵌入式预览中可能无法正常工作,请在 此处 尝试。

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') 的支持已经增加,请参阅以下链接获取浏览器更新: (已弃用)

简单示例

(may not work embedded in this site, read "important" note above)

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 上进行了测试。

(may not work embedded in this site, read "important" note above)

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开发人员文章

剪贴板API

请注意,完整的Clipboard API草案规范可以在此处找到: https://w3c.github.io/clipboard-apis/

是否受支持?

  • document.queryCommandSupported('copy')应返回true,如果浏览器“支持该命令”。
  • 并且document.queryCommandEnabled('copy')如果现在调用document.execCommand('copy')将成功,则返回true。检查以确保从用户启动的线程调用命令以及满足其他要求。
然而,作为浏览器兼容性问题的一个例子,从2015年4月至10月左右,只有当该命令从用户启动的线程中调用时,Google Chrome 才会从 document.queryCommandSupported('copy') 返回 true。
请注意以下兼容性细节。
浏览器兼容性细节
虽然将一个简单的 document.execCommand('copy') 调用包裹在由用户点击引发的 try/catch 块中可以获得最广泛的兼容性,但是下面的方法具有一些前提条件:
任何对 document.execCommand、document.queryCommandSupported 或 document.queryCommandEnabled 的调用都应该包裹在 try/catch 块中。
不同的浏览器实现和浏览器版本在调用时抛出不同类型的异常而不是返回 false。
不同的浏览器实现仍在变化,Clipboard API 仍处于草案阶段,请记得进行测试。

45
抱歉打破好事,但 document.execCommand 已经过时。请参考 https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand - tnkh
3
没问题,但是替代方案(剪贴板 API)目前还没有完全成熟并得到支持。 - forresto
3
剪贴板 API 目前支持全球用户的比例为 91%:https://caniuse.com/mdn-api_clipboard_writetext - forresthopkinsa
2
我刚刚添加了重置焦点的代码,放在回退代码之后: var previousFocusElement = document.activeElement (....所有回退代码...) previousFocusElement.focus(); - Matthias
2
相信来这里的人都是在寻找一个简短的代码片段,带有1或2个限定符,而不是《战争与和平》。 - Andrew
显示剩余7条评论

1385

自动复制到剪贴板可能存在风险,因此大多数浏览器(除了Internet Explorer)都会设置非常严格的限制。个人建议采用以下简单的技巧:

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

用户将会看到提示框,其中要复制的文本已经被选择。现在只需要按下Ctrl+CEnter(关闭框架) - 完成!现在剪贴板复制操作是“安全的”,因为用户以一种简单直观的方式手动完成。当然,它适用于所有浏览器。

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


26
在那个对话框中显示的字符数量有限制,因此可以复制的数据量也有限制。 - Denilson Sá Maia
106
聪明,但这只支持单行。 - Aram Kocharyan
66
将“提示”功能更改为自定义模态框很容易,关键是使用可编辑的内容字段并预先选择文本,并通过强制用户自行执行操作来避免破坏浏览器UI。A++ - Jon z
26
如果您的文本超过2000个字符,它将被截断,但对于较小的文本样本,它非常有效。 - RasTheDestroyer
9
字符截断在2k字符处似乎是Chrome浏览器的问题,但无论如何了解这一点都是很好的。 - Marcus Pope
显示剩余9条评论

525

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

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

注意:您不会看到textarea,因为它是在同步调用Javascript代码的同一次添加和删除内完成的。

如果您要自己实现这些,请注意以下几点:

  • 出于安全原因,这只能从事件处理程序(例如单击(与打开窗口一样))调用。
  • Internet Explorer将在更新剪贴板时首次显示权限对话框。
  • Internet Explorer和Edge将在聚焦于textarea时滚动。
  • execCommand()可能在某些情况下抛出。
  • 换行符和制表符可能会被吞没,除非您使用textarea。(大多数文章似乎建议使用div)
  • Internet Explorer对话框显示时textarea将可见,您需要隐藏它,或使用Internet Explorer特定的clipboardData API。
  • 在Internet Explorer中,系统管理员可以禁用clipboard 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 Internet Explorer 10+.
// Internet Explorer: 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) {
        // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible.
        return window.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 Microsoft 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 prompt("Copy to clipboard: Ctrl+C, Enter", text);
        }
        finally {
            document.body.removeChild(textarea);
        }
    }
}

https://jsfiddle.net/fx6a6n6x/


18
好的,以下是翻译的结果:不错的答案:跨浏览器支持、错误处理和清理。目前通过queryCommandSupported新的支持,在Javascript中复制到剪贴板现在是可行的,这应该成为被接受的答案,而不是笨拙的“window.prompt(“将文本复制到剪贴板:Ctrl + C,Enter”)”解决方法。window.clipboardData在IE9中得到支持,因此您应该将IE9添加到支持的浏览器列表中,我认为可能还有IE8和之前的版本,但需要进行验证。 - user627283
8
2016年的时候,这个回答是值得被采纳的。请考虑重新指定BGT(大绿色勾号)。 - Lawrence Dol
3
@Noitidart 我测试过了,它在 Firefox 54、Chrome 60 和 Edge 浏览器上都完美运行,即使焦点不在 HTML 文档中,你遇到的错误可能是特定于 FF 55 版本的。 - Tosin John
2
@Noitidart 在这里它仍然完美运行,专注于开发工具并没有阻止它。顺便问一下,一个普通的Web应用程序用户会在开发人员工具上做什么? - Tosin John
3
注意:如果您尝试从模态对话框中使用此方法,将会遇到问题。我怀疑这是因为jQuery UI模态正在管理/操作文档焦点。如果符合您的用例,一个解决方法是先关闭模态对话框,然后再复制文本。或者,使用非模态对话框。我猜想您也可以修改此函数,使其将textarea添加到模态对话框而不是body中。 - rinogo
显示剩余10条评论

173

以下是我的看法...

function copy(text) {
    var input = document.createElement('input');
    input.setAttribute('value', text);
    document.body.appendChild(input);
    input.select();
    var result = document.execCommand('copy');
    document.body.removeChild(input);
    return result;
 }

@korayem: 请注意,使用html input字段将不会尊重换行符\n,并将任何文本变成单行。

如@nikksan在评论中提到的那样,使用textarea将解决这个问题:

function copy(text) {
    var input = document.createElement('textarea');
    input.innerHTML = text;
    document.body.appendChild(input);
    input.select();
    var result = document.execCommand('copy');
    document.body.removeChild(input);
    return result;
}

1
在Win10x64上的Microsoft Edge 42.17134.1.0中无法工作。 - Honsa Stunna
1
由于某些原因,我通常使用的“创建一个隐藏的输入或文本区域,然后选择它并执行命令”的方法不起作用了。尽管其他解决方案很全面,就像完整的维基百科页面一样,但这个解决方案对我来说效果最好,所以点赞+1。 - Justin

101

从网页读取和修改剪贴板会引起安全和隐私问题。但是,在Internet Explorer中,这是可能的。我发现了这个示例代码片段

    <script type="text/javascript">
        function select_all(obj) {
            var text_val=eval(obj);
            text_val.focus();
            text_val.select();
            r = text_val.createTextRange();
            if (!r.execCommand) return; // feature detection
            r.execCommand('copy');
        }
    </script>
    <input value="http://www.sajithmr.com"
     onclick="select_all(this)" name="url" type="text" />


7
使用闪存进行简单的复制操作似乎有些大材小用,好在有一种干净的JS方式来完成这项任务。而且,由于我们处于企业环境中,IE浏览器也完全可以胜任。感谢Bandi! - Eddie
5
请解释一下 execCommand('copy'); 是做什么用的,如果不是将内容复制到IE的剪贴板中?这段代码使用 execCommand() 方法执行一个命令,在此例中是将选定文本复制到剪贴板上。虽然该方法在早期版本的IE浏览器中可以直接使用,但在现代浏览器中需要更多的代码才能实现跨浏览器兼容性。 - RozzA
22
不要使用if(!document.all),而应该使用if(!r.execCommand),以免其他人也实现它!document.all与此完全无关。 - m93a
为什么人们使用Flash更改剪贴板的十年中从未提出过这些隐私问题?如果我们只允许单向(即复制,而不是读取其内容),那么这会产生隐私问题吗? - Muhammad bin Yusrat
虽然不是隐私问题,但这确实是用户体验的问题。考虑到用户已经复制了某些内容,并且认为知道剪贴板上有什么,然后在浏览你的网站时,突然剪贴板里出现了他没有要求的东西,他就会失去一开始复制的内容。 - awe

93

如果你想要一个真正简单的解决方案(不到5分钟即可集成)并且从一开始就看起来不错,那么Clippy是一种很好的替代品,可以避免某些更复杂的解决方案。

它是由GitHub的联合创始人编写的。以下是示例Flash嵌入代码:

<object
    classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
    width="110"
    height="14"
    id="clippy">

    <param name="movie" value="/flash/clippy.swf"/>
    <param name="allowScriptAccess" value="always"/>
    <param name="quality" value="high"/>
    <param name="scale" value="noscale"/>
    <param NAME="FlashVars" value="text=#{text}"/>
    <param name="bgcolor" value="#{bgcolor}"/>
    <embed
        src="/flash/clippy.swf"
        width="110"
        height="14"
        name="clippy"
        quality="high"
        allowScriptAccess="always"
        type="application/x-shockwave-flash"
        pluginspage="http://www.macromedia.com/go/getflashplayer"
        FlashVars="text=#{text}"
        bgcolor="#{bgcolor}"/>
</object>

记得将#{text}替换为需要复制的文本,将#{bgcolor}替换为颜色。


13
有兴趣的人可以在 GitHub 上复制存储库的 URL 时查看 Clippy 的使用。 - Radek

82

我最近写了一篇技术博客,讲述了这个问题(我在Lucidchart工作,我们最近对剪贴板进行了大修)。

如果你尝试在系统复制事件期间(用户按下Ctrl+C或使用浏览器菜单)将纯文本复制到剪贴板中,那么这相对简单。

var isIe = (navigator.userAgent.toLowerCase().indexOf("msie")    != -1 ||
            navigator.userAgent.toLowerCase().indexOf("trident") != -1);

document.addEventListener('copy', function(e) {
    var textToPutOnClipboard = "This is some text";
    if (isIe) {
        window.clipboardData.setData('Text', textToPutOnClipboard);
    } else {
        e.clipboardData.setData('text/plain', textToPutOnClipboard);
    }
    e.preventDefault();
});

在非系统复制事件期间将文本放入剪贴板要困难得多。看起来其他一些答案提到了通过Flash进行操作的方法,这是目前我所了解的唯一跨浏览器的方式。

除此之外,在不同的浏览器中有一些选项可供选择。

在Internet Explorer中,这是最简单的,您可以通过JavaScript随时访问clipboardData对象:

window.clipboardData

但是,如果您在系统剪切、复制或粘贴事件之外尝试执行此操作,则Internet Explorer会提示用户授予Web应用程序剪贴板权限。

在Chrome中,您可以创建一个Chrome扩展程序,为您提供剪贴板权限(这就是我们为Lucidchart所做的)。然后对于已安装您扩展程序的用户,您只需要自己触发系统事件即可:

document.execCommand('copy');

看起来火狐浏览器有一些选项允许用户授予某些网站访问剪贴板的权限,但我个人尚未尝试过。


3
在博客文章中没有提到(我希望在不久的将来更新它)的是使用execCommand触发剪切和复制的功能。这在IE10+,Chrome 43+和Opera29+中得到支持。在这里阅读更多信息:http://updates.html5rocks.com/2015/04/cut-and-copy-commands - Richard Shurtz
这个问题的一个副作用是它会劫持其他正常的复制事件。 - Brock Adams

74

我喜欢这个:

<input onclick="this.select();" type='text' value='copy me' />
如果用户不知道如何在操作系统中复制文本,那么他们可能也不知道怎样粘贴。因此,只需将它自动选择,剩下的就留给用户。

如果用户不知道如何在操作系统中复制文本,那么他们可能也不知道如何进行粘贴。 因此,最好让文本自动选中,剩下的就交给用户处理。


我也喜欢它,因为它很短。你还可以复制:<input onclick="this.select(); document.execCommand('copy');" type='text' value='copy me' /> - Roubi

59

clipboard.js 是一款小巧、非 Flash 的实用工具,可以将文本或 HTML 数据复制到剪贴板。使用起来非常简单,只需包含 .js 文件并使用类似以下的代码:

<button id='markup-copy'>Copy Button</button>

<script>
document.getElementById('markup-copy').addEventListener('click', function() {
  clipboard.copy({
    'text/plain': 'Markup text. Paste me into a rich text editor.',
    'text/html': '<i>here</i> is some <b>rich text</b>'
  }).then(
    function(){console.log('success'); },
    function(err){console.log('failure', err);
  });

});
</script>

clipboard.js也在 GitHub 上。

注意: 此版本已被弃用,请迁移到此处


这个库被angular.io用于其英雄之旅和优雅模式的回退,以显示预选文本,用户只需复制即可,以解决不支持execCommand的浏览器问题。 - John-Philip
2
看起来 clipboard.js 已经被替换或者分支了,但是它似乎仍然存在并且在 https://www.npmjs.com/package/clipboard 上得到积极维护。 - Joao

47

2018 年,以下是如何进行的方法:

async copySomething(text?) {
  try {
    const toCopy = text || location.href;
    await navigator.clipboard.writeText(toCopy);
    console.log('Text or Page URL copied');
  }
  catch (err) {
    console.error('Failed to copy: ', err);
  }
}

这是我在Angular 6+代码中的使用方式:

<button mat-menu-item (click)="copySomething()">
    <span>Copy link</span>
</button>

如果我传入一个字符串,它会复制它。如果没有传入任何内容,则会复制页面的URL。

还可以对剪贴板相关操作进行更多的处理。在此处查看更多信息:

解除剪贴板访问限制


你已经链接到本地主机。 - Joe Warner
2
请注意,这在Safari(版本11.1.2)中不起作用。 - arjunattam
2
@arjun27 希望有一天苹果能够赶上来。尽管 https://caniuse.com/#feat=clipboard 显示你提到的版本部分支持。 - KhoPhi
4
根据提供的链接,“navigator.clipboard仅支持通过HTTPS提供的页面”。 - TimH - Codidact

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接