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.z-code");
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.querySelector("code[data-lang]")?.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 button.blur();
60 header.classList.add("active");
61 button.setAttribute("disabled", true);
62
63 header.addEventListener(
64 "animationend",
65 () => {
66 header.classList.remove("active");
67 button.removeAttribute("disabled");
68 },
69 { once: true },
70 );
71 } catch (err) {
72 console.error("Failed to copy:", err);
73 }
74 }
75 }
76}
77
78// Since the script has defer attribute, the DOM is already loaded when this runs
79if (document.readyState === 'loading') {
80 document.addEventListener('DOMContentLoaded', initCopyButtons);
81} else {
82 initCopyButtons();
83}