Skip to main content

The Ultimate Guide to Drag and Drop Image Uploading with Pure JavaScript

In this guide, I’ll be explaining how to upload images by dragging and dropping. This includes dragging images
  1. From OS to browser
  2. From browser to browser
I’ll be using Pure Javascript (no frameworks or libraries), and the code will be compatible with all modern browsers including IE 9+. Also, I haven’t used ES6 which means you won’t need a compiler like Babel to run the code.
Our Drag & Drop function will do 5 things.
  1. Listen for drag and drop
  2. Indicate when a file is hovering on the drop region
  3. Validate dropped images
  4. Preview images
  5. Upload images
However, relying completely on drag & drop is a bad idea because mobile users won’t like it. Also, most mobile browsers don’t even support the API. Here’s the browser support for Drag & Drop API from caniuse.com.
the browser support for Drag & Drop API
the browser support for Drag & Drop API
Therefore, we will also allow users to simply upload files by selecting them (Via <input type="file").
Here’s the final result of this tutorial:
The Final Result
A demo is available on JSFiddle
Let’s start with basic HTML.

Then, save the elements in Javascript variables.

File Selector

For file selecting, we have to use <input type="file"> here. However, those default file selectors look old-fashioned and hard to style with CSS. Therefore, using fake file input, we can make dropRegoin opening the file selector when clicked.

Note that the accept attribute (fakeInput.accept = "image/*") is used to limit the files to image files. You can also specify the mime type too. For example, if you only need users to select only GIF files, you can use image/gif. Or, multiple values like image/png, image/jpeg. The multiple attribute allows users to select multiple images at once.
Now, when the dropRegion is clicked, the fakeInput will be clicked. So, the browser will open the OS file selector.
Let’s add the onChange event to the file input.

fakeInput.files is a FileList which we can use to preview and upload images. We will create the handleFiles() function later.

Drag Events

The Drag & Drop API defines 8 events: 4 events for the draggable element and 4 events for the droppable element. We will only need the latter 4 when developing a drag & drop image uploading.
  • dragenter: a dragged item enters a valid drop target.
  • dragleave: a dragged item leaves a valid drop target.
  • dragover: a dragged item is being dragged over a valid drop target. Triggered every few hundred milliseconds.
  • drop: an item is dropped on a valid drop target.

Adding Drag & Drop Functionality

When a file is dragged into the browser from the OS, the browser will try to open and display it by default. We have to prevent default behaviors and stop propagating to upper elements for all the events. This ensures any external event (especially an event of an outer element) doesn’t crash the functionality.

Then, we can handle the drop event.

e.dataTransfer is a Data Transfer object which contains the dragged data. e.dataTransfer.filescontains the dragged local files as a FileList which is exactly same as the files variable in the change event handler of the file input.
Now it’s the time to create the handleFiles() function which gets a File List and upload each item.

We can loop through the FileList (files here) using a simple for loop. If each file is a valid image, we will preview and upload it.
Wait! There’s a problem. This only works if the files are dragged from the local file system. What if an image is dragged from another webpage? We will need to optimize our drop handler for this (This is quite tricky).
Here’s the upgraded handleDrop function.

Here if files are not selected, we will check for browser images. When you drag an image on the browser to another place, the image is dragged as HTML. So, we can get the HTML and check for src="" attribute which is the image URL. Then, we can fetch the image with Image object and convert it to a canvas. Finally, the canvas can be converted to a blob, and we can use our handleFiles function as usual.
However, there some limitations to this implementation. This would not work if the image is from a server that blocks cross-domain requests. To solve that, you can use a proxy image server to fetch images. And, also dragging from chrome to firefox can also show errors. Even with those limitations, dragging from one page to another is a cool feature.

Validating Images


This function validates two attributes.
  1. File type – In this example, I have allowed jpg, png, and gif files. However, you can add or remove any valid MIME type.
2. File size – I have set the maximum size to 10MB. It is a good practice to limit the file size to prevent malicious oversized uploading.
Validating on the client-side has one advantage. The user can know instantly whether their image is valid. If we completely rely on server-side validation, the user has to wait until the upload is finished and the server processes the response. This can be a burden for the server if the image files are very large.
(Validating file size from the client-side inadequate. You must validate it again on the server-side.)

Previewing and Uploading Images

1. Previewing

There are several ways to preview the image.
  1. Preview after uploading – Upload the image, get the image URL, and display it.
  2. Preview before uploading – We can preview the image before uploading. This is fast and efficient. There are two Javascript methods we can use.
    1. URL.createObjectURL()
    2. FileReader.readAsDataURL()
For this example, I’ll choose 2.2: Preview the image before uploading using FileReader.readAsDataURL().
Let’s start

We will read the image in the next step and display it on img element. The overlay will be used to add a faded look to each image until uploaded. We will reduce the width of the overlay when the image is uploading.
Let’s read the image and preview.

Here we create a FileReader object and set up the onload event handler for it. Then, we read the image as a data URL. This is done asynchronously. After the reading is finished, the onload callback will be called. The src attribute of the img element will be set to e.target.result which is a base64 data URL.

2. Uploading

We can use FormData interface to create form data to send them to the server. Then, we can use AJAX with XMLHttpRequest to perform an asynchronous upload.
Creating form data:

AJAX request:

Here uploadLocation should be set to the server URL of the upload handler. The onreadystatechangeevent handler is called when the state is changed. ajax.readyState is 4 when the request is completed. ajax.status is the status code sent by the server. Usually, servers set the status code to 200 when the request is successful. ajax.upload.onprogress event handler is called each time when the progress is updated. In this function, we calculate the percentage of the progress and reduce that from the overlay’s width.
And, the || 100 part is a simple bug fix. Sometimes, (e.loaded / e.total * 100) can return NaN. In that case, the default value, 100, will be used.

Comments

Popular posts from this blog

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

How to solve Puppeteer TimeoutError: Navigation timeout of 30000 ms exceeded

During the automation of multiple tasks on my job and personal projects, i decided to move on  Puppeteer  instead of the old school PhantomJS. One of the most usual problems with pages that contain a lot of content, because of the ads, images etc. is the load time, an exception is thrown (specifically the TimeoutError) after a page takes more than 30000ms (30 seconds) to load totally. To solve this problem, you will have 2 options, either to increase this timeout in the configuration or remove it at all. Personally, i prefer to remove the limit as i know that the pages that i work with will end up loading someday. In this article, i'll explain you briefly 2 ways to bypass this limitation. A. Globally on the tab The option that i prefer, as i browse multiple pages in the same tab, is to remove the timeout limit on the tab that i use to browse. For example, to remove the limit you should add: await page . setDefaultNavigationTimeout ( 0 ) ;  COPY SNIPPET The setDefaultNav...