Clipboard Copy Tool with HTML, CSS & JavaScript
20 DAYS 20 PROJECT CHALLENGE
Day #17
Project Overview
Clipboard Copy Tool is a simple and beginner-friendly project created using HTML, CSS, and JavaScript. This tool lets users copy text (plain text, code blocks, emails, or form values) to the clipboard with one click. The application uses JavaScript to access the text that the user has typed or pastes and the Clipboard API to copy the text to the clipboard. Then, the application can display a confirmation message saying that the text has been copied to the clipboard.
This project is useful for beginners who want to understand how browser features can be used to interact with user input. It provides practice with DOM manipulation, click events, text input, the Clipboard API, asynchronous JavaScript, and user feedback.
Key Features
- Copy Text to Clipboard: Users can type or paste the desired text into the text area and click the Copy button to copy it directly to their clipboard.
- Text Area: This area enables users to provide the text they want to copy or make changes to the text.
- The Clipboard API: JavaScript utilizes the browser’s Clipboard API to copy text from the application to the clipboard.
- Copy Confirmation: The application can also show a confirmation message upon successful copying of text to the clipboard, indicating to the user that the task was completed.
- Empty Input Detection: The application can detect empty input data, preventing it from copying blank text to the clipboard.
- JavaScript Messages: JavaScript can update messages shown on the web page depending on the outcome of the copy operation, improving the overall interactivity of the application.
- Simple and Clean Design: The application features a design that enables easy viewing of the text area and Copy button.
- Responsive Design: The Clipboard Copy Tool adapts to different screen sizes, allowing users to use it on desktops, tablets, and mobile devices.
- Easy Customization: Developers can customize the application’s layout, copy button, messages, and design by changing default values or adjusting specific areas to fit their needs.
What You'll Learn
- Understand how to select HTML elements using JavaScript.
- Learn how to read values entered into a text area.
- Practice handling button click events.
- Understand how the Clipboard API works.
- Learn how
navigator.clipboard.writeText()copies text. - Practice working with asynchronous JavaScript.
- Learn how
asyncandawaitcan handle browser operations. - Understand how to validate user input before performing an action.
- Practice displaying success and error messages dynamically.
- Learn how JavaScript updates webpage content using the DOM.
HTML Code
HTML creates the structure of the Clipboard Copy Tool and the content is placed inside a container that holds the project heading, text input area, Copy button, and message area. The <textarea> element provides a space where users can enter or paste the text they want to copy and the Copy button is used to start the copying process when clicked.
A separate message element can display feedback such as whether the text was copied successfully or whether the user needs to enter some text first. These elements are given IDs or classes so JavaScript can select and update them when needed and the CSS file is linked inside the <head> section so it can control the appearance and layout of the Clipboard Copy Tool, script.js file is loaded near the bottom of the <body> so JavaScript can access the HTML elements and handle the copy functionality.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Day 17 — Clipboard Copy Tool</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main class="card" role="main" aria-labelledby="title">
<header>
<div class="logo">CC</div>
<div>
<h1 id="title">Day 17: Clipboard Copy Tool</h1>
<p class="lead">Click a button to copy text to the clipboard using the Clipboard API (with a fallback).</p>
</div>
</header>
<section class="panel">
<label class="field">
<span class="label">Short text</span>
<div class="row">
<input id="shortText" type="text" value="Hello, copy me!" />
<button class="btn copy-btn" data-copy-target="#shortText">Copy</button>
</div>
</label>
<label class="field">
<span class="label">Email</span>
<div class="row">
<input id="email" type="email" value="user@example.com" />
<button class="btn copy-btn" data-copy-target="#email">Copy</button>
</div>
</label>
<label class="field">
<span class="label">Multi-line / message</span>
<div class="row">
<textarea id="message" rows="3">This is a longer message you can copy to clipboard.</textarea>
<button class="btn copy-btn" data-copy-target="#message">Copy</button>
</div>
</label>
<label class="field">
<span class="label">Code snippet</span>
<pre class="code-block" id="codeBlock" tabindex="0"><code>const sum = (a, b) => a + b;</code></pre>
<div style="display:flex; gap:8px; margin-top:8px;">
<button class="btn copy-btn" data-copy-target="#codeBlock">Copy code</button>
<button class="btn" id="copyAll">Copy All Fields</button>
</div>
</label>
<div id="status" class="muted" aria-live="polite">Ready to copy.</div>
</section>
</main>
<script src="script.js"></script>
</body>
</html>
CSS Code
CSS controls the appearance and layout of the Clipboard Copy Tool. The main container is styled to create a clean and organized interface with appropriate spacing, padding, rounded corners, and other visual properties, text area is styled to provide enough space for users to enter or paste text comfortably. Its width can adjust according to the available screen size, helping the tool work on both large and small devices.
The Copy button is styled to make the main action easy to identify. CSS can also add hover and focus effects to provide visual feedback when users interact with the button. While message area can use different styles for successful and unsuccessful actions. Responsive CSS rules ensure that the Clipboard Copy Tool remains easy to use on desktops, tablets, and mobile screens.
:root {
--bg: #071026;
--card: #0b1220;
--accent: #7c3aed;
--muted: #9aa4b2;
--white: #e6eef6;
font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, Arial;
}
html,
body {
height: 100%;
}
body {
margin: 0;
background: #002252;
color: var(--white);
display: flex;
align-items: center;
justify-content: center;
padding: 28px;
}
.card {
width: min(760px, 96%);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0.01));
border-radius: 12px;
padding: 18px;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
border: 1px solid rgba(255, 255, 255, 0.03);
}
header {
display: flex;
gap: 12px;
align-items: center;
}
.logo {
width: 44px;
height: 44px;
border-radius: 10px;
background: linear-gradient(135deg, var(--accent), #22c1c3);
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
}
h1 {
margin: 0;
font-size: 18px;
}
.lead {
margin: 4px 0 12px;
color: var(--muted);
font-size: 14px;
}
.panel {
display: flex;
flex-direction: column;
gap: 14px;
margin-top: 12px
}
.field {
display: flex;
flex-direction: column;
gap: 8px
}
.label {
font-size: 13px;
color: var(--muted)
}
.row {
display: flex;
gap: 8px;
align-items: center
}
input[type="text"],
input[type="email"],
textarea {
padding: 10px 12px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.04);
background: transparent;
color: inherit;
outline: none;
min-width: 0;
}
.code-block {
background: rgba(255, 255, 255, 0.02);
border-radius: 8px;
padding: 12px;
border: 1px solid rgba(255, 255, 255, 0.03);
overflow: auto;
font-family: ui-monospace, monospace;
}
.btn {
padding: 8px 12px;
border-radius: 8px;
border: 0;
background: linear-gradient(90deg, var(--accent), #22c1c3);
color: white;
font-weight: 600;
cursor: pointer;
}
.copy-btn.copied {
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.06);
color: var(--muted);
animation: pop 220ms ease;
}
@keyframes pop {
from {
transform: scale(0.98);
opacity: 0.7
}
to {
transform: scale(1);
opacity: 1
}
}
.muted {
color: var(--muted);
font-size: 13px
} Javascript Code
JavaScript provides the main functionality of the Clipboard Copy Tool. First, it selects the text area, Copy button, and message element from the HTML. When the user clicks the Copy button, JavaScript reads the value entered into the text area. The code can first use trim() to check whether the user has entered any meaningful text. If the input is empty, an appropriate message can be displayed instead of attempting to copy it.
If text is available, JavaScript uses navigator.clipboard.writeText() to send the text to the browser’s clipboard. Since the Clipboard API works asynchronously, async and await or promises can be used to handle the result. After the text is copied successfully, JavaScript updates the message element to confirm the action. If an error occurs while accessing the clipboard, the application can display an error message, giving users clear feedback about what happened.
// Day 17 — Clipboard Copy Tool
// Uses navigator.clipboard when available, with document.execCommand fallback.
// DOM
const statusEl = document.getElementById('status');
const copyButtons = Array.from(document.querySelectorAll('.copy-btn'));
const copyAllBtn = document.getElementById('copyAll');
// Helper: read text from an element (input/textarea/code/block)
function readTextFromTarget(selectorOrEl) {
const el = typeof selectorOrEl === 'string' ? document.querySelector(selectorOrEl) : selectorOrEl;
if (!el) return '';
// inputs & textareas
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') return el.value;
// code / pre / div — use textContent
return el.textContent || el.innerText || '';
}
// Primary copy function using clipboard API with fallback
async function copyText(text) {
if (!text && text !== '') {
return { ok: false, message: 'Nothing to copy' };
}
// Try modern API first
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
try {
await navigator.clipboard.writeText(text);
return { ok: true };
} catch (err) {
// fallthrough to fallback
console.warn('Clipboard API failed, falling back to legacy method', err);
}
}
// Fallback approach: create a temporary textarea, select, execCommand
try {
const ta = document.createElement('textarea');
ta.value = text;
// avoid flash on screen
ta.style.position = 'fixed';
ta.style.left = '-9999px';
ta.style.top = '0';
document.body.appendChild(ta);
ta.focus();
ta.select();
const successful = document.execCommand('copy');
document.body.removeChild(ta);
if (successful) return { ok: true };
return { ok: false, message: 'Copy command unsuccessful' };
} catch (err) {
console.error('Fallback copy failed', err);
return { ok: false, message: err && err.message ? err.message : 'Copy failed' };
}
}
// UI feedback helper
function showStatus(msg, success = true) {
statusEl.textContent = msg;
statusEl.style.color = success ? '' : '#ffbaba';
}
// Button feedback: temporary change text + class
function indicateButtonCopied(btn) {
const original = btn.textContent;
btn.classList.add('copied');
btn.textContent = 'Copied!';
setTimeout(() => {
btn.classList.remove('copied');
btn.textContent = original;
}, 1200);
}
// Attach per-button handlers
copyButtons.forEach(btn => {
btn.addEventListener('click', async (e) => {
e.preventDefault();
const target = btn.dataset.copyTarget;
if (!target) {
showStatus('No target found to copy.', false);
return;
}
const text = readTextFromTarget(target);
if (!text) {
showStatus('Nothing to copy.', false);
return;
}
const res = await copyText(text);
if (res.ok) {
indicateButtonCopied(btn);
showStatus('Copied to clipboard.');
} else {
showStatus('Copy failed: ' + (res.message || 'unknown error'), false);
}
});
});
// Copy all fields example: combines shortText + email + message + code
copyAllBtn.addEventListener('click', async (e) => {
e.preventDefault();
const parts = [];
const shortText = readTextFromTarget('#shortText');
const email = readTextFromTarget('#email');
const message = readTextFromTarget('#message');
const code = readTextFromTarget('#codeBlock');
if (shortText) parts.push(shortText);
if (email) parts.push(email);
if (message) parts.push(message);
if (code) parts.push('Code:\n' + code);
const combined = parts.join('\n\n');
if (!combined) {
showStatus('Nothing to copy.', false);
return;
}
const res = await copyText(combined);
if (res.ok) {
showStatus('All fields copied to clipboard.');
// visual feedback on the copyAll button
copyAllBtn.textContent = 'Copied!';
setTimeout(() => (copyAllBtn.textContent = 'Copy All Fields'), 1200);
} else {
showStatus('Copy failed: ' + (res.message || 'unknown error'), false);
}
});
// Accessibility: allow Enter/Space to trigger copy when button focused (native)
copyButtons.forEach(b => {
b.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
b.click();
}
});
});
// Optional: show support notice if Clipboard API not available
(function checkSupport(){
if (!navigator.clipboard || typeof navigator.clipboard.writeText !== 'function') {
showStatus('Clipboard API unavailable — using fallback copy method.');
}
})();
Related Projects
Day 15 : Light/Dark Mode Toggle
Switch between light and dark themes with one click (and remember the choice).
Concepts: CSS variables, LocalStorage.
Day 19 : Product Filter List
Filter products or items by search or category in real-time.
Concepts: Array filter(), DOM rendering.
Day 20 : Music Player App
A mini music player with play, pause, next, and progress bar.
Concepts: Audio API, event listeners, state management.