marc walter

Cross-browser copy-to-clipboard support

2017-10-22

Recently I needed to add a copy-to-clipboard functionality to a project. Usually, clipboard.js is an awesome tool to add support for it with great browser support and nice fallback handling for unsupported browsers.

Because the program is written in elm I wanted to keep the number of JavaScript dependencies to a minimum and decided to write this code on my own.

Result is this JsBin. For my purposes it worked well, I only need support for the current versions of the major desktop browsers (Firefox, Chrome, Edge and Safari) and on iOS 11.

Just like clipboard.js I used the Selection and ExecCommand APIs. Fallback mechanisms were not needed for my use case.

Clipboard.js weighs only 4KB minified and gzipped, but the code weighs 24.4KB as readable source code, which drops to 10.7KB minified.

For my "dumb" version which supports enough browsers for me, 1.8KB is enough for the full html example without minification.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
</head>
<body>
  <h1>Cross-browser copy-to-clipboard</h1>
  <h2>Using a text input field</h2>
  <div>
    <input id="input" type="text">
    <button id="button">Copy</button>
  </div>

  <script>
    const isIos = navigator.userAgent.match(/ip[ad|hone|od]/i)

    const input = document.getElementById('input')
    input.value = new Date().toISOString()

    const button = document.getElementById('button')
    button.addEventListener('click', function () { return copyFrom(input) })

    function copyFrom(input) {
      input.parentElement.focus() // for Firefox 56

      const defaults = { // for iOS
        editable: input.contentEditable,
        readOnly: input.readOnly
      }

      // select content of input field
      if (isIos) {
        // prevent on-screen keyboard
        input.contentEditable = true
        input.readOnly = true

        // selection
        const range = document.createRange()
        range.selectNodeContents(input)
        const selection = window.getSelection()
        selection.removeAllRanges()
        selection.addRange(range)
        input.setSelectionRange(0, input.value.length)
      } else {
        input.select()
      }

      // copy selection
      try {
        const result = document.execCommand('copy')
        if (!result) {
          console.warn('Could not copy. Browser returned falsy.')
        } else {
          console.info('Copied.')
        }
      } catch (ex) {
        console.warn('Could not copy.', ex)
      }

      // remove selection
      input.blur()
      if (isIos) {
        input.contentEditable = defaults.editable
        input.readOnly = defaults.readOnly
      }
    }
  </script>
</body>
</html>