HTMLDrag & Drop API

Drag and Drop API

The native HTML5 Drag and Drop API lets users pick up an element with the mouse and drop it somewhere else — reordering a list, moving a card between columns, or dropping a file onto the page. It works through a small set of attributes and events built into the browser, no library required.

Making an Element Draggable

Add the draggable attribute to any element you want the user to be able to pick up. Text, links, and images are draggable by default in most browsers; other elements need it set explicitly.

draggable-basic.html

HTML
<ul id="list">
  <li draggable="true">Task 1</li>
  <li draggable="true">Task 2</li>
  <li draggable="true">Task 3</li>
</ul>
The Core Events

Drag and drop is driven by events fired on the dragged element and on potential drop targets. Understanding which event fires where is the key to using the API correctly.

Event

Fires on

Purpose

dragstart

The dragged element

Set up the data being dragged

drag

The dragged element

Fires repeatedly while dragging (rarely used directly)

dragenter

The drop target

Dragged item enters a potential drop zone

dragover

The drop target

Fires continuously while hovering — must call preventDefault() to allow a drop

dragleave

The drop target

Dragged item leaves the drop zone

drop

The drop target

The item is released over the target

dragend

The dragged element

Drag operation finished (dropped or cancelled)

dragover must call preventDefault
By default, browsers do not allow dropping onto most elements. If you don't call event.preventDefault() inside the dragover handler, the drop event will never fire on that target.
The dataTransfer Object

Every drag event carries a dataTransfer object, which is how you pass information from the drag source to the drop target — since they are usually different elements entirely.

Method

Used in

Purpose

setData(type, value)

dragstart

Store data to be read later

getData(type)

drop

Read the data set at drag start

effectAllowed

dragstart

Hints which operations are allowed (e.g. "move", "copy")

dropEffect

dragover

Controls the drag cursor feedback shown to the user

files

drop

A FileList when the user drags files from their OS

Use Case: Reordering a List

reorder-list.html

HTML
<ul id="list">
  <li draggable="true">Task 1</li>
  <li draggable="true">Task 2</li>
  <li draggable="true">Task 3</li>
</ul>

<script>
  const list = document.getElementById('list')
  let draggedItem = null

  list.addEventListener('dragstart', (event) => {
    draggedItem = event.target
    event.dataTransfer.setData('text/plain', event.target.textContent)
    event.dataTransfer.effectAllowed = 'move'
  })

  list.querySelectorAll('li').forEach((item) => {
    item.addEventListener('dragover', (event) => {
      event.preventDefault() // required to allow dropping
      event.dataTransfer.dropEffect = 'move'
    })

    item.addEventListener('drop', (event) => {
      event.preventDefault()
      if (event.target !== draggedItem) {
        list.insertBefore(draggedItem, event.target)
      }
    })
  })

  list.addEventListener('dragend', () => {
    draggedItem = null
  })
</script>
Before drag: Task 1, Task 2, Task 3
User drags "Task 3" onto "Task 1"
After drop:  Task 3, Task 1, Task 2
Use Case: File Drop Zone

The same events let you build a "drop files here" zone that accepts files dragged in from the user's operating system rather than from within the page.

file-drop-zone.html

HTML
<div id="drop-zone" style="border: 2px dashed #999; padding: 40px; text-align: center;">
  Drop files here
</div>

<script>
  const zone = document.getElementById('drop-zone')

  zone.addEventListener('dragover', (event) => {
    event.preventDefault()
    zone.style.borderColor = '#2e7d32'
  })

  zone.addEventListener('dragleave', () => {
    zone.style.borderColor = '#999'
  })

  zone.addEventListener('drop', (event) => {
    event.preventDefault()
    zone.style.borderColor = '#999'
    const files = event.dataTransfer.files
    Array.from(files).forEach((file) => {
      console.log('Dropped file:', file.name, file.size, 'bytes')
    })
  })
</script>
Also prevent default on the document
Browsers will often navigate to or open a dropped file if the drop lands outside your designated zone. It's common to also listen for dragover and drop on window and call preventDefault() there as a safety net.
Touch Devices
No native touch support
The HTML5 Drag and Drop API is mouse-oriented and has inconsistent or missing support on touch devices. For mobile-friendly drag interactions, most teams reach for a pointer-events-based library (e.g. SortableJS, dnd-kit) rather than the native API alone.
Quick Reference
  • Mark draggable elements with draggable="true".

  • Store data in dragstart with event.dataTransfer.setData(type, value).

  • Always call event.preventDefault() in dragover to enable dropping.

  • Read the data back in drop with event.dataTransfer.getData(type).

  • Use event.dataTransfer.files to handle files dragged from the OS.