错误在于list__item--dragging语义失真、易致样式冲突且脱离原生拖放事件;正确做法是用sortable-list--dragging标记容器全局态,并分别用list__item--drag-source和list__item--drop-target精准标识源项与目标项。

拖拽状态不能用 --dragging 修饰符直接加在列表项上——它不是列表项自身的状态,而是整个拖拽交互流程中的临时视觉反馈,必须绑定到明确的 Block 或其可交互 Element,且需配合 JS 精确控制生命周期。
为什么 list__item--dragging 是错的
列表项(list__item)本身不“处于拖拽中”:它只是被鼠标按住那一刻的起点;真正进入拖拽流程的是整个列表容器(比如 sortable-list),或触发拖拽行为的可拖动元素(如 list__item--draggable)。把 --dragging 强行挂在 list__item 上会导致:
- 语义失真:DOM 上看到
list__item--dragging,但此时 item 可能已脱离文档流(dragstart后被移走),类名残留却无实际意义 - 样式冲突:多个 item 同时带
--dragging(比如多选拖拽)时,无法区分“正在拖的源”和“被悬停覆盖的目标” - 与原生拖放事件脱节:浏览器只在
dragstart/dragend触发时知道“谁开始/结束”,JS 很难靠 DOM 类名反推当前拖拽上下文
正确做法:用 sortable-list--dragging 控制全局态
当用户按下并开始拖动时,应由 JS 在列表容器(Block)上添加 sortable-list--dragging,表示“当前整个列表处于拖拽交互中”。这个修饰符用于统一关闭干扰行为、提供视觉锚点:
- 禁用滚动监听:
.sortable-list--dragging { pointer-events: none; }防止拖动过程中误触滚动条 - 隐藏非关键装饰:
.sortable-list--dragging .list__item::before { opacity: 0; }减少视觉噪音 - 调整 z-index:
.sortable-list--dragging { z-index: 1000; }确保拖拽层不被遮挡 - 注意:该类必须在
dragend事件后立即移除,不可依赖drop—— 因为用户可能拖出区域取消操作
如何标记“被拖拽的源项”和“悬停目标项”
真正需要区分的是两个角色,它们应使用不同层级的修饰符,且均由 JS 主动管理:
- 源项(正在被拖的那个):用
list__item--drag-source,仅在dragstart时添加,dragend时清除。CSS 中可设opacity: 0.6或transform: scale(0.95)表示“已拾起” - 目标项(鼠标正悬停其上的候选位置):用
list__item--drop-target,在dragover事件中根据坐标判断是否插入其上方/下方,并动态添加/移除。切勿用:hover模拟——它无法响应拖拽光标 - 二者不可共存:同一时刻,一个
list__item要么是源,要么是目标,不能同时有--drag-source和--drop-target
移动端适配时的特殊处理
移动端没有 dragstart 等原生事件,需用 touchstart/touchmove 模拟。此时 sortable-list--dragging 的触发时机要更谨慎:
- 不能仅靠
touchstart就加类——得等移动距离 > 4px 才判定为拖拽意图,避免误触 -
list__item--drag-source必须配合touch-action: none阻止默认滚动,否则手指一滑就触发页面滚动而非拖拽 - 目标项高亮建议用
outline或box-shadow,避免改height或margin——这些会触发重排,导致触摸反馈卡顿
最易被忽略的一点:所有拖拽相关修饰符都必须在 JS 中显式添加/移除,不能依赖 CSS 伪类(如 :active)或框架的响应式绑定。因为拖拽是跨帧、跨 DOM 生命周期的异步行为,类名生命周期一旦失控,就会出现“拖完了还高亮”或“目标项没反应”的问题。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











