Copy to Clipboard: First Cut
Tags: javascript, clipboard, copy
Published: 2021-10-16 04:33:23

Following on from [motivations](https://dev.to/thumbone/the-thing-is-on-awesomeness-and-motivation-2i0j) ... the background story starts.

Some while ago I added a copy button to a website I use (maintain and am developing). The goal was to copy an HTML element, specifically a table of tables of unspecified dimension, generally small but conceivably large, to the clipboard for use in emails. 

##The legacy solution :older_man:: Select and Copy

When first researching the options I implemented a simple solution published by [R Brewer](https://stackoverflow.com/users/8801649/r-brewer) on [Stack Overflow](https://stackoverflow.com/a/54674716/4002633).

It uses [`document.createRange`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createRange) and [`Range.selectNodeContents()`](https://developer.mozilla.org/en-US/docs/Web/API/Range/selectNodeContents) for selecting an HTML element, and then [`document.execCommand('copy')`](https://developer.mozilla.org/en-US/docs/Web/API/document/execCommand) to copy it to the clipboard.

The full solution paraphrased a little and reproduced here is:

```js
function copyElementToClipboard(el) {
    var body = document.body, range, sel;
    if (document.createRange && window.getSelection) {
        range = document.createRange();
        sel = window.getSelection();
        sel.removeAllRanges();
        try {
            range.selectNodeContents(el);
            sel.addRange(range);
        } catch (e) {
            range.selectNode(el);
            sel.addRange(range);
        }
    } else if (body.createTextRange) {
        range = body.createTextRange();
        range.moveToElementText(el);
        range.select();
    }
    document.execCommand("Copy");
}
```

That sort of works ... well enough, so I stuck with it for a fair time.

##The legacy problems: It ain't all roses ... :rose::rose:

This solution has a number of problems in practice. They were all tolerable for some while or have fixes or workarounds, but they are problems all the same:

1. Firefox loses much of the styling in the process. Specifically and noticeably to me, the colours of the table rows. All colours are lost. Chromium in contrast honours them and preserves colour in the copy. The workaround here was fine, just used Chromium or Chrome to prepare emails. And all was good. 

2. The selection is visible. That was so clumsy and ugly that it had to go. The fix is not hard, basically the the element is copied, and then moved off-screen and that element selected, so it's not visible when the selection happens. A neat trick. The implemented solution looked like so:

```js
function selectElementContents(el) {
      let body = document.body, range, sel;
      if (document.createRange && window.getSelection) {
          range = document.createRange();
          sel = window.getSelection();
          sel.removeAllRanges();
          try {
              range.selectNodeContents(el);
              sel.addRange(range);
          } catch (e) {
              range.selectNode(el);
              sel.addRange(range);
          }
      } else if (body.createTextRange) {
          range = body.createTextRange();
          range.moveToElementText(el);
          range.select();
      }
}

function copyElementToClipboard(element) {
	   let el = element.cloneNode(true);
	   el.setAttribute('readonly', '');
	   el.style = {position: 'absolute', left: '-9999px'};
	   document.body.appendChild(el);
	   selectElementContents(el);
	   document.execCommand('copy');
       document.body.removeChild(el);
}  
```

This carried one caveat, and it  made me cringe and laugh at the same time. Firefox, actually respected the positioning style and chromium did not. A complete reversal of their behavioral idiosyncrasies. What that meant was Chromium copies still worked fine, but Firefox copies, not only lost all colours, but were positioned way off to the left of the page and pasted into an email it was not visible (unless I looked at the HTML source of email - necessary to see what's going on). 

No problem I thought. I just wrapped it in a `div`, that was positioned, then I copied the element _inside_ the `div`, not the `div` itself. But Firefox was just too smart for me, and still positioned the table off-screen to the left when pasted (inherited position of course, from the wrapping `div`). Never mind, Chromium worked fine. 

And that then, was the working solution for a long time. I just used Chromium to copy these tables.

##The legacy broken: One straw too many ... :dromedary_camel:

![The final straw](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/at291z122sb0oivnlwpi.jpg)

Then one day, I wanted finally to simplify and centralise the colour management on the site, so I sucked out all the CSS colour style values into [CSS variables](https://www.w3schools.com/css/css3_variables.asp). And suddenly the colours all disappeared from my Chromium copies as well.

Oh piffle!
![piffle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o8hcu1xvrreszlgw316m.jpg)

And so commenced the hunt for a fix. How, oh how, to fix this? Alongside the desire to support CSS variables was the desire to lose the selection trick, and do something a little less hackish and more slick.

Step 1 would be survey the options tor getting data onto the clipboard.