如何在JavaScript中交换DOM子节点?

64

如何最简单地交换子节点的顺序?

例如,我想让childNode[3]成为childNode[4],反之亦然。

11个回答

101
没有必要进行克隆。你只需要将一个节点移动到另一个节点之前即可。使用.insertBefore()方法可以将节点从当前位置移动到其他位置(从而实现移动):
childNode[4].parentNode.insertBefore(childNode[4], childNode[3]);

你获取节点的父节点。然后在父节点上调用insertBefore方法,并将childNode[4]节点作为参数传递给它,告诉它要在childNode[3]之前插入。这将使它们的顺序交换,所以当完成时,4会在3之前。
参考insertBefore的文档
任何已经插入到DOM中的节点,如果再次插入,会自动先移除再插入,因此不需要手动先移除。

12
我会将其总结为“一个节点在 DOM 中同一时间只能存在于一个位置”,因此将其插入到其他位置会使它从当前位置移动。 - user166390
如何针对未知节点ID实现相同的操作?假设如果存在childNode [3],则该节点ID是未知的。 - armen
9
仅适用于相邻兄弟元素,这并不是问题的关键点。 - br4nnigan
@brannigan - 呃,问题明确是关于交换 childnode [3]childnode [4] 的,它们肯定是相邻的兄弟姐妹,并且显然对于提问者来说已经完美地运作了(因为他们接受了这个答案)。这确实回答了提问者想要知道的问题。 - jfriend00
2
@brannigan - 如果你想通用地交换任意两个元素,你可以在这里查看代码:交换两个HTML元素 - jfriend00
显示剩余3条评论

24

使用 .before.after

这是原生JS!

childNode[3].before(childNode[4]);
或者
childNode[4].after(childNode[3]);

为了获得更长久的交换体验,请尝试:

function swap(node1, node2) {
    const afterNode2 = node2.nextElementSibling;
    const parent = node2.parentNode;
    node1.replaceWith(node2);
    parent.insertBefore(node1, afterNode2);
}

即使父元素不匹配,这个应该也能工作。

Can I Use - 2021年7月95%的浏览器支持


4
当节点1恰好是节点2的后继节点时,该交换函数需要一个特殊情况的处理:if (node1 === afterNode2) parent.insertBefore(node1, node2); else ... 请参见http://jsfiddle.net/cayhorstmann/c9uqme45。 - cayhorstmann

9

jfriend00的回答并没有真正交换元素(它只能“交换”相邻且在同一父节点下的元素)。这没关系,因为这就是问题所在。

这个例子通过克隆元素来交换它们,而不管它们的位置和DOM级别:

// Note: Cloned copy of element1 will be returned to get a new reference back
function exchangeElements(element1, element2)
{
    var clonedElement1 = element1.cloneNode(true);
    var clonedElement2 = element2.cloneNode(true);

    element2.parentNode.replaceChild(clonedElement1, element2);
    element1.parentNode.replaceChild(clonedElement2, element1);

    return clonedElement1;
}

编辑:新增了返回新引用的部分(如果你想保留引用,比如访问"parentNode"属性时(否则会丢失))。例如:e1 = exchangeElements(e1, e2);


10
这将丢失分配给节点的任何事件处理程序。 - bryc

7
我需要一个函数来交换两个任意节点,使交换后的元素在dom中仍保持原位置。例如,如果a相对于其父元素在位置2,而b相对于其父元素在位置0,则b应替换a以前的父元素的位置2,a应替换b以前的父元素的子元素0。
这是我的解决方案,允许交换在dom的完全不同部分进行。请注意,交换不能是简单的三步交换。必须先从dom中删除每个元素,因为它们可能具有需要更新的兄弟元素等。
解决方案:我放置了两个占位符div,用于保存每个节点的位置,以保持相对兄弟顺序。然后,我重新插入每个节点到另一个占位符中,保持交换节点在交换前的相对位置。(我的解决方案类似于Buck的)。
function swapDom(a,b) 
{
     var aParent = a.parentNode;
     var bParent = b.parentNode;

     var aHolder = document.createElement("div");
     var bHolder = document.createElement("div");

     aParent.replaceChild(aHolder,a);
     bParent.replaceChild(bHolder,b);

     aParent.replaceChild(b,aHolder);
     bParent.replaceChild(a,bHolder);    
}

1
这应该是被接受的答案,因为它将所有事件监听器保留在DOM元素上。 - zyrup

4

使用虚拟兄弟作为临时位置标记,然后使用.before(或.after)方法。这适用于任何兄弟节点(不仅限于相邻节点),并且还保留事件处理程序。

function swap(a, b) {
    let dummy = document.createElement("span")
    a.before(dummy)
    b.before(a)
    dummy.replaceWith(b)
}
<div id="div1">A</div>
<div id="div2">B</div>
<p> parent<div id="div3">C</div>
</p>

<button onclick="swap(div1, div3)">swap</button>

就像使用临时变量交换变量一样,如果缺少更复杂的方法。


1
有趣的是,这个答案是唯一一个在我的用例(jsfiddle)中有效的,我在其中翻转了父元素中标记的元素。 - Benny Bottema
1
这是唯一一个对我完美起作用的答案,赞!!! - undefined

4
最好的方法是创建一个临时节点。
  function swapNodes(node1, node2) {
      const temp = document.createComment('')
      node2.replaceWith(temp)
      node1.replaceWith(node2)
      temp.replaceWith(node1)
  }

2

实现节点的真正交换,不需要使用cloneNode:

    <div id="d1">D1</div>
    <div id="d2">D2</div>
    <div id="d3">D3</div>

With SwapNode function (using PrototypeJS):

function SwapNode(N1, N2)  {
N1 = $(N1);
N2 = $(N2);

if (N1 && N2) {
    var P1 = N1.parentNode;
    var T1 = document.createElement("span");    
    P1.insertBefore(T1, N1);

    var P2 = N2.parentNode;
    var T2 = document.createElement("span");
    P2.insertBefore(T2, N2);

    P1.insertBefore(N2, T1);
    P2.insertBefore(N1, T2);

    P1.removeChild(T1);
    P2.removeChild(T2);
}
}
SwapNode('d1', 'd2');
SwapNode('d2', 'd3');

将会产生:

<div id="d3">D3</div>
<div id="d1">D1</div>
<div id="d2">D2</div>

0

给定要交换的两个元素的索引,无需克隆即可解决的方案:

function swapChildren(parentElement, index1, index2) {

    if (index1 === index2)
        return

    if (index1 > index2) {

        const temp = index1

        index1 = index2
        index2 = temp
    }

    const { [index1]: element1, [index2]: element2 } = parentElement.childNodes

    if (index2 === index1 + 1) {
        parentElement.insertBefore(element2, element1)
    } else {

        const reference = element2.nextSibling

        parentElement.replaceChild(element2, element1)
        parentElement.insertBefore(element1, reference)
    }
}

刚刚添加了一些解释,以及我基于WHATWG规范进行实现的链接。 - undefined

-1

尝试这个方法:

  1. 获取父元素
  2. 存储你想要交换的两个元素
  3. 存储在顺序中最后一个节点的.nextSibling,例如:[1,2,3,4] => 我们想要交换 3 和 2,然后存储 3 的 nextSibling,即 '4'。

  4. .insertBefore(3,2);

  5. .insertBefore(2,nextSibling);

-1

您可以像这样将DOM元素与其下一个兄弟元素交换:

el.parentNode.insertBefore(el, el.nextElementSibling)

或者像这样与其前一个兄弟元素一起:

el.parentNode.insertBefore(el, el.previousElementSibling)

如果您的内容是动态的,您可能需要检查el.nextElementSiblingel.previousElementSibling是否为null。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接