How to Create an Engaging Drag and Drop Experience With JavaScript
Published: 2023-08-04 13:29:55

# How to Create an Engaging Drag and Drop Experience With JavaScript

*Estimated Read Time: 10 min*

Drag and drop functionality is a powerful tool that significantly enhances user interactions on the web. From rearranging to-do lists and file uploads to interactive dashboards, it provides a smooth and engaging user experience. In this tutorial, we'll learn how to implement a simple drag and drop feature for a list of items using JavaScript. By the end, you'll have a better understanding of how drag and drop works and how to make it visually appealing and user-friendly.

## Setting Up the HTML

Let's start by creating the HTML structure for our drag and drop list. To make an element draggable, we can set its `draggable` attribute to `true`. For our list items, we'll set this attribute to enable reordering. Here's the basic HTML structure:

```html
<!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 Tutorial</title>
    <style>
        /* CSS styles will be added later */
    </style>
</head>
<body>
  <h1>Drag and Drop Tutorial</h1>
  <div class="list-container">
    <ul class="draggable-list">
      <li class="draggable-list-item" draggable="true">Item 1</li>
      <li class="draggable-list-item" draggable="true">Item 2</li>
      <li class="draggable-list-item" draggable="true">Item 3</li>
      <li class="draggable-list-item" draggable="true">Item 4</li>
      <li class="draggable-list-item" draggable="true">Item 5</li>
    </ul>
  </div>  
  <script src="script.js"></script>
</body>
</html>
```

## Styling the Elements

While not strictly necessary, styling the elements enhances the visual appeal of our drag and drop list. It also provides important visual cues to users. Here's the CSS to style our list:

```css
body {
  font-family: sans-serif;
  margin: 20px;
  text-align: center;
}

h1 {
  margin-bottom: 20px;
}

.list-container {
  display: inline-block;
  border: 1px solid #ccc;
  padding: 10px;
  width: 20vw;
}

ul.draggable-list {
  list-style: none;
  padding: 0;
}

ul.draggable-list li {
  background-color: #dbe5f1; /* Pastel Blue */
  cursor: grab; /* Cursor style indicates draggable element */
  margin-bottom: 5px;
  padding: 10px;
  transition: background-color 0.2s; /* Smooth transition on hover */
}

ul.draggable-list li:hover {
  background-color: #c6d5ea; /* Lighter shade of Pastel Blue on hover */
}

/* CSS styles for the dragging effect */
.dragging {
  opacity: 0.7;
  cursor: grabbing;
}
```

The CSS styles apply a clean and modern look to the list. The `cursor: grab;` property on list items changes the cursor to indicate that the items are draggable, offering a visual cue to the user. The transition on hover adds a subtle effect when the user hovers over a list item.

## Implementing Drag and Drop

Now, let's dive into the JavaScript code to implement the drag and drop functionality. Drag and drop is event-driven, and there are three key events we need to handle: `dragstart`, `dragover`, and `drop`. Each event plays a critical role in the drag and drop process.

```javascript
const handleDragStart = (e) => {
  const target = e.target

  target.classList.add('dragging')

  e.dataTransfer.setData('text/plain', target.innerHTML)
}

const handleDragOver = (e) => {
  e.preventDefault()

  const target = e.target

  if (target.classList.contains('draggable-list-item')) {
    const container = target.parentNode
    const y = e.clientY - target.getBoundingClientRect().top
    const height = target.offsetHeight / 2
    const draggedItem = document.querySelector('.dragging')

    if (y > height) {
      container.insertBefore(draggedItem, target.nextSibling)
    } else {
      container.insertBefore(draggedItem, target)
    }
  }
}

const handleDrop = (e) => {
  e.preventDefault()

  const target = e.target
  const draggedItem = document.querySelector('.dragging')

  if (target.classList.contains('draggable-list-item')) {
    const container = target.parentNode
    const isFirstItem = target === container.firstElementChild
    const draggedItem = document.querySelector('.dragging')

    if (target !== draggedItem) {
      if (isFirstItem) {
        container.insertBefore(draggedItem, target)
      } else if (target === container.lastElementChild) {
        container.appendChild(draggedItem)
      } else {
        container.insertBefore(draggedItem, target.nextSibling)
      }
    }
  }

  draggedItem.classList.remove('dragging')
}

const handleDragEnd = (e) => {
  const target = e.target

  target.classList.remove('dragging')
}

const listItems = document.querySelectorAll('.draggable-list-item')

listItems.forEach(item => {
  item.addEventListener('dragstart', handleDragStart)
  item.addEventListener('dragover', handleDragOver)
  item.addEventListener('drop', handleDrop)
  item.addEventListener('dragend', handleDragEnd)
})
```

Let's break down the code for each event:

### `handleDragStart` Event

The `dragstart` event fires when the user starts dragging an element. In this event, we add a CSS class `dragging` to the dragged item to visually indicate that it's being dragged. We also set the data to be transferred during the drag operation using `e.dataTransfer.setData()`. In this case, we're setting plain text data to the inner content of the dragged element.

### `handleDragOver` Event

The `dragover` event fires for every element the dragged item is dragged over. To allow drop on a specific element, we need to prevent the default behavior, which is to disallow dropping. We also set the `e.dataTransfer.dropEffect` to 'move' to show that the dragged item can be moved.

### `handleDrop` Event

The `drop` event fires when the dragged item is dropped on a valid drop target. Here, we prevent the default behavior and rearrange the list items using `insertBefore()` method. We place the dragged item after the target element in the list. Finally, we remove the `dragging` class from the dragged item to reset its visual appearance.

## Enhancing the User Experience

For an even better user experience, we can add a subtle opacity effect on the dragged element, giving it a ghostly appearance. We do this via the `dragging` CSS class in the `handleDragStart` and `handleDragEnd` functions.

```javascript
const handleDragStart = (e) => {
  const target = e.target

  target.classList.add('dragging')

  e.dataTransfer.setData('text/plain', target.innerHTML)
}

const handleDragEnd = (e) => {
  const target = e.target

  target.classList.remove('dragging')
}
```

In the `handleDragStart` function, we add the dragging CSS class to the dragged element, creating the ghostly effect. In the `handleDragEnd` function, we remove the dragging CSS class, making the element fully visible again after dragging.

## Conclusion

Congratulations! You've now implemented a captivating drag and drop functionality using JavaScript. By understanding the `dragstart`, `dragover`, and `drop` events, you've unlocked the potential to create various interactive features in your web applications. Additionally, by incorporating subtle CSS effects, like the ghostly opacity, you've improved the overall user experience.

Experiment further with drag and drop interactions, and you'll be able to build more sophisticated features for your web applications, such as dynamic dashboards, Kanban boards, and collaborative tools. The possibilities are endless, and with drag and drop, you can provide a more intuitive and enjoyable user experience for your visitors. Happy coding!

Here is a Live View of this article code:
{% codepen https://codepen.io/aagamezl/pen/GRweOEm %}

If you have any questions or need further assistance, please let me know in the comments section below or message me at [Twitter](https://twitter.com/aagamezl) or [LinkedIn](https://www.linkedin.com/in/alvarojoseagamezlicha).