How to add a copy to clipboard button (with video)
Written by Richard Heyes, RGraph author, on 7th October 2023- A standalone basic example page (tl;dr)
- Introduction
- The JavaScript code to copy the text to the clipboard
- The HTML markup to add the Copy button to pre tags
- The CSS for the code block
- The showNotification function
- Add the Copy links to all of the pre tags
A standalone basic example page (tl;dr)
Here's a link to a basic, standalone page of this technique. All the source code is in the page - nothing external is referenced so it makes it easier to follow. If you want to you can view the source code of the page, copy it and paste it into a file on your desktop and it should work perfectly well.
And here's a link to a more basic example of copying to the clipboard. This code will only work with the one code example on the page but there's a lot less
javascript
to understand. Other than there only being support for a single <pre> tag it's pretty similar.
Introduction
If you have examples of code on your website (this site has a lot of them) adding Copy to clipboard links is a trivial matter - simply add the code inside apre
tag, add the markup for the Copy button and then style the tags with css
- like this:
BEFORE:
new RGraph.Bar({ id: 'cvs', data: [4,8,6,3,5,4,9,8,9,8,6,5], options: { colors: ['blue','yellow'] } }).draw();AFTER: (the button doesn't do anything yet)
// An example of adding the Copy button to pre example code blocks new RGraph.Bar({ id: 'cvs', data: [4,8,6,3,5,4,9,8,9,8,6,5], options: { colors: ['blue','yellow'] } }).draw(); CopySOURCE CODE:
The html
to implement this is as follows:
<pre class="code" style="position: relative"> new RGraph.Bar({ id: 'cvs', data: [4,8,6,3,5,4,9,8,9,8,6,5], options: { colors: ['blue','yellow'] } }).draw(); <span style="position: absolute; top: 5px; right: 5px; background-color: white; color: black; padding: 3px; border: 1px gray dashed">Copy</span> </pre>
The JavaScript code to copy the text to the clipboard
Here's the javascript
code that implements the Copy to clipboard functionality. This is just the function that copies the text that's given to the clipboard.
// // Copies text to the clipboard. // // @param text string The text to copy // @param span object The span tag that's the copy link // function copyToClipboard(text, span) { // Get rid of the label if it's present if (text.endsWith(text)) { text = text.substr(0, text.length - label.length).trim(); } // Use the Clipboard API with promises to // copy text to the clipboard if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(function () { // Change the Copy button to show a Copied label for a few seconds showNotification(span); }, function () { alert('[ERROR] Copying to the clipboard failed!'); }); } }
The HTML markup to add the Copy button to pre tags
Adding the Copy button to pre
tags involves setting the tags css
position
to relative
and adding an absolute
position
span
tag to the top right of the tag. Example code is as follows (this is the same as that shown above). The button doesn't do anything yet - it's just positioned in the top right corner of the pre
tag.
<pre class="code" style="background-color:#eee; position: relative"> new RGraph.Bar({ id: 'cvs', data: [4,8,6,3,5,4,9,8,9,8,6,5], options: { colors: ['blue','yellow'] } }).draw(); <span style="position: absolute; top: 5px; right: 5px; background-color: white; color: black; padding: 3px; border: 1px gray dashed">Copy</span> </pre>
The positioning problem
If you try the example above in the introduction with your browser width reduced then you will see the issue with doing this:- being inside the scrolling pre
tag, the Copy span
tag scrolls left and right as well as the content - because it's just part of the content that's inside the pre
tag.
The positioning solution
There is a solution to this so that the Copy label stays at the top right no matter how much you scroll left or right. The pre
tag has to be wrapped in a div
tag and the span
tag holding the Copy label should be brought outside the pre
tag (but still remain within the div
tag). So it looks like this:
<div class="codeblock"> <pre class="code"> YOUR CONTENT GOES HERE <pre> <span>Copy</span> </div>
The CSS for the code block
Now, obviously, the structure that's shown above is not going to be of much use by itself - so here's the css
code that's necessary to massage the structure into something useful:
/* * This CSS code styles the copy link in the top right of <pre> code samples. The * Copy link stays where it is (the top right corner) no matter whether you scroll left/right. */ /* This styles the <div> that wraps the <pre> tag */ div.codeblock { position: relative; background-color: #eee; /* You can change this to suit your website */ box-sizing: border-box; margin-bottom: 20px; } /* This styles the <pre> tag */ div.codeblock pre { overflow-x: auto; /* This facilitates horizontal scrolling*/ margin: 0; padding: 10px; /* You can change this to give the code more or less space between it and the edge of the block */ width: 100%; /* Make the <pre> tag fill the <div> */ left: 0; /* Position the <pre> to fill the whole <div> */ top: 0; /* Position the <pre> to fill the whole <div> */ right: 0; /* Position the <pre> to fill the whole <div> */ bottom: 0; /* Position the <pre> to fill the whole <div> */ position: absolute; /* Position the <pre> to fill the whole <div> */ box-sizing: border-box; } /* This styles the Copy link that you click on to copy the code to your clipboard */ div.codeblock span { position: absolute; /* Position the link in the top right of the <div> */ top: 15px; /* Position the link in the top right of the <div> */ right: 10px; /* Position the link in the top right of the <div> */ background-color: white; /* Change this to suit your design */ border: 1px dashed gray; /* Change this to suit your design */ padding: 2px 15px; /* Change this to suit your design */ cursor:pointer; /* By changing the cursor it shows the user that they can click this */ font-size: 10pt; /* Change this to suit your design */ font-weight: bold; /* Change this to suit your design */ display: inline-block; line-height: 15px; /* Change this to suit your design */ }
The showNotification function
When the Copy link is clicked this function provides some feedback to the user by changing the Copy link to show Copied for a few seconds before reverting to Copy.
// Change the copy button to Copied for a couple // of seconds // // @param object span The <span> tag object // function showNotification (span) { // Add the notification to the page by creating a new <span> tag and positioning it over the Copy link var el = document.createElement('span'); el.innerText = 'Copied'; el.style.color = 'black'; el.style.transition = 'opacity .25s linear'; // Use a transition to facilitate a fade effect el.style.opacity = 0; // Initially the opacity is zero el.style.position = 'absolute'; // Position the Copied text over the Copy text el.style.top = '2px'; // Position the Copied text over the Copy text el.style.left = 0; // Position the Copied text over the Copy text el.style.backgroundColor = 'white'; el.style.display = 'inline-block'; el.style.textAlign = 'center'; span.appendChild(el); // Set the new <span> tag to cover the Copy text el.style.width = el.parentElement.offsetWidth - 2 + 'px'; el.style.height = el.parentElement.offsetHeight - 4 + 'px'; el.style.width = el.parentElement.offsetWidth; // Set the opacity to 1 - and because of the transition this will fade in over a quarter of a second el.style.opacity = 1; // Set the Opacity of the Copied text back to zero after two seconds setTimeout(function () { el.style.opacity = 0; // Remove the Copied text from the DOM after 0.6 seconds setTimeout(function () { el.parentNode.removeChild(el); }, 600) }, 2000); }
Add the Copy links to all of the pre tags
Obviously, we need to add a Copy button to each of the code samples on the page that the user can click to copy the code onto their clipboard. The button is a span
tag positioned absolutely at the top right. It's a child of the div
tag - not the pre
tag - so it doesn't scroll if the pre
tag scrolls. There's nothing special being done here - just a new span
tag being created in javascript
and then added to the dom
. A click
event listener is added to the span
which calls the copyToClipboard
function passing it the text to put on the clipboard and the span
tag object.
// // Now add a small Copy span to the top right of each code sample pre tag // document.addEventListener('DOMContentLoaded', function () { // Get all of the pre tags on the page var els = document.getElementsByTagName('pre'); // Feel free to change the label to say what you want var label = 'Copy'; // Loop through all of the pre tags for (var i=0; i<els.length; ++i) { // Ensure that the pre tag has a class="code" attribute and if the data-copy="false" attribute is not present // then continue. If the data-copy="false" attribute is present however then skip this particular pre tag. if ( els[i].className.indexOf('code') !== -1 && els[i].getAttribute('data-copy') !== 'false') { // Create a div and wrap the pre with it var div = document.createElement('div'); // Wrap the pre element with the div. The wrapElement function is defined further down. wrapElement(els[i], div); div.className = 'codeblock'; div.style.backgroundColor = '#eee'; div.style.backgroundImage = 'linear-gradient(-45deg, rgba(255, 255, 255, 0.5), transparent)'; // Set the div position to relative so the Copy // label appears in the top right corner els[i].style.position = 'relative'; // Create the span tag that's the Copy link. The style positions // the button at the top right of the pre tag, gives it a background, a // border and some padding. var span = document.createElement('span'); span.innerText = label; span.style.backgroundColor = 'white' span.style.border = '1px dashed black'; span.style.color = 'black'; span.style.display = 'inline-block'; span.style.position = 'absolute'; span.style.top = '9px'; span.style.right = '10px'; span.style.cursor = 'pointer'; span.style.padding = '2px 15px 2px 15px'; span.style.fontWeight = 'bold'; span.style.fontSize = '10pt'; span.style.lineHeight = '15px'; div.appendChild(span); // When the Copy button is clicked call the copyToClipboard function and pass it the contents of the pre span.onclick = function (e) { copyToClipboard(this.parentNode.innerText.replace(label, '').trim(), this) } } } }); // // Wrap an element with another // // @param el object The element to wrap // @param wrapper object The element that becomes the wrapper // function wrapElement(el, wrapper) { el.replaceWith(wrapper); wrapper.appendChild(el); }