2
头图

Typora is a software I often use. It is very comfortable to write MarkDown and has a very good user experience:

  • Real-time preview
  • Custom image upload service
  • Document conversion
  • Theme customization

cause

But I encountered a very interesting thing. When I copy Typora content and paste it into a text editor, I will get the content in MarkDown format; when I copy it to the rich text editor, I can render the rich text effect:

Copy to VS Code:

VS Code

Copy to other rich text editors:

富文本编辑器

I am curious why there are two different results, Typora should be developed using Electron (or similar technology), I tried to use the Clipboard API to test:

// 为什么使用setTimeout:我是在Chrome控制台进行的测试,clipboard依托于页面,所以我需要设置1s延时,以便可以点击页面聚焦
setTimeout(async()=>{
    const clipboardItems = await navigator.clipboard.read();
    console.log(clipboardItems)
},1000)

Then I saw that there are two different types of content in the clipboard: plain text text/plain and rich text text/html . Therefore, different content recipients choose different content as the data, the text editor gets the plain text, and the rich text editor gets the rich text format data.

结果

Let's take a look at the specific content obtained:

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)

image-20211117193144843

Insert the data into the Clipboard and try it out:

setTimeout(async ()=>{
await navigator.clipboard.write([
      new ClipboardItem({
        ["text/plain"]: new Blob(['# 纯文本和富文本'],{type:'text/plain'}),
        ["text/html"]: new Blob(['<h1 cid="n21" mdtype="heading" class="md-end-block md-heading md-focus" style="box-sizing: border-box; break-after: avoid-page; break-inside: avoid; orphans: 4; font-size: 2.25em; margin-top: 1rem; margin-bottom: 1rem; position: relative; font-weight: bold; line-height: 1.2; cursor: text; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); white-space: pre-wrap; caret-color: rgb(51, 51, 51); color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, &quot;Segoe UI Emoji&quot;, sans-serif; font-style: normal; font-variant-caps: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration: none;"><span md-inline="plain" class="md-plain md-expand" style="box-sizing: border-box;">纯文本和富文本</span></h1>'],{type:'text/html'}),
      })
    ]);
},[1000])

The results of trying several rich text editors (the specific implementation of different rich text editors may be different):

  • If there is only plain text (only the plain text part of the above code is retained), the plain text content in the clipboard will be read
  • If there are plain text and rich text, the rich text content in the clipboard will be read

Did Typora help us achieve this effect?

Let's take a look at the default behavior of copying rich text. Open a webpage, copy the text of the webpage, and then try it with the code just now to see the contents of the clipboard that are read.

image-20211118103737687

We can see that when copying rich text, the clipboard API implemented by Chrome will generate two results, one is the plain text format text/plain and the other is the rich text format text/html .

The difference is that when we copy in Typora, we get Markdown format, which Typora helps us process.

Monitor copy, write to clipboard

To monitor copying, we can use HTMLElement.oncopy to achieve:

Open any webpage and switch to the console:

document.body.oncopy = function(e){
      console.log(e)
    var text = e.clipboardData.getData("text");
    console.log(text)
}

Copy the content on the page, we can see the printed result:

监听复制

Originally the data would be in clipboardData, but I tried it and didn't get the content. After looking at the API, I need to set the data through setData in the copy event and getData to get the data in the paste time. We can get the selected content through the Selection API.

document.addEventListener('copy', function(e){
      e.preventDefault(); // 防止我们筛入的数据被覆盖
    const selectionObj = window.getSelection()
        const rangeObj = selectionObj.getRangeAt(0)
    const fragment = rangeObj.cloneContents() // 获取Range包含的文档片段
    const wrapper = document.createElement('div')
    wrapper.append(fragment)
    e.clipboardData.setData('text/plain', wrapper.innerText + '额外的文本');
    e.clipboardData.setData('text/html', wrapper.innerHTML+ '<h1>额外的文本</h1>');
});

Or use clipboard.write to write:

document.body.oncopy = function(e){
    e.preventDefault();
    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,'<h1>额外的富文本</h1>'],{type:'text/html'}),
      })
    ])
}

Monitor replication can also be used to add copyright information, such as the above code additional information will appear in the text copied.

For copying and pasting content, document.execCommand can also be used, but it is currently an API that has been deprecated and is not recommended.

混沌前端

Reference documents:

ClipboardItem

Clipboard-write

element.oncopy

Selection

Range


一颗小行星
56 声望5 粉丝

@混沌前端


引用和评论

0 条评论