How to Implement Drag and Drop With JavaScript
Drop and drop is the Holy Grail of client-side web development because it is a core feature of the graphical interfaces we use on our computer systems. Whether you use Windows, macOS, some flavors of Linux, or even mobile devices, drag and drop is a feature that is fundamental in the way you interact with that system. It therefore makes a lot of sense for us to include drag-and-drop functionality as we build web applications.
Drag and drop is a user interface (UI) interaction that allows users to click and hold on an element, drag it to a target location, and drop it there. This interaction is commonly used in various applications, including file uploads, rearranging items, creating to-do lists, and much more. In this tutorial, we'll create a simple example of a list where you can reorder the items using drag and drop.
Setting Up the HTML
Let's begin by setting up the HTML for our drag-and-drop list. Most elements are not draggable, but we can indicate to the browser that we want to make an element draggable by setting its draggable attribute to true. For example:
1 |
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Tuts+ - Drag and Drop</title> <style> </style></head><body> <h1>Drag and Drop</h1> <div class="list-container"> <ul class="draggable-list"> <li draggable="true">Item 1</li> <li draggable="true">Item 2</li> <li draggable="true">Item 3</li> <li draggable="true">Item 4</li> <li draggable="true">Item 5</li> </ul> </div> <script src="script.js"></script></body></html> |
This is the structure of our HTML document, but pay special attention to the <li/> elements. We want to enable the reordering of <li/> elements by dragging and dropping them to different positions in the list. Therefore, we need to set their draggable attribute to true. Otherwise, the browser will not allow us to drag the elements. Remember that you don't always have to set an element's draggable attribute directly in the HTML; it can also be done programmatically if your situation requires it.
Styling the Elements
It's not necessary to style our elements to make drag and drop work, but it does make our elements easier to work with. It also gives us an opportunity to provide visual cues to our users. Add the following CSS to the <style/> element:
1 |
body { font-family: Arial, sans-serif; text-align: center; margin: 20px;}h1 { margin-bottom: 20px;}.list-container { display: inline-block; border: 1px solid #ccc; padding: 10px;}ul.draggable-list { list-style: none; padding: 0;}ul.draggable-list li { background-color: #f9f9f9; padding: 10px; margin-bottom: 5px; cursor: pointer;}ul.draggable-list li:hover { background-color: #f0f0f0;} |
Most of this CSS is designed to improve the visibility of our HTML in the browser. However, it's important to point out that the cursor property is set to pointer in the ul.draggable-list li rule. This specific UI visual cue informs the user that they can interact with the element, indicating that it is draggable.
Implementing Drag and Drop
Drag and drop is entirely event-driven, and there are three events that we must listen for to implement drag-and-drop functionality. The following table lists those events, along with their respective targets:
| Event Name | Description | Event Target |
|---|---|---|
dragstart |
The beginning of the drag-and-drop process. Fires when an element is dragged. | The element being dragged |
dragover |
Fires for every element the "dragged element" is dragged over | The element the "dragged element" is currently dragged over. |
drop |
Fires when the "dragged element" is dropped. | The element that the "dragged element" is dropped on. |
Let's begin by defining the functions that will handle these events and attaching them to the appropriate event listeners.
1 |
function handleDragStart(e) {}function handleDragOver(e) { }function handleDrop(e) {}const listItems = document.querySelectorAll('.draggable-list li');listItems.forEach(item => { item.addEventListener('dragstart', handleDragStart); item.addEventListener('dragover', handleDragOver); item.addEventListener('drop', handleDrop);}); |
In this code, we define three functions—handleDragStart(), handleDragOver(), and handleDrop()—to handle the dragstart, dragover, and drop events respectively. Next, we select all <li/> elements in the draggable list using document.querySelectorAll() with the .draggable-list li CSS selector. We then iterate over the collection of <li/> elements to add the dragstart, dragover, and drop event listeners.
From this point, we can concentrate on the individual events one at a time. This approach will help us better understand and handle each event's functionality separately. Let's start by working on the dragstart event.
Handling the dragstart Event
As previously mentioned, the dragstart event fires when the user starts dragging an element. The target of the event is the element being dragged, and we will need to manipulate that element when it is out of scope in other events. We therefore need some way to keep track of that element throughout the process.
One solution would be to use a global variable to keep track of the currently dragged element, but that pollutes the global namespace. Instead, we can mark the element with a custom attribute so that we can find it in the DOM later. The following code does that:
1 |
function handleDragStart(e) { const target = e.target; target.setAttribute('data-dragged-item', 'true');} |
In this code, we set a custom attribute called data-dragged-item on the element. The value is arbitrary; we just need the attribute to exist on the element so that we can find the element later. This is all we need to implement the drag-and-drop functionality, but we will revisit this function later.
Using the dragover Event
Handling the dragover event is essential to the drag-and-drop procedure. It fires for every element the dragged item is dragged over, but not every element accepts a dropped object. For example, <div/>, <span/>, and <li/> elements will, by default, do nothing if you drop something on them. But we can change that by using the event object's preventDefault() method inside the dragover handler, like this:
1 |
function handleDragOver(e) { e.preventDefault();} |
If we don't prevent the default behavior in handleDragOver(), the browser's default behavior for the dragover event will be triggered, which may include actions like opening the dragged item as a link, doing nothing, or other default behaviors not related to our custom drag-and-drop functionality.
Drop It Like It's Hot
The drop event is where we do all of the work. In the case of our list, we want to reorder the list by inserting the dragged element after the element that we drop on. The DOM makes it very easy to do that; we just need to use the <ul/> element's insertBefore() method and reference the drop target's next sibling, like this:
1 |
function handleDrop(e) { e.preventDefault(); const target = e.target; const draggedItem = document.querySelector('li[data-dragged-item]'); target.parentNode.insertBefore(draggedItem, target.nextSibling); draggedItem.removeAttribute('data-dragged-item');} |
The first line of the handleDrop() function prevents the default behavior of the drop event. By default, dropping an element onto another may trigger navigation (e.g. opening the dropped item as a link) or other default actions, depending on the element type. Since we want to implement custom drag-and-drop behavior, we use the Event object's preventDefault() method to prevent the default behavior and handle the drop ourselves.
Next, we create the target variable to store a reference to the event target, and we fetch the dragged element using document.querySelector() with the li[data-dragged-item] selector.
The magic happens on the next line, where we use insertBefore() on the parent element (the <ul/> element) to rearrange the items in the list. The insertBefore() method takes two arguments: the element to be inserted (draggedItem) and the reference node (the element before which draggedItem will be inserted). In our case, the reference node is the drop target's next sibling, representing the element after the target element in the list.
At the end of the function, we remove the data-dragged-item attribute from the dragged element since it is no longer being dragged.
At this point, we have a working drag-and-drop implementation. You can drag list items in order to rearrange them. However, we can enhance the user's experience by adding extra visual cues.
Enhancing the Experience
Most computer and mobile operating systems provide some kind of visual cue for the item you're dragging. Usually, this cue comes in the form of a lower opacity level, making it appear as if the item you're dragging is a ghost of its original self.
We can achieve the same effect by changing the opacity of the dragged element in the handleDragStart() function, like this:
1 |
function handleDragStart(event) { const target = event.target; target.setAttribute('data-dragged-item', 'true'); target.style.opacity = 0.5;} |
This code adds a new line at the end of the function to set the element's opacity to 50%, giving it a ghostly appearance.
Now, we need to reset the element's opacity at the completion of the drag-and-drop operation. We could do that in the handleDrop() function, but a better option is to handle the dragend event. The dragend event is similar to dragstart. It fires at the end of the drag operation, and the event target is the dragged element. We can, therefore, use this event to set the dragged element's opacity back to 100%. Here's the code:
1 |
function handleDragEnd(e) { e.target.style.opacity = 1;} |
This code defines the handleDragEnd() function, and it sets the opacity of the dragged element back to 1 (signifying 100%), thus making the element fully visible again after dragging has ended.
Don't forget to attach the dragend event listener!
1 |
item.addEventListener('dragend', handleDragEnd); |
Conclusion
Drag and drop is an essential feature of graphical user interfaces, and it is important to implement it in your applications because it enhances the user experience and allows for intuitive manipulation of elements on a webpage.
Understanding drag and drop is crucial for building a wide range of applications, such as file upload interfaces, interactive dashboards, task management systems, and more. By empowering users to effortlessly rearrange and interact with content, drag and drop provides a fluid and engaging user experience, improving overall usability and accessibility.



