Async Clipboard AP

轉自奇舞週刊,我的學習記錄,侵權刪javascript

編者按:本文做者李鬆峯,資深技術圖書譯者,翻譯出版過40餘部技術及交互設計專著,現任360奇舞團高級前端開發工程師,360前端技術委員會委員、W3C AC表明css

若是我問:你知道「剪貼板」(clipboard)嗎?html

恐怕沒人不知道。咱們天天都不知道本身要在電腦或手機上「複製」、「粘貼」多少回。每次「複製」、「粘貼」的背後,都會用到「剪貼板」。前端

根據「維基百科」:java

The clipboard is a data buffer used for short-term data storage and/or data transfer between documents or applications used by cut, copy and paste operations and provided by the operating system.git

翻譯一下:github

剪貼板是一種數據緩存,用於文檔或應用間短時間數據的存儲/轉移,在用戶執行剪切、複製和粘貼操做時會用到,由操做系統提供。web

這裏最重要的一點在於,「剪貼板」是「由操做系統提供」的,所以它是系統級的一個軟件特性。編程

對於前端開發者來講,若是我問:你知道怎麼操做「剪貼板」嗎?json

不少人的第一反應多是:使用clipboard.js吧……

clipboard.js的原理

clipboard.js(https://clipboardjs.com/)是在Github上有24000多個星,其流行程度可見一斑。關於這個庫的用法,你們能夠本身去看,咱們這裏主要分析其實現原理,以便了解目前操做剪貼板的主流技術。

簡單來講,clipboard.js利用了兩個已有的Web API(前者屬於HTML5,後者屬於HTML Editing API):

  • HTMLInputElement.select()

  • document.execCommand()

相應地,原理也只有兩步。

第一步,建立臨時的texterea元素、經過CSS隱藏起來,而後把要複製的文本賦值給這個文本區,再選擇這個文本區中的全部內容:

// https://github.com/zenorocha/clipboard.js/blob/master/src/clipboard-action.js

 

/**

 

* Creates a fake textarea element, sets its value from `text` property,

 

* and makes a selection on it.

 

*/

 

// selectFake()

 

// 建立臨時的texterea元素

 

this.fakeElem = document.createElement('textarea');

 

// 隱藏這個元素

 

this.fakeElem.style.position = 'absolute';

 

this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';

 

// 把要複製的文本賦給文本區並選擇所有內容

 

this.fakeElem.value = this.text;

 

this.selectedText = select(this.fakeElem);

 

// 觸發複製

 

this.copyText();

第二步,經過腳本觸發copy操做,若是成功則文本會寫入剪貼板,而後根據執行結果派發自定義事件:

// https://github.com/zenorocha/clipboard.js/blob/master/src/clipboard-action.js

 

/**

 

* Executes the copy operation based on the current selection.

 

*/

 

// copyText()

 

succeeded = document.execCommand(this.action);

 

this.handleResult(succeeded);

 

/**

 

* Fires an event based on the copy operation result.

 

* @param {Boolean} succeeded

 

*/

 

handleResult(succeeded) {

 

  this.emitter.emit(succeeded ? 'success' : 'error', {

 

    action: this.action,

 

    text: this.selectedText,

 

    trigger: this.trigger,

 

    clearSelection: this.clearSelection.bind(this)

 

  });

 

}

如前所述,剪貼板是由操做系統提供的,是系統級的。瀏覽器廠商爲安全和用戶體驗考慮,只信任用戶經過應用、文檔或腳本觸發的複製操做。並且,複製到剪貼板的內容來源還必須是已有的DOM元素。

以上覆制操做的結果是將文本內容寫入剪貼板。若是要讀取剪貼板中的內容,必須給粘貼(paste)事件操做註冊處理程序,經過e.clipboardData.getData()方法獲取剪貼板中的內容。這樣能夠在用戶把內容粘貼到目標輸入框以前,對剪貼板上的內容進行一些預處理。但這不是本文的重點。

雖然clipboard.js使用的這種技術幾乎全部瀏覽器都支持,但也存在諸多缺陷。

已有技術的缺陷

之前的技術存在什麼失陷呢?除了前面須要經過編程方式經過execCommand模擬用戶觸發剪貼板的複製操做以外,還有:

  • execCommand的初衷是編輯DOM,並且瀏覽器間實現存在很多差別

  • 複製粘貼操做是同步的,會阻塞主線程,致使頁面沒法響應

  • 若是進而再彈窗申請受權,則可能會惹惱用戶

  • 相應地,妨礙對某些類型的數據(如圖片)進行無害化處理或轉碼操做

  • 而爲避免外部利用,有時候轉碼又是必需的

爲了克服這些問題,W3C開始着手製定相關標準:Clipboard API and events(https://www.w3.org/TR/clipboard-apis/)。

Clipboard API and events定義了Async Clipboard API,相應地增長了clipboardchange事件。

Async Clipboard API

換句話說,爲避免阻塞主線程,這個新標準引入了基於Promise的異步剪貼板API。因爲剪貼板是系統級軟件特性,因此相應的API掛載到了navigator上面:

navigator.clipboard

這個clipboard對象有4個方法:

Promise<DataTransfer> read();

 

Promise<DOMString> readText();

 

Promise<void> write(DataTransfer data);

 

Promise<void> writeText(DOMString data);

兩個讀剪貼板,兩個寫剪貼板。

支持讀寫如下數據類型:

  • text/plain

  • text/uri-list

  • text/csv

  • text/css

  • text/html

  • application/xhtml+xml

  • image/png

  • image/jpg, image/jpeg

  • image/gif

  • image/svg+xml

  • application/xml, text/xml

  • application/javascript

  • application/json

  • application/octet-stream

read()

navigator.clipboard.read().then(function(data) {

 

  for (var i = 0; i < data.items.length; i++) {

 

    if (data.items[i].type == "text/plain") {

 

      console.log("Your string: ", data.items[i].getAs("text/plain"));

 

    } else {

 

      console.error("No text/plain data on clipboard.");

 

    }

 

  }

 

});

readText()

navigator.clipboard.readText().then(function(data) {

 

   console.log("Your string: ", data);

 

});

write(data)

var data = new DataTransfer();

 

data.items.add("text/plain", "Howdy, partner!");

 

navigator.clipboard.write(data).then(function() {

 

  console.log("Copied to clipboard successfully!");

 

}, function() {

 

  console.error("Unable to write to clipboard. :-(");

 

});

writeText(data)

navigator.clipboard.writeText("Howdy, partner!").then(function() {

 

  console.log("Copied to clipboard successfully!");

 

}, function() {

 

  console.error("Unable to write to clipboard. :-(");

 

});

注意事項

  1. navigator.clipboard只能在「安全上下文」中使用。什麼是「安全上下文」?簡單說,就是localhost和HTTPS環境下。(能夠經過window.isSecureContext屬性取得。)

  2. 桌面瀏覽器中目前只有Chrome、Firefox和Opera支持,Safari和IE/Edge還不支持;並且,Chrome也只支持readText()和writeText()。

參考連接:

    • https://clipboardjs.com/

    • https://www.w3.org/TR/clipboard-ap

    • https://developers.google.com/web/updates/2018/03/clipboardapi

    • https://w3c.github.io/webappsec-secure-contexts

相關文章
相關標籤/搜索