Trong ReacJS, làm thế nào để sao chép văn bản vào clipboard?


147

Tôi đang sử dụng ReactJS và khi người dùng nhấp vào liên kết tôi muốn sao chép một số văn bản vào bảng tạm.

Tôi đang sử dụng Chrome 52 và tôi không cần hỗ trợ bất kỳ trình duyệt nào khác.

Tôi không thể thấy lý do tại sao mã này không dẫn đến việc dữ liệu được sao chép vào bảng tạm. (nguồn gốc của đoạn mã là từ một bài đăng Reddit).

Tôi đang làm điều này sai? Bất cứ ai có thể đề nghị là có một cách "chính xác" để thực hiện sao chép vào clipboard bằng cách sử dụng Reacjs?

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
Bạn đã thử sử dụng các giải pháp của bên thứ 3, như clipboardjs.com hoặc github.com/zeroclipboard/zeroclipboard ?
EugZol

11
@EugZol Tôi thực sự thích viết mã hơn là thêm một phụ thuộc khác, giả sử mã này khá nhỏ.
Công tước Dougal

Kiểm tra các câu trả lời stackoverflow.com/questions/400212/ này
elmeister

@elmeister câu hỏi dành riêng cho Reacjs
Duke Dougal

Câu trả lời:


180

Cá nhân tôi không thấy sự cần thiết của một thư viện cho việc này. Nhìn vào http://caniuse.com/#feat=clipboard hiện tại nó được hỗ trợ khá rộng rãi, tuy nhiên bạn vẫn có thể làm những việc như kiểm tra xem liệu chức năng có tồn tại trong ứng dụng khách hiện tại hay không và chỉ cần ẩn nút sao chép nếu không.

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;

Cập nhật: Viết lại bằng React Hook trong 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>
  );
}

26
Đây là câu trả lời tốt nhất. Chúng tôi không nên khuyến khích các nhà phát triển sử dụng các gói cho mọi thứ trừ khi họ cần hỗ trợ trình duyệt cũ.
kéo co

2
Chỉ dành cho bản ghi: vấn đề duy nhất với điều này là nếu bạn đang cố sao chép văn bản chưa có trong một số thành phần văn bản trên trang, bạn sẽ cần phải hack một bộ phần tử DOM, đặt văn bản, sao chép nó, và làm sạch nó Đó là rất nhiều mã cho một cái gì đó rất nhỏ. Thông thường tôi sẽ đồng ý rằng các nhà phát triển không nên được khuyến khích liên tục cài đặt thư viện.
Christopher Ronning

3
Đối với vấn đề cụ thể này, văn bản đã có trong một yếu tố trên trang. Trường hợp nào sẽ có nơi có văn bản hiển thị trên trang mà bạn muốn sao chép không có trong một yếu tố? Đó là một vấn đề hoàn toàn khác mà tôi rất vui khi đưa ra giải pháp. Bạn sẽ không cần phải hack bất cứ thứ gì có phản ứng, bạn chỉ cần cung cấp một yếu tố ẩn trong chức năng kết xuất của bạn cũng chứa văn bản. Không cần phải tạo các yếu tố ad hoc.
Nate

2
Tôi nhận được lỗi bản in này:Property 'select' does not exist on type 'never'
Alex C

3
Tôi nhận được TypeError: textAreaRef.cản.select không phải là một chức năng
pseudozach

118

Sử dụng chức năng onClick đơn giản này trên một nút nếu bạn muốn lập trình ghi dữ liệu vào bảng tạm.

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

3
navigator.clipboard không hỗ trợ tất cả các trình duyệt
Premjeet

8
có vẻ như nó đã hỗ trợ tốt cho các trình duyệt lớn trong năm 2018 caniuse.com/#search=clipboard
gasolin

2
dựa trên liên kết bạn cung cấp, có vẻ như nó chỉ được hỗ trợ hoàn toàn trong safari ...
Nibb

2
hoạt động tốt nhất cho usecase của tôi, nơi văn bản để sao chép không thực sự trên trang. Cảm ơn
NSjonas

1
Hỗ trợ một phần là rất tốt, vì vậy nó hỗ trợ đầy đủ cho hầu hết các trường hợp sử dụng. Và như đã đề cập, đây là giải pháp lập trình tốt nhất.
Dror Bar

40

Bạn chắc chắn nên xem xét việc sử dụng gói như @Shubham ở trên, nhưng tôi đã tạo một codepen hoạt động dựa trên những gì bạn mô tả: http://codepen.io/dtschust/pen/WGwdVN?editors=1111 . Nó hoạt động trong trình duyệt của tôi bằng chrome, có lẽ bạn có thể thấy nếu có điều gì đó tôi đã làm ở đó mà bạn đã bỏ lỡ hoặc nếu có một số phức tạp mở rộng trong ứng dụng của bạn ngăn điều này hoạt động.

// 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
Tại sao một gói tốt hơn giải pháp của bạn?
Công tước Dougal

6
Có khả năng hỗ trợ trình duyệt chéo tốt hơn và có nhiều mắt hơn trong gói trong trường hợp cần sửa lỗi
Drew Schuster

hoạt động như một lá bùa. Đúng. Tôi tự hỏi về hỗ trợ trình duyệt chéo cũng.
Karl Pokus

điều này có gây ra hiện tượng nhấp nháy trên màn hình nếu bạn đang sử dụng appendChild, cho dù sau đó bạn có gỡ bỏ nó nhanh như thế nào không?
robinnnnn

1
Điều này là tốt nhưng nó không hoạt động trên Chrome (72.0) trên Android cũng như trên FF (63.0) trên Android.
colin

35

Cách đơn giản nhất sẽ là sử dụng react-copy-to-clipboardgói npm.

Bạn có thể cài đặt nó bằng lệnh sau

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

Sử dụng nó theo cách sau.

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

Một lời giải thích chi tiết được cung cấp tại liên kết sau

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

Đây là một fiddle chạy .


Có giải pháp nào nếu tôi cần làm ngược lại không? tức là Tác giả sẽ sao chép văn bản từ một email sang vùng văn bản trong ứng dụng Reacjs. Tôi không cần giữ lại các thẻ html, tuy nhiên, tôi chỉ cần duy trì ngắt dòng.
TechTurtle

Bạn có thể cần phải cắm onpastesự kiện
Koen

Làm cách nào để sử dụng gói này nếu tôi muốn sao chép nội dung của bảng html vào khay nhớ tạm? @Shubham Khatri
Jane Fred

19

Tại sao sử dụng bạn cần một gói npm khi bạn có thể nhận được tất cả trong một nút như thế này

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

Tôi hy vọng điều này sẽ giúp @jerryurenaa


16

Tại sao không sử dụng chỉ phương thức bộ sưu tập clipboardData e.clipboardData.setData(type, content)?

Theo tôi là phương pháp tối ưu nhất để đạt được việc đẩy smth vào clipboard, hãy kiểm tra điều này (tôi đã sử dụng nó để sửa đổi dữ liệu trong khi hành động sao chép gốc):

...

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

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

Tôi đã đi theo con đường đó: https://developer.mozilla.org/en-US/docs/Web/Events/copy

Chúc mừng!

EDIT: Để thử nghiệm, tôi đã thêm codepen: https://codepen.io/dprzygodzki/pen/ZaJMKb


3
@KarlPokus Người hỏi chỉ tìm kiếm giải pháp Chrome
TechTurtle

1
Đã thử nghiệm trên Chrome Phiên bản 62.0.3202.94. Nó đang làm việc. codepen.io/dprzygodzki/pen/ZaJMKb
Damian Przygodzki

1
@OliverDixon nó là đối tượng mặc định của sự kiện React. Reacjs.org/docs/events.html
Damian Przygodzki

1
@DamianPrzygodzki Tôi ghét các yếu tố ẩn như thế này, cách tuyệt vời để gây nhầm lẫn cho các nhà phát triển.
Oliver Dixon

1
@OliverDixon tôi cảm thấy bạn, nhưng tôi nghĩ thật tốt khi được sử dụng rằng đôi khi có một số dữ liệu mặc định được áp dụng cho phương thức, đặc biệt là trong các sự kiện.
Damian Przygodzki

8

Mã của bạn sẽ hoạt động hoàn hảo, tôi sử dụng nó theo cùng một cách. Chỉ đảm bảo rằng nếu sự kiện nhấp được kích hoạt từ trong màn hình bật lên như phương thức bootstrap hoặc thứ gì đó, thì phần tử được tạo phải nằm trong phương thức đó nếu không nó sẽ không sao chép. Bạn luôn có thể cung cấp id của một phần tử trong phương thức đó (dưới dạng tham số thứ hai) và truy xuất nó bằng getEuityById, sau đó nối phần tử mới được tạo vào phần tử đó thay vì tài liệu. Một cái gì đó như thế này:

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

Tôi đã thực hiện một cách tiếp cận rất giống như một số cách ở trên, nhưng tôi nghĩ nó cụ thể hơn một chút. Tại đây, một thành phần cha mẹ sẽ truyền url (hoặc bất kỳ văn bản nào bạn muốn) làm chỗ dựa.

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

Điều này rất hữu ích vì tôi muốn có thẻ đoạn thay vào Textarea
Ehsan Ahmadi

Cảm ơn! Vấn đề duy nhất là ẩn trường văn bản
ithinkswhenitscold

3

Đối với những người đang cố gắng chọn từ DIV thay vì trường văn bản, đây là mã. Mã này là tự giải thích nhưng bình luận ở đây nếu bạn muốn biết thêm thông tin:

     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

Đây là một trường hợp sử dụng khác, nếu bạn muốn sao chép url hiện tại vào clipboard của mình:

Xác định một phương thức

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

Gọi phương thức đó

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

3

Giải pháp tốt nhất với móc phản ứng, không cần thư viện bên ngoài cho điều đó

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

kiểm tra ở đây để biết thêm tài liệu về bảng điều khiển navigator.clip , tài liệu navigator.clipboard navigotor.clipboard được hỗ trợ bởi một số lượng lớn trình duyệt xem tại đây trình duyệt được hỗ trợ


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

Nếu bạn muốn chọn từ DIV thay vì trường văn bản, đây là mã. "Mã" là giá trị phải được sao chép

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
Cách thực hành tốt nhất của SO là hoàn thành mã của bạn bằng một lời giải thích. Xin vui lòng, làm điều đó.
MartenCatcher

0

đây là mã của tôi:

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
Vui lòng thêm một lời giải thích về cách mã này giải quyết vấn đề, thay vì chỉ đăng mã.
Alexander van Oostenrijk

0

Tìm thấy cách tốt nhất để làm điều đó. ý tôi là cách nhanh nhất: w3school

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

Bên trong một thành phần chức năng phản ứng. Tạo một hàm có tên là handCopy:

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>

</>

Nếu không sử dụng React, w3schools cũng có một cách thú vị để làm điều này với tooltip bao gồm: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_copy_clipboard2

Nếu sử dụng React, một suy nghĩ tuyệt vời cần làm: Sử dụng Toastify để cảnh báo tin nhắn. https://github.com/fkhadra/react-toastify Đây là lib rất dễ sử dụng. Sau khi cài đặt, bạn có thể thay đổi dòng này:

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

Đối với một cái gì đó như:

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

Nếu bạn muốn sử dụng nó, đừng quên Cài đặt toastify. nhập ToastContainer và cũng nâng cao css:

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

và thêm hộp đựng bánh mì nướng bên trong trở lại.

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

Câu trả lời của bạn chỉ chứa tham chiếu đến tài nguyên khác, nhưng không có câu trả lời cụ thể. Nếu liên kết w3schools là giải pháp chính xác, vui lòng nhập nó ở đây.
f.khantsis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.