Copy 一个复制操作的类
前言
js有一个31k多的star的开源复制库:clipboard.js;但是一些简单复制并不想安装一个库来解决,所以就想自己写一个。
copy所需要的东西
Selection 对象
用于获取被用户选中的部分,通过toString()
方法可以获取被选中的文本内容,以及js操作选中。
MDN文档:Selection
execCommand 对象
用于以命令的形式来操作网页的内容,说白了就是用它来实现复制文本操作,复制的是选中的文本
MDN文档:execCommand
需要注意的是,execCommand在未来将会被遗弃,因为这个api本身是从ie浏览器那边继承的,久而久之各大浏览器都对其做了兼容,虽然说遗弃,但是目前除了它没有别的办法进行复制操作。
暂时没有可用的替代方法。
确切的说这个 API 本来也不是标准 API,而是一个 IE 的私有 API,在 IE9 时被引入,后续的若干年里陆续被 Chrome / Firefix / Opera 等浏览器也做了兼容支持,但始终没有形成标准。
这个 API 被废弃的主要原因第一个就是安全问题,在用户未经授权的情况下就可以执行一些敏感操作,这就很恐怖了;第二个问题是因为这是一个同步方法,而且操作了 DOM 对象,会阻塞页面渲染和脚本执行,因当初还没 Promise,所以没设计成异步,挖坑了。新设计的 API 肯定是要解决这两个问题。
不过 W3C 也正在拟草案,大概率以后会引入一个叫Clipboard
的类型(Chrome 66.0 开始已经有这个类型了,不过还不能用,相关 API 仅存在于文档中),用来处理跟剪贴版相关的操作,不过之后肯定会是像现在获取地理位置啊、麦克风啊什么的,浏览器先会弹出一个对话框让用户授权,你才能读写剪贴板了。
- W3C Clipboard 草案:https://www.w3.org/TR/clipboa...
- Google Clipboard API 文档:https://developers.google.com...
- MDN Clipboard API 文档:https://developer.mozilla.org...
出自: 如果document.execCommand被正式删除,用什么可以实现复制到剪贴板功能呢?
功能要求
传入普通文本,数字,dom元素都能进行复制操作,dom就复制其文本内容,一般用于复制dom内的文本内容。
抛出一个promise对象,方便捕获
Copy类源码
class Copy {
constructor(data) {
return this.init(data);
}
//初始化
init(data) {
return new Promise((resolve, reject) => {
let copyVal = "";
let element = null;
if (this.isNode(data)) {
//元素
if (data.nodeName === 'SELECT') {
copyVal = data.value;
element = this.createElemnt(copyVal);
document.body.appendChild(element);
element.select();
element.setSelectionRange(0, copyVal.length);
} else if (data.nodeName === 'INPUT' || data.nodeName === 'TEXTAREA') {
const isReadOnly = data.hasAttribute('readonly'); //是否存在只读属性(只读可以防止移动端键盘被拉起)
if (!isReadOnly) {
data.setAttribute('readonly', '');
}
data.select();
data.setSelectionRange(0, data.value.length);
if (!isReadOnly) {
data.removeAttribute('readonly', '');
}
copyVal = data.value;
} else {
if (data.hasAttribute('contenteditable')) {
data.focus();
}
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(data);
selection.removeAllRanges();
selection.addRange(range);
copyVal = selection.toString();
}
} else if (typeof data === 'string' || typeof data === 'number') {
//纯文本&数字
copyVal = typeof data === 'number' ? String(data) : data;
element = this.createElemnt(copyVal);
document.body.appendChild(element);
element.select();
element.setSelectionRange(0, copyVal.length);
} else {
return reject({ msg: "不支持复制的参数", copyVal, origin: data });
}
const status = document.execCommand('copy');
element && element.remove(); //移除创建的dom
!element && data.blur(); //取消聚焦
window.getSelection().removeAllRanges(); //取消选中
if (status) {
return resolve({ msg: "复制成功", copyVal, origin: data });
} else {
return reject({ msg: "复制失败", copyVal, origin: data });
}
})
}
//是否node
isNode(value) {
return value !== undefined && value instanceof HTMLElement && value.nodeType === 1;
}
//创建textarea
createElemnt(value) {
const isRTL = document.documentElement.getAttribute('dir') === 'rtl';
const element = document.createElement("textarea");
// Prevent zooming on iOS
element.style.fontSize = '12pt';
// Reset box model
element.style.border = '0';
element.style.padding = '0';
element.style.margin = '0';
// Move element out of screen horizontally
element.style.position = 'absolute';
element.style[isRTL ? 'right' : 'left'] = '-9999px';
// Move element to the same position vertically
const yPosition = window.pageYOffset || document.documentElement.scrollTop;
element.style.top = `${yPosition}px`;
element.setAttribute('readonly', '');
element.value = value;
return element;
}
}
使用
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
new Copy(document.querySelector("code")).then((res) => {
console.log(res)
}).catch(error => console.log(error));
});
一些特别的地方:
ios端复制内容不全的问题
data.select();
data.setSelectionRange(0, data.value.length);
当data是input或者textarea元素时,虽然select()
可以让他选中所有文本,但是在ios端无法选中所有文本,所以还需要使用setSelectionRange
移动端会拉起键盘
data.setAttribute('readonly', '');
通过设置表单元素的readonly
属性,可以解决这个问题
复制指令也是有状态的
document.execCommand('copy'); //true||false
表示复制成功或者失败
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据