A modern file upload component with drag and drop functionality
<head>
.<!-- Font Awesome Icons; Make sure to add it in the <head> tag -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<div class="file-upload-container">
<div class="file-upload-area" id="drop-area">
<div class="file-upload-message">
<i class="fas fa-cloud-upload-alt"></i>
<h3>Drag & Drop Files Here</h3>
<p>or</p>
<label for="fileUpload" class="file-upload-btn">Browse Files</label>
<input type="file" id="fileUpload" multiple hidden>
<p class="file-upload-info">Supports: JPG, PNG, PDF, DOC, MP4 (Max: 10MB)</p>
</div>
<div class="file-upload-preview" id="previewArea"></div>
</div>
</div>
:root {
--upload-bg: #f8f9fa;
--upload-border: #e0e0e0;
--upload-border-active: #4a6cf7;
--upload-text: #4a4a4a;
--upload-icon: #4a6cf7;
--upload-btn-bg: #4a6cf7;
--upload-btn-bg-hover: #3652cc;
--upload-btn-text: #ffffff;
--upload-item-bg: #ffffff;
--upload-item-border: #e0e0e0;
--upload-item-progress: #4a6cf7;
--upload-item-icon: #27ae60;
--upload-item-remove: #e74c3c;
--upload-transition: all 0.3s ease;
}
/* Dark mode variables */
@media (prefers-color-scheme: dark) {
:root {
--upload-bg: #1e1e1e;
--upload-border: #333333;
--upload-border-active: #6c8fff;
--upload-text: #e1e1e1;
--upload-icon: #6c8fff;
--upload-btn-bg: #6c8fff;
--upload-btn-bg-hover: #5373e0;
--upload-btn-text: #ffffff;
--upload-item-bg: #2a2a2a;
--upload-item-border: #333333;
--upload-item-progress: #6c8fff;
--upload-item-icon: #27ae60;
--upload-item-remove: #e74c3c;
}
}
.file-upload-container {
width: 100%;
max-width: 600px;
margin: 0 auto;
}
.file-upload-area {
position: relative;
width: 100%;
min-height: 250px;
background-color: var(--upload-bg);
border: 2px dashed var(--upload-border);
border-radius: 10px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 30px 20px;
box-sizing: border-box;
transition: var(--upload-transition);
overflow: hidden;
}
.file-upload-area.active {
border-color: var(--upload-border-active);
background-color: rgba(74, 108, 247, 0.05);
}
.file-upload-message {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: var(--upload-text);
width: 100%;
}
.file-upload-message i {
font-size: 3rem;
color: var(--upload-icon);
margin-bottom: 1rem;
}
.file-upload-message h3 {
margin: 0 0 0.5rem;
font-size: 1.2rem;
}
.file-upload-message p {
margin: 0.5rem 0;
}
.file-upload-btn {
display: inline-block;
padding: 10px 20px;
margin: 10px 0;
background-color: var(--upload-btn-bg);
color: var(--upload-btn-text);
border-radius: 5px;
font-weight: 500;
cursor: pointer;
transition: var(--upload-transition);
}
.file-upload-btn:hover {
background-color: var(--upload-btn-bg-hover);
}
.file-upload-info {
font-size: 0.8rem;
opacity: 0.7;
}
.file-upload-preview {
width: 100%;
margin-top: 20px;
display: flex;
flex-direction: column;
gap: 10px;
}
.upload-item {
display: flex;
align-items: center;
background-color: var(--upload-item-bg);
border: 1px solid var(--upload-item-border);
border-radius: 5px;
padding: 10px;
position: relative;
}
.upload-item-icon {
font-size: 1.5rem;
margin-right: 15px;
}
.upload-item-details {
flex: 1;
}
.upload-item-name {
font-weight: 500;
margin-bottom: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}
.upload-item-size {
font-size: 0.8rem;
opacity: 0.7;
}
.upload-item-progress-container {
width: 100%;
height: 4px;
background-color: var(--upload-border);
border-radius: 2px;
overflow: hidden;
margin-top: 5px;
}
.upload-item-progress {
height: 100%;
background-color: var(--upload-item-progress);
border-radius: 2px;
transition: width 0.3s ease;
}
.upload-item-actions {
display: flex;
align-items: center;
}
.upload-item-status {
margin-right: 10px;
font-size: 1rem;
}
.upload-item-remove {
background: none;
border: none;
color: var(--upload-item-remove);
cursor: pointer;
font-size: 1rem;
padding: 5px;
transition: var(--upload-transition);
}
.upload-item-remove:hover {
opacity: 0.8;
}
document.addEventListener('DOMContentLoaded', function() {
const dropArea = document.getElementById('drop-area');
const fileInput = document.getElementById('fileUpload');
const previewArea = document.getElementById('previewArea');
// Max file size in bytes (10MB)
const MAX_FILE_SIZE = 10 * 1024 * 1024;
// Allowed file types
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'video/mp4'];
// Prevent default drag behaviors
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false);
document.body.addEventListener(eventName, preventDefaults, false);
});
// Highlight drop area when item is dragged over it
['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false);
});
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false);
// Handle files from input field
fileInput.addEventListener('change', function() {
handleFiles(this.files);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
function highlight() {
dropArea.classList.add('active');
}
function unhighlight() {
dropArea.classList.remove('active');
}
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
function handleFiles(files) {
const filesArray = Array.from(files);
if (filesArray.length === 0) return;
// Process each file
filesArray.forEach(file => {
// Validate file type and size
if (!validateFile(file)) return;
// Create preview item
const uploadItem = createUploadItem(file);
previewArea.appendChild(uploadItem);
// Simulate upload
simulateUpload(uploadItem, file);
});
// Clear the input field to allow selecting the same file again
fileInput.value = '';
}
function validateFile(file) {
// Check file type
if (!ALLOWED_TYPES.includes(file.type)) {
alert(`File type not allowed: ${file.type}`);
return false;
}
// Check file size
if (file.size > MAX_FILE_SIZE) {
alert(`File too large: ${file.name} (${formatFileSize(file.size)})`);
return false;
}
return true;
}
function createUploadItem(file) {
const item = document.createElement('div');
item.className = 'upload-item';
const iconClass = getFileIconClass(file.type);
item.innerHTML = `
<div class="upload-item-icon">
<i class="${iconClass}"></i>
</div>
<div class="upload-item-details">
<div class="upload-item-name">${file.name}</div>
<div class="upload-item-size">${formatFileSize(file.size)}</div>
<div class="upload-item-progress-container">
<div class="upload-item-progress" style="width: 0%"></div>
</div>
</div>
<div class="upload-item-actions">
<div class="upload-item-status">
<i class="fas fa-spinner fa-spin"></i>
</div>
<button class="upload-item-remove">
<i class="fas fa-times"></i>
</button>
</div>
`;
// Add event listener to remove button
item.querySelector('.upload-item-remove').addEventListener('click', function() {
item.remove();
});
return item;
}
function simulateUpload(item, file) {
const progressBar = item.querySelector('.upload-item-progress');
const statusIcon = item.querySelector('.upload-item-status i');
let progress = 0;
// Simulate upload progress
const interval = setInterval(() => {
progress += Math.random() * 10;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
// Update status icon
statusIcon.className = 'fas fa-check';
statusIcon.style.color = 'var(--upload-item-icon)';
}
progressBar.style.width = `${progress}%`;
}, 200);
}
function getFileIconClass(fileType) {
if (fileType.startsWith('image/')) {
return 'fas fa-image';
} else if (fileType === 'application/pdf') {
return 'fas fa-file-pdf';
} else if (fileType.includes('word')) {
return 'fas fa-file-word';
} else if (fileType.startsWith('video/')) {
return 'fas fa-file-video';
} else {
return 'fas fa-file';
}
}
function formatFileSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) return '0 Byte';
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
}
});