Code Copy Button
By Amr
One-click copy functionality for code blocks with visual feedback and clipboard API integration.
Estimated reading time: 5 minutes
Table of Contents
Code Copy Button
Automatic copy buttons on all code blocks for easy clipboard copying.
Overview
- Automatic Injection: Buttons added to all code blocks
- Clipboard API: Modern async clipboard access
- Visual Feedback: “Copied!” confirmation
- Accessible: ARIA labels and keyboard support
Implementation
JavaScript
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('pre.highlight, pre code').forEach(function(pre) {
// Skip if already has button
if (pre.querySelector('.copy')) return;
var preElement = pre.tagName === 'PRE' ? pre : pre.closest('pre');
if (!preElement) return;
var button = document.createElement('button');
button.className = 'copy';
button.type = 'button';
button.setAttribute('aria-label', 'Copy code to clipboard');
button.innerHTML = '<i class="bi bi-clipboard me-1"></i>Copy';
button.addEventListener('click', function(e) {
e.preventDefault();
var code = preElement.querySelector('code');
if (!code) return;
navigator.clipboard.writeText(code.textContent).then(function() {
button.innerHTML = '<i class="bi bi-check me-1"></i>Copied!';
setTimeout(function() {
button.innerHTML = '<i class="bi bi-clipboard me-1"></i>Copy';
}, 2000);
});
});
preElement.appendChild(button);
});
});
Styling
Button Positioning
pre.highlight {
position: relative;
}
pre .copy {
position: absolute;
top: 0.5rem;
right: 0.5rem;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
background: var(--bs-secondary);
color: white;
border: none;
border-radius: var(--bs-border-radius);
opacity: 0;
transition: opacity 0.2s;
}
pre:hover .copy {
opacity: 1;
}
pre .copy:hover {
background: var(--bs-primary);
}
Success State
pre .copy.copied {
background: var(--bs-success);
}
Customization
Button Text
var copyText = 'Copy';
var copiedText = 'Copied!';
Icons
// Bootstrap Icons
button.innerHTML = '<i class="bi bi-clipboard"></i>';
// Text only
button.textContent = 'Copy';
// SVG icon
button.innerHTML = '<svg>...</svg>';
Always Visible
pre .copy {
opacity: 1;
}
Different Position
/* Bottom right */
pre .copy {
top: auto;
bottom: 0.5rem;
}
/* Top left */
pre .copy {
right: auto;
left: 0.5rem;
}
Clipboard API
Modern Approach
navigator.clipboard.writeText(text)
.then(() => console.log('Copied!'))
.catch(err => console.error('Failed to copy:', err));
Fallback for Older Browsers
function copyToClipboard(text) {
if (navigator.clipboard) {
return navigator.clipboard.writeText(text);
}
// Fallback
var textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
return Promise.resolve();
}
Accessibility
ARIA Labels
<button aria-label="Copy code to clipboard"
title="Copy code to clipboard">
Copy
</button>
Focus Styles
pre .copy:focus {
outline: 2px solid var(--bs-primary);
outline-offset: 2px;
}
pre .copy:focus-visible {
opacity: 1;
}
Screen Reader Feedback
// Announce copy success
button.setAttribute('aria-label', 'Copied to clipboard');
setTimeout(() => {
button.setAttribute('aria-label', 'Copy code to clipboard');
}, 2000);
Language-Specific
Skip Certain Languages
// Don't add to terminal output
if (pre.classList.contains('language-output')) return;
if (pre.classList.contains('language-console')) return;
Custom Label by Language
var lang = pre.className.match(/language-(\w+)/);
if (lang) {
button.setAttribute('aria-label', `Copy ${lang[1]} code`);
}
Troubleshooting
Button Not Appearing
- Check code block has
pre.highlightorpre code - Verify JavaScript is loaded
- Check CSS isn’t hiding button
- Inspect for duplicate buttons
Copy Not Working
- Check browser clipboard permissions
- Verify HTTPS (required for Clipboard API)
- Test fallback method
- Check for JavaScript errors
Styling Issues
- Verify
position: relativeon pre - Check z-index conflicts
- Test hover states
- Verify button is inside pre