web一鍵復(fù)制與粘貼

本文節(jié)選至https://mp.weixin.qq.com/s?__biz=MzIzNjcwNzA2Mw==&mid=2247486973&idx=1&sn=2bff5a7321f25af822306e9386727f6b&chksm=e8d28065dfa50973c350892ff2c98bec155d7622967c8fcfaa45eba5540a9f03a6c2e216e065&mpshare=1&scene=1&srcid=&sharer_sharetime=1575630540373&sharer_shareid=0756610ab67db8a88654263d0b215c95&exportkey=CFdXhh9udMqbF3pXEUAtgLg%3D&pass_ticket=ofUBk1VeYf%2BvkUVA66BU8dwqSMgwAyoNP5MkdxkzFBsJ81%2BzHBZa4%2Bn1BXcFsTXs#rd

在Web中, 剪切板的API曾被禁止使用, 如今又開放了相關(guān)接口, 讓我們一起探索吧撼嗓。

在最近的 Web 開發(fā)中, 有遇到使用Clipboard的場景。即在 B 側(cè) Web 業(yè)務(wù)中, 對于復(fù)雜頁面的配置, 希望提供復(fù)制粘貼功能镶奉。思考了幾種方案:

  • 依賴后臺接口, 新增數(shù)據(jù)

    從需求角度來講, 比較簡單的方案就是調(diào)用后臺接口, 生成一條新數(shù)據(jù), 用戶在新增數(shù)據(jù)上進行修改即可阅懦。此方法適用于同一環(huán)境(productdevnet)的復(fù)制粘貼亏推。

  • 前端本地存儲, 新增操作時檢測

    在用戶觸發(fā)復(fù)制 行為時, 將數(shù)據(jù)存入本地localStorage, 當用戶進行新增 操作時, 檢測localStorage是否有已復(fù)制數(shù)據(jù)。由于是前端保留了復(fù)制 的數(shù)據(jù), 就可以不用考慮后臺的環(huán)境問題, 可以使用測試環(huán)境現(xiàn)網(wǎng)環(huán)境之間的復(fù)制粘貼。但這里的測試環(huán)境與現(xiàn)網(wǎng)環(huán)境切換依賴了代理配置咏连。對于通過不同域名區(qū)分環(huán)境的應(yīng)用(例如, https://xxx.xx.com or https://test.xxx.xx.com), localStorage 被同源策略限制, 無法跨域讀取數(shù)據(jù)敏弃。

  • 使用 Clipboard

    在上述前端本地存儲方案的基礎(chǔ)上, 想到了clipboard的方案卦羡。類似于淘口令的方案, 將數(shù)據(jù)存入 Clipboard, 然后在新增數(shù)據(jù)時, 檢測 Clipboard 即可。使用 Clipboard 是利用了系統(tǒng)的數(shù)據(jù)存儲, 也解決了不同域名間的跨域問題。

Clipboard 的寫入

document.execCommand

docment.execCommand是一個可以操作可編輯內(nèi)容區(qū)域同步方法绿饵。

// 語法
bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument);

方法第一個參數(shù)aCommandName傳入命令字, 包括了copy, cut, bold, backColor等操作欠肾。方法第二個參數(shù)aShowDefaultUI指是否展示用戶界面, 一般不使用這個參數(shù)。方法第三個參數(shù)aValueArgument是傳入操作命令時的一些額外參數(shù)拟赊。方法返回了一個 bool 值, 描述操作是否成功刺桃。詳細情況可以參考MDN(https://developer.mozilla.org/zh-CN/docs/Web/API/Document/execCommand

我們要做的需求是將需要的內(nèi)容寫入 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就會被寫入剪切板, 之后就可以將剪切板內(nèi)容內(nèi)容復(fù)制粘貼到其他地方了。

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

那如果不想頁面中出現(xiàn)可編輯區(qū)域, 那可以怎么辦呢?


/**
 * @description 復(fù)制內(nèi)容到剪切板
 * @param {string} content 文本內(nèi)容
 */
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 實現(xiàn)剪切板的操作, 各大瀏覽器廠商最近也都支持了navigator.clipboardAPI.

image

<figcaption style="margin: 5px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; text-align: center; color: rgb(136, 136, 136); font-size: 14px;">瀏覽器兼容性</figcaption>

那么如何使用呢? 我們還是從代碼一一道來。

navigator.permissions.query({ name: 'clipboard-write' }).then(function(result) {
    // 可能是 'granted', 'denied' or 'prompt':
    if (result.state === 'granted') {
        // 可以使用權(quán)限
        // 進行clipboard的操作
        navigator.clipboard.writeText('Clip Content').then(
            function() {
                /* clipboard successfully set */
                // 成功設(shè)置了剪切板
            },
            function() {
                /* clipboard write failed */
                // 剪切板內(nèi)容寫入失敗
            }
        );
    } else if (result.state === 'prompt') {
        // 彈窗彈框申請使用權(quán)限
    } else {
        // 如果被拒絕欢搜,請不要做任何操作封豪。
    }
});
image

<figcaption style="margin: 5px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; text-align: center; color: rgb(136, 136, 136); font-size: 14px;">剪切板權(quán)限申請?zhí)崾?br> </figcaption>

navigator.permissions是瀏覽器向用戶請求使用敏感接口的 API, 調(diào)用接口會向用戶彈出 Prompt 框, 詢問用戶是否可以使用相關(guān)權(quán)限。上述代碼先查詢請求使用了clipboard-write剪切板的使用權(quán)限炒瘟。在權(quán)限通過之后, 調(diào)用了navigator.clipboard.writeText方法吹埠。

navigator.clipboardAPI 被計劃用于取代document.execCommand接口, 所以也建議使用clipboardAPI 去進行復(fù)制操作

代碼如下:

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);
}

Clipboard 的讀取

document.execCommand

考慮到安全原因, document.execCommand('paste')操作已經(jīng)被禁止了疮装。

那有什么辦法可以讀取剪切板的內(nèi)容呢?

監(jiān)聽 paste 事件

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

在本需求場景中, 希望可以由前端讀取的剪切板內(nèi)容, 而不是用戶主動觸發(fā), 所以這里就不再詳述了缘琅。

那還有什么方案呢?

navigator.clipboard.readText

navigator.clipboard.readText也是瀏覽器提供的剪切板操作 API, 與writeText類似, 也需要請求剪切板權(quán)限。


// 申請使用剪切板讀取權(quán)限
navigator.permissions.query({ name: 'clipboard-read' }).then(function(result) {
    // 可能是 'granted', 'denied' or 'prompt':
    if (result.state === 'granted') {
        // 可以使用權(quán)限
        // 進行clipboard的操作
        navigator.clipboard
            .readText()
            .then(text => {
                console.log('復(fù)制粘貼文本: ', text);
            })
            .catch(err => {
                // 讀取剪切板內(nèi)容失敗
                console.error('Failed to read clipboard contents: ', err);
            });
    } else if (result.state === 'prompt') {
        // 彈窗彈框申請使用權(quán)限
    } else {
        // 如果被拒絕廓推,請不要做任何操作刷袍。
    }
});

首先我們要申請使用剪切板的clipboard-read權(quán)限, 在獲得用戶權(quán)限后, 即可通過navigator.clipboard.readText獲取權(quán)限了。

當然監(jiān)聽paste事件也是可以的

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

因此, 我們就可以將讀取剪切板內(nèi)容的功能抽象出來:

/**
 * @description 讀取剪切板內(nèi)容
 * @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);
}

總結(jié)與注意

清空剪切板內(nèi)容

瀏覽器并沒有提供可以清理剪切板的接口樊展。如果網(wǎng)站在使用完剪切板內(nèi)容后, 需要進行清理內(nèi)容的話, 可以重新寫入數(shù)據(jù)

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

安全問題

Web操作剪切板內(nèi)容具有一定的安全風險呻纹。瀏覽器為了保證用戶隱私, 要求使用navigator.clipboardAPI必須要接入HTTPS

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

從用戶角度考慮, 也建議大家的網(wǎng)站都接入HTTPS

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末专缠,一起剝皮案震驚了整個濱河市雷酪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涝婉,老刑警劉巖哥力,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異墩弯,居然都是意外死亡吩跋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進店門渔工,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锌钮,“玉大人,你說我怎么就攤上這事涨缚≡冢” “怎么了?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵脓魏,是天一觀的道長兰吟。 經(jīng)常有香客問我,道長茂翔,這世上最難降的妖魔是什么混蔼? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任犬耻,我火速辦了婚禮昭抒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缺亮。我一直安慰自己悔政,他們只是感情好晚吞,可當我...
    茶點故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谋国,像睡著了一般槽地。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芦瘾,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天捌蚊,我揣著相機與錄音,去河邊找鬼近弟。 笑死缅糟,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的祷愉。 我是一名探鬼主播窗宦,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谣辞!你這毒婦竟也來了迫摔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤泥从,失蹤者是張志新(化名)和其女友劉穎句占,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躯嫉,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡纱烘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了祈餐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片擂啥。...
    茶點故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖帆阳,靈堂內(nèi)的尸體忽然破棺而出哺壶,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布山宾,位于F島的核電站至扰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏资锰。R本人自食惡果不足惜敢课,卻給世界環(huán)境...
    茶點故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绷杜。 院中可真熱鬧直秆,春花似錦、人聲如沸鞭盟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽齿诉。三九已至疫稿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鹃两,已是汗流浹背遗座。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俊扳,地道東北人途蒋。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像馋记,于是被迫代替她去往敵國和親号坡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內(nèi)容