如何在JavaScript中交换HTML元素?

22

我正在使用经典的JavaScript进行DOM脚本编写,我在一个容器DIV中有一组DIV。 当子DIV被点击时,我希望引起事件的子DIV与其上方的DIV交换位置.. 以下是我的代码:

 <div id="container">
        <div onclick="swapDiv(event);">1</div>
        <div onclick="swapDiv(event);">2</div>
        <div onclick="swapDiv(event);">3</div>
 </div>

如果DIV 2被点击,则应将其与DIV 1交换。


1
如果点击 div 1 呢? - Marcel Korpel
如果div为1,则不进行交换... - Harish Kurup
交换只是内容,还是整个元素? - karim79
如果你想使用jQuery:.replaceWith() - Lorenzo Polidori
7个回答

22
这段代码会实现你想要的功能(如果你想交换所选元素和第一个子元素)。如果你想要其他功能,你需要更加具体地描述。
<script type="text/javascript">
  function swapDiv(event, elem) {
    elem.parentNode.insertBefore(elem, elem.parentNode.firstChild);
  }
</script>


<div id="container">
  <div onclick="swapDiv(event,this);">1</div>
  <div onclick="swapDiv(event,this);">2</div>
  <div onclick="swapDiv(event,this);">3</div>
</div>


是的,这与我想的差不多,但他并不总是想把它放在第一位,他想把它放在前面的 div 上面。所以使用 previousSibling。这对他来说是一个很好的开始。 - CharlesLeaf
1
在这段代码中,当您点击 DIV 3 时,DIV 3 与 DIV 2 相互交换,而不是与 DIV 1 相互交换。 - Harish Kurup
很高兴,你能够将它修改为适合你的情况。 - Thariama

19

parentNode属性可以获取元素的父节点。元素有一个insertBefore函数,可以在另一个参考元素之前插入一个元素(如果它已经在树中的其他地方,则移动它)。节点有一个previousSibling属性,可以获取前一个同级节点(可能是元素也可能不是)。因此:

function swapDiv(elm) {
    var previous = findPrevious(elm);
    if (previous) {
        elm.parentNode.insertBefore(elm, previous);
    }
}

......其中findPrevious看起来像这样:

function findPrevious(elm) {
   do {
       elm = elm.previousSibling;
   } while (elm && elm.nodeType != 1);
   return elm;
}

...其中你的onclick属性应该放置在:

onclick="swapDiv(this);"

虽然您可能想查看DOM2事件挂钩而不是使用addEventListener或IE上的attachEvent,但这也是一种选择。

略微偏题,但我可以推荐使用任何可用的几个库来使您的生活更轻松,例如PrototypejQueryClosure其他几个库。实际上,在早期版本中存在错误,因为我已经很久没有直接处理DOM了。 :-)


这已经被回答得很清楚了,但我会给你点赞,因为你提供了一个完整的好答案。 - CharlesLeaf
正是我所需要的。 - jing3142

11

交换任意节点,不是兄弟节点,不是相邻兄弟节点,不使用临时节点,不克隆节点,不使用jQuery...适用于IE9+。

function swapNodes(n1, n2) {

    var p1 = n1.parentNode;
    var p2 = n2.parentNode;
    var i1, i2;

    if ( !p1 || !p2 || p1.isEqualNode(n2) || p2.isEqualNode(n1) ) return;

    for (var i = 0; i < p1.children.length; i++) {
        if (p1.children[i].isEqualNode(n1)) {
            i1 = i;
        }
    }
    for (var i = 0; i < p2.children.length; i++) {
        if (p2.children[i].isEqualNode(n2)) {
            i2 = i;
        }
    }

    if ( p1.isEqualNode(p2) && i1 < i2 ) {
        i2++;
    }
    p1.insertBefore(n2, p1.children[i1]);
    p2.insertBefore(n1, p2.children[i2]);
}

谢谢。这非常有帮助。在所有地方使用isSameNode(或===)代替isEqualNode,怎么样? - Justin Harris

5

这对于兄弟节点有效,对于任何两个节点都应该有效:

function swapElements(el1, el2) {
    let prev1 = el1.previousSibling;
    let prev2 = el2.previousSibling;

    prev1.after(el2);
    prev2.after(el1);
}

1
我不确定如果其中一个元素没有前一个兄弟节点,是否会起作用,因为元素之间没有空格。 - StefansArya

2

原则上,您只需要在单击的元素之前插入其先前的兄弟元素。然而,在传统脚本中,空格文本节点的存在使这更加麻烦。 元素遍历API将使此过程更加容易,但在浏览器中广泛支持之前,需要使用辅助函数,例如:

<div id="container">
    <div>1</div>
    <div>2</div>
    <div>3</div>
</div>

<script type="text/javascript">
    // Bind event to each line div
    //
    var div= firstElementChild(document.getElementById('container'));
    while (div!==null) {
        div.onclick= swapWithPreviousElement;
        div= nextElementSibling(div);
    }

    // Move element before its previous element sibling
    //
    function swapWithPreviousElement() {
        var previous= previousElementSibling(this);
        if (previous!==null)
            this.parentNode.insertBefore(this, previous);
    }


    // We've used some Element Traversal API methods but some
    // browsers don't support them yet. Provide wrapper functions
    // for compatibility.
    //
    function previousElementSibling(element) {
        if ('previousElementSibling' in element)
            return element.previousElementSibling;
        do
            element= element.previousSibling;
        while (element!==null && element.nodeType!==1);
        return element;
    }
    function nextElementSibling(element) {
        if ('nextElementSibling' in element)
            return element.nextElementSibling;
        do
            element= element.nextSibling;
        while (element!==null && element.nodeType!==1);
        return element;
    }
    function firstElementChild(element) {
        if ('firstElementChild' in element)
            return element.firstElementChild;
        var child= element.firstChild;
        while (child!==null && child.nodeType!==1)
            child= child.nextSibling;
        return child;
    }
</script>

2
听起来你只是想将这个 div 和前面的 div 交换位置,使用 previousSibling 可以实现。你可以尝试使用 insertBefore 方法,但不要创建新的元素,而是使用已有的元素。不过我不确定附加的事件是否能够正确地复制过去。

1
previousSibling通常是一个空白文本节点(除了IE)。 - bobince
1
返回翻译后的文本:确实,但只需测试nodeType,如果它是3,则获取previousSibling(如果有)直到找到一个nodeType 1。这并不太困难。 - CharlesLeaf

0

你可以像这样使用编程来交换一个DOM元素和它的前一个兄弟节点:

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

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