Typora是我经常使用的一款软件,用来写MarkDown很舒适,有着非常优秀的使用体验:
- 实时预览
- 自定义图片上传服务
- 文档转换
- 主题自定义
起因
不过我遇到一个非常好玩的事情,当我复制Typora内容粘贴到文本编辑器时,会得到MarkDown格式的内容;复制到富文本编辑器时,可以渲染出富文本效果:
复制到VS Code:
复制到其他富文本编辑器:
我很好奇为什么会出现两种不同的结果,Typora应该是使用Electron(或类似技术)开发的,我尝试用Clipboard API来进行测试:
// 为什么使用setTimeout:我是在Chrome控制台进行的测试,clipboard依托于页面,所以我需要设置1s延时,以便可以点击页面聚焦
setTimeout(async()=>{
const clipboardItems = await navigator.clipboard.read();
console.log(clipboardItems)
},1000)
然后看到了剪切板中有两种不同类型的内容:纯文本text/plain
和富文本text/html
。所以不同的内容接收者选择了不同的内容作为数据,文本编辑器拿到的是纯文本,富文本编辑器获取的是富文本格式数据。
再来看看获取到的具体内容吧:
setTimeout(async()=>{
const clipboardItems = await navigator.clipboard.read();
console.log(clipboardItems)
for (const clipboardItem of clipboardItems) {
for (const type of clipboardItem.types) {
const contentBlob = await clipboardItem.getType(type)
const text = await contentBlob.text()
console.log(text)
}
}
},1000)
Clipboard塞入数据试一下:
setTimeout(async ()=>{
await navigator.clipboard.write([
new ClipboardItem({
["text/plain"]: new Blob(['# 纯文本和富文本'],{type:'text/plain'}),
["text/html"]: new Blob(['
'],{type:'text/html'}),
})
]);
},[1000])
尝试了几个富文本编辑器得到的结果(不同富文本编辑器的具体实现可能存在差异):
- 如果只存在纯文本(仅保留上段代码中的纯文本部分), 会读取剪切板中纯文本内容
- 如果存在纯文本和富文本,会读取剪切板中富文本内容
那这个效果是Typora帮我们实现的吗?
我们先来看一下复制富文本的默认行为,打开一个网页,复制网页文本,然后使用刚才的代码尝试一下,看看读取到的剪切板内容。
我们可以看到,在复制富文本的时候,Chrome实现的clipboard API都会生成两份结果,一份是纯文本格式text/plain
,一份是富文本格式text/html
。
不同的是:当我们在Typora复制时,得到的是Markdown格式的纯文本和富文本,是Typora帮我们进行了处理。
监听复制,写入剪切板
监听复制我们可以使用HTMLElement.oncopy实现:
打开任意一个网页,切换到控制台:
document.body.oncopy = function(e){
console.log(e)
var text = e.clipboardData.getData("text");
console.log(text)
}
复制页面中内容,我们就可以的看到打印的结果了:
本来为数据会在clipboardData中,但是尝试了一下并没有获取到内容,看来只能另辟蹊径了,我们可以通过Range API来获取选中的内容。
document.body.oncopy = function(e){
const selectionObj = window.getSelection()
const rangeObj = selectionObj.getRangeAt(0)
const fragment = rangeObj.cloneContents() // 获取Range包含的文档片段
const wrapper = document.createElement('div')
wrapper.append(fragment)
navigator.clipboard.write([
new ClipboardItem({
["text/plain"]: new Blob([wrapper.innerText,'额外的文本'],{type:'text/plain'}),
["text/html"]: new Blob([wrapper.innerHTML,'额外的富文本
'],{type:'text/html'}),
})
])
}
监听复制还可以用来添加版权信息,比如上面代码中的额外信息就会出现在复制的文本中。
对于复制和粘贴内容也可以通过document.execCommand,不过目前属于已经被弃用的API,不建议使用