拖拽表格行时数据和视图不同步怎么办?

Top丶雨晨 阅读 23

我用原生 JS 实现了表格行的拖拽排序,但拖完之后页面上看起来顺序对了,实际数据数组没变,点保存还是老顺序,这咋整?

试过在 drop 事件里手动 splice 调整数组,但索引老是算错,特别是跨行拖动的时候。有没有靠谱的处理方式?

function handleDrop(e) {
  const draggedIndex = parseInt(e.dataTransfer.getData('text/plain'));
  const targetIndex = [...table.rows].indexOf(e.target.closest('tr'));
  // 这里交换数组元素经常出错
  const item = dataList[draggedIndex];
  dataList.splice(draggedIndex, 1);
  dataList.splice(targetIndex, 0, item);
}
我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
程序员小菊
问题应该出在你先删除再插入的时候,索引已经变了。

你想想,当你把第 0 行拖到第 3 行的位置,先执行 splice(0, 1) 删除了元素,数组长度减 1,原来第 3 行的元素现在实际在第 2 行的位置。这时候你再用 splice(3, 0, item) 插入,位置就错了。

正确的做法是根据拖拽方向来调整插入位置。往下拖的时候,目标索引要减 1;往上拖的时候直接用目标索引就行。

给你改了一版:

function handleDrop(e) {
e.preventDefault();

const draggedIndex = parseInt(e.dataTransfer.getData('text/plain'));
const targetRow = e.target.closest('tr');

if (!targetRow) return;

// 获取目标索引,注意排除表头的影响
const rows = [...table.querySelectorAll('tbody tr')];
const targetIndex = rows.indexOf(targetRow);

// 拖到自己身上或者没找到就不管
if (targetIndex === -1 || targetIndex === draggedIndex) return;

// 先把元素拿出来
const item = dataList.splice(draggedIndex, 1)[0];

// 关键:根据拖拽方向调整插入位置
const insertIndex = draggedIndex < targetIndex ? targetIndex - 1 : targetIndex;
dataList.splice(insertIndex, 0, item);

// 数据变了,视图得重新渲染
renderTable();
}


还有几个坑得提醒你。一个是 e.target 可能是 td 或者 span 之类的子元素,必须用 closest('tr') 找到行元素,这块你倒是处理了。另一个是如果表格有 thead,table.rows 会把表头也算进去,索引就对不上了,建议用 querySelectorAll('tbody tr') 只取数据行。

最后别忘了在 dragover 事件里 e.preventDefault(),不然 drop 事件压根不会触发,这个低级错误我以前也踩过。
点赞 2
2026-02-28 19:33