搜索
首页web前端js教程js控制文件拖拽及获取拖拽内容

js控制文件拖拽及获取拖拽内容

Feb 23, 2018 pm 02:52 PM
javascript内容获取

在用户拖拽文件到浏览器的某个元素上时,js可以监听到与拖拽相关的事件,并对拖拽结果进行处理,本文讨论下和拖拽文件相关的一些问题,不过没有处理太多关于兼容性的问题。

拖拽事件

<span style="font-size: 14px;">js</span>能够监听到拖拽的事件有<span style="font-size: 14px;">drag</span><span style="font-size: 14px;">dragend</span><span style="font-size: 14px;">dragenter</span><span style="font-size: 14px;">dragexit(没有浏览器实现)</span><span style="font-size: 14px;">dragleave</span><span style="font-size: 14px;">dragover</span><span style="font-size: 14px;">dragstart</span><span style="font-size: 14px;">drop</span>,详细的内容可以看MDN。

其中,与拖拽文件相关的事件有<span style="font-size: 14px;">dragenter(文件拖拽进)</span><span style="font-size: 14px;">dragover(文件拖拽在悬浮)</span><span style="font-size: 14px;">dragleave(文件拖拽离开)</span><span style="font-size: 14px;">drop(文件拖拽放下)</span>
拖拽事件可以绑定到指定的DOM元素上,可以绑定到整个页面中。

<span style="font-size: 14px;">var dropEle = document.querySelector('#dropZone');<br>dropEle.addEventListener('drop', function (e) {<br>    // <br>}, false);<br><br>document.addEventListener('drop', function (e) {<br>    // <br>}, false);<br></span>

阻止默认行为

一般来说,我们只需要把处理拖拽文件的业务逻辑写到<span style="font-size: 14px;">drop</span>事件中就可以了,为什么还要绑定<span style="font-size: 14px;">dragenter</span><span style="font-size: 14px;">dragover</span><span style="font-size: 14px;">dragleave</span>这三个事件呢?

因为当你拖拽一个文件到没有对拖拽事件进行处理的浏览器中的时候,浏览器会打开这个文件,比如拖拽一张图片浏览器会打开这个图片,在没有PDF阅读器的时候也可以拖拽一个PDF到浏览器中,浏览器就会打开这个PDF文件。

如果浏览器打开了拖拽的文件,页面就跳走了,我们希望得到拖拽的文件,而不是让页面跳走。上面说到浏览器会打开拖拽的文件是浏览器的默认行为,我们需要阻止这个默认行为,就需要再上述的事件中进行阻止。

<span style="font-size: 14px;">dropZone.addEventListener("dragenter", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br>}, false);<br><br>dropZone.addEventListener("dragover", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br>}, false);<br><br>dropZone.addEventListener("dragleave", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br>}, false);<br><br>dropZone.addEventListener("drop", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br>    // 处理拖拽文件的逻辑<br>}<br></span>

实际上<span style="font-size: 14px;">dragenter</span>不阻止默认行为也不会触发浏览器打开文件,为了防止某些浏览器可能有的兼容性问题,把拖拽周期中的所有的事件都阻止默认行为并且阻止了事件冒泡。

获得拖拽的文件

我们会在<span style="font-size: 14px;">drop</span>这个事件的回调中的事件对象能够得到文件对象。

在事件对象中,一个<span style="font-size: 14px;">e.dataTransfer</span>这样的属性,它是一个<span style="font-size: 14px;">DataTransfer</span>类型的数据,有如下的属性

属性 类型 说明
<span style="font-size: 14px;">dropEffect</span> String 用来hack某些兼容性问题
effectAllowed String 暂时不用
<span style="font-size: 14px;">files</span> FileList 拖拽的文件列表
<span style="font-size: 14px;">items</span> DataTransferItemList 拖拽的数据(有可能是字符串)
types Array 拖拽的数据类型 该属性在Safari下比较混乱

<span style="font-size: 14px;">Chrome</span>中我们用<span style="font-size: 14px;">items</span>对象获得文件,其他浏览器用<span style="font-size: 14px;">files</span>获得文件,主要是为了处理拖拽文件夹的问题,最好不允许用户拖拽文件夹,因为文件夹内可能还有文件夹,递归上传文件会很久,如果不递归查找,只上传目录第一层级的文件,用户可能以为上传功能了,但是没有上传子目录文件,所以还是禁止上传文件夹比较好,后面我会说要怎么处理。

Chrome获取文件

<span style="font-size: 14px;">dropZone.addEventListener("drop", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br>    <br>    var df = e.dataTransfer;<br>    var dropFiles = []; // 存放拖拽的文件对象<br>    <br>    if(df.items !== undefined) {<br>        // Chrome有items属性,对Chrome的单独处理<br>        for(var i = 0; i < df.items.length; i++) {<br>            var item = df.items[i];<br>            // 用webkitGetAsEntry禁止上传目录<br>            if(item.kind === "file" && item.webkitGetAsEntry().isFile) {<br>                var file = item.getAsFile();<br>                dropFiles.push(file);<br>            }<br>        }<br>    }<br>}<br></span>

其他浏览器获取文件

这里只测试了Safari,其他浏览器并没有测试,不过看完本文一定也有思路处理其他浏览器的兼容情况。

<span style="font-size: 14px;">dropZone.addEventListener("drop", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br>    <br>    var df = e.dataTransfer;<br>    var dropFiles = []; // 存放拖拽的文件对象<br>    <br>    if(df.items !== undefined) {<br>        // Chrome拖拽文件逻辑<br>    } else {<br>        for(var i = 0; i < df.files.length; i++) {<br>            dropFiles.push(df.files[i]);<br>        }<br>    }<br>}<br></span>

由于<span style="font-size: 14px;">Safari</span>没有<span style="font-size: 14px;">item</span>,自然也没有<span style="font-size: 14px;">webkitGetAsEntry</span>,所以在Safari无法确定拖拽的是否是文件还是文件夹。

非Chrome内核浏览器判断目录的方法

浏览器获取到的每个file对象有四个属性:<span style="font-size: 14px;">lastModified</span><span style="font-size: 14px;">name</span><span style="font-size: 14px;">size</span><span style="font-size: 14px;">type</span>,其中<span style="font-size: 14px;">type</span>是文件的<span style="font-size: 14px;">MIME Type</span>,文件夹的<span style="font-size: 14px;">type</span>是空的,但是有些文件没有<span style="font-size: 14px;">MIME Type</span>,如果按照<span style="font-size: 14px;">type</span>是否为空判断是不是拖拽的文件夹的话,会误伤一部分文件,所以这个方法行。

那么还有什么方法可以判断呢,思路大概是这样子的,用户拖拽的文件和文件夹应该是不一样的东西,用<span style="font-size: 14px;">File API</span>操作的时候应该会有区别,比如进行某些操作的时候,文件就能够正常操作,但是文件夹就会报错,通过错误的捕获就能够判断是文件还是文件夹了,好我们根据这个思路来写一下。

<span style="font-size: 14px;">dropZone.addEventListener("drop", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br><br>    var df = e.dataTransfer;<br>    var dropFiles = [];<br>    <br>    if(df.items !== undefined){<br>        // Chrome拖拽文件逻辑<br>    } else {<br>        for(var i = 0; i < df.files.length; i++){<br>            var dropFile = df.files[i];<br>            if ( dropFile.type ) {<br>                // 如果type不是空串,一定是文件<br>                dropFiles.push(dropFile);<br>            } else {<br>                try {<br>                    var fileReader = new FileReader();<br>                    fileReader.readAsDataURL(dropFile.slice(0, 3));<br><br>                    fileReader.addEventListener('load', function (e) {<br>                        console.log(e, 'load');<br>                        dropFiles.push(dropFile);<br>                    }, false);<br><br>                    fileReader.addEventListener('error', function (e) {<br>                        console.log(e, 'error,不可以上传文件夹');<br>                    }, false);<br><br>                } catch (e) {<br>                    console.log(e, 'catch error,不可以上传文件夹');<br>                }<br>            }<br>        }<br>    }<br>}, false);<br></span>

上面代码创建了一个<span style="font-size: 14px;">FileReader</span>实例,通过这个实例对文件进行读取,我测试读取一个1G多的文件要3S多,时间有点长,就用<span style="font-size: 14px;">slice</span>截取了前3个字符,为什么是前3个不是前2个或者前4个呢,因为代码是我写的,我开心这么写呗~

如果<span style="font-size: 14px;">load</span>事件触发了,就说明拖拽过来的东西是文件,如果<span style="font-size: 14px;">error</span>事件触发了,就说明是文件夹,为了防止其他可能的潜在错误,用<span style="font-size: 14px;">try</span>包起来这段代码。

三方应用的一点点小hack

经过测试发现通过<span style="font-size: 14px;">Mac</span><span style="font-size: 14px;">Finder</span>拖拽文件没有问题,但是有时候文件并不一定在<span style="font-size: 14px;">Finder</span>中,也可能在某些应用中,有一个应用叫做<span style="font-size: 14px;">圈点</span>,这个应用的用户反馈文件拖拽失效,去看了其他开源文件上传的源码,发现了这样一行代码:

<span style="font-size: 14px;">dropZone.addEventListener("dragover", function (e) {<br>    e.dataTransfer.dropEffect = 'copy'; // 兼容某些三方应用,如圈点<br>    e.preventDefault();<br>    e.stopPropagation();<br>}, false);<br></span>

需要把<span style="font-size: 14px;">dropEffect</span>置为<span style="font-size: 14px;">copy</span>,上网搜了下这个问题,源码文档中也没有说为什么要加这个,有兴趣的同学可以找一下为什么。

可以拿来就用的代码

由于用了<span style="font-size: 14px;">FileReader</span>去读取文件,这是一个异步IO操作,为了记录当前处理了多少个文件,以及什么时候触发拖拽结束的回调,写了一个<span style="font-size: 14px;">checkDropFinish</span>的方法一直去比较处理的文件数量和文件总数,确定所有文件处理完了后就去调用完成的回调。

另外,我在最后调试异步处理的时候,用的断点调试,发现断点调试在<span style="font-size: 14px;">Safari</span>中会导致异步回调不触发,需要自己调试定制功能的同学注意下。

<span style="font-size: 14px;">// 获得拖拽文件的回调函数<br>function getDropFileCallBack (dropFiles) {<br>    console.log(dropFiles, dropFiles.length);<br>}<br><br>var dropZone = document.querySelector("#dropZone");<br>dropZone.addEventListener("dragenter", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br>}, false);<br><br>dropZone.addEventListener("dragover", function (e) {<br>    e.dataTransfer.dropEffect = 'copy'; // 兼容某些三方应用,如圈点<br>    e.preventDefault();<br>    e.stopPropagation();<br>}, false);<br><br>dropZone.addEventListener("dragleave", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br>}, false);<br><br>dropZone.addEventListener("drop", function (e) {<br>    e.preventDefault();<br>    e.stopPropagation();<br><br>    var df = e.dataTransfer;<br>    var dropFiles = []; // 拖拽的文件,会放到这里<br>    var dealFileCnt = 0; // 读取文件是个异步的过程,需要记录处理了多少个文件了<br>    var allFileLen = df.files.length; // 所有的文件的数量,给非Chrome浏览器使用的变量<br><br>    // 检测是否已经把所有的文件都遍历过了<br>    function checkDropFinish () {<br>        if ( dealFileCnt === allFileLen-1 ) {<br>            getDropFileCallBack(dropFiles);<br>        }<br>        dealFileCnt++;<br>    }<br><br>    if(df.items !== undefined){<br>        // Chrome拖拽文件逻辑<br>        for(var i = 0; i < df.items.length; i++) {<br>            var item = df.items[i];<br>            if(item.kind === "file" && item.webkitGetAsEntry().isFile) {<br>                var file = item.getAsFile();<br>                dropFiles.push(file);<br>                console.log(file);<br>            }<br>        }<br>    } else {<br>        // 非Chrome拖拽文件逻辑<br>        for(var i = 0; i < allFileLen; i++) {<br>            var dropFile = df.files[i];<br>            if ( dropFile.type ) {<br>                dropFiles.push(dropFile);<br>                checkDropFinish();<br>            } else {<br>                try {<br>                    var fileReader = new FileReader();<br>                    fileReader.readAsDataURL(dropFile.slice(0, 3));<br><br>                    fileReader.addEventListener('load', function (e) {<br>                        console.log(e, 'load');<br>                        dropFiles.push(dropFile);<br>                        checkDropFinish();<br>                    }, false);<br><br>                    fileReader.addEventListener('error', function (e) {<br>                        console.log(e, 'error,不可以上传文件夹');<br>                        checkDropFinish();<br>                    }, false);<br><br>                } catch (e) {<br>                    console.log(e, 'catch error,不可以上传文件夹');<br>                    checkDropFinish();<br>                }<br>            }<br>        }<br>    }<br>}, false);<br></span>

相关推荐:

如何实现文件拖拽上传

html5文件拖拽上传的示例代码分享

Dropzone.js实现文件拖拽上传功能

以上是js控制文件拖拽及获取拖拽内容的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
JavaScript数据类型:浏览器和nodejs之间是否有区别?JavaScript数据类型:浏览器和nodejs之间是否有区别?May 14, 2025 am 12:15 AM

JavaScript核心数据类型在浏览器和Node.js中一致,但处理方式和额外类型有所不同。1)全局对象在浏览器中为window,在Node.js中为global。2)Node.js独有Buffer对象,用于处理二进制数据。3)性能和时间处理在两者间也有差异,需根据环境调整代码。

JavaScript评论:使用//和 / * * / * / * /JavaScript评论:使用//和 / * * / * / * /May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript:开发人员的比较分析Python vs. JavaScript:开发人员的比较分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

Python vs. JavaScript:选择合适的工具Python vs. JavaScript:选择合适的工具May 08, 2025 am 12:10 AM

选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

Python和JavaScript:了解每个的优势Python和JavaScript:了解每个的优势May 06, 2025 am 12:15 AM

Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

JavaScript的核心:它是在C还是C上构建的?JavaScript的核心:它是在C还是C上构建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

JavaScript应用程序:从前端到后端JavaScript应用程序:从前端到后端May 04, 2025 am 12:12 AM

JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

Python vs. JavaScript:您应该学到哪种语言?Python vs. JavaScript:您应该学到哪种语言?May 03, 2025 am 12:10 AM

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)