Component Deep Dive #32: File Drop Zone
Drag-and-drop upload looks smooth, but the dragleave event trigger count will drive you crazy.
The File Drop Zone is standard in modern web apps — from Gmail attachments to GitHub PR uploads. But this component has a classic pitfall: dragleave fires multiple times.
The dragenter/dragleave Counter Trap
When you drag a file into the dropzone, dragenter and dragleave fire multiple times — once per child element entered. This causes the highlight to flash and disappear immediately.
Solution: a counter. dragCounter increments on dragenter, decrements on dragleave. Only remove highlight when counter reaches zero — meaning the drag has truly left the dropzone.
dragover's e.preventDefault() is mandatory. Browsers default to blocking file drops (they open the file instead). Without preventing, drop never fires.
File Validation
Type Validation
file.type can be spoofed. For security-critical scenarios, also check the extension.
Extension Double-Check
function getExtension(filename) {
return filename.slice(filename.lastIndexOf('.') + 1).toLowerCase();
}
Upload Progress
Use XMLHttpRequest instead of fetch — fetch doesn't support upload progress. This is the one scenario where XHR beats fetch.
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
fileItem.progressBar.style.width = percent + '%';
}
});
Click to Select
fileInput.value = ''; // Reset to allow re-selecting the same file
Without resetting, change won't fire if the user selects the same file again (value unchanged).
Common Pitfalls
- dragleave counter trap — without a counter, highlight flickers
- dragover must preventDefault — otherwise drop doesn't fire
- fetch doesn't support upload progress — use XHR
- fileInput.value must be reset — otherwise can't re-select the same file
- accept attribute is only a suggestion — users can select other types, JS must validate again
This article was originally published on Deskless Daily.
Top comments (0)