剪贴板
在前端日常开发中,对剪贴板相关的操作相对较少。但针对某些特定的应用场景,又是必不可少会用到的。比如:富文本编辑器开发、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]
[2]
[3]
[4]
[5]