Readers of a WordPress tutorial or documentation site copy code all the time, and selecting a long block by hand often misses the first or last line. This snippet adds a Copy button to every code block on your WordPress site. One click puts the whole block on the clipboard, and the button says Copied!.
It is plain CSS and JavaScript, with no plugin, jQuery or library. It works with the core Code block and with any <pre> your theme or a page builder prints.
The CSS
/* Copy button on code blocks (dpc). */
.dpc-copy {
position: relative;
}
.dpc-copy-btn {
position: absolute;
top: 0.5em;
right: 0.5em;
padding: 0.25em 0.6em;
border: 1px solid currentColor;
border-radius: 4px;
background: rgba(255, 255, 255, 0.9);
color: #1e1e1e;
font: 600 12px/1.4 system-ui, sans-serif;
cursor: pointer;
opacity: 0;
transition: opacity 0.15s;
}
.dpc-copy:hover .dpc-copy-btn,
.dpc-copy-btn:focus-visible,
.dpc-copy-btn.is-copied {
opacity: 1;
}
@media (hover: none) {
.dpc-copy-btn {
opacity: 1;
}
}
The JavaScript
(function () {
var LABEL = 'Copy';
var COPIED = 'Copied!';
var FAILED = 'Press Ctrl+C';
function copyText(text) {
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(text);
}
// Fallback for plain http:// sites, where the Clipboard API is not available.
return new Promise(function (resolve, reject) {
var area = document.createElement('textarea');
area.value = text;
area.setAttribute('readonly', '');
area.style.position = 'fixed';
area.style.opacity = '0';
document.body.appendChild(area);
area.select();
var ok = false;
try {
ok = document.execCommand('copy');
} catch (e) {
ok = false;
}
document.body.removeChild(area);
if (ok) {
resolve();
} else {
reject(new Error('copy failed'));
}
});
}
function addButton(pre) {
if (pre.classList.contains('dpc-copy')) {
return;
}
var button = document.createElement('button');
button.type = 'button';
button.className = 'dpc-copy-btn';
button.textContent = LABEL;
button.setAttribute('aria-live', 'polite');
button.addEventListener('click', function () {
var code = pre.querySelector('code');
var text = code ? code.textContent : Array.prototype.filter.call(pre.childNodes, function (node) {
return node !== button;
}).map(function (node) {
return node.textContent;
}).join('');
copyText(text).then(function () {
button.textContent = COPIED;
}, function () {
button.textContent = FAILED;
}).then(function () {
button.classList.add('is-copied');
setTimeout(function () {
button.textContent = LABEL;
button.classList.remove('is-copied');
}, 2000);
});
});
pre.classList.add('dpc-copy');
pre.appendChild(button);
}
function init() {
document.querySelectorAll('pre').forEach(addButton);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
Add it with Scripts Organizer
In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Conditions and leave Template on All. Under Script location tick Header and Footer.
- Header: set Header Mode to CSS and paste the CSS.
- Footer: set Footer Mode to Javascript and paste the JavaScript.
Publish and enable the block. Scripts Organizer prints Header and Footer code on the front end only with the Conditions trigger, not with Everywhere. To load it only on your blog, narrow the Template condition to posts.
How it works
- On page load the script finds every
<pre>, adds the classdpc-copyand appends a<button>. The class also stops it from adding a second button. - The copied text is the
textContentof the<code>inside the<pre>, or of the<pre>itself without the button. Syntax highlighters that wrap tokens in<span>s still give the plain code. navigator.clipboard.writeText()does the copying. It only exists on HTTPS (andlocalhost), so on plainhttp://the script falls back to a hidden textarea anddocument.execCommand('copy').- The button is a real
<button>: keyboard users can tab to it, andaria-livemakes screen readers announce Copied!. It shows on hover, on keyboard focus, and always on touch screens. - The older version of this post, based on WPJohnny’s Code Click-to-Copy plugin, copied on a click anywhere in the code, including inline
<code>in paragraphs, so readers could no longer select part of a block. It also used the deprecated globaleventand an Oxygen colour variable.
Customise it
- Other labels or language: change
LABEL,COPIEDandFAILEDat the top of the script. - Only core Code blocks: change
querySelectorAll('pre')toquerySelectorAll('pre.wp-block-code'). - Colours and position: edit
.dpc-copy-btnin the CSS. Use your theme’s colour variables there if it has them.
Gotchas
- On a code block that scrolls sideways, the button sits at the top right of the visible box and moves with the content when it scrolls. Wrap long lines with
white-space: pre-wrapif that bothers you. - Code added later by JavaScript (tabs, AJAX loading) gets no button. Add
window.dpcCopyInit = init;above thereadyStatecheck and calldpcCopyInit()after the new content is in the page. - The script does not load in the block editor; there it would get in the way of editing.
In the Scripts Organizer library Import it with one click from Scripts Organizer.