dragenter/dragleave 必须配对监听并调用 preventdefault() 才能稳定高亮,dragover 仅用于阻止默认行为以启用 drop;dropzone 需设 tabindex="0" 和 draggable="false",校验应基于 datatransfer.items 在 dragenter 中完成。

dragenter/dragleave 必须配对监听才能稳定高亮
只监听 dragover 是无法可靠触发视觉反馈的——它高频触发但不区分“刚进入”和“还在里面”,容易导致样式反复切换或卡顿。真正控制高亮时机的是 dragenter 和 dragleave:前者设高亮类,后者移除。
常见错误是漏掉 dragleave,结果用户拖出区域后边框还亮着;或者没阻止 dragenter 的默认行为,导致部分浏览器(如旧版 Safari)不触发该事件。
-
dragenter中调用e.preventDefault(),否则某些浏览器不保证后续事件流完整 - 用
event.stopPropagation()防止父容器也收到dragenter(尤其当区域嵌套时) - 不要在
dragenter里做文件类型校验——此时e.dataTransfer.files可能为空,应改用e.dataTransfer.items - 高亮样式建议用 CSS 类切换,而非直接操作
style.borderColor,便于复用和主题适配
dropzone 元素必须加 tabindex="0" 才支持键盘聚焦高亮
仅靠鼠标悬停高亮不够。键盘用户按 Tab 键聚焦到上传区时,若没 tabindex="0",区域不会获得焦点,:focus 样式不生效,也无法用空格/回车触发文件选择——这直接违反 WCAG 2.1。
同时要设 draggable="false",否则内部文本可能被意外拖走,干扰 drop 行为(Safari 尤其敏感)。
- HTML 结构示例:
<div id="dropzone" tabindex="0" draggable="false"> <li>配套 CSS:<code>#dropzone:focus { outline: 2px solid #007bff; } - 别用
<button></button>或<label></label>包整个区域——它们自带点击逻辑,和drop事件冲突概率高 - 加上
role="region"和aria-label="上传文件区域",屏幕阅读器才能正确播报功能 - 错误写法:
if (isValidType) e.preventDefault()→ 高亮失效,drop 不触发 - 正确写法:
e.preventDefault()无条件执行,校验逻辑放dragenter或drop - 避免在
dragover里做任何耗时操作(如读取文件内容、调 API),它每几十毫秒就触发一次 - 移动端 iOS Safari 直至 16.4 才有限支持
dataTransfer.files,高亮逻辑需降级为仅响应点击 - 校验示例:
for (let item of e.dataTransfer.items) { if (item.kind === 'file' && item.type.startsWith('image/')) { /* 允许 */ } } -
item.type可被伪造,仅作前端友好提示,后端仍需二进制检测 - 若所有
items都不匹配,立刻添加error类并显示提示文字,同时禁用提交按钮(如有) - 别在
dragover中重复校验——它会高频触发,造成卡顿
dragover 里 preventDefault() 是高亮生效的前提,不是可选项
如果 dragover 回调里没写 e.preventDefault(),浏览器会执行默认行为(比如打开图片、跳转链接),drop 事件根本不会触发——高亮再准也没用。这不是兼容性补丁,是 HTML5 拖拽规范的硬性要求。
这个调用必须出现在 dragover 中,且不能被条件语句包裹(例如不能只在满足某格式时才 prevent)。哪怕你只想高亮、不处理上传,也得先过这一关。
悬停阶段就提示格式错误,别等 drop 后才报错
用户把不支持的文件(比如 .exe)拖进区域,等松手才弹“仅支持 JPG/PNG”——体验割裂。应该在 dragenter 阶段就解析 e.dataTransfer.items,根据 item.type 判断 MIME 类型并实时反馈。
注意 items 比 files 更早可用,且不依赖文件实际读取,适合做前置拦截。但它返回的是 DataTransferItem,不是 File,所以不能访问 .size 或 .lastModified。
高亮逻辑看似简单,但绕不开 tabindex、preventDefault()、items 和 dragleave 这四个点。少一个,要么键盘用户不可用,要么松手就跳页,要么提示滞后,要么样式粘连。这些不是“优化项”,是拖拽上传能跑起来的最小闭环。











