358 votes

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

J'utilise ReactJS et lorsqu'un utilisateur clique sur un lien, je veux 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 ne permet pas de copier les données dans le presse-papiers. (l'origine de ce bout de code provient d'un post Reddit).

Est-ce que je m'y prends mal ? Quelqu'un peut-il suggérer une manière "correcte" d'implémenter la copie vers 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()
}

1 votes

Avez-vous essayé d'utiliser des solutions tierces, comme clipboardjs.com ou github.com/zeroclipboard/zeroclipboard ?

44 votes

@EugZol Je préfère vraiment écrire du code plutôt que d'ajouter une autre dépendance, en supposant que le code est assez petit.

0 votes

Vérifiez ces réponses stackoverflow.com/questions/400212/

516voto

Gary Vernon Grubb Points 198

Utilisez cette simple fonction onClick en ligne sur un bouton si vous souhaitez écrire de manière programmatique des données dans le presse-papiers.

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

12 votes

Navigator.clipboard ne supporte pas tous les navigateurs

32 votes

Semble avoir été bien supporté par les principaux navigateurs en 2018. caniuse.com/#search=clipboard

5 votes

D'après le lien que vous avez fourni, il semble qu'il ne soit totalement pris en charge que dans Safari...

263voto

Nate Points 2488

Personnellement, je ne vois pas l'utilité d'une bibliothèque pour cela. En regardant http://caniuse.com/#feat=clipboard La prise en charge est assez répandue 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 en utilisant 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>
  );
}

3 votes

Pour mémoire, le seul problème avec cette méthode est que si vous essayez de copier du texte qui n'est pas déjà dans un élément de texte de la page, vous devrez pirater un ensemble d'éléments DOM, définir le texte, le copier et le nettoyer. C'est beaucoup de code pour quelque chose de très petit. Normalement, je serais d'accord pour dire que les développeurs ne devraient pas être encouragés à installer constamment des bibliothèques.

3 votes

Pour ce problème particulier, le texte se trouve déjà dans un élément de la page. Quel serait le cas d'un texte visible sur la page que vous voudriez copier et qui ne se trouve pas dans un élément ? Il s'agit d'un problème totalement différent pour lequel je serais heureux de vous 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.

0 votes

Au lieu de "e.targe.focus()", au cas où il n'y aurait pas d'événement selon la façon dont quelqu'un pourrait l'utiliser, vous pouvez utiliser ce qui suit pour désélectionner la zone de saisie/texte : 1. Pour les navigateurs croisés : window.getSelection(). 2. Pour les navigateurs problématiques : document.selection.empty(). Plus d'informations : stackoverflow.com/questions/6186844/

52voto

Drew Schuster Points 1491

Vous devriez certainement envisager d'utiliser un paquet comme @Shubham ci-dessus le conseille, 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 chrome, peut-être pouvez-vous voir s'il y a quelque chose que j'ai fait là que vous avez manqué, ou s'il y a une certaine 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'))

4 votes

Pourquoi un paquet est-il meilleur que votre solution ?

9 votes

Une meilleure prise en charge des navigateurs croisés et plus d'attention sur le paquet au cas où des bogues devraient être corrigés.

0 votes

Fonctionne comme un charme. Oui. Je m'interroge également sur la prise en charge des navigateurs croisés.

47voto

Shubham Khatri Points 67350

Le moyen le plus simple sera d'utiliser le react-copy-to-clipboard paquet 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 exemple de fonctionnement violon .

0 votes

Existe-t-il une solution si j'ai besoin de faire l'inverse ? c'est-à-dire que l'auteur va copier le texte d'un email vers une zone de texte dans l'application reactjs. Je n'ai pas besoin de conserver les balises html, mais je dois conserver uniquement les sauts de ligne.

1 votes

Vous devez probablement brancher le onpaste événement

0 votes

Comment puis-je utiliser ce paquet si je veux copier le contenu d'un tableau html dans le presse-papiers ? @Shubham Khatri

20voto

Damian Przygodzki Points 124

Pourquoi ne pas utiliser uniquement la méthode de collecte des données du presse-papiers des événements e.clipboardData.setData(type, content) ?

A mon avis, c'est la méthode la plus simple pour pousser quelque chose dans le presse-papiers, regardez ceci (j'ai utilisé cette méthode pour modifier des données pendant une action de copie native) :

...

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

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

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

A la vôtre !

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

3 votes

@KarlPokus Le questionneur ne cherche que la solution Chrome

1 votes

Testé sur Chrome Version 62.0.3202.94. Il fonctionne. codepen.io/dprzygodzki/pen/ZaJMKb

0 votes

@ian et aussi, n'hésitez pas à partager le code du problème reproductible.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X