Copy HTML content with navigator clipboard
Tags: javascript, navigator, programming, tutorial
Published: 2023-12-13 08:20:52

We have often seen “Copy” buttons for plain text. Thanks to [navigator.clipboard‘s API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/clipboard), we can also programatically copy and paste HTML content.

## The copy code

Let’s imagine we have a bit of HTML content that includes links and other basic HTML code. We want our end users to be able to copy it exactly as it is and use it elsewhere.

For this exercise, we will be working with a simple HTML page with a section with `id=”copy-content”` that contains the HTML to be copied, and a “Copy to clipboard” button, with `id=”copy-button”`. The code for this example can be found in [this gist](https://gist.github.com/carmemias/c79f68cd7b65d331da31e3acd6cd7574).

Initially, the javascript that makes it all happen looks like this:

```js
const copyButton = document.getElementById("copy-button");
copyButton.addEventListener("click", copyContent);

function copyContent() {
  const content = document.getElementById("copy-content").innerHTML;
  addHtmlToClipboard(content);
}

function addHtmlToClipboard(content) {
  const type = "text/html";
  const blob = new Blob([content], { type });
  const data = [new ClipboardItem({ [type]: blob })];

  navigator.clipboard.write(data).then(
    () => {
      console.log("copied to clipboard");
    },
    () => {
      console.log("failed to copy to clipboard");
    },
  );
}
```

First, we add a “click” event to the “Copy to clipboard” button so that, when the button is clicked, the `copyContent` function is executed.

This `copyContent` function, gets the HTML content we want to copy and passes it to the `addHtmlToClipboard` function.

The `addHtmlToClipboard` function then takes this content, converts it into an HTML `Blob` and creates a new `ClipboardItem` with it. This is then writen into the clipboard using `navigator.clipboard.write`.

If the clipboard API succedes, the content is ready to be pasted elsewhere. For example, in a Word document or an email.

Note the inline CSS it is also copied but not that which is in a separate file.

## Firefox gotcha

Unfortunately the `ClipboardItem` funtionality is not yet supported by all browsers, notably Firefox.

As a compromise, we can change the code so that at least the text content is copied to clipboard if the full functionality cannot be used. Here is the updated code:

```js
const copyButton = document.getElementById("copy-button");
copyButton.addEventListener("click", copyContent);

function copyContent() {
  const content = document.getElementById("copy-content").innerHTML;
  
  if(typeof ClipboardItem === "function") {
    addHtmlToClipboard(content);
  } else {
    const textContent = sanitize(content);
    addTextToClipboard(textContent);
  }
}

function addHtmlToClipboard(content) {
  const type = "text/html";
  const blob = new Blob([content], { type });
  const data = [new ClipboardItem({ [type]: blob })];

  navigator.clipboard.write(data).then(
    () => {
      console.log("copied html to clipboard");
    },
    () => {
      console.log("failed to copy html to clipboard");
    },
  );
}

// removes all HTML tags and empty spaces at either end
function sanitize(content) {
  const pattern = /<\/?[\w ="'-:;\/\.]+>/g;

  return content.replaceAll(pattern, "").trim();
}

function addTextToClipboard(text) {
  navigator.clipboard.writeText(text).then(
    () => {
      console.log("copied text to clipboard");
    },
    () => {
      console.log("failed to copy text to clipboard");
    },
  );
}
```

So what has changed? First of all, after getting the content to be copied, the `copyContent` function checks whether the `ClipboardItem` functionality is available.

If it is, the `addHtmlToClipboard` function is run just as before. If it isn’t available, the new `addTextToClipboard` function is run instead.

This new `addTextToClipboard` takes the content to be copied, removes all traces of HTML code from it (via the `sanitize` function) and then writes it as text into the clipboard using `navigator.clipboard.writeText`.

From the callbacks, instead of `console.log`s, you could for example trigger a success notification or failure warning.

Try it out to see it working!