js在可编辑div中实现ctrl+v粘贴上传图片(兼容chrome、firefox、ie11)
我们或多或少都使用过各式各样的富文本编辑器,其中有一个很方便功能,复制一张图片然后粘贴进文本框,这张图片就被上传了,那么这个方便的功能是如何实现的呢?
原理分析
提取操作:复制=>粘贴=>上传
在这个操作过程中,我们需要做的就是:监听粘贴事件=>获取剪贴板里的内容=>发请求上传
为方便理解下文,需要先明白几点:
- 我们只能上传网页图(在网页上右键图片,然后复制)和截图(截图工具截的图片,eg:qq截图),不能粘贴上传系统里的图片(从桌面上、硬盘里复制),他们是存在完全不同的地方的。
- 截图工具截的图与在网页点击右键复制的图是有些不同的,因此处理方式也不一样。
- 知悉paste event这个事件:当进行粘贴(右键paste/ctrl+v)操作时,该动作将触发名为'paste'的剪贴板事件,这个事件的触发是在剪贴板里的数据插入到目标元素之前。如果目标元素(光标所在位置)是可编辑的元素(eg:设置了contenteditable属性的div。textarea并不行。),粘贴动作将把剪贴板里的数据,以最合适的格式,插入到目标元素里;如果目标元素不可编辑,则不会插入数据,但依然触发paste event。数据在粘贴的过程中是只读的。此段是翻译于w3.org_the-paste-action。
- 可惜的是,经过试验,发现chrome(当前最新版)、firefox(当前最新版)、ie11对paste事件的实现并不是完全按照w3c来的,各自也有区别(w3c的paste标准也因此只是草案阶段)。
test代码及截图如下:
chrome:
|
1
2
3
4
5
6
7
8
|
<textarea ></textarea> <div contenteditable style="width: 100px;height: 100px; border:1px solid"> </div> <script> document.addEventListener('paste', function (event) { console.log(event) })</script> |






- event有clipboardData属性,且clipboardData有item属性,clipboardData.item中的元素(对象)有type和kind属性;
- 无论在哪进行粘贴,均可触发paste事件;
- 在div(未特殊声明时,本文div均指设置了contenteditable属性的div) 里粘贴截图,不显示图片。img.src为base64编码字符串;
- 在div里粘贴网页图片,直接显示图片,img.src为图片地址。
firefox:


- event有clipboardData属性,clipboardData没有item属性;
- 只有在textarea里或者可编辑的div(里才粘贴才触发paste事件;
- 在div里粘贴截图,直接显示图片,img.src为base64编码字符串;
- 在div里粘贴网页图片,表现同chrome。
ie11:(不截图了,可自行试验,其他浏览器同理<( ̄▽ ̄)/,因为懒...)
- event没有clipboardData属性;
- 只在可编辑的div中粘贴才触发paste事件;
- 在div里粘贴截图,直接显示图片,img.src为base64编码字符串;
- 在div里粘贴网页图片,表现同chrome。
监听了paste事件,也知道了表现形式,接下来就是如何获取数据了:
chrome有特定的方法,利用clipboardData.items、getAsFile()、new FileReader()等api可以在paste回调函数里获取到剪贴板里图片的base64编码字符串(无论是截图粘贴的还是网页图片复制粘贴的),ie11,firefox没有这样的api,不过依然有办法可以获取,因为数据已经表现在img的src里了,对于截图粘贴的,直接取img的src属性值(base64),对于网页粘贴的,则把地址传给后台,然后根据地址down下来,存在自己的服务器,最后把新地址返回来交给前端展示就ok了。为了保持一致性便于管理,统一将所有情况(截图、网页)中的img的src属性替换为自己存储的地址。因此可以得到以下核心代码(注释很全哦~~):
操作:
1,为可编辑元素添加paste粘贴事件
let dom = document.getElementsByClassName('sendMsg')[0]
dom.addEventListener('paste', self.pasteWatcher)
获取粘贴图片相关数据:
pasteWatcher(event) { var self = this //上传用户粘贴的图片 console.log(event) var isChrome = false if (event.clipboardData || event.originalEvent) { //not for ie11 某些chrome版本使用的是event.originalEvent var clipboardData = event.clipboardData || event.originalEvent.clipboardData if (clipboardData.items) { // for chrome var items = clipboardData.items, len = items.length, blob = null isChrome = true //items.length比较有意思,初步判断是根据mime类型来的,即有几种mime类型,长度就是几(待验证) //如果粘贴纯文本,那么len=1,如果粘贴网页图片,len=2, items[0].type = 'text/plain', items[1].type = 'image/*' //如果使用截图工具粘贴图片,len=1, items[0].type = 'image/png' //如果粘贴纯文本+HTML,len=2, items[0].type = 'text/plain', items[1].type = 'text/html' // console.log('len:' + len); // console.log(items[0]); // console.log(items[1]); // console.log( 'items[0] kind:', items[0].kind ); // console.log( 'items[0] MIME type:', items[0].type ); // console.log( 'items[1] kind:', items[1].kind ); // console.log( 'items[1] MIME type:', items[1].type ); //阻止默认行为即不让剪贴板内容在div中显示出来 event.preventDefault() //在items里找粘贴的image,据上面分析,需要循环 for (var i = 0; i < len; i++) { if (items[i].type.indexOf('image') !== -1) { // console.log(items[i]); // console.log( typeof (items[i])); //getAsFile() 此方法只是living standard firefox ie11 并不支持 blob = items[i].getAsFile() } } if (blob !== null) {
//请求上传方法 self.uploadImgFromPaste(blob, 'paste', isChrome) } } else { //for firefox setTimeout(function() { //设置setTimeout的原因是为了保证图片先插入到div里,然后去获取值 var imgList = document.querySelectorAll('.sendMsg img'), len = imgList.length, src_str = '', i for (i = 0; i < len; i++) { if (imgList[i].className !== 'my_img') { //如果是截图那么src_str就是base64 如果是复制的其他网页图片那么src_str就是此图片在别人服务器的地址 src_str = imgList[i].src } } // uploadImgFromPaste(src_str, 'paste', isChrome) }, 1) } } else { //for ie11 setTimeout(function() { var imgList = document.querySelectorAll('.sendMsg img'), len = imgList.length, src_str = '', i for (i = 0; i < len; i++) { if (imgList[i].className !== 'my_img') { src_str = imgList[i].src } } // uploadImgFromPaste(src_str, 'paste', isChrome) }, 1) } },
上传请求:
uploadImgFromPaste(file, type, isChrome) { var self = this var formData = new FormData() formData.append('file', file) // formData.append('submission-type', type) var xhr = new XMLHttpRequest()
//action:请求地址 xhr.open('POST', self.action) xhr.onload = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { if (xhr.response) { let response = JSON.parse(xhr.response) if (response.data != null) {
//使用返回数据 self.tempImgList.push(response.data) } } } else { console.log(xhr.statusText) } } } xhr.onerror = function(e) { console.log(xhr.statusText) } xhr.send(formData) },
参考文章:https://www.jb51.net/article/80657.htm

浙公网安备 33010602011771号