天天看點

【譯】JavaScript實作文字剪貼闆&React版本

目錄

  • 怎麼使用JavaScript實作一個剪貼
    • 上面的方法不是很完美我們優化一下
    • 思考一個問題
  • 使用react和typescript重寫和優化一下
    • 如何使用copyme
  • 原文參考

寫在最前面

  • 有一個簡單的需求,使用者需要快捷的複制一些相關的資訊,然後進行下一步資訊的填寫。前端這裡需要做一個剪貼闆友善使用者體驗。想直接參考 react 使用的可以看 使用react和typescript改寫和優化一下
  • 大概設計如下,有多條資訊,然後使用者可以點選右邊的複制 icon 進行快捷的複制。
【譯】JavaScript實作文字剪貼闆&React版本

怎麼使用JavaScript實作一個剪貼闆

  • 具體分為五步
    • 1、建立一個

      textarea

      ,把需要的文本放進

      textarea

    • 2、将

      textarea

      元素插入

      body

      中。
    • 3、使用

      HTMLInputElement.select()

      方法選擇

      textarea

      中的文本内容
    • 4、使用

      document.execCommand('copy')

      複制

      textarea

      中的文本内容到剪貼闆
    • 5、從

      body

      删除

      textarea

      元素
  • code
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 參考

  • developer.mozilla.org/zh-CN/docs/…

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

  • 這個方法不是在每個地方都能運作,由于 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改寫和優化一下

  • 學習了上面的文章,結合産品的需求改寫一下相關代碼。
  • 思路
    • 1、首先建立一個 targetNode,設定絕對布局,赢藏我們的元素
    • 2、document.getSelection() 已經由 window.getSelection() 替代了,具體流程如上
    • 3、建立一個 result 标記能否能正常 使用剪貼功能,不能的傳回 false
    • 4、删除這個 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 參考

  • window/getSelection
  • Selection/getRangeAt
  • Range/selectNodeContents

原文參考

  • hackernoon.com/copying-tex…