Skip to content

Instantly share code, notes, and snippets.

@ryanwjackson
Created February 9, 2021 15:01
Show Gist options
  • Save ryanwjackson/c379b3d9cb6a605e8a62f272e6fb37b8 to your computer and use it in GitHub Desktop.
Save ryanwjackson/c379b3d9cb6a605e8a62f272e6fb37b8 to your computer and use it in GitHub Desktop.
Copyable text element
<html>
<head>
<style>
.copyable {
display: inline-block;
padding: 4px;
border-radius: 4px;
border: 1px solid #ccc;
}
.copyable:hover {
border-color: #999;
cursor: copy;
}
.copyable-copied {
position: absolute;
}
.copy-icon {
display: inline-block;
padding-left: 8px;
padding-right: 4px;
margin-left: 4px;
border-left: 1px solid #ccc;
}
.copy-icon svg {
height: 16px;
width: 16px;
}
</style>
</head>
<body>
<div>
<span class="copyable">
Hello World!
</span>
</div>
<div>
<span class="copyable">
foobar
</span>
</div>
<script>
function createCopyIcon() {
var xmlns = "http://www.w3.org/2000/svg";
var path = document.createElementNS(xmlns, "path");
path.setAttribute("d", "M16.5 4H14V1.5C14 0.673 13.327 0 12.5 0H5C4.867 0 4.74 0.053 4.646 0.146L1.146 3.646C1.052 3.74 1 3.867 1 4V14.5C1 15.327 1.673 16 2.5 16H5V18.5C5 19.327 5.673 20 6.5 20H16.5C17.327 20 18 19.327 18 18.5V5.5C18 4.673 17.327 4 16.5 4ZM5 1.207V3.5C5 3.776 4.776 4 4.5 4H2.207L5 1.207ZM2.5 15C2.224 15 2 14.776 2 14.5V5H4.5C5.327 5 6 4.327 6 3.5V1H12.5C12.776 1 13 1.224 13 1.5V4H9C8.867 4 8.74 4.053 8.646 4.146L5.146 7.646C5.052 7.74 5 7.867 5 8V15H2.5ZM9 5.207V7.5C9 7.776 8.776 8 8.5 8H6.207L9 5.207ZM17 18.5C17 18.776 16.776 19 16.5 19H6.5C6.224 19 6 18.776 6 18.5V9H8.5C9.327 9 10 8.327 10 7.5V5H16.5C16.776 5 17 5.224 17 5.5V18.5Z");
path.setAttribute("fill", "black");
var svg = document.createElementNS(xmlns, "svg");
/* svg.setAttribute("width", "20"); */
/* svg.setAttribute("height", "20"); */
svg.setAttribute("viewBox", "0 0 20 20");
svg.setAttribute("fill", "none");
svg.appendChild(path);
var copySpan = document.createElement("span");
copySpan.setAttribute("class", "copy-icon");
copySpan.appendChild(svg);
return copySpan;
}
function createCopyContentSpan(content) {
var copyContentSpan = document.createElement("span");
copyContentSpan.setAttribute("class", "copy-content");
copyContentSpan.innerText = content;
return copyContentSpan;
}
function createCopiedSpan(content) {
var span = document.createElement("span");
span.setAttribute("class", "copyable-copied");
span.style.display = "none";
span.innerText = "Copied!";
return span;
}
var copyables = document.getElementsByClassName("copyable");
Array.from(copyables).forEach(function(copyable) {
var content = copyable.innerText;
copyable.innerText = "";
var copiedSpan = createCopiedSpan();
copyable.append(copiedSpan);
var contentSpan = createCopyContentSpan(content);
copyable.append(contentSpan);
var copyIcons = copyable.getElementsByClassName("copy-icon");
if (copyIcons.length == 0) {
var icon = createCopyIcon();
copyable.appendChild(icon);
} else {
var icon = Array.from(copyIcons)[0]
}
copyable.onclick = function() {
copyTextToClipboard(contentSpan, content, copiedSpan);
};
});
function copyTextToClipboard(element, text, copiedSpan) {
var textArea = document.createElement("textarea");
/*
// *** This styling is an extra step which is likely not required. ***
//
// Why is it here? To ensure:
// 1. the element is able to have focus and selection.
// 2. if the element was to flash render it has minimal visual impact.
// 3. less flakyness with selection and copying which **might** occur if
// the textarea element is not visible.
//
// The likelihood is the element won't even render, not even a
// flash, so some of these are just precautions. However in
// Internet Explorer the element is visible whilst the popup
// box asking the user for permission for the web page to
// copy to the clipboard.
*/
/* Place in the top-left corner of screen regardless of scroll position. */
textArea.style.position = 'fixed';
textArea.style.top = 0;
textArea.style.left = 0;
/* Ensure it has a small width and height. Setting to 1px / 1em */
/* doesn't work as this gives a negative w/h on some browsers. */
textArea.style.width = '2em';
textArea.style.height = '2em';
/* We don't need padding, reducing the size if it does flash render. */
textArea.style.padding = 0;
/* Clean up any borders. */
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
/* Avoid flash of the white box if rendered for any reason. */
textArea.style.background = 'transparent';
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
if (!successful) {
console.log('Copying text command was ' + msg);
} else {
element.style.visibility = "hidden";
copiedSpan.style.display = "inline-block";
setTimeout(function() {
element.style.visibility = "visible";
copiedSpan.style.display = "none";
}, 1000);
}
} catch (err) {
console.log('Oops, unable to copy');
}
document.body.removeChild(textArea);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment