剪贴板在生产力平台上有很高的使用频率。剪贴板API在主流的浏览器上也已经有了不错的支持率。基于前端技术的生产力工具就需要考虑接入这些的能力。
技术上可以实现的场景有:
在onpaste事件读取剪贴板内容
在oncopy事件中可以阻止默认行为、写入自定义数据到剪贴板……
在onclick等事件中,可以用js程序执行剪贴板写入
这些技术场景可以支持到的业务场景比如:
word文本粘贴到页面(支持plain text、html、rtf等格式)
图片、音频、视频文件粘贴到网页
用户复制文本时,加入版权提示
禁止用户复制文本、复制行为的检测
点击按钮复制数据到剪贴板
复制react组件的状态到另一个组件
接下来就来看看具体的实现
复制外部资源到浏览器
在浏览器的onpaste事件中,可以取到一个DataTransfer类型的字段event.clipboardData。通过它就可以获取的粘贴进浏览器的资源信息。我们把这个过程封装成通用的函数。
function readDataTransferItemAsString(item){
return new Promise(function (resolve){
item.getAsString(resolve);
})
}
async function getClipboardData(data){
const { items } = data;
const result = {};
await Promise.all([...items].map(async item => {
console.log(item)
const { type, kind } = item;
if (kind === 'string') {
result[type] = await readDataTransferItemAsString(item);
} else if (kind === 'file') {
result[type] = item.getAsFile();
}
}))
console.log(result)
return result;
}
function handlePaste(e){
getClipboardData(e.clipboardData).then(setClipInfo)
}
document.removeEventListener('paste', handlePaste);
复制代码
在OneNote复制一段文本到浏览器:
可以看到,粘贴进来的信息同时包括了text/plain、text/html、text/rtf、image/png三种格式,可以根据自己的业务需要选择使用。
阻止并替换浏览器的默认行为
有时候我们希望保护页面内容不被复制。可以在document上监听oncopy事件。例如:
function handleCopy(e){
e.preventDefault()
const { clipboardData } = e
clipboardData.clearData();
clipboardData.setData('text/plain', '版权保护中')
}
document.addEventListener('copy', handleCopy)
复制代码
这时但我们在页面上选择文本并复制时:
默认行为被改写了,粘贴板被清空并填入了我们设置好的文案。
点击按钮复制
function handleClick(e){
function handleCopy(e){
e.preventDefault()
const { clipboardData } = e
clipboardData.clearData();
const msg = `code: ${Math.random().toString().slice(2, 10)}\ncopied at: ${new Date().toISOString()}`;
clipboardData.setData('text/plain', msg);
document.removeEventListener('copy', handleCopy);
setResult(msg)
}
document.addEventListener('copy', handleCopy)
document.execCommand('copy');
}
复制代码
在Demo2点击按钮,就自动把一段文本写入了剪贴板
注意:document.execCommand('copy');这个操作只在特定的事件(比如onClick)响应函数内才会生效。
复制一个组件的状态到另一个组件
我写了两个React组件,其中剪贴板相关的状态逻辑封装成了useClipboardData:
import { useState, useEffect } from 'react';
function readDataTransferItemAsString(item){
return new Promise(function (resolve){
item.getAsString(resolve);
})
}
async function getClipboardData(data){
const { items } = data;
const result = {};
await Promise.all([...items].map(async item => {
console.log(item)
const { type, kind } = item;
if (kind === 'string') {
result[type] = await readDataTransferItemAsString(item);
} else if (kind === 'file') {
result[type] = item.getAsFile();
}
}))
console.log(result)
return result;
}
export function useClipboardState(initData){
const [data, setData] = useState(initData);
useEffect(function (){
async function handlePaste(e){
const d = await getClipboardData(e.clipboardData);
if (d && d['text/custom']) {
try {
setData(JSON.parse(d['text/custom']))
} catch (e) {
console.log('paste failed.')
}
}
}
function handleCopy(e){
e.preventDefault()
const { clipboardData } = e
clipboardData.clearData();
clipboardData.setData('text/custom', JSON.stringify(data))
}
document.addEventListener('copy', handleCopy)
document.addEventListener('paste', handlePaste);
return function (){
document.removeEventListener('paste', handlePaste);
document.removeEventListener('copy', handleCopy)
}
}, [data]);
return [data, setData]
}
复制代码
在Demo3页面可以按下Ctrl+C复制它的状态到剪贴板,到Demo4页面按下Ctrl+V粘贴。同时,序列化以后的状态信息也支持通过邮件、聊天框等工具发送。这种交互逻辑可以极大的方便用户操作。
最后
以上就是我总结的几个剪贴板API应用场景。这些API已经在我负责的项目(富文本编辑、试卷排版系统)中投入使用,并获得了用户好评。有需要的同学可以参考。