Drag and Drop

In this guide I will show you how to implement drag and drop in your application without using any third party libraries.

What is a drag and drop?

Drag and drop is a common interaction technique added to allow users to move things around on a web page. It is a perfect fit for applications like kanban board (e.g. Trello) and todo list where dragging transfers data between different parts of the application.

Most of the guides are using some third party libraries: React DnD, React Beautiful DnD, etc. It's ok to use it, but in this guide, you'll learn about the native drag and drop API and how to use it. This interface enable applications to use drag and drop features in browsers just from under the hood. It sounds amazing, let's try it!

But first of all, let's start with the live demo to see the algorithm in action. You can drag and drop cards below between three columns: to do, doing and done.

To do
Learn drag and drop
Doing
Done

There are two things you need to know about drag and drop API:

1. Types of elements

There are two types of elements:

  1. Draggable: elements what we want to drag somewhere
  2. Droppable or target: elements where we want to drop our draggable elements

How to make elements draggable? All you need is to make an element draggable by adding draggable attribute and add onDragStart event and handler.

index.html
1<div
2  class="draggable"
3  onDragStart={() => handleDragStart()}
4  draggable>
5  {{ draggable element }}
6</div>
component.js
1const handleDragStart = () => {
2  {{ init dragging }}
3};

How to make elements droppable? No additional attributes like aboves draggable required. You have to attach handlers to all drop targets to handle drop events. Wait, what events? Now is time to find out the second thing: drag and drop events.

2. Events

Drag and drop operation can be divided into two parts: dragging and then dropping a bit obvious, huh. Dragging begins when a user selects a draggable element and then drags it to a droppable element where dropping part starts. The operation ends when user releases the dragged element.

Let's look at the events of each part. Several event types are fired during dragging. These events fire in the following sequence:

  1. onDragStart fires only when a user selects a draggable element. It indicates the user starts dragging the element.
  2. onDrag fires repeatedly as long as a user drags the element.
  3. onDragEnd fires when user stops dragging the element.

These events fire in the following sequence when user drags the element over a droppable element or a drop target:

  1. onDragEnter fires when the dragged element enters a valid drop target.
  2. onDragOver fires repeatedly as long as user are dragging the element within the boundary of the drop target.
  3. onDragLeave or onDrop. These events are fires depending on the type of drop. onDragLeave fires when the element leaves a valid drop target. onDrop fires instead of the onDragLeave event when the element is dropped on a valid drop target.
index.html
1<div
2  class='droppable'
3  onDragEnd={() => handleDragEnd()}
4  onDragOver={(e) => allowDrop(e)}
5  onDrop={(e) => handleDrop(e)}>
6  {{ droppable element }}
7</div>
8
index.js
1const handleDragEnd = () => {
2  {{ handle drag end }}
3};
4
5const allowDrop = (e) => {
6  e.preventDefault();
7
8  {{ handle drag over }}
9};
10
11const handleDrop = (e) => {
12  e.preventDefault();
13
14  {{ handle drop the draggable element}}
15};

You could notice e.preventDefault() method is using in the example above. You must cancel the default action calling this method. It's necessary because in the event you're dragging, the browser'll try to execute that and not do drag and drop

Hint

Let me give you a little hint.

Add to the draggable element cursor: grab; and cursor: grabbing; properties to improve user experience. In general it indicates that something can be grabbed or is being grabbed.

In the example below you can see implementation. onDragStart event is used here to set grab cursor to indicate that the element was grabbed. onDragEnd fires when user stops dragging the element so this event resets it.

style.css
1.draggable {
2  {{ draggable element style }}
3  cursor: grab;
4
5  &.grabbing {
6    cursor: grabbing;
7  }
8}
index.html
1<div
2  className={`
3    draggable
4    ${isGrabbing || 'grabbing'}
5  `}
6  draggable
7  onDragStart={(e) => handleDragStart(e)}
8  onDragEnd={() => handleDragEnd()}>
9  {{ draggable element }}
10</div>
component.js
1const [isGrabbing, setIsGrabbing] = useState(false);
2
3const handleDragStart = (e): => {
4  {{ do something with e }}
5
6  setIsGrabbing(true);
7};
8
9const handleDragEnd = () => {
10  setIsGrabbing(false);
11};

Summary

All you have to do for implementation of drag and drop on your application:

  1. Add draggable attribute to an element you want to drag and drop.
  2. Attach onDragStart handler to selected draggable element.
  3. Attach handlers to all drop targets.

And that's it!

Note: React events are named using camelCase, rather than lowercase. You have to rename events from camelCase to lowercase if you want implement drag and drop in JavaScript app, e.g. dragstart instead of onDragStart. All the rest work the same way.

Read more

  1. Drag and Drop API docs
  2. Drag and drop HTML standart
  3. CSS cursor property docs