前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【译】JavaScript实现文字剪贴板&React版本

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

作者头像
西南_张家辉
发布2021-02-02 10:17:03
4910
发布2021-02-02 10:17:03
举报
文章被收录于专栏:张家辉的树屋

目录

写在最前面

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

怎么使用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 参考

上面的方法不是很完美我们优化一下

  • 这个方法不是在每个地方都能运行,由于 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>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在最前面
  • 怎么使用JavaScript实现一个剪贴板
    • 必要 api 参考
      • 上面的方法不是很完美我们优化一下
        • 思考一个问题
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档