需求: 写一个外部引用的js,可以实现用户一进页面就把一个信息放在用户手机或者浏览器的粘贴板上去。
由于是自己封装一个js来给别人引用,所以不考虑引用第三方插件,只能自己手撸代码,好在h5有这么一个API能直接实现复制粘贴功能,这个API叫做document.exeCommand。
先来介绍这个API的用法,参考(https://developer.mozilla.org/zh-CN/docs/Web/API/Document/execCommand)
语法
bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)
返回值
一个 Boolean ,如果是 false 则表示操作不被支持或未被启用。
参数
aCommandName
一个 DOMString ,命令的名称。可用命令列表请参阅 命令 。
aShowDefaultUI
一个 Boolean, 是否展示用户界面,一般为 false。Mozilla 没有实现。
aValueArgument
一些命令(例如insertImage)需要额外的参数(insertImage需要提供插入image的url),默认为null。
用到的命令
copy: 拷贝当前选中内容到剪贴板 。
第一次尝试,直接看代码
var _mtac = {"senseQuery":1};
(function(window) {
var sztCopyText = ''
function fetchData () {
var ajax = new XMLHttpRequest();
ajax.open('get','XXXX');
ajax.send();
ajax.onreadystatechange = function () {
if (ajax.readyState==4 &&ajax.status==200) {
sztCopyText = JSON.parse(ajax.responseText).result.web_pwd; // 拿到接口返回的数据
copyTextHandle()
}
}
}
fetchData()
function copyTextHandle () {
if (window.sztCopyText) {
return;
}
var textbox;
if (document.getElementById('sztCopyInput')) {
textbox = document.getElementById('sztCopyInput');
} else {
textbox = document.createElement('input');
}
textbox.style.opacity = '0';
textbox.style.position = 'absolute';
textbox.value = sztCopyText;
textbox.contentEditable = true;
textbox.readOnly = false;
textbox.setAttribute('id', 'sztCopyInput');
document.body.appendChild(textbox);
if (textbox.createTextRange) {
var range = textbox.createTextRange();
range.collapse(true);
range.moveStart('character', 0);
range.moveEnd('character', textbox.value.length - 0);
range.select();
} else {
textbox.setSelectionRange(0, textbox.value.length);
textbox.focus();
}
var success = document.execCommand('Copy');
if (success) {
document.body.removeChild(textbox);
window.sztCopyText = sztCopyText;
}
textbox.blur();
}
})(window)
从逻辑上角度来看,先从接口中拿到数据,然后把它赋值到新生成的input的框中,然后选中input框,将input的值复制到粘贴板上,然后在移除掉input框。这样就能做到神不知鬼不觉的将你所想的内容放在用户粘贴板上去了。没毛病,然后现实是残酷的,这样做的后果是document.execCommand('Copy’)返回的事false!让我们来分析下原因:
经过调查发现以下几点
-
window.execCommand()方法不支持异步,像setTimeout,ajax异步中去执行都会返回false。
-
input框不能设置display:none;width: 0;,IOS下contentEditable属性必须为true,readOnly不能为false。
-
不能通过js直接去执行window.exeCommand()方法,例如把复制事件绑定到一个元素的点击事件,然后通过执行click()方法来触发复制事件,这样的结果放回的是也是false。
-
将复制事件绑定到document.body上,然后用户点击任意地方来触发复制事件,这样做在Android上是可行,但是IOS上不行,估计是安全策略方面的原因。
-
不能通过touchstart,touchmove,touchend事件来触发复制事件,返回的都是false。
通过无限采坑,最后确定最初的想法实现不了,只能在用户点击某个页面元素的时候去触发该事件,所以代码做了如下修改。直接看代码。
(function(window) {
var sztCopyText = ''
function fetchData () {
var ajax = new XMLHttpRequest();
ajax.open('get','XXXXX');
ajax.send();
ajax.onreadystatechange = function () {
if (ajax.readyState==4 &&ajax.status==200) {
sztCopyText = JSON.parse(ajax.responseText).result.web_pwd;
}
}
}
// 获取数据
fetchData()
// 给body下的dom元素绑定点击复制事件
function createMex () {
childBox = document.body.children
for(var i = 0; i < childBox.length; i++) {
if (childBox[i].tagName !== 'NOSCRIPT' && childBox[i].tagName !== 'SCRIPT') {
childBox[i].addEventListener('click', copyTextHandle, false)
}
}
}
// 延时执行,不能用window.onload,预防原页面用过。window.onload只能执行一次。
setTimeout(() => {
createMex();
}, 500)
// 复制事件
function copyTextHandle () {
if (!sztCopyText) {
fetchData();
return;
}
if (window.sztCopyText) {
return;
}
var textbox;
if (document.getElementById('sztCopyInput')) {
textbox = document.getElementById('sztCopyInput');
} else {
textbox = document.createElement('input');
}
textbox.style.opacity = '0';
textbox.style.position = 'absolute';
textbox.value = sztCopyText;
textbox.contentEditable = true;
textbox.readOnly = false;
textbox.setAttribute('id', 'sztCopyInput');
document.body.appendChild(textbox);
if (textbox.createTextRange) {
var range = textbox.createTextRange();
range.collapse(true);
range.moveStart('character', 0);
range.moveEnd('character', textbox.value.length - 0);
range.select();
} else {
textbox.setSelectionRange(0, textbox.value.length);
textbox.focus();
}
var success = document.execCommand('Copy');
if (success) {
document.body.removeChild(textbox);
window.sztCopyText = sztCopyText;
}
textbox.blur();
}
})(window)
通过把复制事件绑定到body下的dom元素上,当用户一点击dom就会默默的执行了复制事件,也达到了神不知鬼不觉的效果。但是这样写法也有缺点
-
原页面做了阻止事件冒泡的话,则无法触发复制事件。
-
用户不点击页面也达不到需求。
如果您看到这边文章有更好的解决办法,欢迎留言评论。万分感谢。