Original link: https://blog.loikein.one/posts/2022-08-11-hugo-copy-code-button/
I’ve wanted to do this for a long time, but because I haven’t studied JavaScript seriously, I give up halfway every time because I can’t piece together the desired effect. There are actually many articles on the Internet (see: Issue #5619 · python-poetry/poetry , but I couldn’t find a version that considered accessibility anyway. There is a copy code button that supports keyboard browsing (Enter / Spacebar) on MDN , but the code is really It’s too abstract for me to understand…
However, as the saying goes, fishing is the primary productive force. Today, I was nervously fishing for fish, and I suddenly remembered this thing. After two hours of sewing, it actually came out for me. Fishing is really a scary… process.
First results:
hello world
The file paths for this article are based on the entire Hugo website folder.
Copy code function
Source: How to Add Copy to Clipboard Buttons to Code Blocks in Hugo – Simplernerd
code
First, create a new JavaScript file as: ./themes/diary/static/js/clipboard.js
// buttons const svgCopy = '<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg>' ; const svgCheck = '<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path></svg>' ; // add button function const addCopyButtons = (clipboard) => { // 1. Look for pre > code elements in the DOM document .querySelectorAll( "pre > code" ).forEach((codeBlock) => { // 2. Create a button that will trigger a copy operation const button = document .createElement( "button" ); button.className = "clipboard-button" ; button.type = "button" ; button.title = "Copy" ; button.innerHTML = svgCopy; button.addEventListener( "click" , () => { clipboard.writeText(codeBlock.innerText).then( () => { button.blur(); button.innerHTML = svgCheck; setTimeout(() => (button.innerHTML = svgCopy), 2000 ); }, (error) => (button.innerHTML = "Error" ) ); }); // 3. Append the button after the pre tag (.highlight > pre > button > code) const pre = codeBlock.parentNode; pre.parentNode.insertBefore(button, pre.nextSibling); }); }; // trigger function if (navigator && navigator.clipboard) { addCopyButtons(navigator.clipboard); } else { const script = document .createElement( "script" ); script.src = "https://cdnjs.cloudflare.com/ajax/libs/clipboard-polyfill/3.0.3/promise/clipboard-polyfill.promise.min.js" ; script.integrity = "sha512-O9Q+AhI1w7LT1/tHysPWDwwrgB1fKJ/nXPNLC30i8LF6RdSz4dGZyWB9WySag3DZMdGuK5yHJEdKXMKI2m5uSQ==" ; script.crossOrigin = "anonymous" ; script.referrerpolicy = "no-referrer" ; script.onload = () => addCopyButtons(clipboard); document .body.appendChild(script); }
There are several changes in the above code:
- Remove
fill
(fill color) ofsvgCheck
and specify it by CSS instead - Add
button.title = "Copy";
(description text when the mouse is over) - Modify the position of the added
button
to the back of thepre
(detailed explanation below) - Update
clipboard-polyfill
version to latest stable version (not tested)
Then, add the following code to ./themes/diary/layouts/partials/footer.html
:
< footer > ... <!-- copy code --> < script src = " / script > </ footer >
explain
First, use Chroma that comes with Hugo for code highlighting, and the rendering result is HTML in the following format:
< div class = "highlight" > < pre class = "..." > < code class = "..." data-lang = "..." >...</ code > </ pre > </ div >
So the condition in the layout is to append JavaScript only when the rendered page contains <code
.
Then comes the JavaScript code. The first part is the SVG of the two buttons, then the addCopyButtons
equation (which includes both adding a button and copying the code), and finally calling the equation.
Regarding the part of adding buttons, the original author put it in front of <pre>
, it should be just a matter of personal preference. However, since I was testing keyboard browsing, the preference for :focus
on the web page was <pre>
instead of <div class="highlight">
, so if you put the button in front of <pre>
, you can’t use sibling selector for the button Added CSS on :focus
. After some searching, I found that the button can be added after the <pre>
by modifying the following:
- pre.parentNode.insertBefore(button, pre); + pre.parentNode.insertBefore(button, pre.nextSibling);
After adding the above code, the rendering result is HTML in the following format:
< div class = "highlight" > < pre class = "..." > < code class = "..." data-lang = "..." >...</ code > </ pre > < button class = "copy-code-button" type = "button" > < svg ...>...</ svg > </ button > </ div >
Finally call the equation. Call it directly if the browser supports the clipboard API , otherwise load the clipboard-polyfill . Remarks: The latter is marked as obsolete, but it will not cause additional requests on supported browsers, and it should not be a big problem to keep it; although I don’t want to support IE, but anyway, people have already written it, no need White need not…
Support keyboard browsing
Add step 4 to the addCopyButtons
equation, leaving the rest unchanged:
const addCopyButtons = (clipboard) => { // 1. Look for pre > code elements in the DOM document .querySelectorAll( "pre > code" ).forEach((codeBlock) => { // 2. Create a button that will trigger a copy operation const button = document .createElement( "button" ); button.className = "copy-code-button" ; button.type = "button" ; button.title = "Copy" ; button.innerHTML = svgCopy; button.addEventListener( "click" , () => { clipboard.writeText(codeBlock.innerText).then( () => { button.blur(); button.innerHTML = svgCheck; setTimeout(() => (button.innerHTML = svgCopy), 2000 ); }, (error) => (button.innerHTML = "Error" ) ); }); // 3. Append the button after the pre tag (.highlight > pre > button > code) const pre = codeBlock.parentNode; pre.parentNode.insertBefore(button, pre.nextSibling); // 4. Listen to keyboard press const highlight = pre.parentNode; highlight.addEventListener( 'keydown' , function (event) { if ( event.key === " " || event.key === "Spacebar" || event.code === "Space" || event.key === "Enter" || event.code === "Enter" ) { clipboard.writeText(codeBlock.innerText).then( () => { button.blur(); button.innerHTML = svgCheck; setTimeout(() => (button.innerHTML = svgCopy), 2000 ); }, (error) => (button.innerHTML = "Error" ) ); } }); }); };
This part of the code refers to several StackOverflow answers. First of all, the code framework comes from this answer , and then because it supports the enter key and the space bar, it refers to this answer . Finally, I learned from this answer that keyCode
and which
have been eliminated and replaced by key
and code
.
Adapt CSS
Also refer to How to Add Copy to Clipboard Buttons to Code Blocks in Hugo – Simplernerd , but this part has changed a lot.
The new CSS file is: ./assets/css/code-fense.css
(follow the way of adding custom CSS written before)
. highlight { position : relative ; } . copy-code-button { color : var ( -- dark - gray ); background-color : rgba ( 255 , 255 , 255 , 50 % ); border : none ; border-radius : 6 px ; padding : 0 5 px 5 px 5 px ; font-size : 1 rem ; position : absolute ; z-index : 1 ; right : 0 ; top : 0 ; margin : 10 px ; transition : .1 s ; opacity : 0.5 ; } . copy-code-button > svg { fill: var ( -- white ); } . copy-code-button :hover , . copy-code-button :focus , pre :active ~ . copy-code-button , pre :focus ~ . copy-code-button , div . highlight :active > . copy-code-button , div . highlight :focus > . copy-code-button { cursor : pointer ; opacity : 1 ; }
Since my blog has a dark mode, plus hover/focus
, I had to write four different styles. I was tired of writing, and after thinking about it for a long time, I came up with the taboo and double transparency. However, this is also based on the fact that I haven’t figured out how to automatically switch the code highlighting style when switching modes. As a result, I am currently lazy and use Dracula, which has a very convenient black background. If the automatic switch is made, the scoring situation will be rewritten again.
First, .highlight{ position: relative; }
and .copy-code-button{ position: absolute; }
are required, otherwise the button will drift out of the text bar. position
is something like black magic, and I don’t particularly want to delve into it. The specific location of the button is actually a little bit of black magic, but it is still relatively easy to understand, that is, it is fixed in the upper right corner of the code box.
Then the button pattern color, because the press is translucent, all the colors only need to write one. The original author seems to write the mouse over the code block to display it. I think it is not obvious, so I changed it to always display.
The last is to cancel the transparency when interacting with the button / <pre>
/ <div class="highlight">
, the pointer becomes a finger. In theory active
and focus
are two different situations, but I don’t have the energy to write a third set of styles for all buttons/links, and it’s better to write them together than not to write active
, so I’ve always written like this so far . The <pre>
part is the reason why the position of the add button was modified in the previous article. If you are interested in further research, you can move to read:
- Adjacent sibling combinator – CSS: Cascading Style Sheets | MDN ( Chinese version )
- General sibling combinator – CSS: Cascading Style Sheets | MDN ( Chinese version )
Off topic: what is accessibility? Why Web Development Needs to Consider Accessibility?
Accessibility is not just for the convenience of others, but also for the convenience of oneself. I have spent a lot of time learning this thing , copy the main reference link below.
- Dive Into Accessibility ( Chinese translation ) (need to be adjusted according to HTML5)
- Home | 18F Accessibility Guide
- Front-end development | Accessibility for Teams
- HTML: A good basis for accessibility – Learn web development | MDN ( Chinese version )
- How to Meet WCAG (Quickref Reference)
- How I do an accessibility check – A11ycasts #11 – YouTube
- Welcome to the Accessibility Developer Guide! – ADG
I took this opportunity to complete the jump to the main content button that I had not finished before, otherwise I would feel shameless to educate others. life.
This article is reproduced from: https://blog.loikein.one/posts/2022-08-11-hugo-copy-code-button/
This site is for inclusion only, and the copyright belongs to the original author.