必须在dragover和drop事件中均调用event.preventDefault()才能获取文件;dataTransfer.files只读且仅在drop中有效; draggable对表单控件无效,需外包容器实现。

不难,但必须踩对三个关键点:draggable 属性只对部分元素默认生效、dragover 事件里漏掉 event.preventDefault() 就完全没反应、dataTransfer.files 只能在 drop 里读取——其他地方全是空的。
为什么拖拽区域“看起来能拖却放不进去”
这是最常卡住人的地方。浏览器对文件拖入有强默认行为(比如直接打开图片、下载文件),它会主动拦截放置逻辑,除非你明确告诉它“我要自己处理”。
关键动作只有两行,缺一不可:
-
drop和dragover两个事件回调里都得调用event.preventDefault() -
dragover还必须加,光加drop不行——因为浏览器在拖入过程中就拒绝了放置资格 - 别信“只在
drop里 prevent 就够了”,那是错觉;实际测试中,90% 的失败都卡在这一步
如何正确拿到拖进来的文件列表
dataTransfer.files 是只读的 FileList 对象,但它不是随时可取的——只在 drop 事件触发时才有效。你在 dragenter 或控制台里打日志都拿不到。
立即学习“前端免费学习笔记(深入)”;
常见误操作:
- 在
dragstart里试图读event.dataTransfer.files→ 永远是空的,因为还没拖文件进来 - 把
files存到全局变量再异步处理 → 有可能被 GC 或跨事件丢失引用 - 没检查
files.length就直接遍历 → 用户松手没拖文件进来时会报错
稳妥写法:
dropZone.addEventListener('drop', function(event) {
event.preventDefault();
const files = event.dataTransfer.files;
if (files.length === 0) return;
Array.from(files).forEach(file => {
console.log(file.name, file.size, file.type);
});
});
表单控件(input、textarea)为啥设了 draggable="true" 还拖不动
Chrome 和 Firefox 对原生表单控件做了限制:<input>、<textarea></textarea>、<select></select> 即使加了 draggable="true",dragstart 也不会触发——这不是 bug,是浏览器故意的。
解法不是硬刚,而是绕开:
- 把表单控件包在
<div> 或 <code><li>里,给容器设draggable="true" - 容器里的
<input>加disabled,防止用户误点;拖入目标区后再启用并动态赋值 - 拖拽时用
event.dataTransfer.setData('text/plain', JSON.stringify({...}))传结构化数据,而不是指望拖 DOM 节点本身
真正容易被忽略的,是 dragover 的触发频率极高,如果在里面做重计算或 DOM 操作,滚动拖拽时会明显卡顿;视觉反馈建议只切 CSS class,别查 offset 或 rerender。



















