剪貼簿
在前端日常開發中,對剪貼簿相關的操作相對較少。但針對某些特定的應用場景,又是必不可少會用到的。比如:富文字編輯器開發、ctrl + v 上傳圖片、支付寶紅包口令自動複製(典型被玩壞兒了的用途)
剪貼簿操作途徑
剪貼簿事件
操作剪貼簿,最基礎的方式為監聽剪貼簿事件。出於相容性考慮,一般都只在剪貼簿事件處理函式中訪問 clipboardData ,非 IE 瀏覽器讀資料只能在 onpaste 事件時可用。要使用剪貼簿事件讀寫資料,就必須得使用者主動使用快捷鍵或者右鍵選單。所以監聽板事件的主要應用場景是使用者觸發後對資料進行攔截處理。
document.execCommand
document.execCommand() 方法用於操縱可編輯區域的內容,例如最常用的 `copy` 命令可將選擇區域的內容複製到剪貼簿中,配合 window.getSelection() 方法就可以實現自動選擇內容區域並寫入內容到剪貼簿。clipboard.js 庫及紅包口令自動複製主要就通過此方法實現的。
此方法也有一些問題:
- Chrome Safari 不支援 `paste` 命令
- Chrome 不能直接在後臺呼叫 `copy` 命令,需使用者點選觸發
- IE 可直接在後臺呼叫和點選觸發 `copy` 命令,但會出現許可權提示
Asynchronous Clipboard API
看過上面的介紹,你會發現,操作剪貼簿處處受限,而且迂迴曲折,更困難的是有些功能是無法實現的。但不可否認,造成這一困惑很大一部分原因是安全考慮。
試想一下,當你瀏覽網頁,點了下頁面或者按鈕,就複製了支付寶紅包口令,會怎樣?呃,還能接受?那麼 `rm -rf /` 呢?(我用 Windows,我怕啥)
而做為 Clipboard API 新增內容的 Asynchronous Clipboard API 及 Clipboard Permissions API 就是為了解決上述問題而來的,專治各種迂迴曲折及安全風險。截至目前(2018.03),Chrome 66 實現了部分功能,預測未來會有變化,本文不做詳細介紹。暫歸納特點如下:
- HTTPS 下才能呼叫
- 頁面處於活動選項卡才能呼叫
- 基於 Promise,便於非同步操作和錯誤處理
- 實現了程式設計方式的貼上(讀)和複製(寫)
- 貼上(讀)和複製(寫)許可權已新增到 Permissions API 中 ,操作需要使用者授權
- Clipboard Permissions API 目前僅適用於 Asynchronous Clipboard API。根據規範,未來會新增到所有 Clipboard API 中
處理貼上 ( paste )
對於貼上,目前廣泛支援的僅有快捷鍵及右鍵選單觸發,而 IE 支援的程式設計方式使用場景不多,這裡不做介紹。
純文字或帶格式文字
對純文字的攔截
El.addEventListener(`paste`, event => {
event.preventDefault();
let clipboardData = event.clipboardData || window.clipboardData, // IE 相容
plainText = clipboardData.getData(`text`); // 無格式文字
// ... 對 plainText 進行一系列處理操作
document.execCommand(`insertText`, false, plainText); // 插入無格式文字
document.execCommand(`paste`, false, plainText); // IE 相容
})
複製程式碼
如需對有格式文字進一步處理,可用以下方法轉換為 DOM 結構,再進行操作。(IE 瀏覽器無法獲得 `text/html` 所以無法實現下面的功能)
/* 仍在上述事件處理函式中,下面均省略 */
let plainHTML = clipboardData.getData(`text/html`), // 有格式文字
domContainer = document.createElement(`div`);
domContainer.innerHTML = plainHTML;
// ... 對 domContainer 進行一系列處理操作
El.innerHTML = domContainer.innerHTML; // 使用 innerHTML 方式整體插入
El.appendChild(nodeOfDomContainer); // 只插入其中某個節點
複製程式碼
圖片
如 DOM 結構中含有圖片標籤,可考慮上傳圖片至伺服器中,然後將其 src 替換為伺服器返回的地址。
如複製的是單個圖片(例如 word 中複製過來的),可以使用下述方法得到圖片的 DataURL,再上傳。(IE 瀏覽器貼上圖片會自動生成帶 DataURL 的圖片標籤,所以不需要下述方法)
let items = clipboardData.items;
for (let i = 0; i < items.length; i++) {
let item = items[i];
if (/image/.test(item.type)) {
let file = item.getAsFile(), // 得到檔案物件
reader = new FileReader();
reader.onload = function() {
upload(reader.result) // 上傳圖片,虛擬碼
}
reader.readAsDataURL(file); // 讀取為 DataURL / Base64
}
}
複製程式碼
處理複製 ( copy )
快捷鍵及右鍵選單觸發
常見應用場景是攔截複製操作,對複製內容加工後再寫入剪貼簿,例如在知乎複製大段文字後加入版權資訊。
El.addEventListener(`copy`, event => {
event.preventDefault();
let clipboardData = event.clipboardData || window.clipboardData,
text = window.getSelection().toString();
// ... 對 text 進行一系列處理操作
clipboardData.setData(`text/plain`, text); // 將處理好的 text 寫入剪貼簿
clipboardData.setData(`text`, text); // IE 相容
})
複製程式碼
程式設計式複製
雖是程式設計式複製,但無法做到完全自動複製,需要使用者先觸發 點選 事件。clipboard.js 庫及紅包口令自動複製(在 document 上監聽 click 事件)主要就通過此方法實現的。
固定內容
Button.addEventListener(`click`, event => {
let sometext = `紅包碼 xxxxxx`,
hiddenInput = document.createElement(`input`);
hiddenInput.value = sometext;
hiddenInput.setAttribute(`readonly`, ``);
hiddenInput.style.position = `absolute`;
hiddenInput.style.left = `-9999px`;
document.body.appendChild(hiddenInput);
hiddenInput.select();
hiddenInput.setSelectionRange(0, hiddenInput.value.length); // ios
document.execCommand(`copy`);
document.body.removeChild(hiddenInput);
})
複製程式碼
複製某個元素內的文字
Button.addEventListener(`click`, event => {
let someElement = document.getElementById(`someelement`),
selection = window.getSelection(),
range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
document.execCommand(`copy`);
})
複製程式碼
可以發現,目前對剪貼簿的操作真是千奇百怪,希望新的 API 快快成為現實吧(
做夢)!
參考
[1] Clipboard API and events | w3c
[3] 利用 javascript 實現富文字編輯器 | 掘金
[4] 富文字編輯器初探 | 掘金