在Web开发中,复制文本到剪贴板是常见功能,例如一键复制分享链接、邀请码或代码片段。传统方案 document.execCommand('copy') 兼容性好但需临时DOM元素,现代 navigator.clipboard API 简洁但依赖安全上下文(HTTPS)。本文实现一个统一工具函数 copyText,自动检测环境并降级,同时提供灵活配置。
## API演进与对比
document.execCommand 方案:- const textarea = document.createElement('textarea')
- textarea.value = content
- document.body.appendChild(textarea)
- textarea.select()
- document.execCommand('copy')
- document.body.removeChild(textarea)
复制代码 优点:支持所有主流浏览器;缺点:代码冗长,需创建临时元素。
navigator.clipboard 方案:- await navigator.clipboard.writeText(content)
复制代码 优点:直接操作剪贴板;缺点:需HTTPS或localhost,部分浏览器受限。
## 统一工具函数实现
使用TypeScript定义输入输出接口,核心函数如下:- export interface CopyTextOptions {
- allowWhitespace?: boolean
- legacy?: boolean
- }
- export interface CopyTextReturn {
- success: boolean
- message: string
- }
- export async function copyText(content: string, options: CopyTextOptions = {}): Promise<CopyTextReturn> {
- try {
- const { allowWhitespace = false, legacy = false } = options
- if (!allowWhitespace && (!content || content.trim() === '')) {
- return { success: false, message: '复制内容不能为空' }
- } else if (navigator.clipboard && window.isSecureContext && !legacy) {
- await navigator.clipboard.writeText(content)
- } else {
- const textarea = document.createElement('textarea')
- textarea.style.cssText = 'position:fixed; opacity:0; z-index:-9999; left:-9999px; top:-9999px;'
- textarea.value = content
- document.body.appendChild(textarea)
- textarea.select()
- textarea.setSelectionRange?.(0, content.length)
- const copied = document.execCommand('copy')
- document.body.removeChild(textarea)
- if (!copied) throw new Error('浏览器限制或无法复制')
- }
- return { success: true, message: '复制成功' }
- } catch (error: unknown) {
- const errMsg = error instanceof Error ? error.message : '未知错误'
- return { success: false, message: `${errMsg}` }
- }
- }
复制代码
## 关键参数与降级逻辑
- allowWhitespace:默认为false,拒绝空字符串或纯空格内容,防止误操作。设为true可复制空白内容。
- legacy:强制使用 execCommand 方案,适用于iframe等特殊环境。
执行优先级判断:
1. 若 navigator.clipboard 可用且 isSecureContext 为 true(HTTPS或localhost),且 legacy 未启用,则使用现代API。
2. 否则降级到 execCommand:创建隐藏textarea,自动选中文本,执行复制命令。
3. 若 execCommand 返回false,抛出异常并返回失败信息。
兼容性细节:textarea.style.cssText 中设置了 left:-9999px 和 opacity:0 确保不影响布局,且 iOS Safari 需要调用 setSelectionRange 才能正确选中文本。
## 安全上下文要求
navigator.clipboard 要求页面处于安全上下文:HTTPS、localhost 或 Chrome Extension。生产环境务必使用HTTPS,否则自动降级。
## 使用示例
基础用法:- const result = await copyText('hello world')
- if (result.success) {
- console.log('复制成功')
- } else {
- console.error(result.message)
- }
复制代码
允许空白内容:- const result = await copyText(userInput, { allowWhitespace: true })
复制代码
强制传统方案:- const result = await copyText(content, { legacy: true })
复制代码
集成UI提示(以Element Plus为例):- import { ElMessage } from 'element-plus'
- // 复制前检查
- if (!allowWhitespace && (!content || content.trim() === '')) {
- ElMessage.error('复制内容不能为空')
- return { success: false, message: '复制内容不能为空' }
- }
- // 复制成功后
- ElMessage.success('复制成功')
复制代码
## 总结
该工具函数约50行,覆盖了主流浏览器复制场景:自动降级、安全上下文判断、灵活配置、统一返回结构。可直接集成到项目中,满足大多数Web应用的复制需求。 |