Today's Question:  What does your personal desk look like?        GIVE A SHOUT

Using JavaScript to operate clipboard

  sonic0002        2021-01-23 23:23:34       3,247        0    

Browsers allow JavaScript to read and write data on clipboard. Generally script should not modify user's clipboard to avoid impacting user expectation, but there are cases where this can indeed bring convenience to users. For example, for some code snippet, user can copy it to clipboard with one click instead of select and copy manually.

There are three options for clipboard operation provided in JavaScript/browser:

  1. document.execCommand()
  2. Asynchronous Clipboard API
  3. copy and paste events

This post will briefly talk about these three options.

document.execCommand()

This is the early option for operating clipboard data through script. It is supported by all modern browsers.  It supports copy, cut and paste.

document.execCommand('copy')// copy
document.execCommand('cut')// cut
document.execCommand('paste')// paste

copy

When doing copy, first select the text to be copied and then execute copy command, the selected text will be copied to clipboard.

const inputElement = document.querySelector('#input');
inputElement.select();
document.execCommand('copy');

Note this copy operation is suggested to be put in event handler logic as some browsers would throw error or warning if the copy is not triggered by a user event for security consideration.

paste

When execute paste command, the content in the clipboard will be pasted into the element with focus.

const pasteText = document.querySelector('#output');
pasteText.focus();
document.execCommand('paste');

Although document.execCommand() is easy to use, it does have some drawbacks.

  • It can only copy the selected content, cannot copy arbitrary content
  • It is a synchronous operation, if the content to be copied is very large and complex, it may block the other operations and cause lagging on page response.

Asynchronous Clipboard API

Clipboard API is the next generation clipboard operation option. It is more powerful than document.execCommand(). All its operations are asynchronous and a Promise would be returned and would not cause the page to be unresponsive. In addition, it supports copying any content including images.

navigator.clipboard will refer to the Clipboard object and all operations are happening with this object.

const clipboardObj = navigator.clipboard;

If nagivator.clipboard returns undefined, it means the browser doesn't support Clipboard API.

Since this API supports copying any content, there is risk where secrets like password would be copied as well, hence there are more security restrictions enforced by browsers to use this API. 

For example, in Chrome, this API can be used only if the site is HTTPS encrypted(although it can be used without HTTPS if the host is localhost for local testing purpose). 

Also, before the API can be called, there are some permissions need to be granted. There are two major clipboard related permissions: clipboard-write and clipboard-read. Write permission would be granted to the script automatically, however read permission needs to be explicitly requested. Normally there would be some pop up asking for permission granting.

One more note for developers, the script will always read content from current page. Hence in local development, when developer tool console is open,  it may cause problem when copying the script and run in developer console as the current page is developer console which doesn't support DOM which is needed by Clipboard API.

(async () => {
  const text = await navigator.clipboard.readText();
  console.log(text);
})();

Hence normally can add setTimeout() and clip on the webpage once copied the script to developer console and execute so that the current page is focusing on the webpage.

setTimeout(async () => {
  const text = await navigator.clipboard.readText();
  console.log(text);
}, 2000);

The Clipboard object provides 4 functions.

Clipboard.readText()

Clipboard.readText() is used to copy text content to clipboard.

document.body.addEventListener(
  'click',
  async (e) => {
    const text = await navigator.clipboard.readText();
    console.log(text);
  }
)

When user clicks the page, it will try to copy the content. The browser may prompt a dialog asking for whether allowing this operation. If user chooses not allow, an error would be thrown. To handle this, can add try catch.

async function getClipboardContents() {
  try {
    const text = await navigator.clipboard.readText();
    console.log('Pasted content: ', text);
  } catch (err) {
    console.error('Failed to read clipboard contents: ', err);
  }
}

Clipboard.read()

Clipboard.read() is used to copy all kinds of data to clipboard including text, images and other binary data. This function would return a Promise, once the promise is resolved, it would return a data array which contains ClipboardItem instances.

async function getClipboardContents() {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      for (const type of clipboardItem.types) {
        const blob = await clipboardItem.getType(type);
        console.log(URL.createObjectURL(blob));
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
}

Clipboard.writeText()

Clipboard.writeText() is to write text to clipboard.

document.body.addEventListener(
  'click',
  async (e) => {
    await navigator.clipboard.writeText('Yo')
  }
)

This function doesn't require user's permission. But for best practice, can put the logic in try catch as well.

async function copyPageUrl() {
  try {
    await navigator.clipboard.writeText(location.href);
    console.log('Page URL copied to clipboard');
  } catch (err) {
    console.error('Failed to copy: ', err);
  }
}

Clipboard.write()

Clipboard.write() is used to write binary data to clipboard. It takes ClipboardItem as input.

try {
  const imgURL = 'https://dummyimage.com/300.png';
  const data = await fetch(imgURL);
  const blob = await data.blob();
  await navigator.clipboard.write([
    new ClipboardItem({
      [blob.type]: blob
    })
  ]);
  console.log('Image copied.');
} catch (err) {
  console.error(err.name, err.message);
}

copy event

When user copies content to clipboard, there would be a copy event triggered.  Below snippet would convert the copied content to uppercase.

const source = document.querySelector('.source');

source.addEventListener('copy', (event) => {
  const selection = document.getSelection();
  event.clipboardData.setData('text/plain', selection.toString().toUpperCase());
  event.preventDefault();
});

clipboardData of event object has different properties and functions.

Event.clipboardData.setData(type, data):// set clipboard data, need to provide data type
Event.clipboardData..getData(type):// get clipboard data
Event.clipboardData.clearData([type]):// clear clipboard data, if type is not provided, will clear all types of data
Event.clipboardData.items:// contains all clipboard items(usually has only 1 item)

Below script will intercept the default copy behavior and put specified content in clipboard.

const clipboardItems = [];

document.addEventListener('copy', async (e) => {
  e.preventDefault();
  try {
    let clipboardItems = [];
    for (const item of e.clipboardData.items) {
      if (!item.type.startsWith('image/')) {
        continue;
      }
      clipboardItems.push(
        new ClipboardItem({
          [item.type]: item,
        })
      );
      await navigator.clipboard.write(clipboardItems);
      console.log('Image copied.');
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
});

When user pastes data, a paste event would be triggered as well.

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const text = await navigator.clipboard.readText();
  console.log('Pasted text: ', text);
});

Reference: å‰ªè´´æ¿æ“ä½œ Clipboard API 教程 - 阮一峰的网络日志 (ruanyifeng.com)

NAVIGATOR.CLIPBOARD  CLIPBOARD  JAVASCRIPT 

Share on Facebook  Share on Twitter  Share on Weibo  Share on Reddit 

  RELATED


  0 COMMENT


No comment for this article.