Implementing Drag-Drop for File Input on the Web.
Published: 2024-07-16 12:17:54

Drag-drop is one of the most engaging ways of transferring files between windows on a computer. All modern file explorers come with this functionality. Browsers also happen to have a default behavior to drag-drop. By default, the browser will try to open a file dropped into it unless it is programmed to do something else. In this article, we will learn how to program the browser to treat a drag-drop as a file input operation. We will also extend the program to allow us to open the file input dialog and select a file by clicking the drop-drop target.

Implementing actions during drag-drop can be done using the `DragEvent` interface provided in the browser by JavaScript. The `DragEvent` interface provides several events from when a drag begins to when it ends. In this article, we will leverage the power of the `DragEvent` interface to create a drag-drop file input user interface. 

We will implement the proposed user interface in the following order:
1. Create the HTML Markup
2. Style the Markup
3. Make it interactive
4. Style the interaction
5. Make it clickable

Let's begin ...

## 1. Create the HTML Markup
Before going any further into the article, let us create a simple HTML markup.

```
index.html

<body>
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Drag and Drop File Upload</title>
      <link rel="stylesheet" href="./style.css">
    </head>
    <body>
      <div id="drop-zone" class="drop-zone">
        <p>Drag & drop files here or click to upload</p>
      </div>
      <script src="./drag-drop.js"></script>
    </body>
    </html>
    
</body>
</html>
```

## 2. Style the Markup
The following stylesheet provides a few basic styles for the page.

```
style.css

body {
    font-family: Arial, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f0f0f0;
  }
  
  .drop-zone {
    width: 300px;
    height: 200px;
    border: 2px dashed #007bff;
    border-radius: 4px;
    display: flex;
    justify-content: center;
    align-items: center;
    text-align: center;
    color: #007bff;
    cursor: pointer;
    transition: background-color 0.2s ease;
  }
```

The above markup and style create a container with a blue dotted border and prompt text. The screenshot below shows what we have just created when opened in the browser.


![The Drop zone container](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0av6o8d8nhgu1lo0rlik.png)

## 3. Make it interactive
Dragging and dropping files into the dotted container or clicking it to input files won't work just yet. We need JavaScript for that part. In the script, we will add various event listeners to elements in the document. The first event we will listen for is the `DOMContentLoaded` event on the `document`. 

```
document.addEventListener('DOMContentLoaded', () => {
    
})
```

You might be wondering what the `DOMContentLoaded` event is and how it is different from the `load` event. Even more, why did we choose to use it over the `load` event? 

The `DOMContentLoaded` event is fired as soon as the `DOM` tree has been created. This event does not wait for other resources such as the styles, images, data, and other external resources to be loaded. The `DOMContentLoaded` is fired as soon as the elements of the page have been inserted into the DOM tree. On the other hand, the `load` event waits a little longer before it is fired. The `load` event is fired after all the resources of a page including styles, images, fetched data, and all other external resources have been loaded into the browser.

Our interest in this project has nothing to do with external resources but everything to do with a few DOM elements. This is the reason why we are choosing to use the `DOMContentLoaded` event over the `load` event. The `load` event could work just as well but we do not need to wait for that long. We want to attach the event listeners to the target element as soon as they are added to the `DOM`.

We will perform all subsequent operations and listen for all the other required events within the `DOMContentLoaded` event handler.

### The drop zone container
We want to make the drop zone container aware of the drag-drop interaction. Here, we will tell the browser what to do when a file is dropped within the borders of the drop-zone container. 

First, we need to get the drop zone container element from the `DOM` as done below.

```
document.addEventListener('DOMContentLoaded', () => {
    const dropZone = document.querySelector('#drop-zone')
})
```
Next, we attach the `drop` event to the drop zone container and provide an event handler. 

```
dropZone.addEventListener('drop', (event) =>{
        
    })
```
In the event handler, we will program the browser to treat the drag-and-drop interactions as a file input. This means that if the file is dragged and dropped within the borders of the drop zone container, the browser will override its default behavior with the program in the event handler function.  You can do anything with the dropped file. In our case, we will simply log the file information in the browser's console. Below is the implementation:

```
dropZone.addEventListener('dragover', (event) =>{
        event.preventDefault()

    })

 dropZone.addEventListener('drop', (event) =>{
        event.preventDefault()
        
        const files = event.dataTransfer.files
        
        console.log(files)
    })
```

The code snippet above adds two even listeners to the drop zone container, the `dragover` and the `drop` events. 

Technically, we only care about dropping the file within the borders of the container. So why bother with the `dragover` event? We take care of the `dragover` event because we are dragging the file over the drop zone container before we can drop it and the browser has a default behavior to the `dragover` event. The least we can do about the default behavior of the browser is to prevent it. We access the files array from `event.dataTransfer` and log the list on the browser console.

Everything works well but the look, not very much so. In the next section, we will add styles to the process.  

## 4. Style the interaction
What if you wanted to do more than prevent the default behavior of the browser? What if you intend to change the background color of the drag zone container throughout the process? Say one color when dragging over the drop zone container, a different color when you drag past the borders of the target container, and another color when you drop the file in the intended container. Well, that can easily be done as shown below. 

```
 dropZone.addEventListener('dragover', (event) =>{
        event.preventDefault()

        dropZone.style.backgroundColor = '#e0e0e0'
    })

    dropZone.addEventListener('dragleave', () =>{
        dropZone.style.backgroundColor = '#eb6363a6'
    })
    
    dropZone.addEventListener('drop', (event) =>{
        event.preventDefault()

        dropZone.style.backgroundColor = '#f0f0f0'
        
        const files = event.dataTransfer.files
        
        console.log(files)
    })
```

The adjustments above involve altering the background color of the drop zone container from the initial background color to a gray color when the file is dragged into the drop zone container. The color changes to a shade of red if the drag leaves the container without dropping the file. Dropping the file resets the color to the initial background color. 

That's all good but didn't we say we could also click it? Up next ...

## 5. Make it clickable
The prompt in the container says we can also click the box to input a file. Unfortunately, clicking the box now doesn't open the file explorer as promised. How can we fix that? You guessed it right, add a click event listener to the container. 

```
 dropZone.addEventListener('click', () =>{
        
    })
```

We have added the `click` event listener, now what? 

In standard user interfaces, we trigger the opening of the file explorer by clicking a file input element that is displayed on a web page. In our case, we don't want to display a file input element. Lucky for us, we can add the element to the `DOM` and hide it from display using the `hidden` attribute. We can also click it programmatically using JavaScript. 

Extend the initial HTML markup to include the `input` element as shown below:

```
<body>
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Drag and Drop File Upload</title>
      <link rel="stylesheet" href="./style.css">
    </head>
    <body>
      <div id="drop-zone" class="drop-zone">
        <p>Drag & drop files here or click to upload</p>
        <input type="file" id="file-input" multiple hidden>
      </div>
      <script src="./drag-drop.js"></script>
    </body>
    </html>
    
</body>
</html>
```
The `hidden` attribute allows us to exclude the input element from the display while keeping it in the `DOM` tree.

Now that we have the input element in the `DOM`, we can access it from our JS scripts and click it programmatically when the drop zone container is clicked. The code snippet below implements the program.

```
dropZone.addEventListener('click', () =>{
        
        const fileInputElement = document.querySelector('input')
        fileInputElement.click()
    })
```

## Summary
That is how we create a clickable drag-drop file input user interface. The drag-drop file input user interface uses the `DragEvent` interface which provides the `dragover`, `dragleave`, `drop`, and other browser events. When implementing the drag-drop file input, it is important to alter the styles of the drop target to improve the user experience. In addition to the drag-drop input, users will always appreciate an additional option for clicking the drop zone to open the file explorer. You can add a hidden file input element to your markup and click it programmatically when the drop zone is clicked. That's all for the article, thanks for reading. Feel free to say something in the comments section. Bye bye. 

Read more about [the `DragEvent` interface on MDN](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent).