Copy button, take 1
   3 min read

The first time I tried to set up a Copy button for code boxes, I left an issue on Zdoc’s GitHub.
Some days ago, another Zdoc user published the solution that worked for him, and stimulated by that, I tried again.
Unfortunately, I must have manipulated my theme’s DOM code too much (tracking upstream is going to be impossible until I nuke it and pave it on the next Zdoc release) to be able to use his code, but I tried fiddling a little more with the code I had tried to use previously, which was based on this proposal by Tom Spencer.
With my changes, it ended up like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// insert just before the </script> tag
// tomspencer, https://www.tomspencer.dev/blog/2018/09/14/adding-click-to-copy-buttons-to-a-hugo-powered-blog/
(function() {
  'use strict';

  if(!document.queryCommandSupported('copy')) {
    return;
  }

  function flashCopyMessage(el, msg) {
    el.textContent = msg;
    setTimeout(function() {
      el.textContent = "COPY";
    }, 1000);
  }

  function selectText(node) {
    var selection = window.getSelection();
    var range = document.createRange();
    range.selectNodeContents(node);
    selection.removeAllRanges();
    selection.addRange(range);
    return selection;
  }

  function addCopyButton(containerEl) {
    var copyBtn = document.createElement("button");
    copyBtn.className = "highlight-copy-btn";
    copyBtn.textContent = "COPY";

    var codeEl = containerEl.firstElementChild;
    copyBtn.addEventListener('click', function() {
      try {
        var selection = selectText(codeEl);
        document.execCommand('copy');
        selection.removeAllRanges();

        flashCopyMessage(copyBtn, 'Copied!')
      } catch(e) {
        console && console.log(e);
        flashCopyMessage(copyBtn, 'Failed :\'(')
        }
      });
   
      containerEl.appendChild(copyBtn);
    }

  // Add copy button to code blocks
  var highlightBlocks = document.getElementsByClassName('highlight');
  for (var i = 0; i < highlightBlocks.length; i++) {
    addCopyButton(highlightBlocks[i]);
  }
})();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
.highlight-copy-btn {
  position: relative;
  bottom: 22px;
  right: 0px;
  border: 0;
  border-radius: 4px;
  padding: 1px;
  font-size: 0.6em;
  line-height: 1.0;
  opacity: 0.6;
  min-width: 65px;
  text-align: center;
  letter-spacing: 2px;
  @include themify($codeblock) {
  color: themed('content-pre-color');
  background: themed('content-pre-background-color');
  border: 1.25px solid themed('border-line-color');
  }
  border-top-left-radius: 0;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 4px;
  white-space: nowrap;
  padding: 4px 4px 5px 4px;
  margin: 0 0 0 1px;
  cursor: pointer;
  opacity: 0.6;

}

.highlight-copy-btn:hover {
  @include themify($themes) {
    color: themed('link');
    background-color: themed('body-background-color');
  }
}

It kinda works, but only in code blocks for which a syntax was detected (meaning, div class is highlight and chroma has been activated). For any other code blocks (notably, the native MarkDown code blocks), it doesn’t, and there are times when I can’t or won’t activate chroma. What then?

Preston Rodriguez’s code, which I’m thankful for, doesn’t work with the changes I’ve already done to the theme and I can’t undo them nor notice where they are messing up (did I say I’m not really grokking JavaScript right now?)

I also tried to repeat, without success, Tom Spencer’s code in layouts/partials/head/scripts.html, but using this as the DOM selector instead:

var codeBlocks = document.querySelectorAll('pre > code');
codeBlocks.forEach(function(plainBlock) {
  addCopyButton(plainBlock);
}

(which is a perfect demonstration of what’s happening - right now the COPY button is not showing here, but whenever I can get this working, it should be there as well)

What's on this Page