剪贴板事件
剪贴板事件(Clipboard Events)在用户执行复制、剪切、粘贴操作时触发。通过监听这些事件,可以自定义剪贴板行为、处理粘贴的数据格式、实现复制增强功能等。现代浏览器还提供了异步的 Clipboard API,用于更安全和灵活的剪贴板操作。
前置知识
阅读本节前,建议先了解:拖放事件
基础概念
剪贴板事件类型
| 事件 | 触发时机 | 说明 |
|---|---|---|
copy | 复制(Ctrl+C / Cmd+C) | 将选中内容复制到剪贴板 |
cut | 剪切(Ctrl+X / Cmd+X) | 将选中内容剪切到剪贴板 |
paste | 粘贴(Ctrl+V / Cmd+V) | 从剪贴板粘贴内容 |
html
<!-- HTML 事件属性 -->
<div oncopy="handleCopy(event)"
oncut="handleCut(event)"
onpaste="handlePaste(event)">
可以复制、剪切和粘贴的文本
</div>
<!-- 阻止复制 -->
<div oncopy="event.preventDefault(); showTip('不允许复制')">
受保护的内容
</div>Clipboard API vs 事件
| 方式 | 用途 | 特点 |
|---|---|---|
copy/cut/paste 事件 | 拦截/修改剪贴板操作 | 同步,需要用户触发 |
navigator.clipboard API | 主动读写剪贴板 | 异步,需要权限 |
document.execCommand | 旧式剪贴板操作 | 已废弃 |
语法
HTML 事件属性
html
<input onpaste="formatPastedText(event)"
oncopy="addCopyright(event)">
<textarea oncut="logCutContent(event)"
onpaste="sanitizeHTML(event)"></textarea>JavaScript 绑定
javascript
const editor = document.getElementById('editor');
// 复制事件
editor.addEventListener('copy', (e) => {
const selection = window.getSelection().toString();
e.clipboardData.setData('text/plain', selection + '\n\n--- 来自我的网站');
});
// 粘贴事件
editor.addEventListener('paste', (e) => {
const text = e.clipboardData.getData('text/plain');
console.log('粘贴内容:', text);
});详细说明
ClipboardEvent 对象
javascript
element.addEventListener('copy', (e) => {
// 读取当前选中内容
const selection = window.getSelection().toString();
console.log('选中内容:', selection);
// 修改剪贴板数据
e.clipboardData.setData('text/plain', '自定义复制内容');
e.clipboardData.setData('text/html', '<b>自定义</b>内容');
// 阻止默认复制行为
e.preventDefault();
});
element.addEventListener('paste', (e) => {
// 获取剪贴板数据
const text = e.clipboardData.getData('text/plain');
const html = e.clipboardData.getData('text/html');
console.log('纯文本:', text);
console.log('HTML:', html);
// 阻止默认粘贴行为
e.preventDefault();
// 自定义粘贴处理
document.execCommand('insertText', false, text.trim());
});Clipboard API(现代方式)
javascript
// 写入剪贴板(需要 HTTPS 或 localhost)
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log('已复制到剪贴板');
} catch (err) {
console.error('复制失败:', err);
// 降级方案
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
textarea.remove();
}
}
// 读取剪贴板
async function readClipboard() {
try {
const text = await navigator.clipboard.readText();
console.log('剪贴板内容:', text);
} catch (err) {
console.error('读取失败:', err);
}
}
// 写入富文本
async function copyRichText() {
const blob = new Blob(['<b>粗体</b>文字'], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({ 'text/html': blob }),
]);
}实战示例
完整的复制增强功能
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>剪贴板事件示例</title>
<style>
.code-block {
position: relative;
background: #1e293b;
color: #e2e8f0;
padding: 16px;
border-radius: 8px;
font-family: monospace;
white-space: pre;
overflow-x: auto;
}
.copy-btn {
position: absolute;
top: 8px;
right: 8px;
padding: 4px 12px;
background: #334155;
color: #94a3b8;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
}
.copy-btn:hover { background: #475569; }
.copy-btn.copied { background: #22c55e; color: white; }
.paste-area {
min-height: 200px;
border: 2px dashed #e2e8f0;
border-radius: 8px;
padding: 16px;
margin-top: 16px;
}
.paste-area.drag-over {
border-color: #3b82f6;
background: #eff6ff;
}
.toast {
position: fixed;
bottom: 20px;
right: 20px;
padding: 8px 16px;
border-radius: 8px;
color: white;
font-size: 14px;
z-index: 9999;
animation: fadeIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div class="code-block" id="code-block">
<button class="copy-btn" onclick="copyCode()">复制</button>
const greeting = "Hello, World!";
console.log(greeting);
</div>
<h3>粘贴区域(粘贴时自动清理格式)</h3>
<div class="paste-area" contenteditable="true" id="paste-area">
<p>在此处粘贴内容,HTML 格式将被自动清理。</p>
</div>
<script>
// 复制代码功能
async function copyCode() {
const code = document.getElementById('code-block').textContent
.replace('复制', '').trim();
try {
await navigator.clipboard.writeText(code);
showToast('已复制到剪贴板', '#22c55e');
} catch {
// 降级方案
const btn = document.querySelector('.copy-btn');
btn.textContent = '已复制';
btn.classList.add('copied');
setTimeout(() => {
btn.textContent = '复制';
btn.classList.remove('copied');
}, 2000);
}
}
// 粘贴时清理格式
document.getElementById('paste-area').addEventListener('paste', (e) => {
e.preventDefault();
// 获取纯文本
const text = e.clipboardData.getData('text/plain');
const html = e.clipboardData.getData('text/html');
// 如果有 HTML 格式,清理后只保留纯文本
if (html) {
// 使用 DOMParser 清理 HTML
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const cleanText = doc.body.textContent || text;
document.execCommand('insertText', false, cleanText);
showToast('已清理格式', '#3b82f6');
} else {
document.execCommand('insertText', false, text);
}
});
function showToast(msg, color) {
const toast = document.createElement('div');
toast.className = 'toast';
toast.style.background = color;
toast.textContent = msg;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2000);
}
</script>
</body>
</html>注意事项
安全限制
- 剪贴板 API 只能在安全上下文(HTTPS 或 localhost)中使用
navigator.clipboard.read()需要用户授权paste事件中e.clipboardData.getData()在某些浏览器中可能受限copy/cut事件中的e.preventDefault()可以阻止复制
浏览器兼容性
| API | Chrome | Firefox | Safari |
|---|---|---|---|
| copy/cut/paste 事件 | 全部支持 | 全部支持 | 全部支持 |
| clipboard.writeText | 66+ | 63+ | 13.1+ |
| clipboard.readText | 66+ | 63+ | 13.1+ |
| clipboard.write | 76+ | 未支持 | 13.1+ |
最佳实践
1. 提供复制按钮作为辅助
javascript
// 推荐:提供复制按钮
button.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(targetText);
showSuccess();
} catch {
fallbackCopy(targetText);
}
});下一节
继续学习:媒体事件