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
clipboardjs.comgithub.com/zeroclipboard/zeroclipboardなどのサードパーティのソリューションを使用してみましたか?
EugZol

11
@EugZolコードがかなり小さいと想定して、別の依存関係を追加するよりも、コードを書く方を本当に好みます。
デューク

これらの答えをチェックstackoverflow.com/questions/400212/...
elmeister

@elmeister質問はreactjsに固有です
Duke Dougal

回答:


180

個人的には、このためのライブラリが必要だとは思いません。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フックを使用して書き直されました

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
これが最良の答えです。開発者が古いブラウザーのサポートを必要としない限り、小さなことすべてにパッケージを使用するように勧めるべきではありません。
tugce

2
参考までに、これの唯一の問題は、ページの一部のテキスト要素にまだないテキストをコピーしようとする場合、一連のDOM要素をハックし、テキストを設定してコピーする必要があることです。そしてそれをきれいにします。これは非常に小さなもののコードがたくさんあります。通常、私は開発者が常にライブラリをインストールすることを推奨されるべきではないことに同意します。
クリストファーロニング

3
この特定の問題では、テキストはすでにページの要素にあります。コピーしたいページの要素にないテキストが表示されている場合はどのような場合がありますか?それは、私が解決策を提示したいと思います完全に異なる問題です。あなたはreactで何かをハックする必要はありません、あなたはあなたのrender関数にテキストも保持する隠し要素を提供するだけです。アドホックな要素を作成する必要はありません。
ネイト

2
私は、このtypescriptですエラーが発生します:Property 'select' does not exist on type 'never'
アレックス・C

3
TypeErrorが発生する:textAreaRef.current.select is not a function
pseudozach

118

プログラムでクリップボードにデータを書き込む場合は、ボタンでこの単純なインラインonClick関数を使用します。

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

3
navigator.clipboardはすべてのブラウザをサポートしているわけではありません
Premjeet '30

8
2018年に主要なブラウザーを十分にサポートしているようです。caniuse.com

2
あなたが提供したリンクに基づいて、それはサファリで唯一完全にサポートされているようです...
Nibb

2
コピーするテキストが実際にページ上にない私のユースケースに最適です。ありがとう
NSjonas

1
部分的なサポートは非​​常に優れているため、ほとんどのユースケースで完全にサポートされています。そして前述のように、これは最高のプログラムによるソリューションです。
Dror Bar

40

上記の@Shubhamのようなパッケージの使用を確実に検討する必要がありますが、私はあなたの説明に基づいて動作するコードペンを作成しました:http ://codepen.io/dtschust/pen/WGwdVN?editors=1111 。それは私のブラウザのクロムで動作します。おそらく私が見逃した何かがあるかどうか、またはこれが機能しないようにするアプリケーションに拡張された複雑さがあるかどうかを確認できます。

// 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
パッケージがソリューションよりも優れているのはなぜですか?
デュークドゥガル

6
潜在的に改善されたクロスブラウザーサポート、およびバグを修正する必要がある場合に備えてパッケージへの
Drew Schuster

魅力のように動作します。はい。クロスブラウザーのサポートについても疑問に思います。
Karl Pokus 2017

appendChildを使用しているため、後でどれだけすばやく削除しても、画面でちらつきが発生しますか?
robinnnnn 2018

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 2017

おそらくonpasteイベントを接続する必要があります
Koen

HTMLテーブルの内容をクリップボードにコピーする場合、このパッケージをどのように使用できますか?@Shubham Khatri
Jane Fred

19

このように1つのボタン内ですべてを取得できるのにnpmパッケージが必要な理由

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

これが@jerryurenaaのお役に立てば幸いです


16

イベントclipboardDataコレクションメソッドだけを使用しないのはなぜ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 2017

1
Chromeバージョン62.0.3202.94でテスト済み。動いています。 codepen.io/dprzygodzki/pen/ZaJMKb
Damian Przygodzki 2017年

1
@OliverDixonは、Reactイベントのデフォルトオブジェクトです。reactjs.org/docs/events.html
Damian Przygodzki

1
@DamianPrzygodzki私はこのような隠された要素を嫌い、開発者を混乱させる素晴らしい方法です。
Oliver Dixon

1
@OliverDixon私はあなたを感じますが、特にイベントでは、メソッドに適用されるデフォルトのデータが時々あることに慣れるのは良いことだと思います。
Damian Przygodzki 2018

8

あなたのコードは完璧に動くはずです、私はそれを同じように使います。ブートストラップモーダルなどのポップアップ画面内からクリックイベントがトリガーされた場合にのみ、作成された要素がそのモーダル内にある必要があります。それ以外の場合はコピーされません。そのモーダル内の要素のIDを(2番目のパラメーターとして)常に指定し、それを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

ありがとう!問題は
テキストフィールドを

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

現在のURLをクリップボードにコピーする場合の別の使用例を次に示します。

メソッドを定義する

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

そのメソッドを呼び出す

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

3

反応フックを使用した最良のソリューション、そのための外部ライブラリーは不要

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ボードについての詳細なドキュメントについてはこちらをチェックし、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
単にコードを投稿するのではなく、このコードが問題を解決する方法の説明を追加してください。
Alexander van Oostenrijk

0

それを行うための最良の方法を見つけました。私は最速の方法を意味します:w3school

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

反応機能コンポーネントの内部。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にはツールチップを含めてこれを行う1つのクールな方法があります。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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.