JS67 複製到剪貼板

Clipboard API

Clipboard API的接口提供了一種讀寫操作系統剪貼板的方式,它有四個方法,分別是:

  • read,從剪貼板讀取數據,比如圖片
  • readText,從剪貼板讀取文本
  • write,寫入數據(比如圖片)到操作系統剪貼板
  • writeText,寫入文本到剪貼板

我的需求是實現將文字寫入到剪貼板,使用writeText方法即可:

const promise = navigator.clipboard.writeText(newClipText)

clipboard是掛在navigator對象下的,接受一個字符串作爲參數,返回一個Promise。如果寫入成功就會被resolve,如果沒有權限則會被reject

很簡單,但是它的兼容性不怎麼樣:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Qod8RyMA-1575537196910)(http://image.oldzhou.cn/Fjk7CltiHWAv8h8aTged4jvu3s6q)]

可以看出,IE、Edge、Safari全軍覆沒,即便FireFox和Chrome也都是很新的版本才支持,所以在生產環境上這個API肯定是不能完全勝任的。

document.execCommand

這個方法可以用來操作可編輯區域(比如鼠標選擇的文本)的內容,實現富文本功能時大多會用到這個方法:

bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)

返回值是一個Boolean,如果是false代表操作不被支持。MDN上提示:注意不要用這個返回值去校驗瀏覽器的兼容性(不太明白原理,如此驗證會導致性能問題還是報錯?既然不用來驗證,返回值還有什麼用?

它有三個參數:

(1)第一個參數是一個字符串,是要執行的命令的名稱。能夠執行的命令非常多,不僅僅包括我們後面會用到的將選中內容複製到剪貼板的copy方法,還有例如fontSizeinsertImagecut等方法,具體可以參考命令列表

(2)第二個參數是一個Boolean,表示是否展示用戶界面,一般爲false,因爲Mozilla沒有實現這個功能

(3)第三個參數是給一些命令使用的額外的參數,默認爲null

想要用它實現複製輸入框內容,必須有可編輯區域,比較常用的就是input(或者是contenteditabletrue的區域),同時必須將可編輯區域中的內容,如果使用的容器是input,那麼需要讓inputfocus,然後通過inputselect方法選中輸入框中的內容(如果是contenteditabletrue時則需要使用window.getSelection獲得用戶選擇的文本範圍或光標位置)

在我們的需求中,可以利用現成的input來當做容器,但是如果是作爲通用的方法,如果用戶雙擊一段文本後複製,那麼就需要自己動手創建一個input來實現。但是這個input應該是隱藏的,不應該被用戶發現,但是如果使用dispaly: none是不行的,這樣是沒有辦法獲取到input中的文本(用type="hidden"也不行),所以我採用的是絕對定位,把input定位到屏幕外,眼不見心不煩。

結合上面的Clipboard API,實現了一個公共方法(其中的Message是一個用來提示的方法)

/**
 * 將文字複製到系統剪切板
 * @param { string } text 複製到剪切板的文字
 */
export function copyTextToClipboard(text) {
  // 支持 Clipboard API 並且在安全環境下(localhost 或者 https)
  if (navigator && navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
    navigator.clipboard.writeText(text).then(() => {
      Message({
        message: '內容已複製到剪切板',
        type: 'success',
        duration: 1000,
      });
    }).catch(() => {
      // 靜默失敗
    });
  } else if (document && document.execCommand && typeof document.execCommand === 'function') {
    // 使用 execCommand 的後退方案
    const input = document.createElement('input');
    input.style.cssText = 'position: absolute; left: -9999px; top: -9999px; z-index: -9999;';
    input.readOnly = true;
    document.body.appendChild(input);
    input.value = text;
    input.focus();
    input.select();
    const isSucceed = document.execCommand('copy', true);
    document.body.removeChild(input);
    if (isSucceed) {
      Message({
        message: '內容已複製到剪切板',
        type: 'success',
        duration: 1000,
      });
    } else {
      // 靜默失敗
    }
  }
}

document.execCommand的兼容性還是相當不錯的:

所以上面的方法基本上可以滿足生產環境的要求了。

clipboard.js

實際上有現成的庫可以使用,那就是clipboard.js,官網在這裏,Github的Star數和NPM的下載數都很高,並且壓縮後體積只有3KB,直接使用應該是沒有什麼問題的。

它可以從一個元素複製、剪切文本,也可以從性中來複制文本,比如在Vue中我們可以直接使用在一個Button元素上,通過v-bind綁定一個響應式數據在data-clipboard-text屬性上,就可以實現點擊Button進行復制的功能

<button class="btn" :data-clipboard-text="copyValue">Copy</button>

它還有對應的成功或者失敗的回調函數:

var clipboard = new ClipboardJS('.btn');

clipboard.on('success', function(e) {
    console.info('Action:', e.action);
    console.info('Text:', e.text);
    console.info('Trigger:', e.trigger);

    e.clearSelection();
});

clipboard.on('error', function(e) {
    console.error('Action:', e.action);
    console.error('Trigger:', e.trigger);
});

更多的細節和高級用法可以參考它的官網

它主要使用就是SelectionexeceCommand這兩個API,所以兼容性也是很不錯的:

它的源碼也不是很複雜,可以閱讀這篇文章對源碼的解讀,其實自己去讀一下也是可以的。

總結

所以,如果需要實現的功能不太複雜,那麼上面自己封裝的函數應該可以滿足需求,如果需要頻繁使用或者需要一些高級功能,那麼可以直接使用clipboard.js

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章