拖拽树形节点到子节点时层级结构变乱怎么办?

宇文雨欣 阅读 42

在做部门管理树形结构拖拽时,遇到拖拽父节点到子节点位置,保存后的数据层级完全错乱。我用Vue3+Sortable.js实现,设置了group和animation,拖拽时视觉显示没问题,但更新数据时发现目标节点的children数组被错误挂载到顶层。尝试过用onAdd监听事件手动处理,但遇到TypeError: Cannot read properties of undefined (reading 'children')

代码大概是这样写的:


onAdd(rootEvent) {
  const { item, target } = rootEvent;
  const parent = target.parentNode; // 这里可能拿错了父节点
  this.updateTree(parent.id, item.id); // 更新父子关系时出错
}

调试发现当拖到叶子节点时,target.parentNode会变成同级元素,该怎么准确获取目标节点的父级?

我来解答 赞 11 收藏
二维码
手机扫码查看
2 条解答
宇文雯婷
拖拽树形结构时,确实容易遇到父子关系错乱的问题。你在使用Sortable.js时,target.parentNode 确实可能拿不到预期的父节点,特别是在拖拽到叶子节点的情况。

首先,确保你理解了Sortable.js的事件参数。onAdd 事件中,rootEvent 包含了 itemtarget,但 target 并不一定就是你想要的父节点。你需要通过Sortable.js提供的其他信息来确定实际的目标父节点。

可以考虑使用 to 属性来获取目标容器(也就是真正的父节点),而不是 target.parentNode。通常,to 就是你想放的父节点对应的DOM元素。然后,你可以通过这个DOM元素找到它对应的ID,再进行后续的更新操作。

这里有一个修改后的代码示例:

onAdd(rootEvent) {
const { item, to } = rootEvent;
const parentId = to.getAttribute('data-id'); // 假设每个节点都有一个data-id属性
if (!parentId) {
console.error('Parent node ID not found');
return;
}
this.updateTree(parentId, item.id);
}


确保每个树节点都有一个唯一的ID,并且在DOM上可以通过类似 data-id 的属性来访问。这样,当你拖拽一个节点到另一个节点下时,可以通过 to.getAttribute('data-id') 来获取目标父节点的ID。

另外,检查 updateTree 方法,确保它能正确处理父子关系的更新。如果 updateTree 方法中出现了 TypeError: Cannot read properties of undefined (reading 'children'),那可能是某个节点的数据结构不完整或者在更新时没有正确初始化 children 数组。

希望这些调整能帮到你,解决拖拽时的层级错乱问题。如果还有其他问题,尽管说。
点赞
2026-03-21 17:07
峻成
峻成 Lv1
这个问题主要是因为拖拽时 target 的层级关系没处理对,Sortable.js 的事件对象里确实容易搞混。我一般会直接从数据源入手,而不是依赖 DOM 结构。代码放这了:

onAdd(rootEvent) {
const { item, to, from } = rootEvent;
const draggedId = item.dataset.id; // 拖拽节点的唯一id
const targetId = to.dataset.id; // 目标节点的唯一id

if (!draggedId || !targetId) return;

// 找到对应的树节点对象
const findNode = (tree, id) => {
for (let node of tree) {
if (node.id === id) return node;
if (node.children) {
const result = findNode(node.children, id);
if (result) return result;
}
}
return null;
};

// 先把拖拽节点移出原位置
const removeNode = (tree, id) => {
for (let i = 0; i < tree.length; i++) {
if (tree[i].id === id) {
return tree.splice(i, 1)[0];
}
if (tree[i].children) {
const removed = removeNode(tree[i].children, id);
if (removed) return removed;
}
}
return null;
};

// 添加到新位置
const draggedNode = removeNode(this.treeData, draggedId);
const targetNode = findNode(this.treeData, targetId);

if (draggedNode && targetNode) {
if (!targetNode.children) targetNode.children = [];
targetNode.children.push(draggedNode);
}

// 更新视图
this.treeData = [...this.treeData];
}


这里的关键是用数据驱动,别直接操作 DOM。先通过唯一标识找到对应的节点,然后在数据结构上重新挂载。记得给每个节点加个唯一的 id,比如 data-id 属性。这个方法能保证层级关系不出错,调试也简单。
点赞 4
2026-02-16 11:09