最近的项目上需要做复制粘贴相关的操作,来总结下吧
复制、剪切、粘贴事件:
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 nodeconsole.log("... Drop:plain")} else if((data[i].kind == 'string') &&(data[i].type.match('^text/html'))) {// Drag data item is HTMLdata[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 URIconsole.log("... Drop: URI");} else if((data[i].kind == 'file') &&(data[i].type.match('^image/'))) {// Drag data item is an image fileconsole.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 = nulllet copyFont = window.getSelection().toString(); // 被复制的文字 等下插入// 防知乎掘金 复制一两个字则不添加版权信息 超过一定长度的文字 就添加版权信息if(copyFont.length > 10) {textFont =`内容: ${copyFont} 作者:codingWeb 链接:/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 禁止文本选择 这样不会触发jsbody {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的dominput.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)