copy-to-clipboard 源码解析,隐藏的内容比想象的要多

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: copy-to-clipboard 是一个 js 的剪切板库,可用来复制内容到剪切板,看源码后发现其中隐藏的内容着实不少,今天一起来解读下其源码。

网络异常,图片无法展示
|

本文针对的源码版本为:193826f

copy-to-clipboard 是一个 js 的剪切板库,可用来复制内容到剪切板,看源码后发现其中隐藏的内容着实不少,今天一起来解读下其源码。

使用方式

我们先看下使用方式:

import copy from 'copy-to-clipboard';
copy('Text');
// Copy with options
copy('Text', {
    debug: true,
    message: 'Press #{key} to copy'
});
复制代码

可以看到 API 非常简单。

该库只抛出一个 copy 函数,函数接口为:copy(text: string, options: object): boolean。

第一个参数为一个文本值为用来复制的内容。

options 包含 4 个参数:

  • debug - 是否开启调试模式,开启后会将复制信息打印到 console 中
  • message - 通过 prompt 模式兼容时的提示信息
  • format - 设置复制内容的 mime type
  • onCopy - 复制后的回调,接口为:function onCopy(clipboardData: object): void

源码解读

该库源码比较简单,其中使用到了一个依赖库:toggle-selection,作用是取消和恢复当前选中的文本的选中状态。

主体方案

先看看主体部分:

reselectPrevious = deselectCurrent();
range = document.createRange();
selection = document.getSelection();
mark = document.createElement('span');
mark.textContent = text;
// ...
mark.addEventListener('copy', function (e) {
    // ...
});
document.body.appendChild(mark);
range.selectNodeContents(mark);
selection.addRange(range);
var successful = document.execCommand('copy');
if (!successful) {
    throw new Error('copy command was unsuccessful');
}
success = true;
复制代码

这部分首先取消了当前页面中已有的选择状态,然后创建了一个 range,range 可用来包含文本或节点片段。随后通过 getSelection 获得了一个 Selection 对象,该对象包含当前用户选中的或鼠标所在的内容。

然后它创建了一个 span 元素,将要复制的内容设置为该元素的文本,然后通过各种样式设置等,主要是为了避免该元素被发现、被读取、无法复制等问题。随后它在该元素中添加了 copy 事件,将元素添加到 body,然后通过 range.selectNodeContents 和 selection.addRange 选中该元素,并通过 document.execCommand 调用 copy 命令即可将选中的内容进行复制。

下面再看一下 copy 事件的处理:

e.stopPropagation();
if (options.format) {
    e.preventDefault();
    if (typeof e.clipboardData === 'undefined') {
        // IE 11
        debug && console.warn('unable to use e.clipboardData');
        debug && console.warn('trying IE specific stuff');
        window.clipboardData.clearData();
        var format = clipboardToIE11Formatting[options.format] || clipboardToIE11Formatting['default'];
        window.clipboardData.setData(format, text);
    } else {
        // all other browsers
        e.clipboardData.clearData();
        e.clipboardData.setData(options.format, text);
    }
}
if (options.onCopy) {
    e.preventDefault();
    options.onCopy(e.clipboardData);
}
复制代码

主要是分两部分,一部分是当存在自定义 format 时会组织默认的复制行为,然后通过 clipboardData.setData 重新设置内容格式,其中包含一部分 IE 兼容的代码。第二部分则是调用 onCopy 回调,这里要注意,使用 onCopy 后它会阻止默认事件,此时你需要自己将内容设置到剪切板中。

兼容方案

在上述使用 execCommand 报错后,它会尝试使用 clipboardData 做第二次尝试,这个主要是针对 IE 所做的兼容:

window.clipboardData.setData(options.format || 'text', text);
options.onCopy && options.onCopy(window.clipboardData);
success = true;
复制代码

如果该方案依旧失败,则会降级到终极方案 - prompt:

message = format('message' in options ? options.message : defaultMessage);
window.prompt(message, text);
复制代码

其中 format 主要是为了让 prompt 框的提示信息中的 ctrl 在 mac 中替换为 command。

在老浏览器中 prompt 会弹出弹窗,内容区域显示要复制的信息,然后提示区域提示用户 ctrl+c 复制。

收尾

复制完成后,它会将之前的 selection、range 和 mark 进行清理,并通过 reselectPrevious 恢复之前的选择状态。

if (selection) {
    if (typeof selection.removeRange == 'function') {
        selection.removeRange(range);
    } else {
        selection.removeAllRanges();
    }
}
if (mark) {
    document.body.removeChild(mark);
}
reselectPrevious();
复制代码

兼容性

该库兼容几乎所有的主流浏览器版本,包括 IE,因为 execCommand 虽然被废弃,但是兼容性很高,然后它还使用了 clipboardData 做 IE 兼容,并且使用了 prompt 做降级方案。

不过文档中提示在一些老的 IOS 设备中,由于无法定制 prompt 内容,所以无法兼容。

总结

该库虽然源码很简短,但是用到了相当多平时编程中很少甚至不会用到的 API,如:

  • Range
  • Selection
  • execCommand
  • clipboardData
  • style.all
  • style.clip

不过代码依然有优化的空间,如大量引用 mark.style 处可创建引用、随处可见的 debug 可做成预处理函数、兼容方案中 onCopy 行为不一致等。

相关文章
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
66 0
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
52 0
|
1月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
59 0
|
1天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
9 2
|
2天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
14天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
35 3
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
53 5
|
1月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
104 5
|
1月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
1月前
|
算法 Java 程序员
Map - TreeSet & TreeMap 源码解析
Map - TreeSet & TreeMap 源码解析
33 0

推荐镜像

更多