【譯】JavaScript實現文字剪貼板&React版本

目錄

寫在最前面

  • 有一個簡單的需求,用戶須要快捷的複製一些相關的信息,而後進行下一步信息的填寫。前端這裏須要作一個剪貼板方便用戶體驗。想直接參考 react 使用的能夠看 使用react和typescript改寫和優化一下
  • 大概設計以下,有多條信息,而後用戶能夠點擊右邊的複製 icon 進行快捷的複製。

image

怎麼使用JavaScript實現一個剪貼板

  • 具體分爲五步javascript

    • 一、建立一個 textarea ,把須要的文本放進 textarea
    • 二、將 textarea 元素插入 body 中。
    • 三、使用 HTMLInputElement.select() 方法選擇 textarea 中的文本內容
    • 四、使用 document.execCommand('copy') 複製 textarea 中的文本內容到剪貼板
    • 五、從 body 刪除 textarea 元素
  • codecss

const copyToClipboard = str => {
  const el = document.createElement('textarea');
  el.value = str;
  document.body.appendChild(el);
  el.select();
  document.execCommand('copy');
  document.body.removeChild(el);
};
複製代碼

必要 api 參考

上面的方法不是很完美咱們優化一下

  • 這個方法不是在每一個地方都能運行,因爲 textarea 的插入和移除,有時候會出現頁面的頻閃和抖動
  • 下面用 css 優化一下咱們的 textarea 樣式,隱藏 textarea 的顯示。
const copyToClipboard = str => {
  const el = document.createElement('textarea');
  el.value = str;
  el.setAttribute('readonly', '');
  el.style.position = 'absolute';
  el.style.left = '-9999px';
  
  document.body.appendChild(el);
  el.select();
  document.execCommand('copy');
  document.body.removeChild(el);
};
複製代碼

思考一個問題

  • 咱們用戶在使用咱們的剪貼板以前可能已經選擇了已存在 html 中的文本內容了,因此咱們這裏須要多加一些判斷防止遺漏用戶選擇的文本。
  • DocumentOrShadowRoot.getSelection(), Selection.rangeCount, Selection.getRangeAt(), Selection.removeAllRanges() and Selection.addRange() 這些方法存儲用戶選擇的文本內容和解決範圍選擇的問題
const copyToClipboard = str => {
  const el = document.createElement('textarea');  // Create a <textarea> element
  el.value = str;                                 // Set its value to the string that you want copied
  el.setAttribute('readonly', '');                // Make it readonly to be tamper-proof
  el.style.position = 'absolute';                 
  el.style.left = '-9999px';                      // Move outside the screen to make it invisible
  document.body.appendChild(el);                  // Append the <textarea> element to the HTML document
  const selected =            
    document.getSelection().rangeCount > 0        // Check if there is any content selected previously
      ? document.getSelection().getRangeAt(0)     // Store selection if found
      : false;                                    // Mark as false to know no selection existed before
  el.select();                                    // Select the <textarea> content
  document.execCommand('copy');                   // Copy - only works as a result of a user action (e.g. click events)
  document.body.removeChild(el);                  // Remove the <textarea> element
  if (selected) {                                 // If a selection existed before copying
    document.getSelection().removeAllRanges();    // Unselect everything on the HTML document
    document.getSelection().addRange(selected);   // Restore the original selection
  }
};
複製代碼

使用react和typescript改寫和優化一下

  • 學習了上面的文章,結合產品的需求改寫一下相關代碼。
  • 思路
    • 一、首先建立一個 targetNode,設置絕對佈局,贏藏咱們的元素
    • 二、document.getSelection() 已經由 window.getSelection() 替代了,具體流程如上
    • 三、建立一個 result 標記可否能正常 使用剪貼功能,不能的返回 false
    • 四、刪除這個 targetNode
function createNode(text) {
    const node = document.createElement('div');

    node.innerText = text;
    node.style.cssText = 'position:absolute; top: 0; left: 0; height:0; width:0; pointer-events: none;';

    document.body.appendChild(node);

    return node;
}

export default function copyMe(text) {
    const targetNode = createNode(text);
    const range = document.createRange();
    
    const selection = window.getSelection()!;
    const selected = selection.rangeCount > 0       
      ? selection.getRangeAt(0)    
      : false;  

    targetNode.focus(); // focus 咱們須要的文本
    range.selectNodeContents(targetNode); 
    
    if(selected){
        selection.removeAllRanges();
        selection.addRange(range);
    }

    let result; 

    try {
        result = document.execCommand('copy');
    } catch (e) {
        result = false;
    }

    document.body.removeChild(targetNode);

    return result;
}
複製代碼

如何使用copyme

import React, { Fragment } from 'react';
import copyMe from 'utils/copyMe';

 interface ItemProps {
    value?: string | number;
}

const Item: React.FC<ItemProps> = props => {
    const { value } = props;

    const copyme = () => {
       alert(copyMe(value) ? 'Copied!' : 'Failed!');
    };

    return (
        <Fragment> {value && ( <div> {value} <textarea value={value} readOnly></textarea> <span onClick={copyme}></span> </div> )} </Fragment>
    );
};

export default Item;
複製代碼

必要 api 參考

原文參考

相關文章
相關標籤/搜索