剪贴板
在前端日常开发中,对剪贴板相关的操作相对较少。但针对某些特定的应用场景,又是必不可少会用到的。比如:富文本编辑器开发、ctrl + v 上传图片、支付宝红包口令自动复制(典型被玩坏儿了的用途)
剪贴板操作途径
剪贴板事件
操作剪贴板,最基础的方式为监听剪贴板事件。出于兼容性考虑,一般都只在剪贴板事件处理函数中访问 clipboardData ,非 IE 浏览器读数据只能在 onpaste 事件时可用。要使用剪贴板事件读写数据,就必须得用户主动使用快捷键或者右键菜单。所以监听板事件的主要应用场景是用户触发后对数据进行拦截处理。
document.execCommand
document.execCommand() 方法用于操纵可编辑区域的内容,例如最常用的 'copy' 命令可将选择区域的内容复制到剪贴板中,配合 window.getSelection() 方法就可以实现自动选择内容区域并写入内容到剪贴板。clipboard.js 库及红包口令自动复制主要就通过此方法实现的。
此方法也有一些问题:
- Chrome \ Safari 不支持 'paste' 命令
- Chrome 不能直接在后台调用 'copy' 命令,需用户点击触发
- IE 可直接在后台调用和点击触发 'copy' 命令,但会出现权限提示
Asynchronous Clipboard API
看过上面的介绍,你会发现,操作剪贴板处处受限,而且迂回曲折,更困难的是有些功能是无法实现的。但不可否认,造成这一困惑很大一部分原因是安全考虑。
试想一下,当你浏览网页,点了下页面或者按钮,就复制了支付宝红包口令,会怎样?呃,还能接受?那么 'rm -rf /' 呢?(我用 Windows,我怕啥)
而做为 Clipboard API 新增内容的 Asynchronous Clipboard API 及 Clipboard Permissions API 就是为了解决上述问题而来的,专治各种迂回曲折及安全风险。截至目前(2018.03),Chrome 66 实现了部分功能,预测未来会有变化,本文不做详细介绍。暂归纳特点如下:
- HTTPS 下才能调用
- 页面处于活动选项卡才能调用
- 基于 Promise,便于异步操作和错误处理
- 实现了编程方式的粘贴(读)和复制(写)
- 粘贴(读)和复制(写)权限已添加到 Permissions API 中 ,操作需要用户授权
- Clipboard Permissions API 目前仅适用于 Asynchronous Clipboard API。根据规范,未来会添加到所有 Clipboard API 中
处理粘贴 ( paste )
对于粘贴,目前广泛支持的仅有快捷键及右键菜单触发,而 IE 支持的编程方式使用场景不多,这里不做介绍。
纯文本或带格式文本
对纯文本的拦截
El.addEventListener('paste', event => {
event.preventDefault();
let clipboardData = event.clipboardData || window.clipboardData, // IE 兼容
plainText = clipboardData.getData('text'); // 无格式文本
// ... 对 plainText 进行一系列处理操作
document.execCommand('insertText', false, plainText); // 插入无格式文本
document.execCommand('paste', false, plainText); // IE 兼容
})
如需对有格式文本进一步处理,可用以下方法转换为 DOM 结构,再进行操作。(IE 浏览器无法获得 'text/html' 所以无法实现下面的功能)
/* 仍在上述事件处理函数中,下面均省略 */
let plainHTML = clipboardData.getData('text/html'), // 有格式文本
domContainer = document.createElement('div');
domContainer.innerHTML = plainHTML;
// ... 对 domContainer 进行一系列处理操作
El.innerHTML = domContainer.innerHTML; // 使用 innerHTML 方式整体插入
El.appendChild(nodeOfDomContainer); // 只插入其中某个节点
图片
如 DOM 结构中含有图片标签,可考虑上传图片至服务器中,然后将其 src 替换为服务器返回的地址。
如复制的是单个图片(例如 word 中复制过来的),可以使用下述方法得到图片的 DataURL,再上传。(IE 浏览器粘贴图片会自动生成带 DataURL 的图片标签,所以不需要下述方法)
let items = clipboardData.items;
for (let i = 0; i < items.length; i++) {
let item = items[i];
if (/image/.test(item.type)) {
let file = item.getAsFile(), // 得到文件对象
reader = new FileReader();
reader.onload = function() {
upload(reader.result) // 上传图片,伪代码
}
reader.readAsDataURL(file); // 读取为 DataURL / Base64
}
}
处理复制 ( copy )
快捷键及右键菜单触发
常见应用场景是拦截复制操作,对复制内容加工后再写入剪贴板,例如在知乎复制大段文本后加入版权信息。
El.addEventListener('copy', event => {
event.preventDefault();
let clipboardData = event.clipboardData || window.clipboardData,
text = window.getSelection().toString();
// ... 对 text 进行一系列处理操作
clipboardData.setData('text/plain', text); // 将处理好的 text 写入剪贴板
clipboardData.setData('text', text); // IE 兼容
})
编程式复制
虽是编程式复制,但无法做到完全自动复制,需要用户先触发 点击 事件。clipboard.js 库及红包口令自动复制(在 document 上监听 click 事件)主要就通过此方法实现的。
固定内容
Button.addEventListener('click', event => {
let sometext = '红包码 xxxxxx',
hiddenInput = document.createElement('input');
hiddenInput.value = sometext;
hiddenInput.setAttribute('readonly', '');
hiddenInput.style.position = 'absolute';
hiddenInput.style.left = '-9999px';
document.body.appendChild(hiddenInput);
hiddenInput.select();
hiddenInput.setSelectionRange(0, hiddenInput.value.length); // ios
document.execCommand('copy');
document.body.removeChild(hiddenInput);
})
复制某个元素内的文本
Button.addEventListener('click', event => {
let someElement = document.getElementById('someelement'),
selection = window.getSelection(),
range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
document.execCommand('copy');
})
可以发现,目前对剪贴板的操作真是千奇百怪,希望新的 API 快快成为现实吧(
做梦)!
参考
[1] Clipboard API and events | w3c
[3] 利用 javascript 实现富文本编辑器 | 掘金
[4] 富文本编辑器初探 | 掘金