Copy element to clipboard

tip These methods copy text only. If you want to copy (non-markdown) formatted text or other content (like images), you might want to consider ClipboardJS.

Here's a general 'vanilla js' solution:

<pre id="mypre">Hello world2!</pre>
<button onclick="copytext(document.querySelectorAll('#mypre')[0].textContent)">hello</button>

<script>
function copytext(copyText) {
	const textArea = document.createElement('textarea');
	textArea.style.position = "absolute";
	// to prevent scrolling on IE/Safari, place the element beside us... (not a perfect solution)
	textArea.style.top = "" + ((document.documentElement && document.documentElement.scrollTop) ||  document.body.scrollTop) + "px";
	textArea.style.left = "-100%";
	textArea.textContent = copyText.trim();
	document.body.appendChild(textArea);
	textArea.select();
	document.execCommand("copy");
	// Consider: visually indicate success... (see example below)
	textArea.parentNode.removeChild(textArea);
}
</script>

(Note the trim()... most people forget that, i think it matters a lot. You don't want leading/trailing spaces, tabs, new lines. It's standard in enough browsers no, and happily it's not just spaces)

When you want just a single word/term that is copyable....

<code onclick="copytext('%name%');" class="copyable" title="click to copy">%name%</code>
<code onclick="copytext('%email%');" class="copyable" title="click to copy">%email%</code>
<code onclick="copytext('%unsub%');" class="copyable" title="click to copy">%unsub%</code>

And this styling works for me:

code.copyable {
	background-color:white;
	border:1px solid #CCC;
	border-radius:5px;
	padding:2px 4px;
}
code.copyable:hover {
	cursor: pointer;
}

To automatically hook up all pre and code items to be copyable (but not allow code inside pre to trigger a second copy...)

Run this code:

for(let preOrCode of $('pre, code')) {
	preOrCode.setAttribute('title', "Click to copy.");
	preOrCode.setAttribute('onclick', "copyItemText(this);");
}

// Don't accidentally wire yourself to a code inside a pre (those are common)
for(let codeInPre of $('pre code' )) {
	codeInPre.removeAttribute('onclick');
}

Which relies on these functions:

function copyItemText(item) {
	item.setAttribute('title', "Copied. Click to copy again.");
	copytext(item.textContent);
}

function copytext(copyText) {
	const textArea = document.createElement('textarea');
	textArea.style.position = "absolute";
	// to prevent scrolling on IE/Safari, place the element beside us...
	textArea.style.top = "" + ((document.documentElement && document.documentElement.scrollTop) ||  document.body.scrollTop) + "px";
	textArea.style.left = "-100%";
	textArea.textContent = copyText.trim();
	document.body.appendChild(textArea);
	textArea.select();
	document.execCommand("copy");
	textArea.parentNode.removeChild(textArea);
}

function $(selector) {
	return document.querySelectorAll(selector);
}

Fading out the Tooltip at Copy Time

Here's another variation, this time with a fading out tooltip.

To support the tooltip I have these two CSS classes:

/*
	Used for notifications that clipboard text has been copied.
	Can be used for other transient js messages.
*/
.floating-message {
	/* initial opacity/top margin */
	opacity: 1;
	margin-top: 0;
	padding: 5px;
	box-shadow: 0 0 3px #888;
	background-color: #FFC;
	color: #333;
	border-radius: 5px;
	font-size: 0.8em;
	transform: translate(-50%,-120%);
}

.fade-message-out {
	/* when 'hidden' it will fade out and float "up" over 1 second */
	opacity: 0;
	margin-top: -50px;
	/*transition: opacity 1s ease-in-out, margin 1s ease-in-out;*/
	transition: opacity 1s ease-in-out, margin 1s ease-in-out;
}

And here's the function that shows the tool tip. It is centered above the element passed to it.

And the function to show a message that floats away is like this:

// Message will be displayed near the element and disappear soon after
function showFloatingMessage(message, element) {
	var rect = element.getBoundingClientRect();
	const tip = document.createElement('span');
	tip.innerText = message;
	tip.classList.add("floating-message");
	tip.style.position = "absolute";
	var top = rect.top + ((document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop);
	tip.style.top = top + "px";
	tip.style.left = (rect.left + ((rect.right - rect.left) / 2)) + "px";
	document.body.appendChild(tip);

	// apply 'fade-message-out' to make it fade with css animation -- and then remove it altogether.
	setTimeout(function () {
		tip.classList.add("fade-message-out");
		setTimeout(function () { tip.parentNode.removeChild(tip); }, 1000);
	}, 10);
}

And here's the revised function that does the copying itself, and which then notifies the showFloatingMessage. It now has an extra parameter, item.

function copyToClipboard(value, element) {
	const textArea = document.createElement('textarea');
	textArea.style.position = "absolute";
	// top is at current height, to avoid causing a scroll on IE/Safari.
	var lastScrollHeight = element.scrollHeight; // this is used to prevent any unwanted scrolling (particularly in IE and Safari)
	textArea.style.left = "-100%"; // off screen
	textArea.style.width = "200px";
	textArea.textContent = value.trim();
	element.parentNode.appendChild(textArea);
	textArea.select();
	document.execCommand("copy");
	textArea.parentNode.removeChild(textArea);
	showFloatingMessage("copied to clipboard.", element);
	var scrollDiff = element.scrollHeight - lastScrollHeight;
	element.scrollTop += scrollDiff; // scroll us back where we were... if there has been any change.

}

And I call it via this helper method:

function copyItemText(item) {
	copyToClipboard(item.textContent, item);
}

And wire that up to every pre/code element like this:

for(let preOrCode of $('pre, code')) {
	preOrCode.setAttribute('title', "Click to copy.");
	preOrCode.setAttribute('onclick', "copyItemText(this);");
}

Using this with JQuery

The methods above are all vanilla JS. If wiring it up with jQuery, note that you have to pass a DOM element, not a JQuery element.

For example:

// Inject a button before every pre...
$("<button class='copy-text btn btn-sm' title='copy code to clipboard'>copy</button>").insertBefore($("pre"));

// And have it call 'copy to clipboard'
$(".copy-text").click(function (e) {
	copyToClipboard($(this).next("pre").text(), this); // that final *this* is the DOM element.
	// stop any other consequence fo this click from occurring.
	e.preventDefault();
	return false;
});

And to style that little button, this CSS is my starting point:

.copy-text {
	float: right;
	background-color: #F5F5F5; /* same as pre */
	box-shadow: 0 0 3px #AAA, 0 0 3px #AAA;
	margin-top: 5px;
	margin-right: 5px;
}

See also