我使用的是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()
}

当前回答

对于那些试图从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)}
                        />
                    );
            }

其他回答

受@nate回答的启发,我创建了一个withCopyText反应钩子。并且,添加了navigator.clipboard.writeText支持execCommand回退。

钩子意味着它可以跨许多组件重用,而无需重复代码。有关实现,请参阅示例组件CopyText。

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

const withCopyText = (textElementRef) => {
  if (!textElementRef) throw 'withCopyText: ref is required';

  const [copyStatus, setCopyStatus] = useState('');
  const [support, setSupport] = useState({
    navigatorClipboard: !!navigator.clipboard,
    exec: !!document.queryCommandSupported('copy'),
  });

  const copyToClipboard = (e) => {
    if ('' !== copyStatus) {
      setCopyStatus('');
      await new Promise((resolve) => setTimeout(resolve, 200));
    }

    // clipboard.writeText has wide but not 100% support
    // https://caniuse.com/?search=writeText
    if (support.navigatorClipboard) {
      try {
        navigator.clipboard.writeText(textElementRef.current.value);
        return setCopyStatus('success');
      } catch (e) {
        setSupport({ ...support, navigatorClipboard: false });
      }
    }
    // execCommand has > 97% support but is deprecated, use it as a fallback
    // https://caniuse.com/?search=execCommand
    // https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
    if (!support.navigatorClipboard) {
      try {
        textElementRef.current.select();
        document.execCommand('copy');
        e.target.focus();
        setCopyStatus('success');
      } catch (e) {
        setSupport({ ...support, exec: false });
        return setCopyStatus('fail');
      }
    }
  };

  return {
    copyStatus,
    copyToClipboard,
    support: Object.values(support).includes(true),
  };
};

const CopyText = ({ text }) => {
  const textElementRef = useRef(null);

  const { copyStatus, copyToClipboard, support } = withCopyText(textElementRef);

  return (
    <span>
      {support && <button onClick={copyToClipboard}>Copy</button>}
      {'success' === copyStatus && <span>Copied to clipboard!</span>}
      {'fail' === copyStatus && <span>Sorry, copy to clipboard failed</span>}
      <input type="text" ref={textElementRef} value={text} readOnly={true} />
    </span>
  );
};

export { CopyText, withCopyText };

首先创建BTN,然后添加这个onClick:

onClick={() =>  navigator.clipboard.writeText(textState)}

or

onClick={() =>  navigator.clipboard.writeText('Your text for copy')}

您可以使用事件clipboardData收集方法e.clipboardData。setData(类型、内容)。

在我看来,这是实现在剪贴板内推送一些东西的最直接的方法,看看这个(我用它来修改数据,而本地复制动作):

...

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

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

剪贴板将在2021年得到各大浏览器的良好支持。一种方法是首先构建剪贴板函数的副本,然后使用onClick事件处理程序调用它。

function copy(text){
  navigator.clipboard.writeText(text)
}

为了防止硬编码,让我们假设字符串被分配给一个名为someText的变量

<span onClick={() => copy(someText)}>
  {someText}
</span>