Dans reactJS, comment copier du texte dans le presse-papiers?

147

J'utilise ReactJS et lorsqu'un utilisateur clique sur un lien, je souhaite copier du texte dans le presse-papiers.

J'utilise Chrome 52 et je n'ai pas besoin de prendre en charge d'autres navigateurs.

Je ne vois pas pourquoi ce code n'entraîne pas la copie des données dans le presse-papiers. (L'origine de l'extrait de code provient d'un article Reddit).

Est-ce que je fais ça mal? Quelqu'un peut-il suggérer qu'il existe un moyen "correct" d'implémenter la copie dans le presse-papiers en utilisant 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()
}
Duc Dougal
la source
1
Avez-vous essayé d'utiliser des solutions tierces, comme clipboardjs.com ou github.com/zeroclipboard/zeroclipboard ?
EugZol
11
@EugZol Je préfère vraiment écrire du code plutôt que d'ajouter une autre dépendance, en supposant que le code soit assez petit.
Duke Dougal
Vérifiez ces réponses stackoverflow.com/questions/400212/…
elmeister
@elmeister la question est spécifique à reactjs
Duke Dougal

Réponses:

181

Personnellement, je ne vois pas la nécessité d'une bibliothèque pour cela. En regardant http://caniuse.com/#feat=clipboard, il est assez largement pris en charge maintenant, mais vous pouvez toujours faire des choses comme vérifier si la fonctionnalité existe dans le client actuel et simplement cacher le bouton de copie si ce n'est pas le cas.

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;

Mise à jour: réécrit à l'aide de React Hooks dans 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
la source
27
C'est la meilleure réponse. Nous ne devrions pas encourager les développeurs à utiliser des packages pour chaque petite chose, à moins qu'ils n'aient besoin du support d'un ancien navigateur.
tugce
3
Juste pour mémoire: le seul problème avec ceci est que si vous essayez de copier du texte qui n'est pas déjà dans un élément de texte sur la page, vous devrez pirater un ensemble d'éléments DOM, définir le texte, le copier, et nettoyez-le. C'est beaucoup de code pour quelque chose de très petit. Normalement, je conviens que les développeurs ne devraient pas être encouragés à installer constamment des bibliothèques.
Christopher Ronning
3
Pour ce problème particulier, le texte est déjà dans un élément de la page. Dans quel cas y aurait-il du texte visible sur la page que vous souhaitez copier et qui ne figure pas dans un élément? C'est un problème entièrement différent auquel je serais heureux de montrer une solution. Vous n'avez pas besoin de pirater quoi que ce soit avec react, vous devez simplement fournir un élément caché dans votre fonction de rendu qui contient également le texte. Pas besoin de créer des éléments ad hoc.
Nate
2
J'obtiens cette erreur dactylographiée:Property 'select' does not exist on type 'never'
Alex C
3
J'obtiens TypeError: textAreaRef.current.select n'est pas une fonction
pseudozach
120

Utilisez cette fonction onClick en ligne simple sur un bouton si vous souhaitez écrire des données par programme dans le presse-papiers.

onClick={() => {navigator.clipboard.writeText(this.state.textToCopy)}}
Gary Vernon Grubb
la source
3
navigator.clipboard ne prend pas en charge tous les navigateurs
Premjeet
8
semble avoir bien supporté les principaux navigateurs en 2018 caniuse.com/#search=clipboard
gasolin
2
basé sur le lien que vous avez fourni, il semble que son seul totalement pris en charge en safari ...
Nibb
2
fonctionne mieux pour mon cas d'utilisation où le texte à copier n'est pas réellement sur la page. Merci
NSjonas
1
Le support partiel est très bon, il est donc entièrement pris en charge pour la plupart des cas d'utilisation. Et comme mentionné, c'est la meilleure solution programmatique.
Dror Bar
40

Vous devriez certainement envisager d'utiliser un package comme @Shubham ci-dessus est conseillé, mais j'ai créé un codepen fonctionnel basé sur ce que vous avez décrit: http://codepen.io/dtschust/pen/WGwdVN?editors=1111 . Cela fonctionne dans mon navigateur dans Chrome, vous pouvez peut-être voir s'il y a quelque chose que j'ai fait là-bas que vous avez manqué, ou s'il y a une complexité étendue dans votre application qui empêche cela de fonctionner.

// 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
la source
3
Pourquoi un package est-il meilleur que votre solution?
Duke Dougal
6
Potentiellement meilleur support multi-navigateurs, et plus d'yeux sur le package au cas où il faudrait corriger un bug
Drew Schuster
fonctionne comme un charme. Oui. Je m'interroge également sur le support multi-navigateurs.
Karl Pokus
cela provoquerait-il un scintillement sur l'écran si, depuis que vous utilisez appendChild, peu importe la rapidité avec laquelle vous le supprimez par la suite?
robinnnnn
1
C'est bien mais ça ne marche pas sur Chrome (72.0) sur Android ni sur FF (63.0) sur Android.
colin
35

Le moyen le plus simple sera d'utiliser le react-copy-to-clipboardpackage npm.

Vous pouvez l'installer avec la commande suivante

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

Utilisez-le de la manière suivante.

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

Une explication détaillée est fournie sur le lien suivant

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

Voici un violon courant .

Shubham Khatri
la source
Y a-t-il une solution si je dois faire l'inverse? c'est-à-dire que l'auteur copiera le texte d'un e-mail dans la zone de texte de l'application reactjs. Je n'ai pas besoin de conserver les balises html, cependant, je ne dois conserver que les sauts de ligne.
TechTurtle
Vous devez probablement brancher l' onpasteévénement
Koen
Comment puis-je utiliser ce package si je souhaite copier le contenu d'une table html dans le presse-papiers? @Shubham Khatri
Jane Fred
19

Pourquoi utiliser, vous avez besoin d'un package npm alors que vous pouvez tout obtenir dans un seul bouton comme celui-ci

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

J'espère que cela aidera @jerryurenaa

jerryurenaa
la source
16

Pourquoi ne pas utiliser uniquement la méthode de collecte d'événements clipboardData e.clipboardData.setData(type, content)?

À mon avis, c'est la méthode la plus simple pour pousser smth à l'intérieur du presse-papiers, vérifiez ceci (je l'ai utilisé pour modifier les données lors de l'action de copie native):

...

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

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

J'ai suivi ce chemin: https://developer.mozilla.org/en-US/docs/Web/Events/copy

À votre santé!

EDIT: À des fins de test, j'ai ajouté codepen: https://codepen.io/dprzygodzki/pen/ZaJMKb

Damian Przygodzki
la source
3
@KarlPokus Le questionneur ne recherche que la solution Chrome
TechTurtle
1
Testé sur la version 62.0.3202.94 de Chrome. Ça fonctionne. codepen.io/dprzygodzki/pen/ZaJMKb
Damian Przygodzki
1
@OliverDixon c'est l'objet par défaut de l'événement React. reactjs.org/docs/events.html
Damian Przygodzki
1
@DamianPrzygodzki Je déteste les éléments cachés comme celui-ci, un excellent moyen de confondre les développeurs.
Oliver Dixon
1
@OliverDixon je vous sens, mais je pense qu'il est bon de s'habituer au fait qu'il y a parfois des données par défaut appliquées à la méthode, en particulier dans les événements.
Damian Przygodzki
8

Votre code doit fonctionner parfaitement, je l'utilise de la même manière. Assurez-vous seulement que si l'événement de clic est déclenché à partir d'un écran contextuel comme un modal bootstrap ou quelque chose, l'élément créé doit être dans ce modal sinon il ne sera pas copié. Vous pouvez toujours donner l'id d'un élément dans ce modal (comme deuxième paramètre) et le récupérer avec getElementById, puis ajouter l'élément nouvellement créé à celui-ci au lieu du document. Quelque chose comme ça:

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
la source
8

J'ai adopté une approche très similaire à certaines d'entre elles, mais je l'ai rendue un peu plus concrète, je pense. Ici, un composant parent passera l'url (ou le texte de votre choix) comme accessoire.

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
la source
Cela a été utile parce que je voulais avoir une balise de paragraphe à la place de Textarea
Ehsan Ahmadi
Merci! Le seul problème est de cacher le
champ de texte
3

Pour les personnes qui essaient de sélectionner dans le DIV au lieu du champ de texte, voici le code. Le code est explicite mais commentez ici si vous voulez plus d'informations:

     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
la source
3

Voici un autre cas d'utilisation, si vous souhaitez copier l'URL actuelle dans votre presse-papiers:

Définir une méthode

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

Appelez cette méthode

<button copyToClipboard={shareLink}>
   Click to copy current url to clipboard
</button>
Jasonleonhard
la source
3

Meilleure solution avec des hooks de réaction, pas besoin de bibliothèques externes pour cela

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

consultez ici pour plus de documentation sur navigator.clip board , navigator.clipboard documentation navigotor.clipboard est pris en charge par un grand nombre de navigateurs regardez ici navigateur pris en charge

Jaman-Dedy
la source
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
la source
1

Si vous souhaitez sélectionner dans le DIV au lieu du champ de texte, voici le code. Le "code" est la valeur qui doit être copiée

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
la source
1
La meilleure pratique de SO est d'accomplir votre code avec une explication. S'il vous plaît, faites-le.
MartenCatcher le
0

voici mon code:

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
la source
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
la source
7
Veuillez ajouter une explication de la façon dont ce code résout le problème, plutôt que de simplement publier du code.
Alexander van Oostenrijk
0

J'ai trouvé la meilleure façon de le faire. je veux dire le moyen le plus rapide: w3school

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

À l'intérieur d'un composant fonctionnel de réaction. Créez une fonction nommée 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>

</>

Si vous n'utilisez pas React, w3schools a également un moyen intéressant de le faire avec l'info-bulle incluse: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_copy_clipboard2

Si vous utilisez React, pensez à faire: utilisez un Toastify pour alerter le message. https://github.com/fkhadra/react-toastify C'est la librairie très facile à utiliser. Après l'installation, vous pourrez peut-être modifier cette ligne:

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

Pour quelque chose comme:

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

Si vous souhaitez l'utiliser, n'oubliez pas d'installer toastify. import ToastContainer et toasts css:

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

et ajoutez le contenant de pain grillé à l'intérieur du retour.

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
la source
Votre réponse ne contient que la référence à une autre ressource, mais aucune réponse spécifique. Si le lien w3schools est la bonne solution, veuillez le saisir ici.
f.khantsis