Copying to the clipboard with JS

This is a surprisingly simple way to get data to the OS clipboard. It does involve some JS, but not much, not very hard, not browser-inconsistent (IE8 is not a browser).

For this to work, we need to be reacting to a user event (such as a click). The copying method will not work otherwise. The copying JS code we have to execute to copy text, is really simple, it's just:

document.execCommand("copy")

This will get whatever text we have selected and deliver place it on the clipboard. The tricky part is getting the text we want to be recognized as "the selected text" (and thus, copied). Let's see how it's done

I: Copying from input (Easy)

This is the simplest.

const copy = element=> {
  const copyText = document.getElementById(element)
  copyText.select()
  copyText.setSelectionRange(0, 99999)
  document.execCommand("copy")
  window.getSelection().removeAllRanges()
}

With a coulpe of lines of code we can get the content of the input to be selected, and then copied. Not much sweating here, but, having an input with the text we want is a heavy constraint. Fortunately, it's not our only solution.

II: Copying from text elements (Medium)

const copy = element=> {
  const range = document.createRange()
  range.selectNode(document.getElementById(element))
  window.getSelection().removeAllRanges()
  window.getSelection().addRange(range)
  document.execCommand("copy")
  window.getSelection().removeAllRanges()
}

Okey, this got trickier, since now we have document.createRange() involved. This is the actual approach for the DOM elements that don't have a .select() method that leverages this for us.

A little tricker but not too much, we've only added 2 additional lines of code.

III: Copying out of nowhere (Hard)

This is the hardest, since we have to do quite a few things here, including a "dirty" DOM element append.

So, basically, now the text is nowhere in the DOM, so we have to place it there, in a "visible" textarea. This I do not like, but see no way around, so we just put it very far away from the rest of the content, and hope nobody notices.

const copy = text=> {
  const faux = document.createElement("textarea")
  faux.value = text
  faux.setAttribute("readonly", "")
  faux.style.position = "absolute"
  faux.style.left = "-9999px"
  document.body.appendChild(faux)
  const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false
  window.getSelection().removeAllRanges()
  faux.select()
  document.execCommand("copy")
  document.body.removeChild(faux)
  if (selected) {
    window.getSelection().removeAllRanges()
    window.getSelection().addRange(selected)
  }
}

Since we've added a textarea, the rest of the code could simply be what I've described in the first step, whoever, we like complicated but neat things, so it's not going to be that simple.

Since the text we are going to copy is not in the DOM, we are going to preserve whatever selection the user had on the DOM, by copying the current selection range (if any) and then later restoring it.

IIII: The future

This felt hacky to me while writing it, and apparently, it also felt kind of improvable to a bunch of other people, so there is a Work In Progress API to handle clipboard elements.

Like most new APIs this requires user permissions, a secure context (HTTPS), and works asynchronously with promises. It's very promising, but unfortunately, not finished yet. Check out the details in the MDN site

More concepts