【译】JavaScript实现文字剪贴板&React版本

2021-02-02 10:17:03 浏览数 (1)

目录

  • 怎么使用JavaScript实现一个剪贴
    • 上面的方法不是很完美我们优化一下
    • 思考一个问题
  • 使用react和typescript重写和优化一下
    • 如何使用copyme
  • 原文参考

写在最前面

  • 有一个简单的需求,用户需要快捷的复制一些相关的信息,然后进行下一步信息的填写。前端这里需要做一个剪贴板方便用户体验。想直接参考 react 使用的可以看 使用react和typescript改写和优化一下
  • 大概设计如下,有多条信息,然后用户可以点击右边的复制 icon 进行快捷的复制。

怎么使用JavaScript实现一个剪贴板

  • 具体分为五步
    • 1、创建一个 textarea ,把需要的文本放进 textarea
    • 2、将 textarea 元素插入 body 中。
    • 3、使用 HTMLInputElement.select() 方法选择 textarea 中的文本内容
    • 4、使用 document.execCommand('copy') 复制 textarea 中的文本内容到剪贴板
    • 5、从 body 删除 textarea 元素
  • code
代码语言:javascript复制
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 的显示。
代码语言:javascript复制
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() 这些方法存储用户选择的文本内容和解决范围选择的问题
代码语言:javascript复制
const copyToClipboard = str => {
  const el = document.createElement('textarea');  // Create a  element</span>
  el.value = str;                                 <span class="hljs-comment">// Set its value to the string that you want copied</span>
  el.setAttribute(<span class="hljs-string">'readonly'</span>, <span class="hljs-string">''</span>);                <span class="hljs-comment">// Make it readonly to be tamper-proof</span>
  el.style.position = <span class="hljs-string">'absolute'</span>;                 
  el.style.left = <span class="hljs-string">'-9999px'</span>;                      <span class="hljs-comment">// Move outside the screen to make it invisible</span>
  <span class="hljs-built_in">document</span>.body.appendChild(el);                  <span class="hljs-comment">// Append the <textarea> element to the HTML document</span>
  <span class="hljs-keyword">const</span> selected =            
    <span class="hljs-built_in">document</span>.getSelection().rangeCount > <span class="hljs-number">0</span>        <span class="hljs-comment">// Check if there is any content selected previously</span>
      ? <span class="hljs-built_in">document</span>.getSelection().getRangeAt(<span class="hljs-number">0</span>)     <span class="hljs-comment">// Store selection if found</span>
      : <span class="hljs-literal">false</span>;                                    <span class="hljs-comment">// Mark as false to know no selection existed before</span>
  el.select();                                    <span class="hljs-comment">// Select the <textarea> content</span>
  <span class="hljs-built_in">document</span>.execCommand(<span class="hljs-string">'copy'</span>);                   <span class="hljs-comment">// Copy - only works as a result of a user action (e.g. click events)</span>
  <span class="hljs-built_in">document</span>.body.removeChild(el);                  <span class="hljs-comment">// Remove the <textarea> element</span>
  <span class="hljs-keyword">if</span> (selected) {                                 <span class="hljs-comment">// If a selection existed before copying</span>
    <span class="hljs-built_in">document</span>.getSelection().removeAllRanges();    <span class="hljs-comment">// Unselect everything on the HTML document</span>
    <span class="hljs-built_in">document</span>.getSelection().addRange(selected);   <span class="hljs-comment">// Restore the original selection</span>
  }
};
<span class="copy-code-btn">复制代码</span></code></pre><h2 class="heading" data-id="heading-6">使用react和typescript改写和优化一下</h2>
<ul>
<li>学习了上面的文章,结合产品的需求改写一下相关代码。</li>
<li>思路
<ul>
<li>1、首先创建一个 targetNode,设置绝对布局,赢藏我们的元素</li>
<li>2、document.getSelection() 已经由 window.getSelection() 替代了,具体流程如上</li>
<li>3、创建一个 result 标记能否能正常 使用剪贴功能,不能的返回 false</li>
<li>4、删除这个 targetNode</li>
</ul>
</li>
</ul>
<pre><code class="hljs js copyable" lang="js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createNode</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> node = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>);

    node.innerText = text;
    node.style.cssText = <span class="hljs-string">'position:absolute; top: 0; left: 0; height:0; width:0; pointer-events: none;'</span>;

    <span class="hljs-built_in">document</span>.body.appendChild(node);

    <span class="hljs-keyword">return</span> node;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">copyMe</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> targetNode = createNode(text);
    <span class="hljs-keyword">const</span> range = <span class="hljs-built_in">document</span>.createRange();
    
    <span class="hljs-keyword">const</span> selection = <span class="hljs-built_in">window</span>.getSelection()!;
    <span class="hljs-keyword">const</span> selected = selection.rangeCount > <span class="hljs-number">0</span>       
      ? selection.getRangeAt(<span class="hljs-number">0</span>)    
      : <span class="hljs-literal">false</span>;  

    targetNode.focus(); <span class="hljs-comment">// focus 我们需要的文本</span>
    range.selectNodeContents(targetNode); 
    
    <span class="hljs-keyword">if</span>(selected){
        selection.removeAllRanges();
        selection.addRange(range);
    }

    <span class="hljs-keyword">let</span> result; 

    <span class="hljs-keyword">try</span> {
        result = <span class="hljs-built_in">document</span>.execCommand(<span class="hljs-string">'copy'</span>);
    } <span class="hljs-keyword">catch</span> (e) {
        result = <span class="hljs-literal">false</span>;
    }

    <span class="hljs-built_in">document</span>.body.removeChild(targetNode);

    <span class="hljs-keyword">return</span> result;
}
<span class="copy-code-btn">复制代码</span></code></pre><h3 class="heading" data-id="heading-7">如何使用copyme</h3>
<pre><code class="hljs js copyable" lang="js"><span class="hljs-keyword">import</span> React, { Fragment } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> copyMe <span class="hljs-keyword">from</span> <span class="hljs-string">'utils/copyMe'</span>;

 interface ItemProps {
    value?: string | number;
}

<span class="hljs-keyword">const</span> Item: React.FC<ItemProps> = <span class="hljs-function"><span class="hljs-params">props</span> =></span> {
    <span class="hljs-keyword">const</span> { value } = props;

    <span class="hljs-keyword">const</span> copyme = <span class="hljs-function"><span class="hljs-params">()</span> =></span> {
       alert(copyMe(value) ? <span class="hljs-string">'Copied!'</span> : <span class="hljs-string">'Failed!'</span>);
    };

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag"><<span class="hljs-name">Fragment</span>></span>
            {value && (
                <span class="hljs-tag"><<span class="hljs-name">div</span>></span>
                    {value}
                    <span class="hljs-tag"><<span class="hljs-name">textarea</span>  <span class="hljs-attr">value</span>=<span class="hljs-string">{value}</span> <span class="hljs-attr">readOnly</span>></span><span class="hljs-tag"></<span class="hljs-name">textarea</span>></span>
                    <span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{copyme}</span>></span><span class="hljs-tag"></<span class="hljs-name">span</span>></span>
                <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
            )}
        <span class="hljs-tag"></<span class="hljs-name">Fragment</span>></span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Item;
<span class="copy-code-btn">复制代码</span></code></pre><h4 class="heading" data-id="heading-8">必要 api 参考</h4>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/zh-CN/docs/Web/API/Window/getSelection" rel="nofollow noopener noreferrer">window/getSelection</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/zh-CN/docs/Web/API/Selection/getRangeAt" rel="nofollow noopener noreferrer">Selection/getRangeAt</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/zh-CN/docs/Web/API/Range/selectNodeContents" rel="nofollow noopener noreferrer">Range/selectNodeContents</a></li>
</ul>
<h2 class="heading" data-id="heading-9">原文参考</h2>
<ul>
<li><a target="_blank" href="https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f" rel="nofollow noopener noreferrer">hackernoon.com/copying-tex…</a></li>
</ul>
</div> <div class="image-viewer-box" data-v-78c9b824=""><!----></div>

0 人点赞