Di reactJS, bagaimana cara menyalin teks ke clipboard?

147

Saya menggunakan ReactJS dan ketika pengguna mengklik tautan, saya ingin menyalin beberapa teks ke clipboard.

Saya menggunakan Chrome 52 dan saya tidak perlu mendukung browser lain.

Saya tidak bisa melihat mengapa kode ini tidak menghasilkan data yang disalin ke clipboard. (asal potongan kode berasal dari pos Reddit).

Apakah saya melakukan ini salah? Adakah yang bisa menyarankan apakah ada cara yang "benar" untuk menerapkan copy ke clipboard menggunakan 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()
}
Duke Dougal
sumber
1
Apakah Anda mencoba menggunakan solusi pihak ke-3, seperti clipboardjs.com atau github.com/zeroclipboard/zeroclipboard ?
EugZol
11
@EugZol Saya benar-benar lebih suka menulis kode daripada menambahkan ketergantungan lain, dengan asumsi kode ini cukup kecil.
Duke Dougal
Periksa jawaban ini stackoverflow.com/questions/400212/…
elmeister
@elmeister, pertanyaannya khusus untuk reactjs
Duke Dougal

Jawaban:

180

Saya pribadi tidak melihat perlunya perpustakaan untuk ini. Melihat http://caniuse.com/#feat=clipboard sudah cukup banyak didukung sekarang, namun Anda masih dapat melakukan hal-hal seperti memeriksa untuk melihat apakah fungsionalitas ada di klien saat ini dan cukup sembunyikan tombol salin jika tidak.

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;

Pembaruan: Ditulis ulang menggunakan React Hooks di React 16.7.0-alpha.0

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>
  );
}
Nate
sumber
26
Ini jawaban terbaik. Kami tidak seharusnya mendorong para dev untuk menggunakan paket untuk setiap hal kecil kecuali mereka membutuhkan dukungan browser lama.
tarik
2
Sebagai catatan: satu-satunya masalah dengan ini adalah jika Anda mencoba menyalin teks yang belum ada di beberapa elemen teks pada halaman, Anda perlu meretas set elemen DOM, mengatur teks, menyalinnya, dan bersihkan. Itu banyak kode untuk sesuatu yang sangat kecil. Biasanya saya akan setuju bahwa para dev seharusnya tidak didorong untuk terus-menerus menginstal perpustakaan.
Christopher Ronning
3
Untuk masalah khusus ini, teks sudah ada di elemen pada halaman. Kasus apa yang akan ada di mana ada teks terlihat pada halaman yang ingin Anda salin yang tidak ada dalam elemen? Itu masalah yang sama sekali berbeda yang dengan senang hati saya tunjukkan solusinya. Anda tidak perlu meretas apa pun dengan reaksi, Anda hanya akan memberikan elemen tersembunyi di fungsi render Anda yang juga menampung teks. Tidak perlu membuat elemen ad hoc.
Nate
2
Saya mendapatkan kesalahan penulisan naskah ini:Property 'select' does not exist on type 'never'
Alex C
3
Saya mendapatkan TypeError: textAreaRef.current.select bukan fungsi
pseudozach
118

Gunakan fungsi inClick inline sederhana ini pada tombol jika Anda ingin secara terprogram menulis data ke clipboard.

onClick={() => {navigator.clipboard.writeText(this.state.textToCopy)}}
Gary Vernon Grubb
sumber
3
navigator.clipboard tidak mendukung semua browser
Premjeet
8
tampaknya itu adalah dukungan yang baik untuk browser utama di 2018 caniuse.com/#search=clipboard
gasolin
2
berdasarkan tautan yang Anda berikan, sepertinya itu hanya didukung sepenuhnya di safari ...
Nibb
2
berfungsi paling baik untuk usecase saya di mana teks yang akan disalin sebenarnya tidak ada di halaman. Terima kasih
NSjonas
1
Dukungan parsial sangat baik, sehingga didukung penuh untuk sebagian besar kasus penggunaan. Dan seperti yang disebutkan, ini adalah solusi terprogram terbaik.
Dror Bar
40

Anda harus mempertimbangkan untuk menggunakan paket seperti @Shubham di atas, tetapi saya membuat codepen yang berfungsi berdasarkan apa yang Anda jelaskan: http://codepen.io/dtschust/pen/WGwdVN?editors=1111 . Ini berfungsi di browser saya di chrome, mungkin Anda dapat melihat apakah ada sesuatu yang saya lakukan di sana yang Anda lewatkan, atau jika ada beberapa kompleksitas yang diperluas dalam aplikasi Anda yang mencegah hal ini bekerja.

// 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'))
Drew Schuster
sumber
3
Mengapa paket lebih baik dari solusi Anda?
Duke Dougal
6
Potensi dukungan lintas peramban yang lebih baik, dan lebih banyak perhatian pada paket ini jika seandainya ada bug yang diperbaiki
Drew Schuster
bekerja seperti pesona. Iya. Saya bertanya-tanya tentang dukungan lintas browser juga.
Karl Pokus
apakah ini akan menyebabkan layar berkedip ketika Anda menggunakan appendChild, tidak peduli seberapa cepat Anda menghapusnya setelah itu?
robinnnnn
1
Ini bagus tetapi tidak bekerja di Chrome (72.0) di Android atau di FF (63.0) di Android.
Colin
35

Cara paling sederhana adalah menggunakan react-copy-to-clipboardpaket npm.

Anda dapat menginstalnya dengan perintah berikut

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

Gunakan dengan cara berikut.

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'));

Penjelasan terperinci disediakan di tautan berikut

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

Ini biola lari .

Shubham Khatri
sumber
Apakah ada solusi jika saya perlu melakukan reverse? yaitu Penulis akan menyalin teks dari email ke area teks dalam aplikasi reactjs. Saya tidak perlu mempertahankan tag html, namun, saya hanya perlu mempertahankan jeda baris.
TechTurtle
Anda mungkin perlu menyambungkan onpasteacara
Koen
Bagaimana saya bisa menggunakan paket ini jika saya ingin menyalin konten dari tabel html ke clipboard? @Shubham Khatri
Jane Fred
19

Mengapa menggunakan Anda memerlukan paket npm ketika Anda bisa mendapatkan semua dalam satu tombol seperti ini

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

Saya harap ini membantu @ jerryurenaa

jerryurenaa
sumber
16

Mengapa tidak menggunakan metode pengumpulan event clipboardData saja e.clipboardData.setData(type, content)?

Menurut pendapat saya adalah metode yang paling sulit untuk mencapai mendorong di dalam clipboard, lihat ini (saya sudah menggunakannya untuk memodifikasi data saat menyalin asli):

...

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

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

Saya mengikuti jalur itu: https://developer.mozilla.org/en-US/docs/Web/Events/copy

Bersulang!

EDIT: Untuk tujuan pengujian, saya telah menambahkan codepen: https://codepen.io/dprzygodzki/pen/ZaJMKb

Damian Przygodzki
sumber
3
@KarlPokus Penanya hanya mencari solusi Chrome
TechTurtle
1
Diuji pada Versi Chrome 62.0.3202.94. Ini bekerja. codepen.io/dprzygodzki/pen/ZaJMKb
Damian Przygodzki
1
@OliverDixon itu adalah objek default dari React event. reactjs.org/docs/events.html
Damian Przygodzki
1
@DamianPrzygodzki Saya benci elemen tersembunyi seperti ini, cara yang bagus untuk membingungkan pengembang.
Oliver Dixon
1
@Liverdixon saya merasa Anda, tetapi saya pikir itu baik untuk digunakan yang kadang-kadang ada beberapa data standar yang diterapkan pada metode, terutama dalam acara
Damian Przygodzki
8

Kode Anda harus bekerja dengan sempurna, saya menggunakannya dengan cara yang sama. Hanya pastikan bahwa jika acara klik dipicu dari dalam layar pop up seperti modal bootstrap atau sesuatu, elemen yang dibuat harus berada di dalam modal itu jika tidak, ia tidak akan menyalin. Anda selalu bisa memberikan id suatu elemen dalam modal tersebut (sebagai parameter kedua) dan mengambilnya dengan getElementById, lalu menambahkan elemen yang baru dibuat ke elemen itu alih-alih dokumen. Sesuatu seperti ini:

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);
}
Kupi
sumber
8

Saya telah mengambil pendekatan yang sangat mirip dengan beberapa di atas, tetapi membuatnya sedikit lebih konkret, saya pikir. Di sini, komponen induk akan meneruskan url (atau teks apa pun yang Anda inginkan) sebagai prop.

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>
  );
};
tjgragg
sumber
Ini berguna karena saya ingin memiliki tag paragraf sebagai gantinya ke Textarea
Ehsan Ahmadi
Terima kasih! Satu-satunya masalah menyembunyikan bidang teks
itshinkswhenitscold
3

Bagi orang-orang yang mencoba untuk memilih dari DIV daripada bidang teks, berikut adalah kodenya. Kode ini jelas tetapi komentari di sini jika Anda ingin informasi lebih lanjut:

     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)}
                        />
                    );
            }
connect2Coder
sumber
3

Ini kasus penggunaan lain, jika Anda ingin menyalin url saat ini ke clipboard Anda:

Tentukan metode

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

Sebut metode itu

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

Solusi terbaik dengan kait reaksi, tidak perlu perpustakaan eksternal untuk itu

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

periksa di sini untuk dokumentasi lebih lanjut tentang papan navigator.clip , dokumentasi navigator.clipboard navigotor.clipboard didukung oleh sejumlah besar browser lihat di sini didukung browser

Jaman-Dedy
sumber
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>
        )
    }
}
Yash Pokar
sumber
1

Jika Anda ingin memilih dari DIV daripada bidang teks, ini adalah kodenya. "Kode" adalah nilai yang harus disalin

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
Haris George
sumber
1
Praktik terbaik SO adalah menyelesaikan kode Anda dengan penjelasan. Tolong lakukan itu.
MartenCatcher
0

ini kode saya:

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
Alan
sumber
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>
bob
sumber
7
Harap tambahkan penjelasan tentang bagaimana kode ini menyelesaikan masalah, bukan hanya mengirim kode.
Alexander van Oostenrijk
0

Menemukan cara terbaik untuk melakukannya. maksud saya cara tercepat: w3school

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

Di dalam komponen fungsional bereaksi. Buat fungsi bernama 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="[email protected]"
                id="mail"
              />
              <button onClick={handleCopy}>Copy email</button>

</>

Jika tidak menggunakan Bereaksi, w3schools juga memiliki satu cara keren untuk melakukan ini dengan tooltip termasuk: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_copy_clipboard2

Jika menggunakan Bereaksi, pikirkanlah hal yang harus dilakukan: Gunakan Toastify untuk mengingatkan pesan. https://github.com/fkhadra/react-toastify Ini adalah lib yang sangat mudah digunakan. Setelah instalasi, Anda mungkin dapat mengubah baris ini:

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

Untuk sesuatu seperti:

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

Jika Anda ingin menggunakannya, jangan lupa untuk Menginstal toastify. impor ToastContainer dan juga toasts css:

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

dan tambahkan wadah roti panggang kembali.

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="[email protected]"
                id="mail"
              />
              <button onClick={handleCopy}>Copy Email</button>
      </Container>
    </>
  )
}
Iago Barreto
sumber
Jawaban Anda hanya berisi referensi ke sumber lain, tetapi tidak ada jawaban spesifik. Jika tautan w3schools sebagai solusi yang tepat, ketik di sini.
f.khantsis