Copy to Clipboard - Copy văn bản vào clipboard
Có 2 cách chính: cách cũ dùng document.execCommand('copy') (deprecated nhưng vẫn dùng được) và cách hiện đại dùng Clipboard API (navigator.clipboard.writeText()).
Cách hiện đại: Clipboard API (Khuyến nghị)
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Copy to Clipboard</title>
<style>
body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; }
.result { font-size: 18px; font-weight: 500; color: rebeccapurple; margin-top: 15px; }
input, button { padding: 10px; font-size: 16px; }
.success { color: green; }
.error { color: red; }
</style>
</head>
<body>
<h1>Copy to Clipboard (Clipboard API)</h1>
<input type="text" id="textInput" placeholder="Nhập văn bản để copy" style="width: 300px;" />
<button id="btnCopy">COPY</button>
<br><br>
<input type="text" id="pasteArea" placeholder="Dán vào đây để kiểm tra" style="width: 300px;" />
<div class="result" id="status"></div>
<script>
const textInput = document.getElementById('textInput');
const pasteArea = document.getElementById('pasteArea');
const btnCopy = document.getElementById('btnCopy');
const status = document.getElementById('status');
btnCopy.addEventListener('click', async () => {
const text = textInput.value;
if (!text) {
status.textContent = 'Vui lòng nhập văn bản!';
status.className = 'result error';
return;
}
try {
// Clipboard API - yêu cầu HTTPS hoặc localhost
await navigator.clipboard.writeText(text);
status.textContent = '✓ Đã copy vào clipboard!';
status.className = 'result success';
} catch (err) {
status.textContent = '✗ Lỗi: ' + err.message;
status.className = 'result error';
}
});
// Cho phép dán (paste) để test
pasteArea.addEventListener('paste', (e) => {
status.textContent = 'Đã dán: ' + e.clipboardData.getData('text');
status.className = 'result';
});
</script>
</body>
</html>
Cách cũ: execCommand (Deprecated)
function copyOldWay(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed'; // Tránh scroll
document.body.appendChild(textarea);
textarea.select();
try {
const successful = document.execCommand('copy');
console.log(successful ? 'Copied!' : 'Failed');
} catch (err) {
console.error('Copy failed:', err);
}
document.body.removeChild(textarea);
}
So sánh
| Đặc điểm | Clipboard API | execCommand |
|---|---|---|
| Trạng thái | Chuẩn hiện đại | Deprecated |
| Async/Promise | ✓ (async/await) | ✗ (Sync) |
| Yêu cầu HTTPS | Có (trừ localhost) | Không |
| Quyền truy cập | Cần permission (user gesture) | Cần user gesture |
| Hỗ trợ | Chrome 66+, FF 63+, Safari 13.1+, Edge 79+ | Mọi trình duyệt cũ |
Xử lý quyền (Permissions)
// Kiểm tra quyền clipboard
const permission = await navigator.permissions.query({ name: 'clipboard-write' });
console.log(permission.state); // 'granted', 'denied', 'prompt'
// Lắng nghe thay đổi quyền
permission.onchange = () => {
console.log('Permission changed to:', permission.state);
};
Copy nội dung phức tạp (HTML, Image)
// Copy HTML
async function copyHTML(html) {
const blob = new Blob([html], { type: 'text/html' });
const data = [new ClipboardItem({ 'text/html': blob })];
await navigator.clipboard.write(data);
}
// Copy Image
async function copyImage(imgElement) {
const canvas = document.createElement('canvas');
canvas.width = imgElement.naturalWidth;
canvas.height = imgElement.naturalHeight;
canvas.getContext('2d').drawImage(imgElement, 0, 0);
const blob = await new Promise(r => canvas.toBlob(r));
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
}
Fallback cho trình duyệt cũ
async function copyToClipboard(text) {
if (navigator.clipboard && window.isSecureContext) {
// Modern API
return navigator.clipboard.writeText(text);
} else {
// Fallback execCommand
return new Promise((resolve, reject) => {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
document.body.appendChild(textarea);
textarea.select();
try {
resolve(document.execCommand('copy'));
} catch (err) {
reject(err);
} finally {
document.body.removeChild(textarea);
}
});
}
}