拖拽上传必须用可监听事件的块级容器(如)而非,需手动处理dragover(必调preventdefault)和drop事件;文件从e.datatransfer.files读取,隐藏input用于降级点击。

拖拽上传区域不能靠 <input type="file"> 自己实现,它只响应点击;真正起作用的是包裹它的容器元素,且必须手动处理 dragover 和 drop 事件。
HTML 结构必须用 <div> 容器而非 <code><input> 直接拖拽
常见错误是把 <input type="file" multiple> 套在 <label></label> 里就以为能拖拽——它只会触发点击选择,完全不响应 drop。拖拽目标必须是可监听事件的块级容器。
- 容器需显式设
draggable="false"(防文本被意外拖走) -
<input type="file">要保留但隐藏(display: none),用于降级点击和复用accept属性 - 不要用
<form></form>直接包裹容器,否则drop可能被表单提交拦截 - 避免容器内含
contenteditable="true"或可聚焦子元素(如<button></button>),它们会劫持dragover
dragover 不调 preventDefault() 就等于没写
这是最常踩的坑:只绑了 drop 事件,却忘了在 dragover 里阻止默认行为。浏览器会直接打开文件或显示“禁止”图标,drop 根本不会触发,也不报错。
- 必须在
dragover回调里调用e.preventDefault()和e.stopPropagation() -
dragover会高频触发,里面只做拦截,别校验文件类型或读取内容 -
dragenter/dragleave可选,仅用于加/移除高亮样式,不影响功能
从 e.dataTransfer.files 读多文件,别碰 items
e.dataTransfer.files 是原生 FileList,天然支持多文件;而 items 可能混入文本、链接等非文件项,徒增过滤成本。
- 直接用
Array.from(e.dataTransfer.files)转数组,方便后续遍历 - 检查
files.length === 0:macOS Safari 拖入文件夹时可能返回空列表 - 别尝试给
input.files赋值——它是只读 DOM 属性,赋值无效 - 文件名 Unicode 字符无需解码,但插入 DOM 时建议用
textContent而非innerText避免转义
移动端 iOS Safari 直到 16.4 才有限支持 dataTransfer.files
真机上拖拽上传在 iOS 上基本不可用,Safari 仍会跳转预览页或静默失败。没有兜底点击方案,用户就卡死。
- 必须保留隐藏的
<input type="file">,并绑定change事件作降级路径 - 不要依赖
dragenter触发视觉反馈,移动端无意义 - 若需兼容旧版 iOS,上传逻辑应统一走
input.files,拖拽只是视觉糖衣











