clipboard.js 核心代码解析

不要被标题吓怕了,这篇文章没有那么高深。javascript

工做中使用了 clipboard.js 这个库,实现文本复制的功能。因而对在浏览器中实现复制功能的原理产生兴趣。html

上网查了一下:vue

  1. 读了这篇文章,知道能够经过 docuemnt.execCommand('cpoy')docuemnt.execCommand('cut') 实现剪切和复制功能,它们的做用相似于手动按下快捷键 Ctrl + CCtrl + X
  2. 还看了一个 Vue 指令库 v-clipboard 的源码(源码量不多,功能也很简单),知道是如何实现的了。
  3. 又看了另外一个 Vue 指令库 vue-clipboard2,star 数量快一千了,这个库是对 clipboard.js 的一层封装。

我有点坐不住了,clipboard.js 到底好在哪里?怀着深深的疑问,我开始看它的源码。看过以后,发现无一例外,clipboard.js 也是使用 docuemnt.execCommand 方法实现复制/剪切功能的。java

核心代码

clipboard.js 核心代码在 clipboard-action.js 这个文件中,抽离出来后就三行(源码位于这里):ios

select(targetElem);
document.execCommand(action) // action 等于 'copy' 或 'cut'
window.getSelection().removeAllRanges()
复制代码

解释一下就是:git

  1. 选择元素
  2. 执行复制/剪切(相似于按下 Ctrl + C/Ctrl + X)
  3. 取消元素选择

第一步中出现的 select() 是引入的一个库——select.js 中提供的方法。它的做用是选择元素内容,而且将元素设置为聚焦状态(可能的话)。github

下面咱们看看它的实现:web

有趣的是,在查看 clipboard.js 源码的时候,我发现源码里引入了三个依赖:tiny-emitterselectgood-listener。刚开始看的时候,有点抓狂,至于嘛。最后查找了一下这些依赖库的地址,发现后两个都是 clipboard.js 做者写的小库,前一个是别人写的。vue-cli

我想做者之因此这样写,一方面是为了得到 IE9+ 以上浏览器的支持(IE9 不支持 CustomEvent());另外一方面是为了让这个库支持 target 特性——复制指定目标元素里的内容,最后一点(可能不重要)——自产自销 😁。浏览器

select.js

这个库的代码量少,五十行都不到。其做用是经过 JavaScript 代码选择 HTML 元素的内容。在全局中暴露了一个 select 函数供调用。

使用 demo 以下:

<input type="text" value="my name" onclick="handleSelectInput()">

<script src="select.js></script> <script> function handleSelectInput() { console.log(select(document.querySelector('input'))) } </script> 复制代码

效果是这样的:

经过观察发现,执行 select 函数后,发生了两件事情:

  1. 元素被聚焦(能够的话)
  2. 选择元素内容(而且内容以函数返回值的形式返回)

除了 input 以外,再来看看其余元素的支持状况:

下面再来看,功能是如何实现的:

代码实现

本库将 HTML 元素分为三类处理:

  1. <select>
  2. <input><textarea>
  3. 其余元素

1、<select> 的逻辑

element.focus();
selectedText = element.value;
复制代码

这里变量 selectedText 是调用 select() 函数后的返回值,下面与此同样。

<select> 元素当前选中值,能够从 value 属性中得到。

2、<input><textarea> 的逻辑

var isReadOnly = element.hasAttribute('readonly');
if (!isReadOnly) { // 若是元素不是只读的,设置成只读的
    element.setAttribute('readonly', '');
}

element.select();
element.setSelectionRange(0, element.value.length);
selectedText = element.value;

if (!isReadOnly) { // 恢复到以前的状态
    element.removeAttribute('readonly');
}
复制代码

<select> 相似,<input><textarea> 输入框中的值,也可经过 value 属性得到。

其实针对 <input><textarea>,执行 element.select() 就能够了,就能实现文本框的全选功能了。多是为了兼容 iOS 系统的缘由,才加了 element.setSelectionRange(0, element.value.length) 这么一句(参见这篇文章)。

3、其余元素

// 若是元素是 contenteditable 的,先 focus 一下
if (element.hasAttribute('contenteditable')) {
    element.focus();
}

var selection = window.getSelection();
var range = document.createRange().selectNodeContents(element) // 生成要选择的文本范围(element 里的内容)
selection.removeAllRanges(); // 先移除当前网页已有的选择
selection.addRange(range); // 选择 element 的内容
selectedText = selection.toString();
复制代码

做用

select.js 在 clipboard.js 这个库里的做用,就是在执行复制/剪切操做以前,选择好要操做的文本内容。

临时元素

上面讲到的实现过程是基于 target 对象来讲的,就是说咱们有目标元素能够获取内容。可是若是只是简单指定要复制的文本内容,该如何实现的呢?

好比下面这样:

<button class="btn" data-clipboard-text="Just because you can doesn't mean you should — clipboard.js">
    Copy to clipboard
</button>
复制代码

点击按钮的时候,怎样将 data-clipboard-text 的值复制到剪切板中?

方法就是建立出一个临时元素(通常使用 <textarea>),添加到文档中(不可见),使用它目标元素,在执行完复制操做后,再从文档中删除。

相似于下面的代码(改写自这里):

var fakeElem = document.createElement('textarea');
fakeElem.style['left' ] = '-9999px'; // 让元素在视口以外
fakeElem.setAttribute('readonly', '');
fakeElem.value = text // 这里的 text 能够理解为上面 `data-clipboard-text` 属性值里文本内容
document.appendChild(fakeElem) // 将这个临时元素添加到文档中

// ... 执行复制操做
复制代码

仅指定文本进行复制的话,会在文档中插入一个“不可见”(实际上在视口以外)的临时元素,做为目标元素进行操做。

总结

到这里就把 clipboard.js 核心部分讲完了。

其实比较起来,对于我我的而言,我更喜欢 v-clipboard 这个库中实现的小而美的代码,而不是 clipboard.js 中的大而全的功能。

可能在心里里,小而美的代码能让我更快阅读,知道某个功能的实现细节,不至于出现 bug 时,在解决 bug 的时间上花费太长。固然跟我们具体项目的需求也是有关系的,但知道实现原理更能驾轻就熟一点,不是嘛。

(完)

相关文章
相关标签/搜索