Web 一鍵復制與粘貼


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

  • 依賴后台接口, 新增數據 從需求角度來講, 比較簡單的方案就是調用后台接口, 生成一條新數據, 用戶在新增數據上進行修改即可。此方法適用於同一環境(productdevnet)的復制粘貼。

  • 前端本地存儲, 新增操作時檢測 在用戶觸發復制行為時, 將數據存入本地localStorage, 當用戶進行新增操作時, 檢測localStorage是否有已復制數據。由於是前端保留了復制的數據, 就可以不用考慮后台的環境問題, 可以使用測試環境現網環境之間的復制粘貼。 但這里的測試環境與現網環境切換依賴了代理配置。對於通過不同域名區分環境的應用(例如, xxx.xx.com or test.xxx.xx.com), localStorage 被同源策略限制, 無法跨域讀取數據。

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

Clipboard 的寫入

document.execCommand

docment.execCommand是一個可以操作可編輯內容區域的同步方法。

// 語法
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就會被寫入剪切板, 之后就可以將剪切板內容內容復制粘貼到其他地方了。

講到這里, 大家就會好奇, "為什么要用input組件呢?", 當然啦, 其實用textarea也是可以的。上述提到了可編輯區域, 只有input, textarea或具有contenteditable屬性的元素才可以被execCommand操作

那如果不想頁面中出現可編輯區域, 那可以怎么辦呢?

/**
 * @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


作者:騰訊IVWEB團隊,鏈接:https://juejin.im/post/5dea4292518825122c4c9b8e


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM