【问题标题】:Swap two html elements and preserve event listeners on them交换两个 html 元素并在它们上保留事件侦听器
【发布时间】:2012-05-29 19:50:36
【问题描述】:

有类似的问题,但所有的答案都是为了只为里面的内容交换html元素。

我需要交换两个 div,其中包含很多内容(表格、选择框、输入等)。 元素上有事件监听器,所以我需要在交换主 div 后保留它们。

我可以使用 jQuery 1.5。所以答案是可以的。

【问题讨论】:

    标签: javascript jquery dom dom-events


    【解决方案1】:

    要在不丢失事件处理程序或破坏 DOM 引用的情况下交换两个 div,您只需在 DOM 中移动它们即可。关键是不要更改 innerHTML,因为这会从头开始重新创建新的 DOM 节点,并且这些 DOM 对象上的所有先前事件处理程序都将丢失。

    但是,如果您只是将 DOM 元素移动到 DOM 中的新位置,则所有事件都会保持关联,因为 DOM 元素只会重新设置父级,而不会更改 DOM 元素本身。

    这是一个快速函数,可以交换 DOM 中的两个元素。只要其中一个不是另一个的子元素,它就可以与任意两个元素一起使用:

    function swapElements(obj1, obj2) {
        // create marker element and insert it where obj1 is
        var temp = document.createElement("div");
        obj1.parentNode.insertBefore(temp, obj1);
    
        // move obj1 to right before obj2
        obj2.parentNode.insertBefore(obj1, obj2);
    
        // move obj2 to right before where obj1 used to be
        temp.parentNode.insertBefore(obj2, temp);
    
        // remove temporary marker node
        temp.parentNode.removeChild(temp);
    }
    

    你可以在这里看到它的工作原理:http://jsfiddle.net/jfriend00/NThjN/


    这是一个无需插入临时元素的版本:

    function swapElements(obj1, obj2) {
        // save the location of obj2
        var parent2 = obj2.parentNode;
        var next2 = obj2.nextSibling;
        // special case for obj1 is the next sibling of obj2
        if (next2 === obj1) {
            // just put obj1 before obj2
            parent2.insertBefore(obj1, obj2);
        } else {
            // insert obj2 right before obj1
            obj1.parentNode.insertBefore(obj2, obj1);
    
            // now insert obj1 where obj2 was
            if (next2) {
                // if there was an element after obj2, then insert obj1 right before that
                parent2.insertBefore(obj1, next2);
            } else {
                // otherwise, just append as last child
                parent2.appendChild(obj1);
            }
        }
    }
    

    工作演示:http://jsfiddle.net/jfriend00/oq92jqrb/

    【讨论】:

    • 我可能会补充一点,除了 2 个极端情况:交换第 n 个或第 n 个项目之外,此代码是唯一真正有效的代码。
    • @philk - 我不明白你认为哪些极端情况不起作用。请解释。也许显示一个 jsFiddle。
    • 这里的工作真的很棒!性能方面,你建议做什么?
    • @Jessica - 你必须在两者上运行类似 jsPerf 的东西来测试性能,但我认为第二个(不创建临时元素)可能更快.
    • @jfriend00 谢谢!另外,我注意到您使用nextSibling 来声明next2。如果您执行nextSibling,而不是nextElementSibling,则该函数将始终仅执行第二个可能的if 选项。但是,如果您执行 nextElementSibling,则需要所有 if 语句。 (因此我假设以下方式更好)jsfiddle.net/8uyptyv0
    【解决方案2】:

    这是一个更简单的函数,用于交换两个元素而无需实际重新加载元素...

    function swapElements(obj1, obj2) {
        obj2.nextSibling === obj1
        ? obj1.parentNode.insertBefore(obj2, obj1.nextSibling)
        : obj1.parentNode.insertBefore(obj2, obj1); 
    }
    

    注意:如果 obj1 有像 YouTube 这样的嵌入视频,则在交换时不会重新加载。 只是元素的位置发生了变化。

    【讨论】:

    • 非常优雅,没有此处投票最多的标记 div 答案
    • 我相信这个答案,obj2 必须在obj1 之后。如果obj1obj2 之前,它们将不会交换。 @jfriend00 答案将交换两个元素,无论它们是在彼此之前还是之后。
    • 这仅在元素彼此相邻且具有相同父级的情况下才能正常工作。用几个元素将它们分开或给它们不同的父母,它不会交换它们。它只是一个前一个地移动。只需尝试this jsFiddle 并按“交换 A C”按钮。
    • 非常优雅。对列表进行排序时效果很好
    【解决方案3】:

    如果您跟踪两个元素的 parentNodes 和一个的 nextSibling,您可以将两个元素与其子元素交换,而无需任何临时占位符。

    如果第二个元素是唯一的子元素,或者是其父元素的最后一个子元素,则其替换(正确)附加到父元素。

    function swap(a, b){
        var p1= a.parentNode, p2= b.parentNode, sib= b.nextSibling;
        if(sib=== a) sib= sib.nextSibling;
        p1.replaceChild(b, a);
        if(sib) p2.insertBefore(a, sib);
        else p2.appendChild(a);
        return true;
    }
    

    【讨论】:

      【解决方案4】:

      我的简单函数似乎是最短和最广泛兼容的。它不需要占位符或重新加载元素或任何类似的混乱:

      function swapElements(el1, el2) {
          var p2 = el2.parentNode, n2 = el2.nextSibling
          if (n2 === el1) return p2.insertBefore(el1, el2)
          el1.parentNode.insertBefore(el2, el1);
          p2.insertBefore(el1, n2);
      }
      

      【讨论】:

        【解决方案5】:

        这在一定程度上取决于您的事件如何绑定到您正在交换的元素。如果您使用的是 jQuery,那么 this 答案就是您所需要的,真的。

        主要的事情不是deleteremoveChildnode元素,而是将html复制粘贴到其他地方。这样做时,某些浏览器会清除所有资源(包括事件处理程序),因此绑定的事件将丢失。尽管 IE 在动态绑定事件方面因泄漏内存而臭名昭著。真的很难预测各种浏览器的行为方式。

        基本上,我会说:以任何你喜欢的方式交换元素,除了复制/粘贴 HTML 字符串,并使用 jQuery 的 .live() 方法绑定你的事件。由于我认为 1.5 没有.on 方法

        【讨论】:

        • 复制,将 HTML 粘贴到其他地方将创建全新的节点,这些节点的事件处理程序与原始节点不同。所以,这行不通。
        • 我知道,这就是我说swap elements in any way you like, except for copy/paste HTML strings的原因。我什至强调了两次。复制粘贴 HTML 是魔鬼的工作,IMO。考虑一下,在这种情况下,我建议克隆节点...
        【解决方案6】:

        由于 jQuery 在问题中被标记,这里有一个 jQuery 解决方案:

          $('#el_to_move').appendTo('#target_parent_el');
        

        就是这样。 jQuery 会将其剪切/粘贴到新位置。


        这也可能会有所帮助:

        https://api.jquery.com/detach/

        【讨论】:

          【解决方案7】:

          考虑到 o1.swapNode(o2);在 IE 中运行良好很长时间。在我的对象和文本节点混合的情况下,仅适用于一个版本(已在此处显示):

          let t = document.createElement("div");
          o1.parentNode.insertBefore(t, o1);
          o2.parentNode.insertBefore(o1, o2);
          t.parentNode.insertBefore(o2, t);
          t.parentNode.removeChild(t);
          

          是的,我讨厌创建临时节点,但是如果你直接使用它(没有调用函数),没有这个技巧的上述版本会失败。有时你不想调用函数。

          【讨论】:

            【解决方案8】:

            如果你在彼此旁边交换元素,这真的很容易:

            <div class="container">
               <div class="item">Item 1</div>
               <div class="item">Item 2</div>
            </div>
            

            $('.container .item:nth-child(2)').insertBefore('.container .item:nth-child(1)');

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-12-10
              • 2020-03-06
              • 1970-01-01
              相关资源
              最近更新 更多