JS复制文字到剪切板的极简实现及扩展

这篇文章发布于 2021年10月28日,星期四,18:02,归类于 JS实例。 阅读 50133 次, 今日 34 次 14 条评论

 

JS复制文字到剪切板

一、execCommand方法

复制一段文字内容实现的老牌方法就是 document.execCommand 方法了,示意:

document.execCommand('copy', true);

且兼容性超级棒,如下截图:

execCommand兼容性

是不是大有大结局 happy ending 的感觉?

别急,execCommand() 并没有表面看上去的那么美好。

1. 执行有限制

首先,必须文字内容选中,再执行 execCommand() 方法才有效,如何让文字选中呢?

通常做法是放在一个 <input> 或者 <textarea> 元素中,然后让这个输入框元素选中,示意如下:

textarea.select();
// 执行复制
document.execCommand('copy', true);

但是,实际开发,怎么可能让你在页面上塞一个输入框呢?

所以,通常做法是创建一个隐藏的输入框,赋值,选中,然后复制。

// 创建输入框
var textarea = document.createElement('textarea');
document.body.appendChild(textarea);
// 隐藏此输入框
textarea.style.position = 'absolute';
textarea.style.clip = 'rect(0 0 0 0)';
// 赋值
textarea.value = '复制的文本内容...';
// 选中
textarea.select();
// 复制
document.execCommand('copy', true);

还没完,有时候,输入框可能不在页面的可视区域之内,此时执行 textarea.select() 方法会触发浏览器默认的控件跳转行为,也就是页面会触发滚动行为进行重定位。

所以,创建的文本域务必要保证在屏幕区域内,否则复制行为执行的时候,会出现意料之外的体验问题(可以使用固定定位解决)。

2. 性能隐患

其次,就算复制功能实现了,性能也是个需要留意的问题。

日常开发,我们需要复制的内容一般是一段口令,一个链接,内容信息并不大,使用 execCommand() 方法完全没问题,是无感知的。

但是,如果需要复制的是非常大段的内容,则 execCommand() 方法可能会引起卡顿,因为 execCommand() 方法是一个同步方法,必须等复制操作结束,才能继续执行后面的代码。

3. 无法修改复制内容

使用 execCommand() 方法复制的文字内容,是无法进行修改与替换的,这其实是不友好的,因为对复制或拖拽的本文内容进行处理还是比较常见的。

正是由于以上一些限制,execCommand() 已经是不推荐的使用的方法,业界推荐使用全新的 Clipboard API。

// zxx: 虽然不推荐,但是架不住兼容性好,且以后浏览器也没有理由不继续支持(否则影响太大了),因此,execCommand() 方法还是目前的主流实现方案。

二、Clipboard API

使用 Clipboard API 实现复制效果就简单了,

if (navigator.clipboard) {
    navigator.clipboard.writeText(text);
}

其中,text 就是需要复制的文本内容,就这么简单。

无需浏览器权限申请,文字内容直接进入剪切板,代码简单,使用方便,同时是异步,不用担心阻塞。

然而,兼容性这块的不足实在是让人遗憾。

clipboard writeText兼容性

三、代码整合与封装

所以要复制一段文字到剪切板,可以试试下面的代码:

var text = '被复制的内容,啦啦啦~';
if (navigator.clipboard) {
    // clipboard api 复制
    navigator.clipboard.writeText(text);
} else {
    var textarea = document.createElement('textarea');
    document.body.appendChild(textarea);
    // 隐藏此输入框
    textarea.style.position = 'fixed';
    textarea.style.clip = 'rect(0 0 0 0)';
    textarea.style.top = '10px';
    // 赋值
    textarea.value = text;
    // 选中
    textarea.select();
    // 复制
    document.execCommand('copy', true);
    // 移除输入框
    document.body.removeChild(textarea);
}

包括 IE 在内的浏览器全兼容。

眼见为实,您可以狠狠地点击这里:复制文字到剪切板JS代码片段demo

点击下图按钮,就可以复制文字内容到剪切板了,可以粘贴到下面的多行文本输入框中进行测试。

demo页面截图示意

上面代码虽好,却没有复制成功的提示效果。

所以我结合实际开发需求,重新整个了个 JS 文件,并放在了 gitee 上。

Gitee 仓库开源

地址是:https://gitee.com/zhangxinxu/copy-text

JS 代码示意

效果是这样的,点击按钮复制后,按钮上方会出现绿色的文字提示一闪而过。

复制成功提示

语法如下:

// content 只能是字符串类型
copyText(content);
// 或者
// content 此时也可以是 DOM 元素对象
copyText(button, content);

其中:

button
可选。Element。表示触发复制的按钮元素。
content
必须。String|Element。表示需要复制的内容。如果是 DOM 元素,则会使用内部的文本或者值作为复制内容。如果使用的是 copyText(content) 语法,content 只能是字符串。

也就是如果有 button 参数,点击时候就会自动出现提示,如果第一个参数是字符串,则直接复制内容。

这样,就可以兼顾多种场景了。

此 JS 代码片段非常时候需要敏捷开发的演示页面,活动页面,对视觉要求不高的后台页面等。

也欢迎大家关注我的 gitee 账号,会不定期更新 JS 相关的小玩意。

四、知名开源 clipboard.js 项目

如果项目对代码体积不那么在意,自己也不想关心具体的实现细节,可以使用这个著名的剪切板项目 clipboard.js:https://github.com/zenorocha/clipboard.js/

不仅有复制的实现,还有剪切等。

提示效果是小黑提示,如下图示意:

复制效果提示截图

更多信息不展开,大家可以去项目主页去查找。

五、结语说明

今天发现 Gitee Page 服务可以使用了,刚刚已经申请了,希望可以一次性通过。

Gitee Page服务申请

通过后,项目中的demo页面就可以直接访问了。

下午参加稀土开发者大会,做了场直播:

直播图

我发现我连续讲话时间不能超过1个小时,否则嗓子就会受不了。

真佩服李佳琦、薇娅等主播,连续讲十个八个小时的,都不累的,天赋异禀,不得不服,不过我可以在河边连续钓鱼十个八个小时的,不知道是不是也是天赋。

好,就说这么多了,希望本文的内容可以帮助到大家。

行为仓促,如果不对,欢迎指正。

(本篇完)

分享到:


发表评论(目前14 条评论)

  1. yanhaijing说道:

    李佳琦的嗓子并强,薇娅是真强

  2. Ronin Lee说道:

    需要append嘛,比如创建a,a.href, a.click(),

  3. wangmeijian说道:

    做了个插件,原理差不多 https://github.com/wangmeijian/auto_clipboard

  4. 敲代码的加菲猫说道:

    如果是事件触发的复制行为,例如:用户点击了按钮,按钮触发了请求得到了res,最终是复制res的内容,以上方法在移动端ios上会失效,有什么好的办法可以解决吗?我看了资料说是上面的复制行为都需要用户主动点击触发复制才可以成功

  5. tantan说道:

    旭哥,clip属性mdn上看到已废弃了,虽然目前还有浏览器支持,这块是不是换个更好的写法

  6. 长弓说道:

    用了示例的代码,收到用户反馈说无法复制,在sentry里查到了 NotAllowedError: Clipboard write was blocked due to lack of user activation. 的错误,看起来像用户不知道啥时候关掉了权限导致新api不能用,是不是加一段判断如果新api返回值reject掉就重新调用旧api好一点

  7. wuyuwei说道:

    薇娅等主播的逃税 也是天赋异禀 【doge】

  8. Rateltalk说道:

    Copilot提供的方法

    if (window.clipboardData && window.clipboardData.setData) {
    // IE specific code path to prevent textarea being shown while dialog is visible.
    return window.clipboardData.setData(‘Text’, text);
    }

  9. glk说道:

    前几天就在你博客里面找关于复制内容到剪贴板的功能,发现还是几年前的。这么快就直接更新了最新方案,你是听到我的心声了吗?大神!😂

  10. 岳俊奇说道:

    您好张老师,看了你copyText.js的实现,发现你的代码语法用的大多是es5的语法,如变量声明用的是var而不是const或let,我想请问下,在实际项目中有必要刻意使用js最新语法来编写吗还是说尽量考虑使用之前的js语法呢

    • ascll说道:

      我觉得,应该是没有必要为这个小工具搞套工具链来进行转译处理吧。直接写 ES5 就完事了没有兼容问题,而且省去了工具链的麻烦。

  11. lillian说道:

    啊,直播有没有回放呀? miss 了

  12. 前端小白说道:

    前端真实强大啊,之前看到这个《Web前端剪切板文本分享到文件发送》还包含了图片,甚至文件&文件夹的复制粘贴相关的能力: https://blog.pyzy.net/post/clipboard.html