the home site for me: also iteration 3 or 4 of my site
1// Based on https://www.roboleary.net/2022/01/13/copy-code-to-clipboard-blog.html
2
3function initCopyButtons() {
4 const blocks = document.querySelectorAll("pre[class^='language-']");
5
6 for (const block of blocks) {
7 // Code block header title
8 const title = document.createElement("span");
9 title.style.color = "var(--accent-text)";
10 const lang = block.getAttribute("data-lang");
11 const comment =
12 block.previousElementSibling &&
13 (block.previousElementSibling.tagName === "blockquote" ||
14 block.previousElementSibling.nodeName === "BLOCKQUOTE")
15 ? block.previousElementSibling
16 : null;
17 if (comment) block.previousElementSibling.remove();
18 title.innerHTML =
19 lang + (comment ? ` (${comment.textContent.trim()})` : "");
20
21 // Copy button icon
22 const icon = document.createElement("i");
23 icon.classList.add("icon");
24
25 // Copy button
26 const button = document.createElement("button");
27 const copyCodeText = "Copy code";
28 button.setAttribute("title", copyCodeText);
29 button.appendChild(icon);
30
31 // Code block header
32 const header = document.createElement("div");
33 header.classList.add("header");
34 header.appendChild(title);
35 header.appendChild(button);
36
37 // Container that holds header and the code block itself
38 const container = document.createElement("div");
39 container.classList.add("pre-container");
40 container.appendChild(header);
41
42 // Move code block into the container
43 block.parentNode.insertBefore(container, block);
44 container.appendChild(block);
45
46 button.addEventListener("click", async () => {
47 await copyCode(block, header, button);
48 });
49 }
50
51 async function copyCode(block, header, button) {
52 const code = block.querySelector("code");
53 const text = code.innerText;
54
55 // Only try to copy if clipboard API is available
56 if (navigator.clipboard) {
57 try {
58 await navigator.clipboard.writeText(text);
59 header.classList.add("active");
60 button.setAttribute("disabled", true);
61
62 header.addEventListener(
63 "animationend",
64 () => {
65 header.classList.remove("active");
66 button.removeAttribute("disabled");
67 },
68 { once: true },
69 );
70 } catch (err) {
71 console.error("Failed to copy:", err);
72 }
73 }
74 }
75}
76
77// Since the script has defer attribute, the DOM is already loaded when this runs
78if (document.readyState === 'loading') {
79 document.addEventListener('DOMContentLoaded', initCopyButtons);
80} else {
81 initCopyButtons();
82}