Js implements the drag-and-drop function for controlling files and obtaining drag-and-drop content. js drag-and-drop
When you drag a file to an element in the browser, js can listen to drag-and-drop events and process the drag-and-drop results. This article discusses some issues related to drag-and-drop files, however, we have not dealt with much compatibility issues.
Drag events
Js can listen to drag events including drag, dragend, dragenter, dragexit (not implemented by a browser), dragleave, dragover, dragstart, and drop. For details, see MDN.
The events related to drag and drop files include dragenter, dragover, dragleave, and drop ).
Drag events can be bound to a specified DOM element and bound to the entire page.
var dropEle = document.querySelector('#dropZone');dropEle.addEventListener('drop', function (e) { // }, false);document.addEventListener('drop', function (e) { // }, false);
Block default behavior
In general, we only need to write the business logic for processing drag and drop files to the drop event. Why do we need to bind the dragenter, dragover, and dragleave events?
Because when you drag a file to a browser that does not process the drag event, the browser will open the file. For example, if you drag an image, the browser will open the image, if you do not have a PDF reader, you can drag a PDF file to your browser to open the PDF file.
If the browser opens the drag-and-drop file, the page will jump away. We want to get the drag-and-drop file, instead of letting the page Jump. As mentioned above, it is the default action of the browser to open the drag-and-drop file. To prevent this default action, we need to block it from the above events.
DropZone. addEventListener ("dragenter", function (e) {e. preventDefault (); e. stopPropagation () ;}, false); dropZone. addEventListener ("dragover", function (e) {e. preventDefault (); e. stopPropagation () ;}, false); dropZone. addEventListener ("dragleave", function (e) {e. preventDefault (); e. stopPropagation () ;}, false); dropZone. addEventListener ("drop", function (e) {e. preventDefault (); e. stopPropagation (); // process the logic of dragging a file}
In fact, dragenter does not block the default behavior and does not trigger the browser to open the file. To prevent some browsers from compatibility problems, all the events in the drag-and-drop cycle are blocked by the default behavior and the event bubble is blocked.
Get the dragged File
We will get the file object in the event object in the drop Event Callback.
In an event object, an attribute such as e. dataTransfer is a data of the DataTransfer type and has the following attributes:
Attribute |
Type |
Description |
DropEffect |
String |
Used for hack compatibility issues |
EffectAllowed |
String |
No |
Files |
FileList |
Drag file list |
Items |
DataTransferItemList |
Drag data (may be a string) |
Types |
Array |
Drag data type. This attribute is messy in Safari. |
In Chrome, we use the items object to get the file, and other browsers use files to get the file, mainly to solve the drag folder problem, it is best not to allow users to drag the folder, because there may be folders in the folder, it takes a long time to upload files recursively. If you do not perform recursive search, you can only upload the first-level files in the directory. You may think that the upload function is complete, but the sub-directory files are not uploaded. Therefore, it is better to disable the upload folder, I will explain what to do later.
Chrome File Retrieval
DropZone. addEventListener ("drop", function (e) {e. preventDefault (); e. stopPropagation (); var df = e. dataTransfer; var dropFiles = []; // stores the drag object if (df. items! = Undefined) {// Chrome has the items attribute. It processes Chrome separately for (var I = 0; I <df. items. length; I ++) {var item = df. items [I]; // use webkitGetAsEntry to disable the upload directory if (item. kind = "file" & item. webkitGetAsEntry (). isFile) {var file = item. getAsFile (); dropFiles. push (file );}}}}
Obtain files from other browsers
Only Safari is tested here, and other browsers are not tested. However, after reading this article, you must be able to handle compatibility of other browsers.
DropZone. addEventListener ("drop", function (e) {e. preventDefault (); e. stopPropagation (); var df = e. dataTransfer; var dropFiles = []; // stores the drag object if (df. items! = Undefined) {// Chrome drag file logic} else {for (var I = 0; I <df. files. length; I ++) {dropFiles. push (df. files [I]) ;}}
Because Safari does not have an item and naturally does not have webkitGetAsEntry, you cannot determine whether to drag a file or a folder in Safari.
Methods for determining directories in non-Chrome browsers
Each file object obtained by the browser has four attributes: lastModified, name, size, and type. The type is the MIME Type of the file, and the type of the folder is empty, however, some files do not have the MIME Type. If you determine whether to drag a folder based on whether the type is null, some files will be accidentally hurt, so this method is used.
So what else can be determined? The idea is probably like this. The files and folders dragged by the user should be different. There should be differences during File API operations, for example, when performing some operations, the file will be able to operate normally, but the folder will report an error. Through the capture of the error, you can determine whether it is a file or a folder, well, let's write it down based on this idea.
DropZone. addEventListener ("drop", function (e) {e. preventDefault (); e. stopPropagation (); var df = e. dataTransfer; var dropFiles = []; if (df. items! = Undefined) {// Chrome drag file logic} else {for (var I = 0; I <df. files. length; I ++) {var dropFile = df. files [I]; if (dropFile. type) {// if the type is not an empty string, it must be file dropFiles. push (dropFile);} else {try {var fileReader = new FileReader (); fileReader. readAsDataURL (dropFile. slice (0, 3); fileReader. addEventListener ('load', function (e) {console. log (e, 'load'); dropFiles. push (dropFile) ;}, false); fileReader. addEventListener ('error', function (e) {console. log (e, 'error, folder cannot be uploaded ');}, false);} catch (e) {console. log (e, 'catch error, folder cannot be uploaded ') ;}}}, false );
The above Code creates a FileReader instance to read files through this instance. It takes more than 3 seconds to read a file larger than 1 GB, and the time is a bit long, I used slice to take the first three characters. Why did the first three not the first two or the first four? Because I wrote the code, I am so happy to write it ~
If the load event is triggered, it indicates that the dragged object is a file. If the error event is triggered, it indicates a folder. To prevent other potential errors, use try to package the code.
A little hack for third-party applications
After testing, it is found that there is no problem to drag a file through the Mac Finder, but sometimes the file is not necessarily in the Finder, or in some applications, an application is called a circle, the user feedback of this application indicates that the drag and drop of the file is invalid. I checked the source code uploaded from other open source files and found such a line of code:
DropZone. addEventListener ("dragover", function (e) {e. dataTransfer. dropEffect = 'copy'; // compatible with some third-party applications, such as circle e. preventDefault (); e. stopPropagation () ;}, false );
You need to set dropEffect to copy and search for this problem on the Internet. The source code document does not explain why you need to add this question. If you are interested, you can find out why.
Code that can be used
Because FileReader is used to read files, this is an asynchronous IO operation. To record how many files are processed and when to trigger the drag-and-drop callback, write a checkDropFinish method to compare the number of processed files and the total number of files. After all the files are processed, call the completed callback.
In addition, when I finally debug asynchronous processing, I used breakpoint debugging to find that resumable debugging in Safari will not trigger asynchronous callback. If you need to debug custom functions yourself, pay attention to it.
// Obtain the callback function getDropFileCallBack (dropFiles) {console. log (dropFiles, dropFiles. length);} var dropZone = document. querySelector ("# dropZone"); dropZone. addEventListener ("dragenter", function (e) {e. preventDefault (); e. stopPropagation () ;}, false); dropZone. addEventListener ("dragover", function (e) {e. dataTransfer. dropEffect = 'copy'; // compatible with some third-party applications, such as circle e. preventDefault (); e. stopPropagation ();}, False); dropZone. addEventListener ("dragleave", function (e) {e. preventDefault (); e. stopPropagation () ;}, false); dropZone. addEventListener ("drop", function (e) {e. preventDefault (); e. stopPropagation (); var df = e. dataTransfer; var dropFiles = []; // drag the file, which is put here var dealFileCnt = 0; // reading the file is an asynchronous process, you need to record how many files have been processed var allFileLen = df. files. length; // The number of all files for non-Chrome browsers. // check whether all files have been traversed by func. Tion checkDropFinish () {if (dealFileCnt = allFileLen-1) {getDropFileCallBack (dropFiles);} dealFileCnt ++;} if (df. items! = Undefined) {// Chrome drag file logic for (var I = 0; I <df. items. length; I ++) {var item = df. items [I]; if (item. kind = "file" & item. webkitGetAsEntry (). isFile) {var file = item. getAsFile (); dropFiles. push (file); console. log (file) ;}} else {// non-Chrome drag file logic for (var I = 0; I <allFileLen; I ++) {var dropFile = df. files [I]; if (dropFile. type) {dropFiles. push (dropFile); checkDropFinish () ;}else {try {var fileReader = new FileReader (); fileReader. readAsDataURL (dropFile. slice (0, 3); fileReader. addEventListener ('load', function (e) {console. log (e, 'load'); dropFiles. push (dropFile); checkDropFinish () ;}, false); fileReader. addEventListener ('error', function (e) {console. log (e, 'error, folder cannot be uploaded '); checkDropFinish () ;}, false);} catch (e) {console. log (e, 'catch error, cannot upload folders '); checkDropFinish () ;}}}, false );