Add “Copy to clipboard” button on any HTML element

Bibhuti Poudyal
Vue.js Developers
Published in
3 min readAug 26, 2021
Photo by Markus Winkler on Unsplash

This post will guide to add “Copy” button to a html element. It will work for all of type of frontend frameworks(including SSR and JAM-stack); core idea will be same but implementation may differ across frameworks.

Lets start will the core logic i.e. copying a piece of text to clipboard.

const copy = async (text) => await navigator.clipboard.writeText(text);

That’s it !

This 1-liner will copy any piece of text from a webpage to clipboard. Its not limited to text though, you can copy any arbitrary data eg. images to clipboard. More info along with its limitations & browser support can be found here.

Lets look at some of the use cases on how this 1-liner can be used on real world scenarios.

Copy code from webpage

Lets say you have a webpage containing lots of code and it needs copy button on every instance.

Typically code are written inside <code> tag wrapper by <pre> tag, which may end up something like this.

<pre>
<code>
var message = "Hello world";
console.log(message);
</code>
</pre>

To place a copy button on every <pre> tag, we can use the following logic.

function enableCopy(selector = "pre", childSelector = "code", btnText = "Copy Me", btnTextSuccess = "Copied", activeClass = "--copy") {
document.querySelectorAll(`${selector}:not(.${activeClass})`).forEach(node => {
// create a "copy" button
let copyBtn = document.createElement("button");
copyBtn.innerText = btnText;
// activeClass acts as flag so we don't add another copy button by mistake
copyBtn.classList.add(activeClass);
node.appendChild(copyBtn);
copyBtn.addEventListener("click", async () => { // copy to clipboard
if (navigator.clipboard) {
let text = node.querySelector(childSelector).innerText;
await navigator.clipboard.writeText();
}
// change text of button after copying
copyBtn.innerText = btnTextSuccess;
// change text back to normal after ### ms
setTimeout(() => icon.innerText = btnText, 2000);
})
})
}

What it does is, it will insert a <button> on each <pre> tag. When its clicked, required text gets copied to clipboard.

In order to use it in our case, just call the method without params; default parameters will work.

Regardless of which tool/library/framework you use, this approach will work.

Implementation as VueJs plugin

Since I come from VueJs background, I will implement the same logic as a VueJS plugin. Core logic will be same but the best way to implement may be different. In case of VueJS we can make use of plugins and lifecycle hooks to ease the process.

This is the case where shiki has been used for code highlighting. It doesn’t come with “copy code” feature though. The way it wraps code is

<pre class="shiki">
<code>
var message = "Hello world";
console.log(message);
</code>
</pre>

To add copy button to above code, using same approach as above, the Vuejs plugin is written like this

// copyPlugin.jsconst CopyPlugin = {    // run this method when plugin installs
install: function (Vue) {

// create a Vue mixin
Vue.mixin({
// on each mounted lifecycle hook, run this code
mounted: function () {
// same function we used above
enableCopy("pre.shiki");
}
})
}
}
export default CopyPlugin;

Its then imported and used in main.js file.

import Vue from "vue";
import CopyPlugin from "./copyPlugin";
Vue.use(CopyPlugin);

This will do the job. Only extra stuff on this approach is wrapping the core logic inside Vuejs Plugin system. Same applies for react, angular or any other tools. After all, whatever tool we use, it all boils down to regular html, css & js.

This approach can be further extended to copy text of any HTML tag. And the text can be dynamic(from server) too. For instance, you can check out this blog post. It’s the same as this one, difference is: code snippets are highlighted with shiki, and copy button placed by the same Vuejs plugin explained in this post.

Btw, if you want to generate beautiful snapshots of code, try RamroCode 👌

--

--