DEV Community

WDSEGA
WDSEGA

Posted on

Component Deep Dive #32: File Drop Zone — Drag, Drop, Validate, Upload

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();
}
Enter fullscreen mode Exit fullscreen mode

Upload Progress

Use XMLHttpRequest instead of fetchfetch 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 + '%';
  }
});
Enter fullscreen mode Exit fullscreen mode

Click to Select

fileInput.value = ''; // Reset to allow re-selecting the same file
Enter fullscreen mode Exit fullscreen mode

Without resetting, change won't fire if the user selects the same file again (value unchanged).

Common Pitfalls

  1. dragleave counter trap — without a counter, highlight flickers
  2. dragover must preventDefault — otherwise drop doesn't fire
  3. fetch doesn't support upload progress — use XHR
  4. fileInput.value must be reset — otherwise can't re-select the same file
  5. 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)