You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

原生JS拖拽脚本改写遇insertBefore错误:目标节点非当前节点子元素

问题

将jQuery拖拽脚本改写为原生JavaScript时,使用insertBefore方法触发错误:NotFoundError: Node.insertBefore: Child to insert before is not a child of this node.,该错误仅在尝试将列表下方元素移至顶部时出现。

原代码

JavaScript

let items = document.querySelectorAll("#items-list > li");

items.forEach((item) => {
  item.setAttribute("draggable", true);
  item.addEventListener("dragstart", dragStart);
  item.addEventListener("drop", dropped);
  item.addEventListener("dragenter", cancelDefault);
  item.addEventListener("dragover", cancelDefault);
});

const getNodeIndex = elm => [...elm.parentNode.children].indexOf(elm)

function dragStart(e) {
  const index = getNodeIndex(e.target);  
  e.dataTransfer.setData("text/plain", index);
}

function dropped(e) {
  cancelDefault(e);

  // 获取新旧索引
  let oldIndex = e.dataTransfer.getData("text/plain");
  let target = e.target;
  let newIndex = getNodeIndex(e.target);

  let droppedElement = e.currentTarget.parentNode.children[oldIndex];
  droppedElement.remove();
  
  // 将元素插入新位置
  if (newIndex < oldIndex) {
    // --- 问题代码行 ---
    target.insertBefore(droppedElement, e.currentTarget.parentNode);
  } else {
    target.after(droppedElement);
  }
}

function cancelDefault(e) {
  e.preventDefault();
  e.stopPropagation();
  return false;
}

HTML

<div class="container">
  <ul id="items-list" class="moveable">
      <li>One</li>
      <li>Two</li>
      <li>Three</li>
      <li>Four</li>
  </ul>
</div>
解决方案

错误根源是insertBefore的调用逻辑错误:你试图在列表项target下插入元素,同时把父节点ul作为“插入位置的前置节点”,但ul并不是target的子节点,因此触发报错。

此外还有两个潜在问题需要修复:

  • e.target可能指向列表项内部的文本节点,导致后续索引计算错误
  • 移除元素后,原索引对应的元素会发生变化,直接通过oldIndex获取元素会失效

修正后的JavaScript代码

let items = document.querySelectorAll("#items-list > li");
const listContainer = document.getElementById("items-list");

items.forEach((item) => {
  item.setAttribute("draggable", true);
  item.addEventListener("dragstart", dragStart);
  item.addEventListener("drop", dropped);
  item.addEventListener("dragenter", cancelDefault);
  item.addEventListener("dragover", cancelDefault);
});

const getNodeIndex = elm => [...elm.parentNode.children].indexOf(elm)

function dragStart(e) {
  // 确保获取的是li元素,而非内部文本节点
  const targetEl = e.target.closest("li");
  const index = getNodeIndex(targetEl);  
  e.dataTransfer.setData("text/plain", index);
}

function dropped(e) {
  cancelDefault(e);

  // 确保获取的是li元素
  const targetEl = e.target.closest("li");
  let oldIndex = parseInt(e.dataTransfer.getData("text/plain"));
  let newIndex = getNodeIndex(targetEl);

  // 先保存要移动的元素,避免移除后索引失效
  let droppedElement = listContainer.children[oldIndex];
  droppedElement.remove();
  
  // 插入到正确位置
  if (newIndex < oldIndex) {
    // 由父容器调用insertBefore,将元素插到目标项之前
    listContainer.insertBefore(droppedElement, targetEl);
  } else {
    targetEl.after(droppedElement);
  }
}

function cancelDefault(e) {
  e.preventDefault();
  e.stopPropagation();
  return false;
}

关键修改说明

  1. 提前获取父容器listContainer,避免重复DOM查询
  2. 使用closest("li")确保始终获取到列表项元素,规避文本节点导致的错误
  3. 修正insertBefore调用逻辑:由父容器执行插入操作,第二个参数为目标列表项
  4. 先保存待移动元素再执行移除,防止索引失效问题

内容的提问来源于stack exchange,提问作者Max Pattern

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 09:53:06