Skip to content

Instantly share code, notes, and snippets.

@ajmas
Last active March 11, 2025 17:43
Show Gist options
  • Save ajmas/5710ac15c48b74b806bee9c2073b44dc to your computer and use it in GitHub Desktop.
Save ajmas/5710ac15c48b74b806bee9c2073b44dc to your computer and use it in GitHub Desktop.
Basic HTML Drag and drop for files
.dropzone.dropzone-active {
box-shadow: 0px 0px 15px 0px rgba(73, 245, 54, 0.75);
}
.dropzone.dropzone-badtype {
cursor: no-drop;
box-shadow: 0px 0px 15px 0px rgba(248, 19, 19, 0.75);
content: url("./white-x-icon.svg");
}
#error_message {
color: red;
}
/**
This is a rudementary implementation to allow drag an drop upload on an HTML page. It
was made for a project I was working on, but thought I'd share it for anyone interested
and for eventual feedback.
- Supports limiting:
- file type
- file size
- number of files uploaded
- Disables dropping files elsewhere on the page
- What you need to provide:
- an element to act as drop zone and adjust vlaue of `dropZoneSelector`
- file input element and ajdust the value of `fileInputSelector`
- error element and adjust the value of `errorSelector`
Note, due to browser security model, we can't get the file size during dragging. It is only available
once the files are dropped.
*/
const supportedFileTypes = ['image/jpeg', 'image/png', 'video/mp4', 'application/zip'];
const maxFileCount = 7;
const maxFileSizeMB = 24;
const dropZoneSelector = '.dropzone';
const fileInputSelector = '#id_file';
const errorSelector = '#error_message';
function toBytes (sizeInMegaBytes) {
return sizeInMegaBytes * 1024 * 1024;
}
function onDragZoneChange () {
document.querySelector(dropZoneSelector).classList.remove('dropzone-active');
document.querySelector(dropZoneSelector).classList.remove('dropzone-badtype');
}
function isValidFileItem (fileItem) {
return !!(supportedFileTypes.indexOf(fileItem.type) > -1);
}
function onFileDrop (event) {
event.preventDefault();
onDragZoneChange();
const errorOutput = document.querySelector(errorSelector);
if (errorOutput) {
errorOutput.innerHTML = ' ';
}
const maxFileSize = toBytes(maxFileSizeMB);
let dataTransfer = new DataTransfer();
let tooBig = false;
let tooManyFiles = false;
if (event.dataTransfer.files) {
[...event.dataTransfer.files].forEach((file) => {
if (file.size > maxFileSize) {
tooBig = true;
return;
} else if (dataTransfer.items.length > maxFileCount) {
tooManyFiles = true;
return;
}
if (isValidFileItem(file) && dataTransfer.items.length < maxFileCount) {
dataTransfer.items.add(file);
}
});
}
const fileInput = document.querySelector(fileInputSelector);
fileInput.files = dataTransfer.files;
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
if (errorSelector) {
const messages = [];
if (tooBig) {
messages.push(`Files larger than ${maxFileSizeMB} MB were ignored`);
}
if (tooManyFiles) {
messages.push(`Uploads are limited to ${maxFileCount} files per submission`);
}
errorOutput.innerHTML = messages.join('; ');
}
}
function onDragOver (event) {
event.preventDefault();
event.stopPropagation();
document.querySelector(dropZoneSelector).classList.add('dropzone-active');
let permitted = true;
if (event.dataTransfer.items) {
[...event.dataTransfer.items].forEach((item) => {
if (!isValidFileItem(item)) {
permitted = false;
}
});
} else {
[...event.dataTransfer.files].forEach((file) => {
if (!isValidFileItem(item)) {
permitted = false;
}
});
}
if (!permitted) {
document.querySelector(dropZoneSelector).classList.add('dropzone-badtype');
event.dataTransfer.dropEffect = 'none';
event.dataTransfer.effectAllowed = 'none';
}
}
document.addEventListener('DOMContentLoaded', () => {
const dropZone = document.querySelector(dropZoneSelector);
if (dropZone) {
dropZone.addEventListener('drop', onFileDrop);
dropZone.addEventListener('dragenter', onDragOver);
dropZone.addEventListener('dragover', onDragOver);
dropZone.addEventListener('dragleave', onDragZoneChange);
}
// logic to prevent items being dropped elsewhere in the page
const body = document.querySelector('body');
body.addEventListener('dragover', (event) => {
event.preventDefault();
event.stopPropagation();
event.dataTransfer.dropEffect = 'none';
event.dataTransfer.effectAllowed = 'none';
});
})
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment