在reactJS中,如何将文本复制到剪贴板?


147

我正在使用ReactJS,当用户单击链接时,我要将一些文本复制到剪贴板。

我正在使用Chrome 52,并且不需要支持任何其他浏览器。

我看不到为什么这段代码不会导致数据被复制到剪贴板。(代码段的来源来自Reddit帖子)。

我做错了吗?谁能建议有一种“正确”的方法来使用reactjs实现复制到剪贴板吗?

copyToClipboard = (text) => {
  console.log('text', text)
  var textField = document.createElement('textarea')
  textField.innerText = text
  document.body.appendChild(textField)
  textField.select()
  document.execCommand('copy')
  textField.remove()
}

1
您是否尝试过使用第三方解决方案,例如剪贴板js.comgithub.com/zeroclipboard/zeroclipboard
EugZol

11
@EugZol我真的更喜欢编写代码,而不是添加另一个依赖关系,假设代码很小。
杜加尔公爵


@elmeister,这个问题专门针对reactjs
Dougal Dougal

Answers:


179

我个人认为不需要为此提供图书馆。查看http://caniuse.com/#feat=clipboard,它现在得到了广泛的支持,但是您仍然可以做一些事情,例如检查当前客户端中是否存在该功能,如果不存在,只需隐藏复制按钮即可。

import React from 'react';

class CopyExample extends React.Component {

  constructor(props) {
    super(props);

    this.state = { copySuccess: '' }
  }

  copyToClipboard = (e) => {
    this.textArea.select();
    document.execCommand('copy');
    // This is just personal preference.
    // I prefer to not show the the whole text area selected.
    e.target.focus();
    this.setState({ copySuccess: 'Copied!' });
  };

  render() {
    return (
      <div>
        {
         /* Logical shortcut for only displaying the 
            button if the copy command exists */
         document.queryCommandSupported('copy') &&
          <div>
            <button onClick={this.copyToClipboard}>Copy</button> 
            {this.state.copySuccess}
          </div>
        }
        <form>
          <textarea
            ref={(textarea) => this.textArea = textarea}
            value='Some text to copy'
          />
        </form>
      </div>
    );
  }

}

export default CopyExample;

更新:在React 16.7.0-alpha.0中使用React Hooks重写

import React, { useRef, useState } from 'react';

export default function CopyExample() {

  const [copySuccess, setCopySuccess] = useState('');
  const textAreaRef = useRef(null);

  function copyToClipboard(e) {
    textAreaRef.current.select();
    document.execCommand('copy');
    // This is just personal preference.
    // I prefer to not show the the whole text area selected.
    e.target.focus();
    setCopySuccess('Copied!');
  };

  return (
    <div>
      {
       /* Logical shortcut for only displaying the 
          button if the copy command exists */
       document.queryCommandSupported('copy') &&
        <div>
          <button onClick={copyToClipboard}>Copy</button> 
          {copySuccess}
        </div>
      }
      <form>
        <textarea
          ref={textAreaRef}
          value='Some text to copy'
        />
      </form>
    </div>
  );
}

26
这是最好的答案。除非他们需要旧的浏览器支持,否则我们不应该鼓励开发人员将软件包用于所有小事情。
拖船

2
仅作记录:唯一的问题是,如果您要复制页面上某些文本元素中尚未包含的文本,则需要破解一组DOM元素,设置文本,进行复制,清理一下 对于很小的东西,这是很多代码。通常,我会同意不鼓励开发人员不断安装库。
Christopher Ronning '18

3
对于此特定问题,文本已在页面上的元素中。在什么情况下要复制的页面上有可见的文本不在元素中,该怎么办?我很乐意展示一个解决方案,这是一个完全不同的问题。您不需要用react修改任何东西,您只需在render函数中提供一个隐藏元素即可保存文本。无需临时创建元素。
Nate

2
我收到此打字稿错误:Property 'select' does not exist on type 'never'
Alex C

3
我收到TypeError:textAreaRef.current.select不是一个函数
pseudozach '19

118

如果要以编程方式将数据写入剪贴板,请在按钮上使用此简单的内联onClick函数。

onClick={() => {navigator.clipboard.writeText(this.state.textToCopy)}}

3
navigator.clipboard不支持所有浏览器
Premjeet '18


2
根据您提供的链接,它似乎完全支持safari ...
Nibb

2
最适合我的用例,该页面上实际上没有文本要复制的地方。谢谢
NSjonas

1
部分支持非常好,因此大多数用例都完全支持它。如前所述,这是最好的编程解决方案。
Dror Bar

40

您绝对应该考虑使用像上面的@Shubham这样的软件包,但是我根据您所描述的内容创建了一个有效的Codepen:http ://codepen.io/dtschust/pen/WGwdVN?editors=1111 。它可以在我的Chrome浏览器中运行,也许您可​​以看到我是否错过了您所做的事情,或者您的应用程序中是否存在扩展的复杂性阻止了此工作。

// html
<html>
  <body>
    <div id="container">

    </div>
  </body>
</html>


// js
const Hello = React.createClass({
  copyToClipboard: () => {
    var textField = document.createElement('textarea')
    textField.innerText = 'foo bar baz'
    document.body.appendChild(textField)
    textField.select()
    document.execCommand('copy')
    textField.remove()
  },
  render: function () {
    return (
      <h1 onClick={this.copyToClipboard}>Click to copy some text</h1>
    )
  }
})

ReactDOM.render(
<Hello/>,
  document.getElementById('container'))

3
为什么包装比您的解决方案更好?
杜加尔(Dougal)公爵

6
可能更好的跨浏览器的支持,更多的目光投向了包的情况下,我们需要有程序错误修正
德鲁舒斯特

奇迹般有效。是。我也想知道跨浏览器的支持。
Karl Pokus

如果由于您正在使用appendChild而造成的屏幕闪烁,无论之后删除的速度有多快?
robinnnnn

1
这很好,但是在Android上的Chrome(72.0)或Android上的FF(63.0)上均不起作用。
科林

35

最简单的方法是使用react-copy-to-clipboardnpm软件包。

您可以使用以下命令进行安装

npm install --save react react-copy-to-clipboard

按以下方式使用它。

const App = React.createClass({
  getInitialState() {
    return {value: '', copied: false};
  },


  onChange({target: {value}}) {
    this.setState({value, copied: false});
  },


  onCopy() {
    this.setState({copied: true});
  },


  render() {
    return (
      <div>

          <input value={this.state.value} size={10} onChange={this.onChange} />

        <CopyToClipboard text={this.state.value} onCopy={this.onCopy}>
          <button>Copy</button>
        </CopyToClipboard>

                <div>
        {this.state.copied ? <span >Copied.</span> : null}
                </div>
        <br />

        <input type="text" />

      </div>
    );
  }
});

ReactDOM.render(<App />, document.getElementById('container'));

以下链接提供了详细说明

https://www.npmjs.com/package/react-copy-to-clipboard

这是奔放的小提琴


如果我需要反向操作,是否有解决方案?即作者将文本从电子邮件复制到reactjs应用程序中的文本区域。我不需要保留html标签,但是,我只需要保留换行符。
TechTurtle

您可能需要插入onpaste活动
Koen

如果要将html表的内容复制到剪贴板,该如何使用该软件包?@Shubham Khatri
简·弗雷德

19

当您可以像这样使用一个按钮获取全部内容时,为什么需要使用npm软件包

<button 
  onClick={() =>  navigator.clipboard.writeText('Copy this text to clipboard')}
>
  Copy
</button>

我希望这对@jerryurenaa有帮助


16

为什么不只使用事件剪贴板数据收集方法e.clipboardData.setData(type, content)

我认为这是实现在剪贴板内推送水木清华的最省力的方法,请检查一下(我在本地复制操作中曾用它来修改数据):

...

handleCopy = (e) => {
    e.preventDefault();
    e.clipboardData.setData('text/plain', 'Hello, world!');
}

render = () =>
    <Component
        onCopy={this.handleCopy}
    />

我遵循的路径是:https : //developer.mozilla.org/en-US/docs/Web/Events/copy

干杯!

编辑:出于测试目的,我添加了代码笔:https://codepen.io/dprzygodzki/pen/ZaJMKb


3
@KarlPokus发问者仅在寻找Chrome解决方案
TechTurtle

1
已在Chrome 62.0.3202.94版上进行测试。这是工作。 codepen.io/dprzygodzki/pen/ZaJMKb
Damian Przygodzki

1
@OliverDixon是React事件的默认对象。reactjs.org/docs/events.html
Damian Przygodzki

1
@DamianPrzygodzki我讨厌这样的隐藏元素,这是使开发人员感到困惑的好方法。
奥利弗·迪克森

1
我感觉到@OliverDixon,但我觉得习惯一些方法将某些默认数据应用于方法是一件好事,尤其是在事件中。
Damian Przygodzki

8

您的代码应该完美运行,我以相同的方式使用它。仅确保如果click事件是从诸如引导程序模态之类的弹出屏幕中触发的,则创建的元素必须在该模态之内,否则它将不会复制。您总是可以给出该模式中元素的ID(作为第二个参数),并使用getElementById进行检索,然后将新创建的元素附加到该元素而不是文档中。像这样:

copyToClipboard = (text, elementId) => {
  const textField = document.createElement('textarea');
  textField.innerText = text;
  const parentElement = document.getElementById(elementId);
  parentElement.appendChild(textField);
  textField.select();
  document.execCommand('copy');
  parentElement.removeChild(textField);
}

8

我认为上述方法与上面的方法非常相似,但是我认为它更加具体。在此,父组件将传递url(或您想要的任何文本)作为道具。

import * as React from 'react'

export const CopyButton = ({ url }: any) => {
  const copyToClipboard = () => {
    const textField = document.createElement('textarea');
    textField.innerText = url;
    document.body.appendChild(textField);
    textField.select();
    document.execCommand('copy');
    textField.remove();
  };

  return (
    <button onClick={copyToClipboard}>
      Copy
    </button>
  );
};

这很有用,因为我想将段落标签改为Textarea
Ehsan Ahmadi

谢谢!唯一的问题是隐藏文本字段
hinkswhenitscold

3

对于那些试图从DIV而不是文本字段中进行选择的人,这里是代码。该代码是不言自明的,但是如果您需要更多信息,请在此处评论:

     import React from 'react';
     ....

    //set ref to your div
          setRef = (ref) => {
            // debugger; //eslint-disable-line
            this.dialogRef = ref;
          };

          createMarkeup = content => ({
            __html: content,
          });

    //following function select and copy data to the clipboard from the selected Div. 
   //Please note that it is only tested in chrome but compatibility for other browsers can be easily done

          copyDataToClipboard = () => {
            try {
              const range = document.createRange();
              const selection = window.getSelection();
              range.selectNodeContents(this.dialogRef);
              selection.removeAllRanges();
              selection.addRange(range);
              document.execCommand('copy');
              this.showNotification('Macro copied successfully.', 'info');
              this.props.closeMacroWindow();
            } catch (err) {
              // console.log(err); //eslint-disable-line
              //alert('Macro copy failed.');
            }
          };

              render() {
                    return (
                        <div
                          id="macroDiv"
                          ref={(el) => {
                            this.dialogRef = el;
                          }}
                          // className={classes.paper}
                          dangerouslySetInnerHTML={this.createMarkeup(this.props.content)}
                        />
                    );
            }

3

如果您要将当前网址复制到剪贴板,这是另一个用例:

定义方法

const copyToClipboard = e => {
  navigator.clipboard.writeText(window.location.toString())
}

调用该方法

<button copyToClipboard={shareLink}>
   Click to copy current url to clipboard
</button>

3

带有react挂钩的最佳解决方案,无需外部库

import React, { useState } from 'react';

const MyComponent = () => {
const [copySuccess, setCopySuccess] = useState('');

// your function to copy here

  const copyToClipBoard = async copyMe => {
    try {
      await navigator.clipboard.writeText(copyMe);
      setCopySuccess('Copied!');
    } catch (err) {
      setCopySuccess('Failed to copy!');
    }
  };

return (
 <div>
    <Button onClick={() => copyToClipBoard('some text to copy')}>
     Click here to copy
     </Button>
  // after copying see the message here
  {copySuccess}
 </div>
)
}

在此处查看有关navigator.clip board的更多文档,navigator.clipboard文档 navigotor.clipboard受大量浏览器支持。


2
import React, { Component } from 'react';

export default class CopyTextOnClick extends Component {
    copyText = () => {
        this.refs.input.select();

        document.execCommand('copy');

        return false;
    }

    render () {
        const { text } = this.state;

        return (
            <button onClick={ this.copyText }>
                { text }

                <input
                    ref="input"
                    type="text"
                    defaultValue={ text }
                    style={{ position: 'fixed', top: '-1000px' }} />
            </button>
        )
    }
}

1

如果要从DIV中选择而不是从文本字段中选择,请使用以下代码。“代码”是必须复制的值

import React from 'react'
class CopyToClipboard extends React.Component {

  copyToClipboard(code) {
    var textField = document.createElement('textarea')
    textField.innerText = code
    document.body.appendChild(textField)
    textField.select()
    document.execCommand('copy')
    textField.remove()
  }
  render() {
    return (
      <div onClick={this.copyToClipboard.bind(this, code)}>
        {code}
      </div>

    )
  }
}

export default CopyToClipboard

1
SO的最佳实践是通过解释完成代码。拜托了
MartenCatcher

0

这是我的代码:

import React from 'react'

class CopyToClipboard extends React.Component {

  textArea: any

  copyClipBoard = () => {
    this.textArea.select()
    document.execCommand('copy')
  }

  render() {
    return (
      <>
        <input style={{display: 'none'}} value="TEXT TO COPY!!" type="text" ref={(textarea) => this.textArea = textarea}  />
        <div onClick={this.copyClipBoard}>
        CLICK
        </div>
      </>

    )
  }
}

export default CopyToClipboard

0
<input
value={get(data, "api_key")}
styleName="input-wrap"
title={get(data, "api_key")}
ref={apikeyObjRef}
/>
  <div
onClick={() => {
  apikeyObjRef.current.select();
  if (document.execCommand("copy")) {
    document.execCommand("copy");
  }
}}
styleName="copy"
>
  复制
</div>

7
请添加有关此代码如何解决问题的说明,而不仅仅是发布代码。
亚历山大·范·奥斯滕里克

0

找到了最好的方法。我的意思是最快的方法:w3school

https://www.w3schools.com/howto/howto_js_copy_clipboard.asp

在React功能组件内部。创建一个名为handleCopy的函数:

function handleCopy() {
  // get the input Element ID. Save the reference into copyText
  var copyText = document.getElementById("mail")
  // select() will select all data from this input field filled  
  copyText.select()
  copyText.setSelectionRange(0, 99999)
  // execCommand() works just fine except IE 8. as w3schools mention
  document.execCommand("copy")
  // alert the copied value from text input
  alert(`Email copied: ${copyText.value} `)
}

<>
              <input
                readOnly
                type="text"
                value="exemple@email.com"
                id="mail"
              />
              <button onClick={handleCopy}>Copy email</button>

</>

如果不使用React,w3schools也提供了一种很酷的方法,包括以下工具提示:https : //www.w3schools.com/howto/tryit.asp? filename =tryhow_js_copy_clipboard2

如果使用React,那么可以考虑:使用Toastify来警告消息。 https://github.com/fkhadra/react-toastify这是非常易于使用的库。安装后,您可以更改此行:

 alert(`Email copied: ${copyText.value} `)

对于类似:

toast.success(`Email Copied: ${copyText.value} `)

如果要使用它,请不要忘记安装toastify。导入ToastContainer并同时敬酒CSS:

import { ToastContainer, toast } from "react-toastify"
import "react-toastify/dist/ReactToastify.css"

并在return内部添加吐司容器。

import React from "react"

import { ToastContainer, toast } from "react-toastify"
import "react-toastify/dist/ReactToastify.css"


export default function Exemple() {
  function handleCopy() {
    var copyText = document.getElementById("mail")
    copyText.select()
    copyText.setSelectionRange(0, 99999)
    document.execCommand("copy")
    toast.success(`Hi! Now you can: ctrl+v: ${copyText.value} `)
  }

  return (
    <>
      <ToastContainer />
      <Container>
                <span>E-mail</span>
              <input
                readOnly
                type="text"
                value="myemail@exemple.com"
                id="mail"
              />
              <button onClick={handleCopy}>Copy Email</button>
      </Container>
    </>
  )
}

您的答案仅包含对其他资源的引用,而没有特定的答案。如果w3schools链接是正确的解决方案,请在此处键入。
f.khantsis
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.