In reazioni, come copiare il testo negli appunti?


147

Sto usando ReactJS e quando un utente fa clic su un collegamento voglio copiare del testo negli Appunti.

Sto utilizzando Chrome 52 e non ho bisogno di supportare altri browser.

Non riesco a capire perché questo codice non comporti la copia dei dati negli Appunti. (l'origine dello snippet di codice proviene da un post di Reddit).

Sto sbagliando? Qualcuno può suggerire che esiste un modo "corretto" per implementare la copia negli Appunti utilizzando reazioni?

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
Hai provato a utilizzare soluzioni di terze parti, come clipboardjs.com o github.com/zeroclipboard/zeroclipboard ?
EugZol,

11
@EugZol Preferisco davvero scrivere codice piuttosto che aggiungere un'altra dipendenza, supponendo che il codice sia abbastanza piccolo.
Duke Dougal,

Controllare queste risposte stackoverflow.com/questions/400212/...
elmeister

@elmeister la domanda è specifica per reazione
Duke Dougal,

Risposte:


181

Personalmente non vedo la necessità di una biblioteca per questo. Guardando http://caniuse.com/#feat=clipboard ora è abbastanza ampiamente supportato, tuttavia puoi ancora fare cose come controllare per vedere se la funzionalità esiste nel client corrente e semplicemente nascondere il pulsante di copia in caso contrario.

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;

Aggiornamento: riscritto usando React Hooks in 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>
  );
}

27
Questa è la risposta migliore Non dovremmo incoraggiare gli sviluppatori a usare i pacchetti per ogni piccola cosa a meno che non abbiano bisogno del vecchio supporto del browser.
tugce,

3
Solo per la cronaca: l'unico problema è che se stai provando a copiare del testo che non è già presente in qualche elemento di testo nella pagina, dovrai hackerare un set di elementi DOM, impostare il testo, copiarlo, e pulirlo. È un sacco di codice per qualcosa di molto piccolo. Normalmente concordo sul fatto che gli sviluppatori non dovrebbero essere incoraggiati a installare costantemente librerie.
Christopher Ronning,

3
Per questo particolare problema, il testo è già in un elemento nella pagina. Quale caso ci sarebbe dove c'è un testo visibile sulla pagina che vuoi copiare che non si trova in un elemento? Questo è un problema completamente diverso a cui sarei felice di mostrare una soluzione. Non avresti bisogno di hackerare nulla con reagire, forniresti solo un elemento nascosto nella tua funzione di rendering che contiene anche il testo. Non è necessario creare elementi ad hoc.
Nate,

2
Ottengo questo errore dattiloscritto:Property 'select' does not exist on type 'never'
Alex C

3
Ottengo TypeError: textAreaRef.current.select non è una funzione
pseudozach

120

Utilizzare questa semplice funzione onClick incorporata su un pulsante se si desidera scrivere programmaticamente dati negli Appunti.

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

3
navigator.clipboard non supporta tutti i browser
Premjeet

8
sembra sia stato un valido
gasolin

2
basato sul link che hai fornito, sembra che sia totalmente supportato in Safari ...
Nibb

2
funziona meglio per il mio caso in cui il testo da copiare non è effettivamente nella pagina. Grazie
NSjonas

1
Il supporto parziale è molto buono, quindi è completamente supportato per la maggior parte dei casi d'uso. E come detto, questa è la migliore soluzione programmatica.
Dror Bar il

40

Dovresti assolutamente prendere in considerazione l'utilizzo di un pacchetto come @Shubham sopra, ma ho creato un codice funzionante basato su ciò che hai descritto: http://codepen.io/dtschust/pen/WGwdVN?editors=1111 . Funziona nel mio browser in Chrome, forse puoi vedere se c'è qualcosa che ho fatto lì che ti sei perso o se c'è una complessità estesa nella tua applicazione che impedisce che funzioni.

// 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
Perché un pacchetto è migliore della tua soluzione?
Duke Dougal,

6
Supporto cross browser potenzialmente migliore e più occhi sul pacchetto nel caso in cui sia necessario correggere un bug
Drew Schuster,

funziona come un fascino. Sì. Mi chiedo anche il supporto cross-browser.
Karl Pokus,

ciò provocherebbe uno sfarfallio sullo schermo se da quando stai usando appendChild, non importa quanto velocemente lo rimuovi in ​​seguito?
robinnnnn,

1
Questo è buono ma non funziona su Chrome (72.0) su Android né su FF (63.0) su Android.
colin

35

Il modo più semplice sarà utilizzare il react-copy-to-clipboardpacchetto npm.

Puoi installarlo con il seguente comando

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

Usalo nel modo seguente.

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

Una spiegazione dettagliata è fornita al seguente link

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

Ecco un violino in esecuzione .


C'è qualche soluzione se devo fare il contrario? vale a dire l'autore copierà il testo da un'e-mail nell'area di testo nell'applicazione Reajs. Non ho bisogno di conservare i tag html, tuttavia, devo preservare solo le interruzioni di riga.
TechTurtle,

Probabilmente dovrai collegare l' onpasteevento
Koen il

Come posso usare questo pacchetto se voglio copiare il contenuto di una tabella html negli appunti? @Shubham Khatri
Jane Fred,

19

Perché utilizzarlo è necessario un pacchetto npm quando è possibile ottenere tutto in un singolo pulsante come questo

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

Spero che questo aiuti @jerryurenaa


16

Perché non usare solo il metodo di raccolta dati negli Appunti evento e.clipboardData.setData(type, content) ?

A mio avviso è il metodo più diretto per ottenere la spinta negli appunti, controlla questo (l'ho usato per modificare i dati durante l'azione di copia nativa):

...

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

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

Ho seguito quel percorso: https://developer.mozilla.org/en-US/docs/Web/Events/copy

Saluti!

EDIT: a scopo di test, ho aggiunto codepen: https://codepen.io/dprzygodzki/pen/ZaJMKb


3
@KarlPokus L'interrogante sta solo cercando una soluzione Chrome
TechTurtle l'

1
Testato sulla versione Chrome 62.0.3202.94. Funziona. codepen.io/dprzygodzki/pen/ZaJMKb
Damian Przygodzki

1
@OliverDixon è l'oggetto predefinito dell'evento React. reazionejs.org/docs/events.html
Damian Przygodzki

1
@DamianPrzygodzki Odio elementi nascosti come questo, ottimo modo per confondere gli sviluppatori.
Oliver Dixon,

1
@OliverDixon Ti sento, ma penso che sia utile abituarsi che a volte ci sono alcuni dati predefiniti applicati al metodo, specialmente negli eventi.
Damian Przygodzki,

8

Il tuo codice dovrebbe funzionare perfettamente, lo uso allo stesso modo. Assicurati solo che se l'evento click viene attivato da una schermata pop-up come un bootstrap modale o qualcosa del genere, l'elemento creato deve trovarsi all'interno di quel modale altrimenti non verrà copiato. Puoi sempre fornire l'id di un elemento all'interno di quel modale (come secondo parametro) e recuperarlo con getElementById, quindi aggiungere l'elemento appena creato a quello invece del documento. Qualcosa come questo:

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

Ho adottato un approccio molto simile a quanto sopra, ma lo ho reso un po 'più concreto, credo. Qui, un componente genitore passerà l'URL (o qualunque testo tu voglia) come 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>
  );
};

Questo è stato utile perché volevo avere un tag di paragrafo invece di Textarea
Ehsan Ahmadi,

Grazie! L'unico problema è nascondere il campo di testo
pensa che l'

3

Per coloro che stanno cercando di selezionare dal DIV invece del campo di testo, ecco il codice. Il codice è autoesplicativo ma commenta qui se vuoi maggiori informazioni:

     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

Ecco un altro caso d'uso, se desideri copiare l'URL corrente negli appunti:

Definire un metodo

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

Chiama quel metodo

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

3

La migliore soluzione con hook di reazione, per questo non sono necessarie librerie esterne

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

controlla qui per ulteriore documentazione su navigator.clipboard , navigator.clipboard documentazione navigotor.clipboard è supportata da un numero enorme di browser guarda qui browser supportato


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

Se vuoi selezionare dal DIV invece del campo di testo, ecco il codice. Il "codice" è il valore che deve essere copiato

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
La migliore pratica di SO è realizzare il tuo codice con una spiegazione. Per favore fallo.
MartenCatcher il

0

ecco il mio codice:

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
Aggiungi una spiegazione di come questo codice risolve il problema, piuttosto che pubblicare semplicemente il codice.
Alexander van Oostenrijk,

0

Trovato il modo migliore per farlo. intendo il modo più veloce: w3school

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

All'interno di un componente funzionale di reazione. Creare una funzione denominata 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>

</>

Se non usi React, w3schools ha anche un modo fantastico per farlo con la descrizione dei comandi inclusa: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_copy_clipboard2

Se si utilizza React, una cosa interessante da fare: utilizzare un Toastify per avvisare il messaggio. https://github.com/fkhadra/react-toastify Questa è la lib molto facile da usare. Dopo l'installazione, potresti essere in grado di cambiare questa linea:

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

Per qualcosa come:

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

Se si desidera utilizzarlo, non dimenticare di installare toastify. importa ToastContainer e brinda anche ai CSS:

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

e aggiungi il contenitore toast all'interno di 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>
    </>
  )
}

La tua risposta contiene solo il riferimento a un'altra risorsa, ma nessuna risposta specifica. Se il link w3schools è la soluzione corretta, digita qui.
f.khantsis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.