請啟用 Javascript 查看內容

JavaScript 剪貼簿複製操作

 ·  ☕ 5 分鐘

JavaScript筆記 目錄

前言

在瀏覽器中,將資料複製到剪貼簿的方法有三種:

  1. Document.execCommand()copy 指令
  2. Clipboard API
  3. ClipboardEventcopypaste 事件

遠古時期還有使用 Flash 來操作剪貼簿,這裡就不討論了。

Document.execCommand()

Document.execCommand() 的 copy 指令能將選取到的內容複製到剪貼簿。

1. 複製操作

執行 document.execCommand('copy') 就可以將當前選取範圍複製到剪貼簿。

1
2
3
4
5
const btnCopy = document.querySelector('.btnCopy');

btnCopy.addEventListener('click', function() {
  document.execCommand('copy');
});

若要顯示當前選取內容值,可以使用 window.getSelection() 表示當前使用者選取的範圍或光標當前位置,回傳 Selection 物件,並使用 + ''toString() 轉字串。

1
2
3
4
5
6
7
const btnCopy = document.querySelector('.btnCopy');

btnCopy.addEventListener('click', function() {
  document.execCommand('copy');
  const selection = window.getSelection();
  alert(selection.toString());
});

2. 複製輸入框內容

document.execCommand('copy') 的缺點就是只能複製選取的內容,如果我們要複製 <textarea><input type="text"> 的內容值,可以使用 select() 選取內容值,再執行 document.execCommand('copy')

1
2
3
4
5
6
7
const btnCopy = document.querySelector('.btnCopy');

btnCopy.addEventListener('click', function() {
 const inputText = document.querySelector('.inputText');
  inputText.select();
  document.execCommand('copy');
});

select() 在 iOS 無反應,使用 setSelectionRange 即可:

1
2
inputText.select();
inputText.setSelectionRange(0, element.value.length);

input.select() does not work on iOS

3. 複製指定元素文字

如果我們要複製某一個元素的文字內容,就需要建立選取範圍。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const btnCopy = document.querySelector('.btnCopy');

btnCopy.addEventListener('click', function() {
  // 建立 Range 物件
  const range = document.createRange();
  // 將指定元素內容加到 Range 中
  const texts = document.querySelector('.texts');
  range.selectNode(texts);
  // 取得 Selection 物件
  const selection = window.getSelection();
  // 先清空當前選取範圍
  selection.removeAllRanges();
  // 加入 Range 
  selection.addRange(range);
  
  document.execCommand('copy');
  selection.removeAllRanges();
});

4. 複製任意內容

如果要將任意內容複製到剪貼簿,需要先建立 <textarea> 元素,並將指定內容設為 value,再插入 DOM 樹中,呼叫 select() 與執行複製,最後刪除 <textarea> 元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const btnCopy = document.querySelector('.btnCopy');

btnCopy.addEventListener('click', function() {
  const value = 'hello!';
  const el = document.createElement('textarea');
  el.value = value;
  document.body.appendChild(el);
  el.select();
  document.execCommand('copy');
  document.body.removeChild(el);
});

優缺點

優點:

  • 基本上支援所有主流瀏覽器。

Document API: execCommand: copy - Can I use

缺點:

  • 只能複製選取中的內容,複製任意內容需要建立臨時的 <textarea> 可能導致畫面閃爍,而且還會取消使用者當前選取的內容。
  • 同步操作,若複製大量資料,可能導致頁面卡頓。

Clipboard API

Clipboard API 是新一代的剪貼簿操作方法,被設計用來取代 document.execCommand(),能直接訪問剪貼簿,為非同步操作,所有操作方法都會回傳 Promise 物件。

1. Clipboard 物件

navigator.clipboard 會回傳 Clipboard 物件,所有操作都是透過它進行。

1
const cb = navigator.clipboard;

navigator.clipboard 回傳 undefined,表示當前瀏覽器不支援。

2. 安全和權限

直接訪問剪貼簿的操作其實並不安全,因此 Clipboard API 僅支持通過 HTTPS 提供的頁面(開發環境 localhost 例外),且必須使用者授權才能訪問。

若是在 <iframe> 中運行,父頁面必須授予 clipboard-read(讀權限) 或 clipboard-write(寫權限)。

1
<iframe allow="clipboard-read; clipboard-write"></iframe>

另外,我們可以使用 Permissions APIPermissions.query() 查看是否有權訪問剪貼簿。

3. 操作方法

  • readText():讀取剪貼簿純文字內容。
  • writeText():對剪貼簿寫入純文字內容。
  • read():讀取剪貼簿複合內容。
  • write():對剪貼簿寫入複合內容。

以上方法皆會回傳一個 Promise 物件,讀取方法有結果值可以接收,若使用者拒絕授權,會拋出 NotAllowedError。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// 讀
navigator.clipboard.readText()
  .then((text) => {
    console.log(text);
  })
  .catch((err) => {
    console.error(err);
  });

// 寫

const text = 'Hello!';
navigator.clipboard.writeText(text)
  .then(() => {
    console.log('內容已複製');
  })
  .catch((err) => {
    console.error(err);
  });

也可以使用 async/await 語法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
async function readFromClipboard() {
  try {
    const text = await navigator.clipboard.readText();
    console.log(text);
  } catch (error) {
    console.error(error);
  }
}

async function writeToClipboard(text) {
  try {
    await navigator.clipboard.writeText(text);
    console.log('內容已複製');
  } catch (error) {
    console.error(error);
  }
}

優缺點

優點:

  • 操作簡單,能直接訪問剪貼簿,不需要像 document.execCommand('copy') 需要有選取範圍才能使用。
  • 非同步操作。

缺點:

  • 需要使用者授權。
  • 瀏覽器支援度不佳。

Clipboard API: readText- Can I use

Clipboard API: writeText- Can I use

Clipboard API: read- Can I use

Clipboard API: write- Can I use

ClipboardEvent

ClipboardEvent 介面描述了與修改剪貼簿相關的事件。當使用者執行剪貼簿相關的操作,我們可以透過監聽 cutcopypaste,攔截事件以進行操作。

cutcopypaste 這些事件僅在當前頁面上操作時觸發。

這些事件的 event 物件中的 clipboardData 屬性 ,有三個方法可以使用:

  • clearData():清除剪貼簿資料,可指定資料類型,若不指定則清除全部。
  • getData():取得剪貼簿資料,需要指定資料類型。
  • setData():修改剪貼簿資料,需要指定資料類型。

1. 複製操作

當我們執行複製操作時,可以監聽 copy 事件,將指定內容放入剪貼簿。

1
2
3
4
document.addEventListener('copy', function(event) {
  event.preventDefault();
  event.clipboardData.setData('text/plain', 'Hello!');
});

我們有時候複製某些網站的內容時,會自動加上版權出處:

1
2
3
4
5
6
7
document.addEventListener('copy', function(event) {
  const text = window.getSelection().toString();
  if (text.length > 15) {
    event.preventDefault();
    event.clipboardData.setData('text/plain', '來源:竹白記事本\n' + text);
  }
});

Clipboard.js

Clipboard.js 是一個將文字複製到剪貼簿的 JavaScript 函式庫,操作簡單、輕量、不依賴其他框架,依賴 Selection APIexecCommand API 支持主流瀏覽器。

安裝:

# NPM 管理
npm install clipboard --save

# 引入 CDN
https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.6/clipboard.min.js

1. 基本用法

建立實體,傳入要綁定的元素:

1
const clipboard = new ClipboardJS('.btn');

可以是 DOM 選擇器HTML 元素HTML 元素陣列

首先,若要複製當前的內容,可以使用 data-clipboard-text 設定內容值: 

1
<button class="btn" data-clipboard-text="hello!">copy</button>

如果要從其他元素取值,可以透過 data-clipboard-target 指定目標元素:

1
2
<input id="foo" type="text" value="https://chupai.github.io/">
<button class="btn" data-clipboard-target="#foo">copy</button> 

目標為 <input><textarea> 可以複製內容值,若是 其他元素,例如 <div> 則複製元素中的文字。

預設操作為 copy 複製,使用 data-clipboard-action 可以設置 cut 剪下。

1
2
<input id="foo" type="text" value="https://chupai.github.io/">
<button class="btn" data-clipboard-target="#foo" data-clipboard-action="cut">cut</button>

但只對 <input><textarea> 有作用。

2. 事件處理

如果想要在剪貼簿操作完成後執行其他操作,可以使用 successerror 自定義事件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const 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);
});

3. 進階用法

若不希望修改 HTML 程式碼(也就是使用 data-),建立實體時,還可以傳入一個選項物件。

target 選項為一個函式,需要回傳一個 Node,作為目標元素:

1
2
3
4
5
6
const clipboard = new ClipboardJS('.btn', {
  target(trigger) {
    // trigger 參數為觸發元素 Node
    return document.querySelector('#foo');
  }
});

text 選項為一個函式,需要回傳一個字串,作為複製內容:

1
2
3
4
5
6
const clipboard = new ClipboardJS('.btn', {
  text(trigger) {
    // trigger 參數為觸發元素 Node
    return 'Hello!';
  }
});

container 選項能傳入獲得焦點的元素:

1
2
3
const clipboard = new ClipboardJS('.btn', {
  container: document.getElementById('modal'),
});

4. 清除

若使用 SPA 單頁應用程式,想更精確管理 DOM 生命週期,想清除事件、實體,可以使用 destroy()

1
2
const clipboard = new ClipboardJS('.btn');
clipboard.destroy();

參考文獻


竹白
作者
竹白
前端筆記

文章目錄