Skip to content

剪贴板事件

剪贴板事件(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() 可以阻止复制

浏览器兼容性

APIChromeFirefoxSafari
copy/cut/paste 事件全部支持全部支持全部支持
clipboard.writeText66+63+13.1+
clipboard.readText66+63+13.1+
clipboard.write76+未支持13.1+

最佳实践

1. 提供复制按钮作为辅助

javascript
// 推荐:提供复制按钮
button.addEventListener('click', async () => {
  try {
    await navigator.clipboard.writeText(targetText);
    showSuccess();
  } catch {
    fallbackCopy(targetText);
  }
});

下一节

继续学习:媒体事件

参考链接