原生 Drag and Drop 实现文件上传与排序需解耦二者逻辑:上传聚焦 dataTransfer.files 的安全校验与反馈,排序依赖自定义 dragData 的位置感知与状态同步,严禁混用 drop 处理函数。
用原生 drag and drop api 实现“复杂”的文件上传与排序交互,关键不在堆砌功能,而在于把两个场景的核心逻辑理清、解耦、再协同。它们共用同一套事件机制,但目标不同:文件上传关注 数据获取与传输,列表排序关注 位置感知与状态同步。只要分清角色、守住边界、处理好过渡状态,就不难落地。
明确拖拽源与投放目标的职责划分
一个页面里同时存在文件上传区和可排序列表时,必须严格区分“谁在拖”“往哪放”“放的是什么”:
- 文件上传区(如
<div id="upload-drop">)只响应 <code>dragenter/dragover/drop,且只处理e.dataTransfer.files - 排序列表(如
<ul id="task-list"></ul>)只对内部<li draggable="true">做拖拽,drop时读取的是自定义标识(如e.dataTransfer.getData("text/plain")) - 两者不能共用同一个
drop处理函数;否则files和getData混用会导致逻辑错乱 - 在
drop中先调用e.preventDefault(),再检查e.dataTransfer.files.length是否大于 0 - 遍历
files,用.type和.size过滤非法项(例如限制仅audio/wav且 - 将合法文件暂存为
Array<file></file>,不立即发请求;提供“确认上传”按钮或自动延时触发,避免误拖即传 - 上传中显示进度条(可用
XMLHttpRequest.upload.onprogress),成功后更新 UI 并清空暂存数组 -
dragover中用getBoundingClientRect()获取每个<li>的上下边界,对比鼠标clientY判断该插在它上方还是下方 - 动态插入一个
<div class="drop-hint"> 或用伪元素显示插入线,并记录目标索引(例如:插到第 2 项前 → <code>targetIndex = 2) -
drop时从原始数组中splice移除被拖项,再splice(targetIndex, 0, item)插入,然后调用统一渲染函数重绘整个<ul></ul> - 禁用
user-select: none防止拖拽时意外选中文本;设e.dataTransfer.effectAllowed = "move"让光标更直观 - 给上传区和列表容器分别加唯一 class(如
.drop-upload/.drop-sortable),所有事件监听都绑定到具体容器,不委托到document - 在
dragenter中判断e.target是否属于当前区域,不是就直接 return,防止跨区域误触发 - 移动端不支持原生 DnD —— 检测
'ontouchstart' in window,真则隐藏拖拽提示,改用长按 + 手势滑动模拟排序,文件上传改回传统<input type="file">
文件上传:聚焦安全获取与用户反馈
拖入文件后,重点不是立刻上传,而是快速校验、清晰提示、可控提交:
列表排序:确保位置判断准确、DOM 与数据一致
排序不是视觉移动,而是“删掉再插到新位置”,必须靠索引计算和状态驱动:
协同处理:避免事件干扰与移动端降级
当两个功能并存时,容易因事件冒泡或监听冲突出问题:

















