限制多列表拖拽层级需在dragstart记录源节点深度,dragover实时校验目标位置合法性,drop阶段二次验证并更新data-depth;使用vuedraggable或Sortable时须手动补全层级控制逻辑。

限制多列表拖拽的层级,核心在于控制节点在树形结构中的深度位置,防止无限嵌套或父子关系错乱。这不是靠单一配置能解决的,而是需要在拖拽交互的每个关键环节加入层级校验与约束逻辑。
拖拽开始时记录源节点深度
在 dragstart 阶段,立即获取被拖拽项当前所处的嵌套层级。常用方式是向上遍历父级 <ul></ul> 或带有 data-depth 属性的容器:
- 通过
el.closest('ul[data-depth]')获取最近的带深度标记的父容器 - 或递归调用
el.parentElement统计缩进层级(如每层子列表缩进一次) - 将该深度值存入
dataTransfer.setData('text/plain', depth),供后续 drop 阶段读取
拖拽过程中实时判断目标位置合法性
在 dragover 事件中,不能只看目标元素类型,还要结合鼠标偏移与目标结构做双重判断:
- 若目标是子列表容器(如
<ul class="children"></ul>),计算鼠标 Y 偏移是否落在其下半区(如 >60% 高度),再检查插入后新深度是否 ≤ 最大允许层级(例如 4) - 若目标是普通列表项(
<li>),需区分“插入同级”和“拖入子级”:仅当目标项本身允许拥有子项(如data-can-children="true")且新深度未超限时,才激活子项插入反馈 - 用
event.preventDefault()启用 drop,但仅对合法位置设置dataTransfer.dropEffect = "move"
执行插入前拦截非法操作
在 drop 阶段,必须再次验证——因为用户可能在拖拽过程中快速移动,导致 dragover 判断滞后:
- 检查目标节点是否包含被拖拽节点自身(
target.contains(draggedNode)),避免父拖入子孙 - 确认目标根容器(如最外层
<ul></ul>)不允许被拖入任何内部,只接受同级插入 - 若判定为拖入子列表,确保目标存在有效的
<ul class="children"></ul>;否则降级为同级插入或取消操作 - 插入后,同步更新整个路径上所有节点的
data-depth值,保持数据与视图一致
借助组件时注意配置边界
若使用 vuedraggable 或 Sortable 等库,需主动补全层级控制:
-
vuedraggable本身不校验层级,需在@change回调中手动遍历新顺序,校验每个节点的level字段 -
Sortable可设maxDepth: 4(部分 fork 支持),但原生版本需配合onMove钩子函数返回false拦截越界行为 - 禁用跨层级拖拽时,可设置
group: { name: 'list', put: ['list'] }并在onMove中比对related.group和dragged.group

















