DEV Community

aagamezl
aagamezl

Posted on

How to Create an Engaging Drag and Drop Experience With JavaScript

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:

<!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>
Enter fullscreen mode Exit fullscreen mode

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:

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

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)
})
Enter fullscreen mode Exit fullscreen mode

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.

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')
}
Enter fullscreen mode Exit fullscreen mode

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:

If you have any questions or need further assistance, please let me know in the comments section below or message me at Twitter or LinkedIn.

Top comments (0)