JS一键复制 兼容PC(包括图片)+移动端
一 兼容性
兼容PC+手机端,Chrome 42+、Edge 12+、Firefox 41+、IE、Opera 29+、Safari 10+,PC支持复制图片,手机端友好交互。其中:
- clipboard.js
- window.clipboardData 兼容IE
- 友好型补充(主要针对移动端)
二 整体思路
最近做了一个需要 一键复制 功能的网站,也是经历了一番挣扎,下面给出自己的一些总结,欢迎大家补充。
- ZeroClipboard.js虽然兼容性较好,但需要Flash,本文不考虑。如不在意flash可以尝试
- 本文主体clipboard.js
- 针对不兼容的IE版本尝试使用window.clipboardData
- 交互友好型补充方案(主要针对移动端)
三 clipboard.js使用
- 官方文档:https://clipboardjs.com/#example-text
- 使用总结:
- 通过npm或者cdn或者直接 引入 clipboard.js
- 使用方法@1 行内
<!-- 复制内容节点 -->
<input id="foo" value="https://github.com/zenorocha/clipboard.js.git">
<!-- 触发节点 -->
<button class="btn" data-clipboard-action="copy" data-clipboard-target="#foo" data-clipboard-text="我是复制的内容,和前一个属性选其一使用">
<img src="assets/clippy.svg" alt="Copy to clipboard">
</button>
<script>
// 通过选择触发节点实例化复制/剪切功能(必须)
var clipboard = new Clipboard('.btn');
// 复制成功or失败后的回调函数,可以弹窗(toast)提示(可选)
clipboard.on('success', function(e) {
console.info('Action:', e.action);
console.info('Text:', e.text);
console.info('Trigger:', e.trigger);
e.clearSelection();
});
clipboard.on('error', function(e) {
console.error('Action:', e.action);
console.error('Trigger:', e.trigger);
});
</script>
其中
- 复制内容节点 可以是任何的html标签,只要有id或者class标识,但 cut (剪切)只支持input或textarea
- data-clipboard-action copy(复制) 或 cut(剪切),默认copy
- data-clipboard-target 目标节点(复制/剪切的内容)
- data-clipboard-text 直接定义复制内容,这个和 data-clipboard-target 选其一使用即可(至于同时使用时哪个生效有兴趣的可以研究),这个可以应付多节点复制,当然下面还有其他方案。
3.使用方法@2 function
new Clipboard('.btn', {
target: function(trigger) {
return trigger.nextElementSibling; // 目标内容为出发节点的下一个节点
// return trigger.previousElementSibling; // 前一个节点
// return trigger.parentElement; // 父节点
// return document.getElementById('id'); // 直接 }
});
new Clipboard('.btn', {
text: function(trigger) {
return trigger.getAttribute('aria-label');
// return trigger.nextElementSibling.innerText;
// return document.getElementById('id').innerText;
}
});
new Clipboard('.btn', {
container: document.getElementById('modal')
});
这种是实例化的时候添加复制目标,添加目标方式有:
- target return的是目标节点,当然return之前还可以执行一些其他事件,参数trigger一般是触发节点。这个方法可以支持复制图片
- text return的是目标内容,不过这里貌似只支持原生JS的方法(innerText/value),我用jQuery的提示无效
-
container 这个我没用过,理解应该是返回复制内容的盒子节点,那就跟target类似了。官方解释是:
For use in Bootstrap Modals or with any other library that changes the
focus you’ll want to set the focused element as the container value.
4.clipboard.js的一些其他方法:
- Clipboard.isSupported(),返回 true/false 可以用于提前检测是否支持clipboard.js
4.针对clipboard.js不支持的IE浏览器
那就是IE8及以下了,因为IE有个clipboardData方法,所以需要兼容的话可以解决,使用这个方法操作的时候会有弹窗提示用户是否允许访问剪贴板
- 首先是判断何时使用这个,第一反应是获取浏览器种类,想来其实是不用的,看代码
if (Clipboard.isSupported()) {
// 使用clipboard.js处理
}else if(window.clipboardData){
var btn = document.getElementById('btn'); // 触发节点
var copyText = document.getElementById('btn').innerText // 或value,要复制的内容
btn.onclick = function(){
window.clipboardData.setData('Text', copyText);
}
}else{
alert('您目前的浏览器不支持一键复制,请手动复制使用其他浏览器试试')
}
- 关于clipboardData,它有3个方法:
- clearData(dataFormat) 删除剪贴板中指定格式的数据dataFormat:”text”,”url”
- getData(dataFormat) 从剪贴板获取指定格式的数据。 dataFormat:”text”,”url”
- setData(dataFormat, data) 给剪贴板赋予指定格式的数据。返回 true 表示操作成功。dataFormat:”text”,”url”,”file”,”html”,”image”;data: 剪贴数据
5. 针对不支持一键复制的友好性设置
当然这部分主要是手机端了,主要是让其更好的完成手动长按复制,那IOS好像有些地方长按并不会有反应(出现复制、全选等)。这种情况一般出现在非input/textarea(个人感觉clipboard.js对input的支持更好一些,但input内容不能换行…textarea移动端有时候会有问题)标签下,比如div,那怎样让其支持呢?
<div contenteditable="true" onkeydown="return false;" onpaste="return false;">
内容1<br>
内容2
</div>
contenteditable属性可以让盒子内容处于可以编辑状态,我们只要这个状态,而不是让用户真的能编辑onkeydown="return false;" onpaste="return false;"
禁用键盘和右键粘贴
-
针对IOS的Safari 10以下或者不支持clipboard.js的环境,有更好的用户体验方案,那就是用户点一下复制内容就自动帮其全部选中,这样用户只需点击复制
针对div contenteditable:
document.addEventListener('selectionchange', function(){
var t = window.getSelection().anchorNode;
var tParent = t.parentNode.id == 'mobileCopyText'; //className亦可
if (t && tParent && t.parentNode.innerText != window.getSelection()) {
window.getSelection().selectAllChildren(tParentNode)
}
})
简单解释一下原理:就是给 选区内容改变(selectionchange)(光标位置也会触发)添加一个监听事件,一旦触发,当(if条件)
- 选取内容开始位置(t)存在(为真)
- 其节点为复制内容的节点
- 选中的区域并不是需要复制内容的全部
这个时候选中 复制内容的父节点(有时候你可能需要parentNode两次)下的所有内容
关于getSelection()感兴趣的可以自己了解更多
针对input:
$('input').focus(function(){
$(this).select()
})
可能有人会问这么好的事情为什么说是只针对IOS,原因是测试发现Android下有些怪异的现象,那就是通过这种方法全选中后你点复制,但事实上你并没有复制到内容(尴尬)可能是个别浏览器的锅。所以最好还是判断一下IOS再用吧,怎么判断这里就不说了。另外手机端如何复制到图片还没有方案。