1. 定义复制拷贝的方法
在某个工具类方法中定义该方法,兼容不同浏览器处理
/**
* @description 拷贝的类方法
*/
class CopyClass {
constructor() {
this.idName = `copy-textarea-${
Math.floor(Math.random() * 10000)}`;
}
queryCopyCommandSupported() {
return new Promise((resolve, reject) => {
const errorObj = new Error('浏览器不支持复制功能!');
try {
const result = document.queryCommandSupported('copy');
if (result) {
resolve();
} else {
reject(errorObj);
}
} catch (error) {
reject(errorObj);
}
});
}
setRange() {
return new Promise((resolve, reject) => {
try {
// 创建range对象
const range = document.createRange();
const dom = document.getElementById(this.idName);
// range.selectNodeContents(textarea);
// // 获取复制内容的 id 选择器
range.selectNode(dom);
// // 创建 selection对象
const selection = window.getSelection();
// // 如果页面已经有选取了的话,会自动删除这个选区,没有选区的话,会把这个选取加入选区
if (selection.rangeCount > 0) {
selection.removeAllRanges();
} else {
//
}
// textarea.setSelectionRange(0, 99999); /* 为移动设备设置 */
// 将range对象添加到selection选区当中,会高亮文本块
selection.addRange(range);
this.execCommand().then(() => {
resolve();
}).catch(e => {
reject(e);
});
resolve();
} catch (error) {
reject(error);
}
});
}
insertDom(val, type) {
return new Promise((resolve, reject) => {
try {
const dom = document.createElement(type || 'div'); // 创建input对象
dom.id = this.idName;
// 避免Safari的选中效果
dom.style.width = 0;
dom.style.position = 'fixed';
dom.style.left = '-999px';
dom.style.top = '10px';
if (type === 'textarea') {
dom.value = val;
dom.setAttribute('readonly', 'readonly');
} else {
dom.innerHTML = val;
}
document.body.appendChild(dom);
resolve();
} catch (error) {
reject(error);
}
});
}
removeInput() {
return new Promise((resolve, reject) => {
try {
document.getElementById(this.idName).remove();
resolve();
} catch (error) {
reject(error);
}
});
}
execCommand() {
return new Promise((resolve, reject) => {
try {
// 该方法已弃用
const result = document.execCommand('copy');
if (result) {
resolve();
} else {
reject(new Error('document.execCommand方法未生效'));
}
} catch (error) {
reject(error);
}
});
}
inputSelectCopy() {
return new Promise((resolve, reject) => {
try {
document.getElementById(this.idName).select();
this.execCommand().then(() => {
resolve();
}).catch(e => {
reject(e);
});
} catch (error) {
reject(error);
}
});
}
clipboardWriteText(val) {
return new Promise((resolve, reject) => {
try {
navigator.clipboard.writeText(val).then(res => {
resolve();
}).catch(e => {
reject(e);
});
} catch (error) {
reject(error);
}
});
}
copy(val) {
return new Promise((resolve, reject) => {
if (val && typeof val === 'string') {
const errorObj = new Error('页面复制内容失败!');
this.clipboardWriteText(val).then(() => {
resolve();
}).catch(e => {
this.queryCopyCommandSupported().then(() => {
this.insertDom(val).then(res => {
this.setRange().then(() => {
resolve();
this.removeInput(() => {
}).catch(e => {
});
}).catch(e => {
this.removeInput(() => {
this.insertDom(val, 'textarea').then(() => {
this.inputSelectCopy().then(() => {
resolve();
this.removeInput(() => {
}).catch(e => {
});
}).catch(e => {
reject(errorObj);
this.removeInput(() => {
}).catch(e => {
});
});
}).catch(e => {
reject(errorObj);
this.removeInput(() => {
}).catch(e => {
});
});
}).catch(e => {
reject(errorObj);
});
});
}).catch(e => {
reject(errorObj);
});
}).catch(e => {
reject(e);
});
});
} else {
reject(new Error('不能复制无效内容!'));
}
});
}
}
/**
* @description 拷贝
*/
export function copy(val) {
const copyClass = new CopyClass();
return copyClass.copy(val);
}
2. 实际调用
import copy from '@/utils/index';
copy('需要拷贝的内容').then((res) => {
// 成功提示
}).catch(e => {
// 报错提示
});
3. 涉及方法
- document.createRange: 返回一个 Range 对象。
- selectNode: Range.selectNode() 方法将 Range 设置为包含整个 Node 及其内容。Range 的起始和结束节点的父节点与 referenceNode 的父节点相同。
- window.getSelection: 返回一个 Selection 对象,表示用户选择的文本范围或光标的当前位置。
- removeAllRanges: 从Selection对象里移除Range对象。
- addRange: 将一个Range对象添加到Selection对象中。
- document.execCommand: 当一个 HTML 文档切换到设计模式时,document暴露 execCommand 方法,该方法允许运行命令来操纵可编辑内容区域的元素。这里我们使用copy命令来复制所选内容。 需要注意的是,
该方法已经被弃用了!!!
- navigator.clipboard.writeText: Clipboard 接口的 writeText() 方法可以写入特定字符串到操作系统的剪切板, 该方法返回一个关于执行情况的Promise。
4. 注意问题
- chrome浏览器规定,只有https协议的页面才能使用
navigator.clipboard
的相关api,同时浏览器提示用户是否授权使用时必须同意才能使用该api。 selectNode
方法只能选择文档中的节点,如果是新建的节点必须先插入文档再去选择。- 为了兼容使用Range对象处理选中内容,不能直接用textarea去
addRange
,否则复制的内容前面出现换行。 document.execCommand
方法已经被弃用了,此处封装是为了兼容更多浏览器使用。document.execCommand
方法在某些浏览器环境下不支持异步操作,建议复制方法直接在选中方法后直接执行,避免出现异步情况。
You can only trigger a copy to the system clipboard in direct response to a trusted user action, such as a click event.