实现右击复制页面渲染好的html标签内容,网上看了很多文章都不适用,感觉有些乱七八糟的看不懂,有些文章已经过时了,我本来也是想去用这种方法的:document.execCommand(),但已经废弃了,我还要兼容客户端的,不过这个复制文本不管是剪贴还是复制都差不多类似,所以就灵活运用场景了。
目前业务需求是仅支持文本和图片的复制:所以我真对这两个做了重点的笔记,如果需要其他类型的内容支持,可以参考:MIME类型列表(就是文本类型,别被名词劝退了)
- 第一种:navigator.clipboard:
处于chrome的安全机制,仅支持localhost端口或https的路径
const copyHandler = async () => {
try {
/**
* 复制文字
*/
await navigator.clipboard.write([
new ClipboardItem({
'text/plain': new Blob(['text align font size'], { type: 'text/plain' })
})
]);
const imgURL = 'http://www.deathghost.cn/public/images/touch-icon-120.png';
const data = await fetch(imgURL);
const blob = await data.blob();
/**
* 复制图片
*/
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob
})
]);
const html = '<div>'+
`<img src='${imgURL}' width="100" />`+
`<p>${imgURL}</p>`+
'</div>';
/**
* 复制html
*/
await navigator.clipboard.write([
new ClipboardItem({
'text/html': new Blob([html], { type: 'text/html' })
})
]);
} catch(err: any) {
console.error(err.name, err.message);
}
}
- 第二种:document.execCommand(‘copy’)
const target: any = document.createElement('div');
target.id = 'tempTarget';
target.style.opacity = '0';
target.innerHTML = `<div>\
22222222222222222222222\
<i style="display:inline-block" cosmo-type="cosmo-img" contentEditable="false"><img contentEditable="false" style="width: 200px;height: 100px;object-fit:cover" src='${audioIcon}'/></i>\
</div>`;
document.body.appendChild(target);
try {
const range = document.createRange();
const sel: any = window.getSelection()
range.selectNode(target);
// 注意:removeAllRanges必须放在addRange之前,
// 否则虽然会打印复制成功,但并未复制上内容
sel.removeAllRanges();
sel.addRange(range);
document.execCommand('copy');
console.log('复制成功')
} catch (e) {
console.log('复制失败')
}
target.parentElement.removeChild(target);
3.使用实例:
const copyHandler = async () => {
console.log(_settingsMsg.chatRss, rightInfo, '===----');
console.log(dealWithDom(_settingsMsg.chatRss, rightInfo), '=====000000000000 rightInfo');
// 复制 参考:https://developer.mozilla.org/zh-CN/docs/Web/API/ClipboardItem
// 复制示例模板:new Blob(['copyStatus! 标签'])
await navigator.clipboard?.write([
new ClipboardItem({
'text/plain': new Blob([dealWithDom(_settingsMsg.chatRss, rightInfo)], {
type: 'text/plain',
}),
}),
]);
};
/**
* 复制和重新编辑、草稿等公用方法
* @param baseUrl: 后端返回域名
* list: 就是当前一条的聊天记录信息
* id?: 草稿中的返回id
* url?: 草稿中是base64的会用本地的域名
*/
export const dealWithDom = (baseUrl: string, list: { message: any[] }, id?: string, url = '') => {
// id存在
let num = 1;
// 要复制数据的枚举
const imageDomFun = (item: { path: string; content: string }) => {
const isBase64 = (item.path || item.content).includes(';base64');
const str = `<img cosmo-type="${
isBase64 ? 'cosmo-img' : 'cosmo-upload-img'
}" style="width: 200px;height: 100px;object-fit:cover" src="${
(isBase64 ? '' : baseUrl) + (item.path || item.content)
}"/>`;
num++;
return str;
};
const setContextEnum = {
image: imageDomFun,
'upload-image': imageDomFun,
text: (item: { content: string }) => {
return `${item.content}`;
},
emo: (item: { content: string }) => {
const newItem = emoDefaultList.find((element) => `[${element.status}]` === item.content);
return newItem
? `<img src="${newItem.path}" alt="${newItem.status}" cosmo-type="cosmo-emo" />`
: '';
},
at: (item: { content: string; userId: string }) =>
`<input type="button" id="${num}" disabled="true" class="atlist" value="${item.content} " cosmo-type="cosmo-@" userid="${item.userId}" style="font-style: normal; color: rgb(20, 135, 251); user-select: none;">`,
};
// 将循环中的得到的各个类型标签都赋值给writeSpan
let writeSpan = '';
// 判断message是否为数组并且数组长度大于0
if (list?.message && list.message.length) {
// 循环处理类型获取所对应的数据
list.message.forEach((ele: { type: string; content: string }) => {
if (setContextEnum[ele.type]) {
writeSpan += setContextEnum[ele.type](ele);
} else {
writeSpan += ele.content;
}
});
}
return writeSpan;
};