Join the AI Workshop and learn to build real-world apps with AI. A hands-on, practical program to level up your skills.
The Drag and Drop API allows users to drag files directly onto an element to upload them.
Basic setup
Example drop zone with drag event handlers:
<div
id="dropzone"
ondragover="dragOverHandler(event)"
ondragleave="dragLeaveHandler(event)"
ondrop="dropHandler(event)"
>
Drop files here
</div>
Handling drag events
Use dragover to add a class when a file is over the zone, and dragleave to remove it:
function dragOverHandler(event) {
event.preventDefault()
const dropzone = document.querySelector('#dropzone')
dropzone.classList.add('dragging_over')
}
function dragLeaveHandler(event) {
event.preventDefault()
const dropzone = document.querySelector('#dropzone')
dropzone.classList.remove('dragging_over')
}
#dropzone.dragging_over {
border: 2px dashed #fff;
background-color: #666;
}
Handling the drop
The drop event fires when files are dropped. Access them via event.dataTransfer.items:
async function dropHandler(event) {
event.preventDefault()
const endpoint = '/api/upload'
if (event.dataTransfer.items) {
const formData = new FormData()
for (const item of event.dataTransfer.items) {
if (item.kind === 'file') {
const file = item.getAsFile()
if (file) {
if (!file.type.match('image.*')) {
alert('Only images are supported')
return
}
formData.append('files', file)
}
}
}
try {
const response = await fetch(endpoint, {
method: 'POST',
body: formData,
})
if (response.ok) {
console.log('File upload successful')
} else {
console.error('File upload failed', response)
}
} catch (error) {
console.error('Error uploading file', error)
}
}
}
Using Alpine.js
Alpine.js simplifies the drag and drop implementation:
<div
x-data="{ dragover: false }"
:class="{ '*:cursor-alias': dragover }"
@dragover.prevent="dragover = true"
@dragleave.prevent="dragover = false"
@drop.prevent="drop"
>
Drop files here
</div>
The .prevent modifier automatically calls preventDefault() to stop the browser from opening the file.
Drop handler:
async function drop(event) {
if (!event.dataTransfer.items) return
const formData = new FormData()
for (let item of event.dataTransfer.items) {
if (item.kind === 'file') {
const file = item.getAsFile()
if (file) {
if (!file.type.match('image.*')) {
alert('Only images are supported')
return
}
formData.append('files', file)
}
}
}
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
})
if (response.ok) {
console.log('File upload successful')
}
}