文:Unblocking Clipboard Accesshtml
在過去的幾年裏咱們只能使用 document.execCommand
來操做剪貼板。不過,這種操做剪貼板的操做是同步的,而且只能讀取和寫入 DOM。java
如今 Chrome 66 已經支持了新的 Async Clipboard API,做爲 execCommand
替代品。git
這個新的 Async Clipboard API 還可使用 Promise 來簡化剪貼板事件並將它們與 Drag-&-Drop API 一塊兒使用。github
演示視頻:zhuanlan.zhihu.com/p/34698155web
writeText()
能夠把文本寫入剪切板。writeText()
是異步的,它返回一個 Promise:chrome
navigator.clipboard.writeText('要複製的文本')
.then(() => {
console.log('文本已經成功複製到剪切板');
})
.catch(err => {
// This can happen if the user denies clipboard permissions:
// 若是用戶沒有受權,則拋出異常
console.error('沒法複製此文本:', err);
});
複製代碼
還可使用異步函數 的 async
和 await
:api
async function copyPageUrl() {
try {
await navigator.clipboard.writeText(location.href);
console.log('Page URL copied to clipboard');
} catch (err) {
console.error('Failed to copy: ', err);
}
}
複製代碼
和複製同樣,能夠經過調用 readText()
從剪貼板中讀取文本,該函數也返回一個 Promise:瀏覽器
navigator.clipboard.readText()
.then(text => {
console.log('Pasted content: ', text);
})
.catch(err => {
console.error('Failed to read clipboard contents: ', err);
});
複製代碼
爲了保持一致性,下面是等效的異步函數:安全
async function getClipboardContents() {
try {
const text = await navigator.clipboard.readText();
console.log('Pasted content: ', text);
} catch (err) {
console.error('Failed to read clipboard contents: ', err);
}
}
複製代碼
有計劃推出檢測剪貼板更改的新事件,但如今最好使用「粘貼」事件。它很適合用於閱讀剪貼板文本的新異步方法:app
document.addEventListener('paste', event => {
event.preventDefault();
navigator.clipboard.readText().then(text => {
console.log('Pasted text: ', text);
});
});
複製代碼
剪貼板訪問一直爲瀏覽器帶來安全問題。若是沒有適當的權限,頁面可能會悄悄地將全部惡意內容複製到用戶的剪貼板,粘貼時會產生災難性的結果。想象一下,一個網頁,靜靜地複製 rm -rf /
或解壓縮炸彈圖像到剪貼板。
讓網頁不受限制地讀取剪貼板更加麻煩。用戶常常將敏感信息(如密碼和我的詳細信息)複製到剪貼板,而後能夠經過任何頁面閱讀,而用戶根本沒法察覺。
與許多新的 API 同樣,navigator.clipboard
僅支持經過 HTTPS 提供的頁面。爲了防止濫用,只有當頁面處於活動選項卡時才容許剪貼板訪問。活動選項卡中的頁面能夠在不請求權限的狀況下寫入剪貼板,但從剪貼板中讀取始終須要權限。
爲了更容易,複製和粘貼的兩個新權限已添加到 Permissions API 中。當頁面處於活動選項卡時,clipboard-write 權限會自動授予頁面。當您經過從剪貼板中讀取數據時,則必需要求獲取 clipboard-read 權限。
{ name: 'clipboard-read' }
{ name: 'clipboard-write' }
複製代碼
與使用權限 API 的任何其它內容同樣,能夠檢查您的應用是否具備與剪貼板交互的權限:
navigator.permissions.query({
name: 'clipboard-read'
}).then(permissionStatus => {
// permissionStatus.state 的值是 'granted'、'denied'、'prompt':
console.log(permissionStatus.state);
// 監聽權限狀態改變事件
permissionStatus.onchange = () => {
console.log(permissionStatus.state);
};
});
複製代碼
如下是剪貼板 API 的「異步」部分真正派上用場的地方:嘗試讀取或寫入剪貼板數據將自動提示用戶得到權限(若是還沒有授予)。因爲 API 是基於 Promise 的,因此若是用戶拒絕剪貼板權限時,Promise 將被 reject,所以頁面能夠適當地做出響應。
由於只有當頁面是當前活動選項卡時,Chrome 才容許剪貼板訪問,所以若是直接粘貼到 DevTools 中,則會發現這裏的一些示例運行不正確,由於此時 DevTools 自己是活動選項卡(頁面不是活動選項卡)。有一個技巧:咱們須要使用 setTimeout 推遲剪貼板訪問,而後在調用函數以前快速單擊頁面內部以使頁面獲取焦點:
setTimeout(async () => {
const text = await navigator.clipboard.readText();
console.log(text);
}, 2000);
複製代碼
在引入異步剪貼板 API 以前,咱們在 Web 瀏覽器中混合了不一樣的複製和粘貼實現。
在大多數瀏覽器中,可使用 document.execCommand('copy')
和觸發瀏覽器本身的複製和粘貼 document.execCommand('paste')
。若是要複製的文本是不存在於 DOM 中的字符串,咱們必須將其插入到 DOM 中並選擇它:
button.addEventListener('click', e => {
const input = document.createElement('input');
document.body.appendChild(input);
input.value = text;
input.focus();
input.select();
const result = document.execCommand('copy');
if (result === 'unsuccessful') {
console.error('Failed to copy text.');
}
})
複製代碼
一樣,如下是您如何在不支持新的 Async Clipboard API 的瀏覽器中處理粘貼的內容:
document.addEventListener('paste', e => {
const text = e.clipboardData.getData('text/plain');
console.log('Got pasted text: ', text);
})
複製代碼
在 Internet Explorer 中,咱們也能夠經過 window.clipboardData
訪問剪貼板。若是在用戶手勢內進行訪問(例如點擊事件) - 以負責任的方式請求權限的一部分 - 則不顯示權限提示。
在支持全部瀏覽器的同時,使用功能檢測來利用異步剪貼板是個不錯的主意。您能夠經過檢查 navigator.clipboard
來檢測對 Async Clipboard API 的支持:
document.addEventListener('paste', async e => {
let text;
if (navigator.clipboard) {
text = await navigator.clipboard.readText()
}
else {
text = e.clipboardData.getData('text/plain');
}
console.log('Got pasted text: ', text);
});
複製代碼
正如你可能已經注意到的那樣,這篇文章只涵蓋了 navigator.clipboard
的文本部分。規範中有更多的通用 read()
和 write()
方法,可是這些會帶來額外的實現複雜性和安全性問題(請記住那些圖像炸彈?)。目前,Chrome 正在推出更簡單的 API 文本部分。