dragover 必须立即调用 preventdefault(),否则 drop 不触发;drop 中文件需从 e.datatransfer.files 获取,用 url.createobjecturl 预览图片并及时 revoke;formdata 上传字段名须与后端严格一致,fetch 不设 content-type。

dragover 必须调用 preventDefault(),否则 drop 根本不触发
浏览器对拖入文件有强默认行为:图片会打开预览页,文本会跳转到 data URL,PDF 可能直接下载。只要 dragover 里没调 e.preventDefault(),drop 事件永远不会到达你的 JS 逻辑——你连调试器都进不去。
常见错误写法:drop 里写了 preventDefault(),但 dragover 没写;或者把 preventDefault() 写在异步回调里(比如 setTimeout 中);又或者容器被子元素(如 <button></button> 或带 contenteditable="true" 的 <div>)拦截了事件冒泡。
<ul><li>必须给拖拽目标容器(如 <code><div id="editor-dropzone">)绑定 <code>dragover,并在处理函数第一行就调 e.preventDefault()
overflow: auto 或内部有可聚焦元素,加一句 e.stopPropagation() 更稳妥dragenter 并调 preventDefault(),但 Chrome/Firefox/Edge 现代版本只靠 dragover 就够drop 里取不到文件?别用 input.files,要用 e.dataTransfer.files
很多人把隐藏的 <input type="file"> 当成“文件来源”,在 drop 事件里去读 document.getElementById('file-input').files——结果永远是空。因为拖拽动作根本没触发那个 input,它只是备用点击入口。
真正有效的文件列表只在 drop 事件对象的 e.dataTransfer.files 上,这是一个只读的 FileList。它和 input.files 类型一致,但来源完全不同。
-
e.dataTransfer.files是用户实际拖进来的所有文件,包含 name、size、type、lastModified 等元数据 - 要遍历处理多个文件,得先转成数组:
Array.from(e.dataTransfer.files) - 注意:它不包含文件夹(除非用
webkitGetAsEntry()手动递归读取,兼容性差,一般不用) - 别漏掉
e.preventDefault()在drop里也得调——双重保险,避免页面跳转
拖进来的文件怎么塞进 HTML 编辑器?别硬塞 base64 图片
编辑器(比如 contenteditable 区域或 Quill/Editor.js 等)不是文件管理器。直接把 FileReader.readAsDataURL() 生成的超长 base64 字符串插进 DOM,轻则卡顿,重则崩溃——尤其拖一个 10MB 的 PNG,base64 字符串轻松破百万字符。
更合理的方式是:用 URL.createObjectURL(file) 创建临时本地 URL,插入 <img src="...">,等上传完成再替换成服务端返回的真实地址。内存可控,体验顺滑。
- 预览阶段优先用
URL.createObjectURL(file),不是readAsDataURL - 上传成功后,记得调
URL.revokeObjectURL(url)清理引用,防内存泄漏 - 如果编辑器支持自定义插入(如 Quill 的
insertEmbed),把临时 URL 当作 placeholder,上传完成再 patch - 非图片文件(如 PDF、DOCX)建议只显示文件名 + 图标,点开才触发下载或新页打开
FormData 上传时 append 的 key 名必须和服务端约定一致
前端拼 FormData 很简单,但后端接收失败十有八九是因为字段名不匹配。比如你写 formData.append('upload', file),而服务端只认 files[] 或 attachment,那就 400 了。
另外,多文件上传时,不要手动循环 append 同名 key 以为能自动变数组——Node.js 的 multer、PHP 的 $_FILES、Python 的 request.files 都依赖原始表单字段结构,必须保持原生语义。
- 单文件:用
formData.append('file', file, file.name),第三个参数保留原始文件名 - 多文件:对每个
file单独调append('files', file, file.name),后端才能按数组收 - 如果要传额外字段(如标题、分类),一起 append 进 FormData,别走 query string
- fetch 时别设
Content-Type头——让浏览器自动设置 boundary,否则 multipart 会解析失败
dragover 开始阻止,到 drop 提取文件,再到 URL.createObjectURL 和 FormData 的衔接方式,每一步断掉都会表现为“看起来没反应”或“上传失败但无报错”。尤其在嵌入编辑器场景下,contenteditable 容器本身可能劫持焦点或事件,务必用真实 DOM 节点做目标,别依赖代理或虚拟节点。











