不要被标题吓怕了,这篇文章没有那么高深。javascript
工做中使用了 clipboard.js 这个库,实现文本复制的功能。因而对在浏览器中实现复制功能的原理产生兴趣。html
上网查了一下:vue
docuemnt.execCommand('cpoy')
和 docuemnt.execCommand('cut')
实现剪切和复制功能,它们的做用相似于手动按下快捷键 Ctrl + C
和 Ctrl + X
。我有点坐不住了,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
第一步中出现的 select()
是引入的一个库——select.js 中提供的方法。它的做用是选择元素内容,而且将元素设置为聚焦状态(可能的话)。github
下面咱们看看它的实现:web
有趣的是,在查看 clipboard.js 源码的时候,我发现源码里引入了三个依赖:tiny-emitter、select、good-listener。刚开始看的时候,有点抓狂,至于嘛。最后查找了一下这些依赖库的地址,发现后两个都是 clipboard.js 做者写的小库,前一个是别人写的。vue-cli
我想做者之因此这样写,一方面是为了得到 IE9+ 以上浏览器的支持(IE9 不支持
CustomEvent()
);另外一方面是为了让这个库支持 target 特性——复制指定目标元素里的内容,最后一点(可能不重要)——自产自销 😁。浏览器
这个库的代码量少,五十行都不到。其做用是经过 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
函数后,发生了两件事情:
除了 input
以外,再来看看其余元素的支持状况:
下面再来看,功能是如何实现的:
本库将 HTML 元素分为三类处理:
<select>
<input>
和 <textarea>
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 的时间上花费太长。固然跟我们具体项目的需求也是有关系的,但知道实现原理更能驾轻就熟一点,不是嘛。
(完)