Drag and Drop in HTML5 and JavaScript: A Complete Guide with Real-World Examples

- 👤 Andrés Cruz

🇪🇸 En español

Drag and Drop in HTML5 and JavaScript: A Complete Guide with Real-World Examples

The Drag and Drop (DnD) system is one of those features that, when well-implemented, elevates the user experience without the need for external libraries. I've been using it for years in real projects—from reorganizing to-do lists to uploading files in interfaces connected to backends with Laravel or Django—and it still surprises me how flexible it can be with just native HTML5 and JavaScript.

In this guide, I explain everything, from events to advanced examples, so you can implement DnD in minutes and master it in hours.

Previously we saw how to generate files in JavaScript, as well as how to read and manipulate them.

What Drag and Drop is and why it's so useful today

Drag and Drop allows you to grab an element, move it, and drop it in another location. Although it sounds simple, it is one of the most intuitive interactions for users.

How the drag and drop interaction works

Three key pieces:

  • Draggable element
  • Data to transfer (dataTransfer)
  • Drop zone (drop)

Actions in Drag and Drop are immediate: you drag a file, you drop it, and voilà.

Modern use cases

  • Reordering lists (Kanban or to-do list style).
    • I use it in my courses and books where I implement a backend with Laravel or Django to reorganize tasks or listings in general.
  • Building Trello or Gmail-style interfaces.
  • Uploading files by dragging them directly into a container.
  • Moving images between galleries.
  • Backoffice interfaces with relocatable blocks.

Drag and Drop Events: Clear and Uncomplicated Explanation

The drag and drop API uses seven key events, and that's all. With practice, you'll get to know them and be able to do truly interesting things. Let's look at them in general terms:

  • Drag events
    • dragstart: starts the drag
    • drag: fires repeatedly while dragging
    • dragend: ends the drag
  • Target (Drop) events
    • dragenter: the draggable element enters the zone
    • dragover: occurs constantly while hovering over
    • dragleave: leaves the zone
    • drop: the element is released
  • Typical errors that break DnD
    • Not calling event.preventDefault() on dragover.
      • This blocks the drop. It happened to me so many times that adding it is a habit now.
    • Modifying the DOM inside dragover.
      • Brutally affects performance.

Not clearing styles on dragleave or dragend.

Drag and Drop is a feature that allows you to "grab" an object and drag it to a different location

Basic drag and drop example

Drag and Drop is one of the big changes incorporated by HTML5. It's the drag and drop feature that can be applied to any kind of element such as containers (divs), textareas, paragraphs, headings, images, etc.

Drag and Drop is an extremely powerful tool that allows you to take user interaction to another level, especially leveraged by the current "fever" for smart mobile devices like tablets or phones that have a touch screen, resulting in a very attractive combination.

An application must be intuitive, and what's more intuitive than dragging an "object" from one part to another.

What does the drag and drop feature consist of?

With Drag and Drop, we simply move an element from A to B by "dragging" it from one side to the other (this doesn't only apply to HTML; all technologies that use it—like Android—employ the same principle).

Although, at the time of development, it might not seem so simple, especially since it has (at least in the HTML5 API) a significant number of methods, events, and properties that allow us to fully exploit this technology.

Starting with Drag and Drop in HTML5

In this section, we will see some key definitions of how this technology works, its components, methods, attributes, and several very practical examples that are sure to be interesting.

To create draggable elements, it's no longer necessary to use a plugin to perform this task; using HTML, or rather HTML5 with native JavaScript, we can perform Drag and Drop on all the elements we want.

Drag and Drop Events (JavaScript) - Drag

The draggable elements produce three events:

1.0 dragstart()

This event is fired when we start dragging an element; specifically in this event, we must specify what we are dragging and set the corresponding values using the JavaScript method setData; this event is only invoked once (when selecting the element).

Additionally, in this event, we can define a custom style for the element being dragged.

2.0 drag()

This event is fired immediately after the dragstart method and occurs when we are dragging the element (we have the click pressed on the element and have not released it) and will continue throughout the "dragging" (drag) of the element; the number of times this event is invoked depends on the browser.

This event is very useful to know the state of the draggable element at all times; for example, to know the exact position of the element.

3.0 dragend()

Occurs when we finish dragging the element and is only executed once; it is also invoked regardless of whether the element is "dropped" inside the container or not.

The attributes we place on the elements are ondragstart, ondrag, and ondragend respectively.

Drag and Drop Events (JavaScript) - Drop

The container produces four events:

1.0 dragenter()

Occurs when we start moving a draggable element into its container, but it has not yet been released; in other words, this event is fired when the "draggable" element enters the "drop zone."

This event is ideal for changing the style rules of the container when the draggable element is "inside."

In this event, we can inspect the transferred data (dataTransfer) via the event; this data was initialized using the setData method; additionally, we can inspect the type of data returned.

2.0 dragleave()

Occurs when a draggable element has been released/moved outside the container.

This event is perfect for removing any style applied to our container.

3.0 dragover()

Occurs continuously when a draggable element is moved inside its container and only stops executing when we drop the draggable element inside the container or leave it; this event is perfect for knowing the position of the draggable element inside the container.

As with the drag event, it is perfect for determining the exact position of the draggable element because it is repeatedly invoked while the "draggable" element is inside the container; the number of times this event is invoked depends on the browser.

4.0 drop()

Occurs when a draggable element is dropped inside a container; in this event, we must collect the information of the draggable element using the getData method.

The attributes we place on the elements are ondragenter, ondragleave, ondragover, and ondrop respectively.

Drag and Drop Attributes in HTML5

Just handling the events is not enough to start working with Drag and Drop in HTML5. The attribute draggable="true" must be added to the DOM elements that we want to be draggable.

Any DOM element can be draggable.

So far, we have explained which events occur in the Drag and Drop lifecycle; that is, how draggable elements interact with their containers; but...

...How do we send the data that is maintained in the dragging operation?

That is, how is it determined what data (that we are dragging) the element being dragged has? For that, we use the following object.

The dataTransfer object in Drag and Drop (HTML5)

Simply dragging and dropping elements left and right is not enough unless the data we are moving is affected; this object is used to customize Drag and Drop operations; for example, it is used to set the information of the draggable event in the dragstart() event and this information is processed in the drop() event. Let's look at some of its most important methods:

.setData(format, data)

With this method, we must insert the information that we want to save from the draggable element (drag) and it must have a defined type, although you must be aware of browser support:

  • If you are going to insert text: "text/plain".
  • If you are going to insert a url: "text/uri-list".

This information must be set in the dragstart event using event.datatransfer.setData(type, data).

We indicate the type of data (format) to insert ("Data", for any type of data) and the data.

.getData(format)

Contrary to the previous method, this one returns the collected data set by the setData method.

To retrieve the information, it must be done only in the drop() event using event.dataTransfer.getData(type).

.clearData()

Clears all data set by the setData(format, data) method using event.dataTransfer.clearData(type).

In summary, the brain of the system is the DataTransfer object.

setData / getData

event.dataTransfer.setData("text/plain", elemento.id);

const id = event.dataTransfer.getData("text/plain");

MIME types

  • "text/plain"
  • "text/html"
  • "text/uri-list"

Custom ghost image

event.dataTransfer.setDragImage(miImagen, 10, 10);

Practical step-by-step examples (ready-to-use code)

Let's look at a series of examples with which you can learn how to use the drag and drop technique and adapt it to your developments.

1.0 Basic Drag and Drop example in HTML5 with the :after selector

This is a fairly basic example in which we use the minimum necessary events to work with Drag and Drop.

Full code (HTML and JavaScript)

function dragstart(caja, evento) {
    // el elemento a arrastrar
    event.dataTransfer.setData('Data', caja.id);
}
function drop(target, evento) {
    // obtenemos los datos
    var caja = event.dataTransfer.getData('Data');
    // agregamos el elemento de arrastre al contenedor
    target.appendChild(document.getElementById(caja));
}

As we saw earlier, when starting to drag (the dragstart event is fired), we indicate the data to transfer using the setData() method, and when the draggable element is positioned inside the container and dropped, the last event (drop) is fired, which reads the transferred information and copies it inside the container.

You can see the full example at:

As you can see, we managed to use the after selector to define another style by varying the content (text) of our draggable element (box) when it is inside the container.

2.0 Basic Drag and Drop example in HTML5 with the :after selector and all events

To better understand how to interact with all the events, the same example presented above is shown, but this time we will define all the events supported by Drag and Drop in HTML5, and a custom message for each one will be displayed in the console:

Full JavaScript code

function dragstart(caja, event) {
    // el elemento a arrastrar
    event.dataTransfer.setData('Data', caja.id);
}
function drag(target, event) {
    console.log("drag");
    return false;
}
function dragend(target, event) {
    console.log("dragend");
    return false;
}
function dragenter(target, event) {
    console.log("dragenter");
    return false;
}
function dragleave(target, event) {
    console.log("dragleave");
    return false;
}
function dragover(event) {
    console.log("dragover");
    event.preventDefault();
    return false;
}
function drop(target, event) {
    // obtenemos los datos
    var caja = event.dataTransfer.getData('Data');
    // agregamos el elemento de arrastre al contenedor
    target.appendChild(document.getElementById(caja));
}

The description seen in the previous example applies to this example; additionally, as an extra note, you will see multiple events executing in the Developer Console as you position the draggable element.

You can see the full example at:

3.0 Dragging and dropping files

Surely you have used the fantastic option offered by Gmail to attach files via Drag and Drop more than once:

Drag and drop in Gmail

With HTML, it is really simple to incorporate this Drag and Drop feature for files into a web application:

Full JavaScript code

var MAX_BYTES = 102400; // 100 KB
function dragenter(event) {
    event.stopPropagation();
    event.preventDefault();
}
function dragover(event) {
    event.stopPropagation();
    event.preventDefault();
}
function drop(event) {
    console.log('drop', event);
    event.stopPropagation();
    event.preventDefault();
    var data = event.dataTransfer;
    var files = data.files;
    var file;
    var reader;
    for (var i = 0; i < files.length; i++) {
        file = files[i];
        reader = new FileReader();
        reader.onloadend = onFileLoaded;
        reader.readAsBinaryString(file);
    }
}
function onFileLoaded(event) {
    document.getElementById("resultado").value = event.currentTarget.result.substr(0, MAX_BYTES);
}
var contenedor = document.getElementById("contenedor");
contenedor.addEventListener("dragenter", dragenter, false);
contenedor.addEventListener("dragover", dragover, false);
contenedor.addEventListener("drop", drop, false);

The JavaScript File API is the topic of another post and you can see it in the previous link.

You can see the full example at:

4.0 Changing element styles with Drag and Drop events

As a final example, we will see how to vary the style of our elements using each of the existing events:

Full JavaScript code

function dragstart(caja, event) {
    // el elemento a arrastrar
    document.getElementById(caja.id).className = "in";
    event.dataTransfer.setData('Data', caja.id);
}
function drag(caja, event) {
    return false;
}
function dragend(caja, event) {
    document.getElementById(caja.id).className = "out";
    return false;
}
function dragenter(target, event) {
    document.getElementById("contenedor").className = "inContainer";
    return false;
}
function dragleave(target, event) {
    document.getElementById("contenedor").className = "outContainer";
    return false;
}
function dragover(event) {
    event.preventDefault();
    return false;
}
function drop(target, event) {
    // obtenemos los datos
    var caja = event.dataTransfer.getData('Data');
    document.getElementById("contenedor").className = "outContainer";
    // agregamos el elemento de arrastre al contenedor
    target.appendChild(document.getElementById(caja));
}

You can see the full example at:

5 Basic Drag and Drop

<div id="origen" draggable="true">A</div>
<div id="destino"></div>
<script>
origen.addEventListener("dragstart", e => {
 e.dataTransfer.setData("text/plain", e.target.id);
});
destino.addEventListener("dragover", e => e.preventDefault());
destino.addEventListener("drop", e => {
 e.preventDefault();
 const id = e.dataTransfer.getData("text/plain");
 destino.appendChild(document.getElementById(id));
});
</script>

6 File upload by dragging from the desktop

This is one of the most practical cases. With this, when the drop event occurs with the file, it gets it and you send it via fetch or wherever you want.

contenedor.addEventListener("dragover", e => e.preventDefault());
contenedor.addEventListener("drop", e => {
 e.preventDefault();
 const archivos = e.dataTransfer.files;
 for (const file of archivos) {
   console.log("Archivo:", file.name);
   // Subida del archivo vía AJAX / fetch
 }
});

How to improve the user's visual experience

  • Dynamic styles with dragenter/dragleave
    • zona.addEventListener("dragenter", () => zona.classList.add("over")); 
      zona.addEventListener("dragleave", () => zona.classList.remove("over"));
  • Visual indicators
    • Dotted borders, background highlighting, action icons.
  • Microinteractions
    • 100–200ms transitions work wonders.

Drag and Drop on mobile and touch screens

Native DnD doesn't work the same on mobile.

  • Limitations
    • Drag events don't always fire
    • The ghost image doesn't appear
      • Movement dependent on the touch system
  • Solutions
    • Combine with touchstart, touchmove, touchend events
    • Or use libraries: SortableJS, InteractJS, Dragula
  • Maintain the fluid experience

In apps where I use Laravel or Django for the backend, I usually mix touch + native DnD to maintain the same flow on desktop and mobile.

Optimization and performance in drag operations

  • Minimize repaints
    • Avoid modifying styles inside dragover.
  • Event delegation
    • Handle drags from a single listener.
  • Real recommendations
    • throttling
    • avoid heavy DOM
    • use CSS transformations to move elements

Security and secure data handling in DnD

  • Sanitization
    • If you use HTML in dataTransfer, always sanitize.
  • Leak prevention
    • Never transfer tokens or sensitive IDs.
  • DnD and files
    • Validate:

      • size

      extension

      • MIME type

In my drag-and-drop uploads for the backend, I always validate the size before sending it to the server.

Conclusion

In this somewhat lengthy introduction to Drag and Drop with HTML5, we saw how to create our own Drag and Drop system and multiple examples that demonstrate the use of the different events and several very useful test cases, such as the one that allows you to get files from the computer.

We also saw that the large number of events it handles is not an inconvenience but quite the opposite, allowing you to get data at all times about the state of both the draggable element and the container and customize these actions almost 100%.

Drag and Drop in HTML5/JS continues to be a powerful, flexible, and modern tool. With just a few events, you can create advanced interfaces, from Trello-like boards to file upload modules.

Quick Checklist

  • draggable="true"
  • Events: dragstart / dragend / dragenter / dragover / dragleave / drop
  • event.preventDefault() on dragover
  • Use DataTransfer correctly
  • Clear visual feedback
  • Accessibility and mobile compatibility

When to use libraries

  • Large or very dynamic projects
  • Complex reordering
  • Need for perfect mobile support

Learn now how to copy and paste using ONLY JavaScript.

I agree to receive announcements of interest about this Blog.

Drag and drop is a feature that allows you to "grab" an object, drag it, and drop it to a different location. In this post, we'll talk about drag and drop in HTML5, including examples, methods, and attributes.

| 👤 Andrés Cruz

🇪🇸 En español