Skip to content

Commit 17c4da8

Browse files
authored
Add copy button to copy code block contents (#345)
* add buttons to copy code block contents Adds a clickable "copy" link in the top-right corner of each code block. If available, uses the navigator.clipboard API. Falls back to selecting the text and calling document.execCommand('copy') to copy text. * hides copy button unless mouse is hovering over code block * change text of copy button when text is copied * add translation keys for copy button text `code_copy` and `code_copied` * To disable use `Params.disableCodeCopy: true` in site config
1 parent f1bc347 commit 17c4da8

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

assets/css/common/main.css

+21-1
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,25 @@
4141
}
4242

4343
code {
44-
direction: ltr
44+
direction: ltr;
45+
}
46+
47+
div.highlight {
48+
position: relative;
49+
}
50+
51+
.copy-code {
52+
display: none;
53+
position: absolute;
54+
top: 4px;
55+
right: 4px;
56+
color: rgba(255, 255, 255, 0.8);
57+
background: rgba(78, 78, 78, 0.8);
58+
border-radius: var(--radius);
59+
padding: 0 5px;
60+
font-size: 14px;
61+
}
62+
63+
div.highlight:hover .copy-code {
64+
display: block;
4565
}

i18n/en.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@
1717

1818
- id: home
1919
translation: "Home"
20+
21+
- id: code_copy
22+
translation: "copy"
23+
24+
- id: code_copied
25+
translation: "copied!"

layouts/partials/footer.html

+40
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,43 @@
8484

8585
</script>
8686
{{- end }}
87+
88+
{{- if (not .Site.Params.disableCodeCopy) }}
89+
<script>
90+
document.querySelectorAll('pre > code').forEach((codeblock) => {
91+
const container = codeblock.parentNode.parentNode;
92+
93+
const copybutton = document.createElement('button');
94+
copybutton.classList.add('copy-code');
95+
copybutton.innerText = '{{- i18n "code_copy" | default "copy" }}';
96+
97+
function copyingDone() {
98+
copybutton.innerText = '{{- i18n "code_copied" | default "copied!" }}';
99+
setTimeout(() => {
100+
copybutton.innerText = '{{- i18n "code_copy" | default "copy" }}';
101+
}, 2000);
102+
}
103+
104+
copybutton.addEventListener('click', (cb) => {
105+
if ('clipboard' in navigator) {
106+
navigator.clipboard.writeText(codeblock.textContent);
107+
copyingDone();
108+
return;
109+
}
110+
111+
const range = document.createRange();
112+
range.selectNodeContents(codeblock);
113+
const selection = window.getSelection();
114+
selection.removeAllRanges();
115+
selection.addRange(range);
116+
try {
117+
document.execCommand('copy');
118+
copyingDone();
119+
} catch (e) { };
120+
selection.removeRange(range);
121+
});
122+
123+
container.appendChild(copybutton);
124+
});
125+
</script>
126+
{{- end }}

0 commit comments

Comments
 (0)