天天看點

我從Typora中學到的Clipboard妙用.md

Typora是我經常使用的一款軟體,用來寫MarkDown很舒适,有着非常優秀的使用體驗:

  • 實時預覽
  • 自定義圖檔上傳服務
  • 文檔轉換
  • 主題自定義

起因

不過我遇到一個非常好玩的事情,當我複制Typora内容粘貼到文本編輯器時,會得到MarkDown格式的内容;複制到富文本編輯器時,可以渲染出富文本效果:

複制到VS Code:

我從Typora中學到的Clipboard妙用.md

複制到其他富文本編輯器:

我從Typora中學到的Clipboard妙用.md

我很好奇為什麼會出現兩種不同的結果,Typora應該是使用Electron(或類似技術)開發的,我嘗試用Clipboard API來進行測試:

// 為什麼使用setTimeout:我是在Chrome控制台進行的測試,clipboard依托于頁面,是以我需要設定1s延時,以便可以點選頁面聚焦
setTimeout(async()=>{
    const clipboardItems = await navigator.clipboard.read();
    console.log(clipboardItems)
},1000)           

然後看到了剪切闆中有兩種不同類型的内容:純文字

text/plain

和富文本

text/html

。是以不同的内容接收者選擇了不同的内容作為資料,文本編輯器拿到的是純文字,富文本編輯器擷取的是富文本格式資料。

我從Typora中學到的Clipboard妙用.md

再來看看擷取到的具體内容吧:

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)           
我從Typora中學到的Clipboard妙用.md

Clipboard塞入資料試一下:

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: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, "Segoe UI Emoji", 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])           

嘗試了幾個富文本編輯器得到的結果(不同富文本編輯器的具體實作可能存在差異):

  • 如果隻存在純文字(僅保留上段代碼中的純文字部分), 會讀取剪切闆中純文字内容
  • 如果存在純文字和富文本,會讀取剪切闆中富文本内容

那這個效果是Typora幫我們實作的嗎?

我們先來看一下複制富文本的預設行為,打開一個網頁,複制網頁文本,然後使用剛才的代碼嘗試一下,看看讀取到的剪切闆内容。

我從Typora中學到的Clipboard妙用.md

我們可以看到,在複制富文本的時候,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)
}           

複制頁面中内容,我們就可以的看到列印的結果了:

我從Typora中學到的Clipboard妙用.md

本來為資料會在clipboardData中,但是嘗試了一下并沒有擷取到内容,看了一下API, 需要在copy事件中通過setData設定資料,在paste時間中getData擷取資料。我們可以通過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>');
});           

或者使用clipboard.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'}),
      })
    ])
}           

監聽複制還可以用來添加版權資訊,比如上面代碼中的額外資訊就會出現在複制的文本中。

對于複制和粘貼内容也可以通過document.execCommand,不過目前屬于已經被棄用的API,不建議使用

更多文章歡迎關注“混沌前端”

參考文檔:

ClipboardItem Clipboard-write element.oncopy Selection Range

繼續閱讀