浏览器复制文案功能兼容所有
/**
* 兼容所有浏览器的**到剪贴板函数(支持 Safari)
* 支持 \n 换行符
* @param {string} text - 要**的文本内容
* @returns {Promise<void>}
*/
export const copyToClipboard = (text: string): Promise<void> => {
return new Promise((resolve, reject) => {
// 检测是否为 Safari 浏览器
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
const isIOS = /ipad|iphone/i.test(navigator.userAgent);
// Safari 浏览器(包括 iOS)直接使用降级方法,因为 navigator.clipboard 在 Safari 中限制较多
if (isSafari || isIOS) {
fallbackCopyToClipboard(text, resolve, reject);
return;
}
// 其他浏览器优先使用现代 Clipboard API
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard
.writeText(text)
.then(() => resolve())
.catch((error) => {
// 如果 Clipboard API 失败,降级到传统方法
console.warn('Clipboard API failed, falling back to execCommand:', error);
fallbackCopyToClipboard(text, resolve, reject);
});
} else {
// 降级到传统方法
fallbackCopyToClipboard(text, resolve, reject);
}
});
};
/**
* 降级**方法(兼容 Safari 旧版本)
* @param {string} text - 要**的文本
* @param {Function} resolve - Promise resolve
* @param {Function} reject - Promise reject
*/
const fallbackCopyToClipboard = (
text: string,
resolve: () => void,
reject: (error: Error) => void
) => {
const isIOS = /ipad|iphone/i.test(navigator.userAgent);
if (isIOS) {
// iOS Safari 使用 contentEditable div 方法
const div = document.createElement('div');
div.contentEditable = 'true';
div.innerHTML = text.replace(/\n/g, '<br>');
div.style.position = 'fixed';
div.style.top = '-9999px';
div.style.left = '-9999px';
div.style.width = '1px';
div.style.height = '1px';
div.style.opacity = '0';
div.style.pointerEvents = 'none';
document.body.appendChild(div);
// 选择内容
const range = document.createRange();
range.selectNodeContents(div);
const selection = window.getSelection();
if (selection) {
selection.removeAllRanges();
selection.addRange(range);
}
setTimeout(() => {
try {
const successful = document.execCommand('copy');
if (document.body.contains(div)) {
document.body.removeChild(div);
}
if (selection) {
selection.removeAllRanges();
}
if (successful) {
resolve();
} else {
reject(new Error('execCommand copy failed on iOS'));
}
} catch (error) {
if (document.body.contains(div)) {
document.body.removeChild(div);
}
if (selection) {
selection.removeAllRanges();
}
reject(error as Error);
}
}, 100);
} else {
// 桌面 Safari 和其他浏览器使用 textarea
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.readOnly = false;
// 设置样式使其不可见但可**
textArea.style.position = 'fixed';
textArea.style.top = '0';
textArea.style.left = '0';
textArea.style.width = '1px';
textArea.style.height = '1px';
textArea.style.padding = '0';
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
textArea.style.background = 'transparent';
textArea.style.opacity = '0';
textArea.style.zIndex = '-9999';
textArea.style.pointerEvents = 'none';
// 添加到 DOM
document.body.appendChild(textArea);
// Safari 需要特殊处理
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (isSafari) {
// Safari 需要先设置 selectionRange,再 focus
textArea.setSelectionRange(0, text.length);
textArea.focus();
} else {
// 其他浏览器先 focus 再 select
textArea.focus();
textArea.select();
}
// 使用 setTimeout 确保选择操作完成(Safari 需要稍长的延迟)
const delay = isSafari ? 10 : 0;
setTimeout(() => {
try {
// 再次确保选择(Safari 有时需要)
if (isSafari) {
textArea.setSelectionRange(0, text.length);
}
// 执行**命令
const successful = document.execCommand('copy');
// 清理:移除临时元素
if (document.body.contains(textArea)) {
document.body.removeChild(textArea);
}
if (successful) {
resolve();
} else {
reject(new Error('execCommand copy failed'));
}
} catch (error) {
// 清理:移除临时元素
if (document.body.contains(textArea)) {
document.body.removeChild(textArea);
}
reject(error as Error);
}
}, delay);
}
};
您还未登录, 登录 后可进行评论
发表
还没有评论哦,来抢个沙发吧!