pin_drop当前位置:知识文库 ❯ 图文

HTML5 API:HTML5 Clipboard API - 完整教程与代码示例

一、教程简介

Clipboard API 提供了在 Web 应用中读写系统剪贴板的能力,允许开发者以编程方式复制文本、图片等数据到剪贴板,或从剪贴板读取内容。该 API 替代了传统的 document.execCommand('copy') 方法,提供了更安全、更强大的异步剪贴板操作接口,广泛应用于复制按钮、粘贴处理、富文本编辑等场景。


二、核心概念

API 概览

方法 说明
navigator.clipboard.readText() 读取剪贴板文本
navigator.clipboard.writeText(text) 写入文本到剪贴板
navigator.clipboard.read() 读取剪贴板数据(含图片等)
navigator.clipboard.write(data) 写入数据到剪贴板(含图片等)

权限模型

操作 权限 触发方式
writeText 无需额外权限 需用户操作触发
write 无需额外权限 需用户操作触发
readText 需要用户授权 需用户操作触发
read 需要用户授权 需用户操作触发

ClipboardEvent

事件 说明
copy 复制操作时触发
cut 剪切操作时触发
paste 粘贴操作时触发

三、语法与用法

写入文本

代码示例

// 异步写入
async function copyText(text) {
    try {
        await navigator.clipboard.writeText(text);
        console.log('复制成功');
    } catch (err) {
        console.error('复制失败:', err);
    }
}

读取文本

代码示例

async function pasteText() {
    try {
        const text = await navigator.clipboard.readText();
        console.log('粘贴内容:', text);
    } catch (err) {
        console.error('读取失败:', err);
    }
}

写入图片

代码示例

async function copyImage(blob) {
    try {
        await navigator.clipboard.write([
            new ClipboardItem({ 'image/png': blob })
        ]);
    } catch (err) {
        console.error('复制图片失败:', err);
    }
}

监听剪贴板事件

代码示例

document.addEventListener('copy', function(e) {
    e.preventDefault();
    e.clipboardData.setData('text/plain', '自定义复制内容');
});

document.addEventListener('paste', function(e) {
    const text = e.clipboardData.getData('text/plain');
    console.log('粘贴:', text);
});

四、代码示例

示例一:剪贴板操作工具

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Clipboard API - 剪贴板工具</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: #f5f7fa;
            min-height: 100vh;
            padding: 30px 20px;
        }
        .container { max-width: 640px; margin: 0 auto; }
        h1 { font-size: 24px; color: #2d3436; margin-bottom: 8px; }
        .desc { color: #636e72; font-size: 14px; margin-bottom: 24px; }
        .card {
            background: #fff;
            border-radius: 12px;
            padding: 24px;
            margin-bottom: 16px;
            box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
        }
        .card h2 { font-size: 16px; color: #2d3436; margin-bottom: 16px; }
        textarea {
            width: 100%;
            padding: 12px;
            border: 1px solid #ddd;
            border-radius: 8px;
            font-size: 14px;
            min-height: 100px;
            resize: vertical;
            outline: none;
            font-family: inherit;
        }
        textarea:focus { border-color: #0984e3; }
        .btn-group { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
        button {
            padding: 8px 18px;
            border: none;
            border-radius: 8px;
            font-size: 13px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s;
        }
        .btn-blue { background: #0984e3; color: #fff; }
        .btn-blue:hover { background: #0770c2; }
        .btn-green { background: #00b894; color: #fff; }
        .btn-green:hover { background: #00a381; }
        .btn-orange { background: #fdcb6e; color: #333; }
        .btn-orange:hover { background: #e5b85c; }
        .toast {
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 12px 20px;
            border-radius: 8px;
            font-size: 14px;
            font-weight: 600;
            color: #fff;
            z-index: 9999;
            transform: translateX(120%);
            transition: transform 0.3s ease;
        }
        .toast.show { transform: translateX(0); }
        .toast.success { background: #00b894; }
        .toast.error { background: #e17055; }
        .copy-items { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
        .copy-item {
            padding: 12px;
            border: 1px solid #eee;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.2s;
        }
        .copy-item:hover { border-color: #0984e3; background: #f0f7ff; }
        .copy-item-label { font-size: 12px; color: #999; margin-bottom: 4px; }
        .copy-item-value { font-size: 14px; color: #333; font-weight: 500; word-break: break-all; }
        .history-list { max-height: 200px; overflow-y: auto; }
        .history-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 0;
            border-bottom: 1px solid #f0f0f0;
            font-size: 13px;
        }
        .history-text { flex: 1; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-right: 12px; }
        .history-time { color: #999; font-size: 11px; white-space: nowrap; }
    </style>
</head>
<body>
    <div class="container">
        <h1>剪贴板操作工具</h1>
        <p class="desc">使用 Clipboard API 进行复制、粘贴和剪贴板事件监听。</p>

        <div class="card">
            <h2>复制文本</h2>
            <div class="copy-items">
                <div class="copy-item" onclick="copyText('hello@example.com')">
                    <div class="copy-item-label">邮箱</div>
                    <div class="copy-item-value">hello@example.com</div>
                </div>
                <div class="copy-item" onclick="copyText('13800138000')">
                    <div class="copy-item-label">电话</div>
                    <div class="copy-item-value">13800138000</div>
                </div>
                <div class="copy-item" onclick="copyText('https://example.com')">
                    <div class="copy-item-label">网址</div>
                    <div class="copy-item-value">https://example.com</div>
                </div>
                <div class="copy-item" onclick="copyText('npm install clipboard')">
                    <div class="copy-item-label">命令</div>
                    <div class="copy-item-value">npm install clipboard</div>
                </div>
            </div>
        </div>

        <div class="card">
            <h2>自定义复制</h2>
            <textarea id="copyInput" placeholder="输入要复制的文本..."></textarea>
            <div class="btn-group">
                <button class="btn-blue" onclick="copyCustom()">复制文本</button>
                <button class="btn-green" onclick="pasteCustom()">粘贴到下方</button>
            </div>
        </div>

        <div class="card">
            <h2>粘贴区域</h2>
            <textarea id="pasteOutput" placeholder="粘贴的内容会显示在这里..." readonly></textarea>
            <div class="btn-group">
                <button class="btn-orange" onclick="document.getElementById('pasteOutput').value=''">清空</button>
            </div>
        </div>

        <div class="card">
            <h2>操作历史</h2>
            <div class="history-list" id="historyList">
                <div style="text-align:center;color:#999;padding:20px;font-size:13px;">暂无操作记录</div>
            </div>
        </div>
    </div>

    <div class="toast" id="toast"></div>

    <script>
        let history = [];

        async function copyText(text) {
            try {
                await navigator.clipboard.writeText(text);
                showToast('复制成功!', 'success');
                addHistory('复制', text);
            } catch (err) {
                fallbackCopy(text);
            }
        }

        async function copyCustom() {
            const text = document.getElementById('copyInput').value;
            if (!text) { showToast('请输入文本', 'error'); return; }
            await copyText(text);
        }

        async function pasteCustom() {
            try {
                const text = await navigator.clipboard.readText();
                document.getElementById('pasteOutput').value = text;
                showToast('粘贴成功!', 'success');
                addHistory('粘贴', text);
            } catch (err) {
                showToast('粘贴失败:' + err.message, 'error');
            }
        }

        function fallbackCopy(text) {
            const textarea = document.createElement('textarea');
            textarea.value = text;
            textarea.style.cssText = 'position:fixed;left:-9999px';
            document.body.appendChild(textarea);
            textarea.select();
            try {
                document.execCommand('copy');
                showToast('复制成功(降级方式)', 'success');
                addHistory('复制', text);
            } catch (err) {
                showToast('复制失败', 'error');
            }
            document.body.removeChild(textarea);
        }

        function showToast(message, type) {
            const toast = document.getElementById('toast');
            toast.textContent = message;
            toast.className = 'toast ' + type + ' show';
            setTimeout(() => toast.classList.remove('show'), 2500);
        }

        function addHistory(action, text) {
            history.unshift({ action, text, time: new Date().toLocaleTimeString('zh-CN') });
            if (history.length > 20) history.pop();
            renderHistory();
        }

        function renderHistory() {
            const list = document.getElementById('historyList');
            list.innerHTML = history.map(function (item) {
                const display = item.text.length > 40 ? item.text.substring(0, 40) + '...' : item.text;
                return '<div class="history-item"><span class="history-text" title="' + escapeHtml(item.text) + '">[' + item.action + '] ' + escapeHtml(display) + '</span><span class="history-time">' + item.time + '</span></div>';
            }).join('');
        }

        function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; }

        document.addEventListener('paste', function (e) {
            const text = e.clipboardData.getData('text/plain');
            if (text) { addHistory('粘贴事件', text); }
        });
    </script>
</body>
</html>

五、浏览器兼容性

浏览器 writeText readText write(图片) read(图片)
Chrome 66+ 66+ 76+ 76+
Firefox 63+ 63+ 不支持 不支持
Safari 13.1+ 13.1+ 13.1+ 13.1+
Edge 79+ 79+ 79+ 79+
IE 不支持 不支持 不支持 不支持

六、注意事项与最佳实践

1. 安全限制

  • 所有 Clipboard API 调用必须由用户操作触发

  • readreadText 需要用户授权

  • 必须在安全上下文(HTTPS)中使用

2. 降级方案

代码示例

async function copyToClipboard(text) {
    if (navigator.clipboard && navigator.clipboard.writeText) {
        try {
            await navigator.clipboard.writeText(text);
            return true;
        } catch (e) { /* 降级 */ }
    }
    // 降级到 execCommand
    const textarea = document.createElement('textarea');
    textarea.value = text;
    textarea.style.cssText = 'position:fixed;opacity:0';
    document.body.appendChild(textarea);
    textarea.select();
    const result = document.execCommand('copy');
    document.body.removeChild(textarea);
    return result;
}

3. 复制图片

代码示例

async function copyCanvasImage(canvas) {
    try {
        const blob = await new Promise(resolve => canvas.toBlob(resolve));
        await navigator.clipboard.write([
            new ClipboardItem({ 'image/png': blob })
        ]);
    } catch (err) {
        console.error('复制图片失败:', err);
    }
}

七、代码规范示例

代码示例

class ClipboardHelper {
    static async writeText(text) {
        if (navigator.clipboard?.writeText) {
            await navigator.clipboard.writeText(text);
            return true;
        }
        return this._fallbackWrite(text);
    }

    static async readText() {
        if (navigator.clipboard?.readText) {
            return await navigator.clipboard.readText();
        }
        throw new Error('Clipboard readText 不可用');
    }

    static async write(items) {
        if (navigator.clipboard?.write) {
            await navigator.clipboard.write(items);
            return true;
        }
        throw new Error('Clipboard write 不可用');
    }

    static _fallbackWrite(text) {
        const el = document.createElement('textarea');
        el.value = text;
        el.style.cssText = 'position:fixed;opacity:0;left:-9999px';
        document.body.appendChild(el);
        el.select();
        const ok = document.execCommand('copy');
        document.body.removeChild(el);
        return ok;
    }
}

八、常见问题与解决方案

常见问题

Clipboard API 在 HTTP 下不可用?

Clipboard API 要求安全上下文(HTTPS 或 localhost)。HTTP 环境下需降级为 execCommand

读取剪贴板时弹出权限提示?

readTextread 需要用户授权。可以先检查权限状态:const permission = await navigator.permissions.query({ name: 'clipboard-read' })

如何复制富文本?

使用 ClipboardEvent 拦截复制事件,通过 e.clipboardData.setData('text/html', '<b>粗体文本</b>') 设置富文本内容。


九、总结

Clipboard API 提供了现代化的异步剪贴板操作接口,支持文本和图片的读写。使用时需注意安全限制(HTTPS、用户操作触发)、权限管理(读取需授权)和降级方案(execCommand)。通过 ClipboardEvent 可以拦截和自定义复制/粘贴行为。封装工具类可以简化 API 调用并自动处理兼容性问题。

标签: 剪贴板API Clipboard API 复制粘贴 异步操作 HTML5 API

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: HTML5 API:HTML5 Notification API - 完整教程与代码示例 下一篇: HTML5 API:HTML5 Mutation Observer - 完整教程与代码示例

poll相关推荐