最近的项目上需要做复制粘贴相关的操作,来总结下吧
复制、剪切、粘贴事件:
- copy 发生复制操作时触发;
- cut 发生剪切操作时触发;
- paste 发生粘贴操作时触发;
每个事件都有一个 before 事件对应:beforecopy、beforecut、beforepaste;
这几个 before 一般不怎么用,所以我们把注意力放在三个事件就可以了。
触发条件:
- 鼠标右键菜单的复制、粘贴、剪切;
- 使用了相应的键盘组合键,比如:ctrl+c、ctrl+v;
复制操作:
copy事件使用示例:
<body>
gggwgzgf
</body>
<script type="text/javascript">
document.body.oncopy = e => {
// 监听全局复制 做点什么
console.log(e)
};
</script>
我们可以看到事件对象中的属性:
我们主要研究的是里面的clipboardData
属性对象
clipboardData
对象: 用于访问以及修改剪贴板中的数据
clipboardData对象中有两个方法:
-
getData():
常配合copy事件使用,用于设置到剪贴板中的值 -
setData():
常配合paste事件使用,用于获取设置到剪贴板中的值
兼容:
不同浏览器,所属的对象不同:在 IE
中这个对象是window对象的属性,在Chrome、Safari和Firefox中,这个对象是相应的event对象的属性。所以我们在使用的时候,需要做一下如下兼容:
document.body.oncopy = e => {
let clipboardData = e.clipboardData || window.clipboardData;
// 获取clipboardData对象 + do something
};
copy配合getSelection实现复制某段文本:
<body>
gggwgzgf
</body>
<script type="text/javascript">
document.body.oncopy = e => {
console.log(window.getSelection().toString())
let copyMsg = window.getSelection().toString()
//把值设置到剪贴板中,方便paste事件触发去拿
e.clipboardData.setData("Text", copyMsg);
};
</script>
为了方便,这次,我直接使用ctrl+c测试了
注意:window.getSelection().toString()
我是调用toString()
方法转成文本的,如果不调用这个方法,直接通过window.getSelection()
取到值存到剪贴板会有出奇的效果,后面会说到,需要配合paste事件
粘贴paste事件
使用示例:
<body>
gggwgzgf
<input placeholder="这里存放粘贴操作的值" />
</body>
<script type="text/javascript">
document.body.oncopy = e => {
console.log(window.getSelection().toString())
let copyMsg = window.getSelection().toString()
e.clipboardData.setData("Text", copyMsg);
};
document.body.onpaste=function(e){
var data = e.clipboardData.getData("Text")
document.querySelector("input").value = data
}
</script>
到这里,你可能有个疑问,只能复制粘贴文本吗,图片可以吗?
这是可以的。
通过patse事件获取剪切板中的图片:
<script type="text/javascript">
document.addEventListener('paste', function(event) {
var items = (event.clipboardData && event.clipboardData.items) || [];
var file = null;
if(items && items.length) {
for(var i = 0; i < items.length; i++) {
if(items[i].type.indexOf('image') !== -1) {
file = items[i].getAsFile();
break;
}
}
}
console.log(file)
});
</script>
解释: 当粘贴事件触发时遍历剪切版对象(clipboardData)
中的所有items
,找到类型为图片的item
并调用getAsFile
方法得到文件对象
拿到file
对象后我们有几种选择:
- 通过
fileReader
得到文件对象的base64
字符串
var reader = new FileReader();
reader.onload = function(e){
// 通过e.target.result取到base64然后上传
// 作为src设到image标签上预览
}
reader.readAsDataURL(file); //此处的file为上面得到的文件对象```
- 通过
formData
文件对象转换为二进制数据
var formData = new FormData();
formData.append('file', file);
- 通过
URL.createObjectURL
转成url地址预览
var blobUrl=URL.createObjectURL(file)
示例代码:
<body>
<img src="" id="imgTest" />
</body>
<script type="text/javascript">
document.addEventListener('paste', function(event) {
var items = (event.clipboardData && event.clipboardData.items) || [];
var file = null;
if(items && items.length) {
for(var i = 0; i < items.length; i++) {
if(items[i].type.indexOf('image') !== -1) {
file = items[i].getAsFile();
break;
}
}
}
var blobUrl = URL.createObjectURL(file);
document.getElementById("imgTest").src = blobUrl;
});
</script>
局限性:
对于qq,微信等的截图或者按print screen得到的截图,还有任意网页的右击复制图片都能完美支持
,但是,对于电脑本地图片文件的复制没办法从剪切版获取到
进阶用法:
配合window.getSelection(),文字图片混合复制粘贴:
<body>
<span>gegegseraw</span>
<img src="a1.png" width="100" />
<hr />
下面为粘贴区域:
<div></div>
</body>
<script type="text/javascript">
document.body.oncopy = function(e) {
let copyMsg = window.getSelection()
e.clipboardData.setData("Text", copyMsg);
}
document.body.addEventListener('paste', function(event) {
var data = (event.clipboardData && event.clipboardData.items) || [];
console.log(data)
let div = document.querySelector("div")
for(var i = 0; i < data.length; i += 1) {
if((data[i].kind == 'string') &&
(data[i].type.match('^text/plain'))) {
// This item is the target node
console.log("... Drop:plain")
} else if((data[i].kind == 'string') &&
(data[i].type.match('^text/html'))) {
// Drag data item is HTML
data[i].getAsString(function(s) {
div.innerHTML = s
});
console.log("... Drop: HTML");
} else if((data[i].kind == 'string') &&
(data[i].type.match('^text/uri-list'))) {
// Drag data item is URI
console.log("... Drop: URI");
} else if((data[i].kind == 'file') &&
(data[i].type.match('^image/'))) {
// Drag data item is an image file
console.log("... Drop: File ");
}
}
});
</script>
效果:
navigator.clipboard介绍:
异步剪贴板 API
是一个相对较新的 API
,浏览器仍在逐渐实现它。由于潜在的安全问题和技术复杂性,大多数浏览器正在逐步集成这个 API
。剪贴板 Clipboard API
为 Navigator
接口添加了只读属性 clipboard
,该属性返回一个可以读写剪切板内容的 Clipboard
对象。 在 Web
应用中,剪切板 API
可用于实现剪切、复制、粘贴的功能。
系统剪贴板暴露于全局属性 Navigator.clipboard
之中,Navigator.clipboard
对象中有四个常用方法:都是异步的
read():
从剪贴板读取数据(比如图片),返回一个 Promise 对象。readText():
从操作系统读取文本,返回一个 Promise 对象。write():
写入任意数据至操作系统剪贴板,返回一个 Promise 对象。writeText():
写入文本至操作系统剪贴板,返回一个 Promise 对象。
注意:只有在用户事先授予网站或应用对剪切板的访问许可之后,才能使用异步剪切板读写方法。许可操作必须通过取得权限 Permissions API 的
"clipboard-read"
和/或"clipboard-write"
项获得。
使用实例:
复制writeText():
navigator.clipboard.writeText('要复制的文本')
.then(() => {
console.log('文本已经成功复制到剪切板');
})
.catch(err => {
// 如果用户没有授权,则抛出异常
console.error('无法复制此文本:', err);
});
async,await优化写法:
async function copyPageUrl() {
try {
await navigator.clipboard.writeText(location.href);
console.log('Page URL copied to clipboard');
} catch (err) {
console.error('Failed to copy: ', err);
}
}
粘贴readText():
navigator.clipboard.readText()
.then(text => {
console.log('Pasted content: ', text);
})
.catch(err => {
console.error('Failed to read clipboard contents: ', err);
});
同理也可写成async,await
write()写入数据/图片:
function setClipboard(text) {
let data = new DataTransfer();
data.items.add("text/plain", text);
navigator.clipboard.write(data).then(function() {
/* success */
}, function() {
/* failure */
});
}
代码创建了一个 DataTransfer
对象,要替换的内容存储在这里。执行 DataTransferItemList.add()
将数据写入进去,然后执行 write()
方法,指定执行成功或错误的结果。
read()读取数据/图片:
navigator.clipboard.read().then(data => {
for (let i=0; i<data.items.length; i++) {
if (data.items[i].type != "text/plain") {
alert("Clipboard contains non-text data. Unable to access it.");
} else {
textElem.innerText = data.items[i].getAs("text/plain");
}
}
});
应用:
实现类知乎/掘金复制大段文本添加版权信息:
<body>
<span>0123456789abcdefg</span>
<hr /> 下面为粘贴区域:
<div></div>
</body>
<script type="text/javascript">
document.body.oncopy = event => {
event.preventDefault(); // 取消默认的复制事件
let textFont = null
let copyFont = window.getSelection().toString(); // 被复制的文字 等下插入
// 防知乎掘金 复制一两个字则不添加版权信息 超过一定长度的文字 就添加版权信息
if(copyFont.length > 10) {
textFont =`
内容: ${
copyFont}
作者:codingWeb
链接:https://blog.csdn.net/fesfsefgs
来源:csdn
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。`
} else {
textFont = copyFont; // 没超过十个字 则采用被复制的内容。
}
event.clipboardData.setData('text', textFont); // 将信息写入粘贴板
};
document.body.onpaste = function(event) {
var data = event.clipboardData.getData("text")
document.querySelector("div").innerHTML = data
}
</script>
实现类起点网的防复制功能:
- 禁止复制+剪切
- 禁止右键,右键某些选项:全选,复制,粘贴等。
- 禁用文字选择,能选择却不能复制,体验很差。
- user-select 用 css 禁止选择文本。
// 禁止右键菜单
document.body.oncontextmenu = e => {
console.log(e, '右键');
return false;
// e.preventDefault();
};
// 禁止文字选择。
document.body.onselectstart = e => {
console.log(e, '文字选择');
return false;
// e.preventDefault();
};
// 禁止复制
document.body.oncopy = e => {
console.log(e, 'copy');
return false;
// e.preventDefault();
}
// 禁止剪切
document.body.oncut = e => {
console.log(e, 'cut');
return false;
// e.preventDefault();
};
// 禁止粘贴
document.body.onpaste = e => {
console.log(e, 'paste');
return false;
// e.preventDefault();
};
// css 禁止文本选择 这样不会触发js
body {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
提示: 使用e.preventDefault()
也可以禁用,示例中document.body
全局都禁用了,也可以对 dom(某些区域)
进行禁用。
点击复制功能:
不能使用 clipboardData:
在 IE 中可以用
window.clipboardData.setData('text','内容')
实现。因为,在 IE
中clipboardData
是window
的属性。而其他浏览器则是相应的event
对象的属性,这实际上是一种安全措施,防止未经授权的访问,为了兼容其他浏览器,所以我们不能通过clipboardData
来实现这种操作。
具体做法:
- 创建一个隐藏的input框
- 点击的时候,将要复制的内容放进input框中
- 选择文本内容input.select()
- 这里只能用input或者textarea才能选择文本。
- document.execCommand(“copy”),执行浏览器的复制命令。
示例:
<body>
<button>#aaaaaa</button>
<button>#bbbbbb</button>
<button>#ffffff</button>
<input />
</body>
<script type="text/javascript">
function copyText(e) {
var text = e.target.innerHTML; // 获取要复制的内容也可以传进来
var input = document.querySelector('input'); // 获取隐藏input的dom
input.value = text; // 修改文本框的内容
input.select(); // 选中文本
document.execCommand('copy'); // 执行浏览器复制命令
console.log('复制成功');
}
document.body.onclick=copyText
</script>
扩展:
如果不通过手动点击元素触发click
事件,代码控制,我们都知道,可以使用dom.click()
触发一次点击操作
那别的事件也可以通过代码控制触发吗?
答案是可以的
事件模拟:
function trigger(el,type){
let ev = document.createEvent("HTMLEvents")
ev.initEvent(type,true,true)
el.dispatchEvent(ev)
}
使用:
trigger(domEle,"copy")
trigger(domEle,"paste")
键盘模拟:
let mockKeyboardEvent = new KeyboardEvent('keyup', {
shiftKey: true, keyCode: 49 })
document.dispatchEvent(mockKeyboardEvent)
鼠标模拟:
let mockClickEvent = new MouseEvent('click', {
...});
document.dispatchEvent(mockClickEvent);
自定义事件模拟:(推荐)
//绑定
document.body.onpaste = function (e) {
console.log(e)
}
//触发,携带自定义参数
let myEvent = new CustomEvent('paste', {
detail: {
username: 'aaaaaaa',
password: '11111111'
}
})
document.body.dispatchEvent(myEvent)