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

Posted

技术标签:

【中文标题】如何在 JavaScript 中交换 DOM 子节点?【英文标题】:How to swap DOM child nodes in JavaScript? 【发布时间】:2012-04-01 17:19:50 【问题描述】:

交换子节点顺序的最简单方法是什么?

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

【问题讨论】:

【参考方案1】:

无需克隆。您可以将一个节点移到另一个节点之前。 .insertBefore() 方法会将其从当前位置取出并插入到其他位置(从而移动它):

childNode[4].parentNode.insertBefore(childNode[4], childNode[3]);

你得到了节点的父节点。然后在父节点上调用 insertBefore 方法,并将 childNode[4] 节点传递给它,并告诉它您希望它在 childNode[3] 之前插入。这将为您提供交换订单的结果,因此完成后 4 将在 3 之前。

参考documentation on insertBefore。

任何插入到 DOM 中的节点都会先自动移除,然后再插入回来,因此无需先手动移除。

【讨论】:

+1 我将其总结为“一个节点一次只能存在于 DOM 中的一个位置”:因此将其插入其他位置会将其从当前位置移动。 如何为未知节点 id 实现相同的目标?说不知道有没有childNode[3] @armen - 你必须有一些方法来获取你想要交换的两个元素。几乎总有一种方法可以做到这一点,但具体如何取决于特定的 html 情况和结构。 仅适用于相邻的兄弟姐妹,这不是问题。 @brannigan - 如果您想通用地交换任何两个元素,您可以在此处查看该代码:Swap two HTML elements。【参考方案2】:

使用.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 - 21 年 7 月 95%

【讨论】:

当 node1 恰好是 node2 的后继者时,该交换函数需要一个特殊情况:if (node1 === afterNode2) parent.insertBefore(node1, node2); else ... 参见jsfiddle.net/cayhorstmann/c9uqme45【参考方案3】:

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);

【讨论】:

这将丢失分配给节点的所有事件处理程序。【参考方案4】:

我需要一个函数来交换两个任意节点,将交换的元素保持在 dom 中的相同位置。例如,如果 a 相对于其父级位于位置 2,而 b 相对于其父级位于位置 0,则 b 应替换位置 2 a 的前父母和 a 的孩子应该替换 b 的前父母的孩子 0。

这是我的解决方案,它允许交换位于 dom 的完全不同的部分。请注意,交换不能是简单的三步交换。这两个元素中的每一个都需要首先从 dom 中删除,因为它们可能有需要更新的兄弟姐妹等。

解决方案:我放入了两个持有者 div 来保存每个节点的位置,以保持相对的兄弟顺序。然后我将每个节点重新插入到另一个占位符中,保持交换节点在交换之前的相对位置。 (我的解决方案类似于巴克的)。

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);    

【讨论】:

【参考方案5】:

对于没有cloneNode的任何节点的真正Swap:

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

带 SwapNode 功能(使用 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>

【讨论】:

【参考方案6】:

试试这个方法:

    Get the parent element 存储two elements you want to swap

    存储.nextSibling of the node that is last in order 例如:[1,2,3,4] => 我们要交换 3 和 2,然后存储 3 的 nextSibling,'4'。

    .insertBefore(3,2);

    .insertBefore(2,nextSibling);

【讨论】:

【参考方案7】:

代码说明

val & val2 是要交换的 2 个节点/元素 equiv(index) 在作为参数传递的索引处获取 DOM 中的当前节点/元素

注意:它会计算评论和文本元素,所以要小心 xD

希望这会有所帮助:)

function equiv(index)
      return Array.prototype.slice.call( document.querySelectorAll("*"))[index];


function swap (val,val2)
    let _key = val.key;
    let _key_ = val2.key;
    _key_ = _key < _key_ ? _key_+1:_key_;
    let _parent_ = val2.parentElement.valueOf();
    if (val.parentElement.children.length ==1)
        val.parentElement.appendChild(val2);
    else
        val.parentElement.insertBefore(val2,val);
    if (_parent_.children.length ==0)
        _parent_.appendChild(val);
    else
        let _sibling_ = equiv(_key_);
        _parent_.insertBefore(val,_sibling_);

【讨论】:

【参考方案8】:

考虑到要交换的两个元素的索引,无需克隆即可工作的解决方案:

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)
    

【讨论】:

【参考方案9】:

使用虚拟兄弟作为临时位置标记,然后使用.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>

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

【讨论】:

以上是关于如何在 JavaScript 中交换 DOM 子节点?的主要内容,如果未能解决你的问题,请参考以下文章

JS DOM中Ajax的使用

如何使用 XPATH for XML 获取子节点数

如何根据子节点数对剑道树视图进行排序

如何在 DOM(javascript 或 jquery)中显示元素的值?

在 Chrome 中单步执行 JavaScript 断点时如何查看 DOM?

JavaScript -DOM 编程艺术 2nd 完