About
RGraph is a JavaScript charts library based on HTML5 SVG and canvas. RGraph is mature (over 15 years old) and has a wealth of features making it an ideal choice to show charts on your website.

More »

 

Download
Get the latest version of RGraph (version 6.17) from the download page. There's also older versions available, minified files and links to cdnjs.com hosted libraries.

More »

 

License
RGraph can be used for free under the GPL or if that doesn't suit your situation there's an inexpensive (£99) commercial license available.

More »

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)

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 a pre 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();
Copy
SOURCE 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);
}