ใน reactJS จะคัดลอกข้อความไปยังคลิปบอร์ดได้อย่างไร?


147

ฉันใช้ ReactJS และเมื่อผู้ใช้คลิกลิงก์ฉันต้องการคัดลอกข้อความไปยังคลิปบอร์ด

ฉันใช้ Chrome 52 และฉันไม่จำเป็นต้องสนับสนุนเบราว์เซอร์อื่น ๆ

ฉันไม่เห็นสาเหตุที่รหัสนี้ไม่ส่งผลให้ข้อมูลถูกคัดลอกไปยังคลิปบอร์ด (ที่มาของข้อมูลโค้ดนั้นมาจากโพสต์ Reddit)

ฉันทำสิ่งนี้ผิดหรือเปล่า? ใครสามารถแนะนำมีวิธี "ถูกต้อง" เพื่อใช้คัดลอกไปยังคลิปบอร์ดโดยใช้ 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
คุณลองใช้โซลูชันของบุคคลที่สามเช่นclipboardjs.comหรือgithub.com/zeroclipboard/zeroclipboardหรือไม่
EugZol

11
@EugZol ฉันชอบที่จะเขียนโค้ดมากกว่าที่จะเพิ่มการพึ่งพาอื่น ๆ โดยสมมติว่ารหัสนั้นมีขนาดค่อนข้างเล็ก
Duke Dougal

ตรวจสอบคำตอบเหล่านี้stackoverflow.com/questions/400212/…
elmeister

@ เอลลิสเตอร์เป็นคำถามเฉพาะสำหรับการตอบสนอง
Duke Dougal

คำตอบ:


180

โดยส่วนตัวฉันไม่เห็นความต้องการห้องสมุดสำหรับสิ่งนี้ ดูhttp://caniuse.com/#feat=clipboardตอนนี้ก็รองรับอย่างกว้างขวางแล้ว แต่คุณยังสามารถทำสิ่งต่าง ๆ เช่นการตรวจสอบเพื่อดูว่าการทำงานมีอยู่ในไคลเอนต์ปัจจุบันและเพียงแค่ซ่อนปุ่มคัดลอกถ้ามันไม่ได้

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;

อัปเดต: เขียนซ้ำโดยใช้ React Hooks ใน 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
นี่คือคำตอบที่ดีที่สุด เราไม่ควรสนับสนุนให้ devs ใช้แพ็คเกจสำหรับทุกสิ่งเว้นแต่พวกเขาต้องการการสนับสนุนเบราว์เซอร์เก่า
ลาก

3
เพียงบันทึก: ปัญหาเดียวของเรื่องนี้คือถ้าคุณพยายามที่จะคัดลอกข้อความที่ไม่ได้อยู่ในองค์ประกอบข้อความบางอย่างบนหน้าคุณจะต้องแฮ็กชุด DOM องค์ประกอบตั้งค่าข้อความคัดลอก และทำความสะอาดมัน นั่นเป็นรหัสจำนวนมากสำหรับบางสิ่งที่เล็กมาก โดยปกติฉันจะยอมรับว่า devs ไม่ควรสนับสนุนให้ติดตั้งไลบรารีอย่างต่อเนื่อง
Christopher Ronning

3
สำหรับปัญหานี้ข้อความอยู่ในองค์ประกอบบนหน้าแล้ว จะมีกรณีใดบ้างที่มีข้อความที่ปรากฏบนหน้าเว็บที่คุณต้องการคัดลอกที่ไม่ได้อยู่ในองค์ประกอบ นั่นเป็นปัญหาที่แตกต่างอย่างสิ้นเชิงที่ฉันยินดีที่จะแสดงวิธีแก้ปัญหาให้ คุณไม่จำเป็นต้องแฮ็คข้อมูลใด ๆ ด้วยการตอบสนองคุณเพียงแค่เตรียมองค์ประกอบที่ซ่อนอยู่ในฟังก์ชั่นการเรนเดอร์ที่เก็บข้อความไว้ด้วย ไม่จำเป็นต้องสร้างองค์ประกอบเฉพาะกิจ
Nate

2
ฉันได้รับข้อผิดพลาด typescript นี้:Property 'select' does not exist on type 'never'
อเล็กซ์ C

3
ฉันได้รับ TypeError: textAreaRef.current.select ไม่ใช่ฟังก์ชัน
pseudozach

120

ใช้ฟังก์ชั่นอินไลน์แบบง่ายนี้ของปุ่มถ้าคุณต้องการเขียนข้อมูลไปยังคลิปบอร์ดโดยทางโปรแกรม

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

3
navigator.clipboard ไม่รองรับเบราว์เซอร์ทั้งหมด
Premjeet

8
ดูเหมือนว่าจะได้รับการสนับสนุนอย่างดีสำหรับเบราว์เซอร์ที่สำคัญในปี 2018 caniuse.com/#search=clipboard
gasolin

2
ขึ้นอยู่กับลิงค์ที่คุณให้ไว้ดูเหมือนว่าจะสนับสนุนเฉพาะในซาฟารีเท่านั้น ...
Nibb

2
ทำงานได้ดีที่สุดสำหรับ usecase ตำแหน่งที่จะคัดลอกข้อความไม่ได้อยู่บนหน้า ขอบคุณ
NSjonas

1
การสนับสนุนบางส่วนนั้นดีมากดังนั้นจึงรองรับการใช้งานส่วนใหญ่อย่างสมบูรณ์ และดังที่กล่าวมานี้เป็นวิธีแก้ปัญหาเชิงโปรแกรมที่ดีที่สุด
Dror Bar

40

แน่นอนคุณควรพิจารณาการใช้แพคเกจเช่น @Shubham ข้างต้นเป็นที่ปรึกษา แต่ฉันสร้างการทำงาน codepen ตามสิ่งที่คุณอธิบาย: http://codepen.io/dtschust/pen/WGwdVN?editors=1111 มันทำงานได้ในเบราว์เซอร์ของฉันในโครเมียมบางทีคุณสามารถดูว่ามีบางอย่างที่ฉันทำไปหรือไม่หรือมีความซับซ้อนเพิ่มขึ้นในแอปพลิเคชันของคุณที่ป้องกันไม่ให้มันทำงาน

// 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
ทำไมแพ็คเกจถึงดีกว่าโซลูชันของคุณ?
Duke Dougal

6
การสนับสนุนข้ามเบราว์เซอร์ที่ดีขึ้นและสายตาที่มากขึ้นเกี่ยวกับแพคเกจในกรณีที่จำเป็นต้องแก้ไขข้อผิดพลาด
Drew Schuster

ทำงานเหมือนจับใจ ใช่. ฉันสงสัยเกี่ยวกับการสนับสนุนข้ามเบราว์เซอร์ด้วย
Karl Pokus

สิ่งนี้จะทำให้เกิดการสั่นไหวบนหน้าจอหรือไม่หากคุณใช้ appendChild ไม่ว่าคุณจะลบออกอย่างรวดเร็วในภายหลัง
robinnnnn

1
สิ่งนี้ดี แต่ใช้งานไม่ได้กับ Chrome (72.0) บน Android หรือบน FF (63.0) บน Android
ลิน

35

วิธีที่ง่ายที่สุดคือใช้react-copy-to-clipboardแพ็คเกจ npm

คุณสามารถติดตั้งได้ด้วยคำสั่งดังต่อไปนี้

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

ใช้ในลักษณะดังต่อไปนี้

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

คำอธิบายโดยละเอียดมีให้ที่ลิงค์ต่อไปนี้

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

นี่คือซอที่ทำงานอยู่


มีวิธีแก้ไขไหมถ้าฉันต้องย้อนกลับ? เช่นผู้เขียนจะคัดลอกข้อความจากอีเมลไปยังพื้นที่ข้อความในแอปพลิเคชัน reactjs ฉันไม่จำเป็นต้องเก็บแท็ก html ไว้ แต่ฉันต้องเก็บเฉพาะการขึ้นบรรทัดใหม่เท่านั้น
TechTurtle

คุณอาจต้องเสียบonpasteเหตุการณ์
Koen

ฉันจะใช้แพ็คเกจนี้ได้อย่างไรหากฉันต้องการคัดลอกเนื้อหาของตาราง html ไปยังคลิปบอร์ด @Shubham Khatri
Jane Fred

19

ทำไมต้องใช้คุณต้องมีแพ็คเกจ npm เมื่อคุณได้รับทั้งหมดภายในปุ่มเดียวเช่นนี้

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

ฉันหวังว่านี่จะช่วย @jerryurenaa


16

ทำไมไม่ใช้เพียงวิธีการรวบรวมเหตุการณ์คลิปบอร์ดข้อมูลe.clipboardData.setData(type, content)?

ในความคิดของฉันเป็นวิธี streightforward ที่สุดในการผลักดัน smth ภายในคลิปบอร์ดให้ตรวจสอบนี้ (ฉันเคยใช้ที่แก้ไขข้อมูลในขณะที่การคัดลอกดั้งเดิม):

...

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

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

ฉันตามเส้นทางนั้น: https://developer.mozilla.org/en-US/docs/Web/Events/copy

ไชโย!

แก้ไข: สำหรับวัตถุประสงค์ในการทดสอบฉันได้เพิ่ม codepen: https://codepen.io/dprzygodzki/pen/ZaJMKb


3
@KarlPokus ผู้ถามกำลังมองหาโซลูชันของ Chrome เท่านั้น
TechTurtle

1
ทดสอบกับ Chrome เวอร์ชัน 62.0.3202.94 มันใช้งานได้ codepen.io/dprzygodzki/pen/ZaJMKb
Damian Przygodzki

1
@OliverDixon มันเป็นวัตถุเริ่มต้นของเหตุการณ์ตอบสนอง reactjs.org/docs/events.html
Damian Przygodzki

1
@DamianPrzygodzki ฉันเกลียดองค์ประกอบที่ซ่อนอยู่เช่นนี้วิธีที่ดีในการสร้างความสับสนให้กับนักพัฒนา
Oliver Dixon

1
@OliverDixon ฉันรู้สึกว่าคุณ แต่ฉันคิดว่ามันดีที่จะได้ใช้มันบางครั้งมีข้อมูลเริ่มต้นที่ใช้กับวิธีการโดยเฉพาะอย่างยิ่งในเหตุการณ์
Damian Przygodzki

8

รหัสของคุณควรทำงานอย่างสมบูรณ์ฉันใช้มันในลักษณะเดียวกัน ตรวจสอบให้แน่ใจว่าหากเหตุการณ์การคลิกถูกกระตุ้นจากภายในหน้าจอป๊อปอัพเช่น bootstrap modal หรือบางอย่างองค์ประกอบที่สร้างขึ้นจะต้องอยู่ในรูปแบบนั้นมิฉะนั้นจะไม่คัดลอก คุณสามารถให้ id ขององค์ประกอบภายใน modal นั้น (เป็นพารามิเตอร์ที่สอง) และดึงมันด้วย getElementById จากนั้นผนวกองค์ประกอบที่สร้างขึ้นใหม่ให้กับองค์ประกอบนั้นแทนที่จะเป็นเอกสาร บางสิ่งเช่นนี้

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

ฉันใช้วิธีที่คล้ายกันมากกับที่กล่าวมาข้างต้น แต่ฉันคิดว่ามันเป็นรูปธรรมมากกว่าเดิมเล็กน้อย ที่นี่องค์ประกอบหลักจะส่ง URL (หรือข้อความใด ๆ ที่คุณต้องการ) เป็นเสา

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

สิ่งนี้มีประโยชน์เพราะฉันต้องการติดแท็กย่อหน้าแทน Textarea
Ehsan Ahmadi

ขอบคุณ! มีเพียงปัญหาเดียวที่ซ่อนอยู่ใน
ฟิลด์

3

สำหรับคนที่พยายามเลือกจาก DIV แทนที่จะเป็นฟิลด์ข้อความนี่คือรหัส รหัสอธิบายตนเอง แต่แสดงความคิดเห็นที่นี่หากคุณต้องการข้อมูลเพิ่มเติม:

     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

นี่คือกรณีการใช้งานอื่นหากคุณต้องการคัดลอก URL ปัจจุบันไปยังคลิปบอร์ดของคุณ:

กำหนดวิธีการ

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

เรียกวิธีการนั้น

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

3

ทางออกที่ดีที่สุดด้วยการตอบกลับ hooks ไม่ต้องใช้ไลบรารีภายนอก

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

ตรวจสอบที่นี่สำหรับเอกสารเพิ่มเติมเกี่ยวกับ navigator.clip board , navigator.clipboard เอกสาร navigotor.clipboard รองรับเบราว์เซอร์จำนวนมากดูที่นี่เบราว์เซอร์ที่รองรับ


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

หากคุณต้องการเลือกจาก DIV แทนฟิลด์ข้อความนี่คือรหัส "รหัส" คือค่าที่จะต้องคัดลอก

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
วิธีปฏิบัติที่ดีที่สุดของ SO คือการทำให้โค้ดของคุณสำเร็จด้วยคำอธิบาย กรุณาทำมัน.
MartenCatcher

0

นี่คือรหัสของฉัน:

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
โปรดเพิ่มคำอธิบายว่ารหัสนี้แก้ปัญหาได้อย่างไรแทนที่จะเป็นเพียงการโพสต์รหัส
Alexander van Oostenrijk

0

พบวิธีที่ดีที่สุดที่จะทำ ฉันหมายถึงวิธีที่เร็วที่สุด: w3school

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

ภายในส่วนประกอบที่ทำปฏิกิริยาได้ สร้างฟังก์ชั่นชื่อ 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>

</>

หากไม่ได้ใช้ React w3schools ก็มีวิธีที่ยอดเยี่ยมในการทำเช่นนี้ด้วยคำแนะนำเครื่องมือ: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_copy_clipboard2

หากใช้ React สิ่งที่ควรทำ: ใช้ Toastify เพื่อแจ้งเตือนข้อความ https://github.com/fkhadra/react-toastifyนี่เป็น lib ที่ใช้งานง่ายมาก หลังจากการติดตั้งคุณอาจสามารถเปลี่ยนบรรทัดนี้:

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

สำหรับสิ่งที่ชอบ:

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

หากคุณต้องการใช้อย่าลืมติดตั้ง toastify นำเข้า ToastContainer และขนมปังปิ้ง CSS:

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

และเพิ่มภาชนะขนมปังภายในผลตอบแทน

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

คำตอบของคุณมีการอ้างอิงไปยังแหล่งข้อมูลอื่นเท่านั้น แต่ไม่มีคำตอบเฉพาะ หากลิงก์ของ w3schools เป็นโซลูชันที่ถูกต้องโปรดพิมพ์ที่นี่
f.khantsis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.