Web 一鍵複製與粘貼

在最近的 Web 開發中, 有遇到使用Clipboard的場景。即在 B 側 Web 業務中, 對於複雜頁面的配置, 但願提供複製粘貼功能。 思考了幾種方案:javascript

  • 依賴後臺接口, 新增數據 從需求角度來說, 比較簡單的方案就是調用後臺接口, 生成一條新數據, 用戶在新增數據上進行修改便可。此方法適用於同一環境(productdevnet)的複製粘貼。前端

  • 前端本地存儲, 新增操做時檢測 在用戶觸發複製行爲時, 將數據存入本地localStorage, 當用戶進行新增操做時, 檢測localStorage是否有已複製數據。因爲是前端保留了複製的數據, 就能夠不用考慮後臺的環境問題, 可使用測試環境現網環境之間的複製粘貼。 但這裏的測試環境與現網環境切換依賴了代理配置。對於經過不一樣域名區分環境的應用(例如, xxx.xx.com or test.xxx.xx.com), localStorage 被同源策略限制, 沒法跨域讀取數據。java

  • 使用 Clipboard 在上述前端本地存儲方案的基礎上, 想到了clipboard的方案。相似於淘口令的方案, 將數據存入 Clipboard, 而後在新增數據時, 檢測 Clipboard 便可。使用 Clipboard 是利用了系統的數據存儲, 也解決了不一樣域名間的跨域問題。web

Clipboard 的寫入

document.execCommand

docment.execCommand是一個能夠操做可編輯內容區域同步方法。api

// 語法
bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument);
複製代碼

方法第一個參數aCommandName傳入命令字, 包括了copy, cut, bold, backColor等操做。方法第二個參數aShowDefaultUI指是否展現用戶界面, 通常不使用這個參數。方法第三個參數aValueArgument是傳入操做命令時的一些額外參數。方法返回了一個 bool 值, 描述操做是否成功。詳細狀況能夠參考MDN跨域

咱們要作的需求是將須要的內容寫入 Clipboard, 使用的也就是上述提到的copy瀏覽器

話很少說, 咱們經過代碼看下如何使用這個功能安全

<div>
    <input type="text" value="Copy Content"/>
    <button onclick="handleClick">copy</button>
</div>
複製代碼
const i = document.querySelector('input');
// 得到焦點
i.select();
document.execCommand('copy');
複製代碼

點擊按鈕, Copy Content就會被寫入剪切板, 以後就能夠將剪切板內容內容複製粘貼到其餘地方了。app

講到這裏, 你們就會好奇, "爲何要用input組件呢?", 固然啦, 其實用textarea也是能夠的。上述提到了可編輯區域, 只有input, textarea或具備contenteditable屬性的元素才能夠被execCommand操做dom

那若是不想頁面中出現可編輯區域, 那能夠怎麼辦呢?

/** * @description 複製內容到剪切板 * @param {string} content 文本內容 */
function copy2Clipboard = content => {

  const dom = document.createElement('input');
  dom.value = content;
  document.body.appendChild(dom);
  dom.select();
  document.execCommand('copy');

  document.body.removeChild(dom);
};
複製代碼

可使用如上取巧方法便可~

navigator.clipboard.writeText

上述document.execCommand是一個同步操做, navigator.clipboard是瀏覽器提供的剪切板 API, 能夠經過此 API 實現剪切板的操做, 各大瀏覽器廠商最近也都支持了navigator.clipboardAPI.

瀏覽器兼容性

那麼如何使用呢? 咱們仍是從代碼一一道來。

navigator.permissions.query({ name: 'clipboard-write' }).then(function(result) {
    // 多是 'granted', 'denied' or 'prompt':
    if (result.state === 'granted') {
        // 可使用權限
        // 進行clipboard的操做
        navigator.clipboard.writeText('Clip Content').then(
            function() {
                /* clipboard successfully set */
                // 成功設置了剪切板
            },
            function() {
                /* clipboard write failed */
                // 剪切板內容寫入失敗
            }
        );
    } else if (result.state === 'prompt') {
        // 彈窗彈框申請使用權限
    } else {
        // 若是被拒絕,請不要作任何操做。
    }
});
複製代碼

剪切板權限申請提示

navigator.permissions是瀏覽器向用戶請求使用敏感接口的 API, 調用接口會向用戶彈出 Prompt 框, 詢問用戶是否可使用相關權限。 上述代碼先查詢請求使用了clipboard-write剪切板的使用權限。 在權限經過以後, 調用了navigator.clipboard.writeText方法。

navigator.clipboardAPI 被計劃用於取代document.execCommand接口, 因此也建議使用clipboardAPI 去進行複製操做

代碼以下:

async function copy2Clipboard(content) {
    const res = await navigator.permissions.query({ name: 'clipboard-write' });
    if (res.state === 'granted') {
        return navigator.clipboard.writeText(content);
    }

    return Promise.reject(res);
}
複製代碼

關注【IVWEB社區】公衆號查看最新技術週刊,作更優秀的本身!


Clipboard 的讀取

document.execCommand

考慮到安全緣由, document.execCommand('paste')操做已經被禁止了。

那有什麼辦法能夠讀取剪切板的內容呢?

監聽 paste 事件

document.addEventListener('paste', event => {
    // 從event中讀取clipboardData
    const pasteContent = (event.clipboardData || window.clipboardData).getData('text');
    // do whatever
});
複製代碼

在本需求場景中, 但願能夠由前端讀取的剪切板內容, 而不是用戶主動觸發, 因此這裏就再也不詳述了。

那還有什麼方案呢?

navigator.clipboard.readText

navigator.clipboard.readText也是瀏覽器提供的剪切板操做 API, 與writeText相似, 也須要請求剪切板權限。

// 申請使用剪切板讀取權限
navigator.permissions.query({ name: 'clipboard-read' }).then(function(result) {
    // 多是 'granted', 'denied' or 'prompt':
    if (result.state === 'granted') {
        // 可使用權限
        // 進行clipboard的操做
        navigator.clipboard
            .readText()
            .then(text => {
                console.log('複製粘貼文本: ', text);
            })
            .catch(err => {
                // 讀取剪切板內容失敗
                console.error('Failed to read clipboard contents: ', err);
            });
    } else if (result.state === 'prompt') {
        // 彈窗彈框申請使用權限
    } else {
        // 若是被拒絕,請不要作任何操做。
    }
});
複製代碼

首先咱們要申請使用剪切板的clipboard-read權限, 在得到用戶權限後, 便可經過navigator.clipboard.readText獲取權限了。

固然監聽paste事件也是能夠的

document.addEventListener('paste', event => {
    event.preventDefault();
    navigator.clipboard.readText().then(text => {
        console.log('Pasted text: ', text);
    });
});
複製代碼

所以, 咱們就能夠將讀取剪切板內容的功能抽象出來:

/** * @description 讀取剪切板內容 * @return {string} */
async function readClipboard() {
    const result = await navigator.permissions.query({ name: 'clipboard-read' });
    if (result.state === 'granted' || result.state === 'prompt') {
        return navigator.clipboard
            .readText()
            .then(text => text)
            .catch(err => Promise.reject(err));
    }
    return Promise.reject(result);
}
複製代碼

總結與注意

清空剪切板內容

瀏覽器並無提供能夠清理剪切板的接口。若是網站在使用完剪切板內容後, 須要進行清理內容的話, 能夠從新寫入數據

// ...
input.value = ' '; // input的值必須有值, 不能是空字符串
input.select();
document.execCommand('copy')
// 或者使用clipboard
navigator.clipboard.writeText('');
複製代碼

安全問題

Web操做剪切板內容具備必定的安全風險。瀏覽器爲了保證用戶隱私, 要求使用navigator.clipboardAPI必需要接入HTTPS

HTTP網站是不支持此接口的, 僅支持document.execCommand('copy')和監聽paste事件

從用戶角度考慮, 也建議你們的網站都接入HTTPS

clipboard的將來

可能會支持更通用的writeread方法, 提供二進制數據的寫入等等(例如圖片等)。

參考

Unblocking Clipboard Access

Cut and Copy Commands