必须在 dragover 事件中调用 preventDefault() 才能触发 drop 事件,否则浏览器默认阻止投放;需结合 getBoundingClientRect() 动态判断鼠标坐标是否在目标区域内,仅在此时阻止,避免影响滚动等正常交互。

dragover 事件不调用 preventDefault() 就别想触发 drop
drop 事件根本不会触发,不是代码写错了,而是浏览器压根没把你当上传目标。只要 dragover 里没写 e.preventDefault(),拖进去的文件就会被浏览器当成“要打开的资源”,直接跳转或下载——你连 drop 的边都摸不到。
必须同时监听 dragenter、dragover、drop(dragleave 可选),且三者都要调用 preventDefault() 和 stopPropagation():
-
dragenter触发一次,适合加高亮样式(如classList.add('drag-over')) -
dragover持续高频触发(每 30–50ms),严禁在里面做文件读取、DOM 更新等耗时操作 -
drop是唯一能安全访问e.dataTransfer.files的地方,别在别的事件里试图提前取文件
FileList 必须转数组才能用 forEach 或 filter
e.dataTransfer.files 是只读的 FileList 对象,不是真数组,没有 forEach、map、filter 方法。直接写 files.forEach(...) 会报 TypeError: files.forEach is not a function。
正确转换方式只有两种:
立即学习“前端免费学习笔记(深入)”;
-
Array.from(files)—— 兼容性好,推荐 -
[...files]—— 简洁,但 IE 不支持
别用 Array.prototype.slice.call(files),冗长且易错;更别误用隐藏的 <input type="file"> 的 files 属性——它始终为空,因为 input 根本没被点击或触发。
移动端 Safari 完全不支持 dragover,必须 fallback 到 click
iOS / iPadOS 上的 Safari 至今(2026 年)仍不触发 dragenter 和 dragover,意味着拖拽逻辑在 iPhone 和 iPad 上默认失效。这不是 bug,是明确的平台限制。
必须提供降级路径:
- 给拖拽容器加
tabindex="0",让它可键盘聚焦 - 监听
click或keydown(空格/回车),然后调用隐藏<input type="file">的.click() - 不要依赖
contenteditable="true"或嵌套<button>,它们会劫持焦点或阻止事件冒泡
很多团队卡在这一步:PC 端测得飞起,一上真机就“点不动”——其实是没处理移动端的交互兜底。
大文件上传别用 readAsDataURL,内存爆炸是秒事
用 FileReader.readAsDataURL() 读一个 10MB 的文件,会生成约 13.3MB 的 base64 字符串,直接吃光页面内存,UI 卡死,甚至触发 Safari 的强制 reload。
除非你要即时预览图片缩略图,否则一律跳过 data URL:
- 纯上传场景:用
readAsArrayBuffer(),再塞进FormData或Blob - 需校验类型/大小:直接读
file.type和file.size,同步、零开销 - 真要预览:用
URL.createObjectURL(file),用完记得URL.revokeObjectURL()
最容易被忽略的是后端字段名匹配——前端 formData.append('upload', file),后端就必须按 upload 这个 key 解析 multipart 数据,拼写差一个字母就 400。



















