Adding a copy button to code blocks in Hugo
A little javascript snippet to automatically add copy buttons to codeblocks in a Hugo site. Add the javascript snippet to assets/js
and include the below in ~/layouts/partials/head.html
:
{{ $copyCode := resources.Get "js/copyCode.js" | minify | fingerprint }}
<-- other code here -->
<script defer src\="{{ $copyCode.Permalink }}"\></script\>
function createCopyButton(highlightDiv) {
const button = document.createElement('button');
button.className = 'copy-code-button';
button.type = 'button';
button.innerText = 'Copy';
button.addEventListener('click', () => copyCodeToClipboard(button, highlightDiv));
addCopyButtonToDom(button, highlightDiv);
}
async function copyCodeToClipboard(button, highlightDiv) {
const codeToCopy = highlightDiv.querySelector(':last-child > .chroma > code').innerText;
try {
result = await navigator.permissions.query({ name: 'clipboard-write' });
if (result.state == 'granted' || result.state == 'prompt') {
await navigator.clipboard.writeText(codeToCopy);
} else {
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
}
} catch (_) {
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
} finally {
codeWasCopied(button);
}
}
function copyCodeBlockExecCommand(codeToCopy, highlightDiv) {
const textArea = document.createElement('textArea');
textArea.contentEditable = 'true';
textArea.readOnly = 'false';
textArea.className = 'copyable-text-area';
textArea.value = codeToCopy;
highlightDiv.insertBefore(textArea, highlightDiv.firstChild);
const range = document.createRange();
range.selectNodeContents(textArea);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
textArea.setSelectionRange(0, 999999);
document.execCommand('copy');
highlightDiv.removeChild(textArea);
}
function codeWasCopied(button) {
button.blur();
button.innerText = 'Copied!';
setTimeout(function () {
button.innerText = 'Copy';
}, 2000);
}
function addCopyButtonToDom(button, highlightDiv) {
highlightDiv.insertBefore(button, highlightDiv.firstChild);
const wrapper = document.createElement('div');
wrapper.className = 'highlight-wrapper';
highlightDiv.parentNode.insertBefore(wrapper, highlightDiv);
wrapper.appendChild(highlightDiv);
}
document.querySelectorAll('.highlight').forEach((highlightDiv) => createCopyButton(highlightDiv));