是否有本机jQuery函数来切换元素?


174

我可以轻松地用jQuery交换两个元素吗?

如果可能的话,我正在寻找一行。

我有一个select元素,并且有两个按钮可以上下移动选项,并且已经有选择对象和目标选择器,我使用if来实现,但是我想知道是否有更简单的方法。


您可以发布标记和代码示例吗?
康斯坦丁·塔尔库斯

这不是jQuery的问题,而是JavaScript的问题:您不能在一条指令中交换DOM元素。然而,[保罗的回答](#698386)是一个伟大的插件;)
勒布

1
对于来自Google的用户:请查看lotif的答案,非常简单,对我来说效果很好。
莫里斯·

1
@Maurice,我改变了接受的答案
胡安

1
仅当元素彼此相邻时才交换它们!请更改接受的答案。
Serhiy

Answers:


233

我发现了一种仅使用jQuery解决此问题的有趣方法:

$("#element1").before($("#element2"));

要么

$("#element1").after($("#element2"));

:)


2
是的,这应该可行:文档中说:“如果以此方式选择的元素插入到其他位置,它将被移动到目标(未克隆)之前”。
Ridcully 2012年

12
我最喜欢这个选项!简单且很好地兼容。尽管我喜欢使用el1.insertBefore(el2)和el1.insertAfter(el2)来提高可读性。
莫里斯

6
不过有一个旁注..(我猜这适用于本页上的所有解决方案),因为我们在这里处理了DOM。当您要移动的元素中存在iframe时,删除和添加效果不佳。iframe将重新加载。此外,它将重新加载到其原始URL,而不是当前URL。非常烦人但与安全性相关。我还没有找到解决方案
莫里斯(Maurice)

202
除非两个元素紧挨着,否则这不会交换两个元素。
BonyT 2012年

12
这不再是公认的答案。在一般情况下不起作用。
thekingoftruth

79

Paulo是对的,但我不确定他为什么要克隆相关要素。这实际上不是必需的,并且会丢失与元素及其后代关联的任何引用或事件侦听器。

这是使用普通DOM方法的非克隆版本(因为jQuery确实没有任何使此特定操作更容易的特殊功能):

function swapNodes(a, b) {
    var aparent = a.parentNode;
    var asibling = a.nextSibling === b ? a : a.nextSibling;
    b.parentNode.insertBefore(a, b);
    aparent.insertBefore(b, asibling);
}

2
docs.jquery.com/Clone-传递“ true”也会克隆事件。我尝试不先克隆它,但它正在做您当前的工作:它将第一个与第二个交换,而将第一个保留原样。
Paolo Bergantino,2009年

如果我有<div id =“ div1”> 1 </ div> <div id =“ div2”> 2 </ div>并调用swapNodes(document.getElementById('div1'),document.getElementById('div2') ); 我得到<div id =“ div1”> 1 </ div> <div id =“ div2”> 1 </ div>
Paolo Bergantino,2009年

3
啊,在一个极端的情况下,a的下一个同胞是b本身。已在上述代码段中修复。jQuery正在做完全相同的事情。
bobince

我有一个非常敏感的订单列表,需要保留事件和ID,这就像一个魅力!谢谢!

1
我觉得您应该添加jQuery版本以从技术上满足此问题的标题。:)
thekingoftruth 2014年

70

不,没有,但是您可以鞭打一个:

jQuery.fn.swapWith = function(to) {
    return this.each(function() {
        var copy_to = $(to).clone(true);
        var copy_from = $(this).clone(true);
        $(to).replaceWith(copy_from);
        $(this).replaceWith(copy_to);
    });
};

用法:

$(selector1).swapWith(selector2);

请注意,这仅在选择器每个仅匹配1个元素的情况下才有效,否则可能会产生奇怪的结果。


2
有必要克隆它们吗?
胡安·

也许您可以使用html()直接编写它们?
Ed James

2
@Ed Woodcock如果这样做,我肯定会丢失那些元素上的绑定事件。
alex 2010年

2
当div不相邻时效果很好:)
2013年

这应该是公认的答案。它扩展了jQuery以提供所需的内容。
爱丽丝·邦德

40

这个问题有很多极端的情况,不能通过公认的答案或bobince的答案来处理。涉及克隆的其他解决方案也步入正轨,但是克隆既昂贵又不必要。由于试图交换两个变量的古老问题,我们很容易克隆,其中一个步骤是将一个变量分配给一个临时变量。在这种情况下,不需要分配(克隆)。这是一个基于jQuery的解决方案:

function swap(a, b) {
    a = $(a); b = $(b);
    var tmp = $('<span>').hide();
    a.before(tmp);
    b.before(a);
    tmp.replaceWith(b);
};

7
这应该是公认的答案。克隆将删除所有事件触发器,这不是必需的。我在代码中使用了这个确切的方法。
thekingoftruth

1
这是更好的答案!我碰巧有一个ul作为交换元素的子元素,这是一个jquery可排序元素。与Paolo Bergantino的答案交换后,列表项将不再被删除。有了user2820356的回答,一切仍然正常。
agoldev

19

这些答案中的许多答案对于一般情况来说都是错误的,如果实际上可行,其他答案则不必要地变得复杂。jQuery .before.after方法完成了大多数您想做的事情,但是许多交换算法的工作方式都需要第三个元素。这非常简单-在移动内容时将一个临时DOM元素用作占位符。无需查看父母或兄弟姐妹,当然也无需克隆...

$.fn.swapWith = function(that) {
  var $this = this;
  var $that = $(that);

  // create temporary placeholder
  var $temp = $("<div>");

  // 3-step swap
  $this.before($temp);
  $that.before($this);
  $temp.after($that).remove();

  return $this;
}

1)把临时div tempthis

2)移动 this在之前that

3)移动 thattemp

3b)移除 temp

然后简单地

$(selectorA).swapWith(selectorB);

演示:http : //codepen.io/anon/pen/akYajE


2
这是最好的答案,所有其他假定元素都彼此相邻。这在任何情况下都有效。
brohr

11

您不需要两个克隆,一个就可以。以保罗·贝尔甘蒂诺的答案,我们有:

jQuery.fn.swapWith = function(to) {
    return this.each(function() {
        var copy_to = $(to).clone(true);
        $(to).replaceWith(this);
        $(this).replaceWith(copy_to);
    });
};

应该更快。传递两个元素中较小的一个也可以加快速度。


4
这个问题以及Paolo的问题是,它们不能交换具有ID的元素,因为ID必须是唯一的,所以克隆无法正常工作。在这种情况下,Bobince的解决方案确实有效。
Ruud

另一个问题是,这将从copy_to中删除事件。
Jan Willem B

8

我以前使用过这样的技术。我将其用于http://mybackupbox.com上的连接器列表

// clone element1 and put the clone before element2
$('element1').clone().before('element2').end();

// replace the original element1 with element2
// leaving the element1 clone in it's place
$('element1').replaceWith('element2');

这是imo最好的解决方案,但是end()如果您不继续进行连锁,则无需这样做。怎么样$('element1').clone().before('element2').end().replaceWith('element2');
robisrob

1
只是注意到clone()data()除非您指定,否则不会保留任何clone(true)
jquery-

3

我做了一个功能,允许您向上或向下移动多个选择的选项

$('#your_select_box').move_selected_options('down');
$('#your_select_boxt').move_selected_options('up');

依存关系:

$.fn.reverse = [].reverse;
function swapWith() (Paolo Bergantino)

首先,它检查第一个/最后一个选择的选项是否能够上/下移动。然后循环遍历所有元素并调用

swapWith(element.next()或element.prev())

jQuery.fn.move_selected_options = function(up_or_down) {
  if(up_or_down == 'up'){
      var first_can_move_up = $("#" + this.attr('id') + ' option:selected:first').prev().size();
      if(first_can_move_up){
          $.each($("#" + this.attr('id') + ' option:selected'), function(index, option){
              $(option).swapWith($(option).prev());
          });
      }
  } else {
      var last_can_move_down = $("#" + this.attr('id') + ' option:selected:last').next().size();
      if(last_can_move_down){
        $.each($("#" + this.attr('id') + ' option:selected').reverse(), function(index, option){
            $(option).swapWith($(option).next());
        });
      }
  }
  return $(this);
}

无论是在编写代码的方式还是如何执行方面,这都是低效的。
布莱斯

这不是我们应用程序的主要功能,我只是使用了少量选项。我只想要快速便捷的东西...如果您可以改善这一点,我会喜欢的!那很好啊!
汤姆·梅克尔贝格2011年

3

另一个没有克隆的:

我有一个实际和名义上的要交换的元素:

            $nominal.before('<div />')
            $nb=$nominal.prev()
            $nominal.insertAfter($actual)
            $actual.insertAfter($nb)
            $nb.remove()

然后insert <div> beforeremove仅在您不能确保之后,才需要在此之前始终存在一个元素(在我的情况下是这样)


或者你可以只检查.prev()的长度,如果为0,然后.prepend().parent() :)这样,你不需要创建额外的元素。
jave.web,2016年


2

这是基于@lotif的答案的答案逻辑的答案,但有点笼统

如果您在元素实际移动 之后/之前添加/添加
=>无需克隆
=>保留事件

可能发生两种情况

  1. 一个目标有一些“ .prev()错” =>我们可以把另一个目标放进去.after()
  2. 一个目标是它的第一个子代.parent()=>我们可以.prepend()将另一个目标作为父代。

代码

这段代码可以做得更短,但是为了便于阅读,我将其保留下来。请注意,必须强制保存父母(如果需要)和先前的元素。

$(function(){
  var $one = $("#one");
  var $two = $("#two");
  
  var $onePrev = $one.prev(); 
  if( $onePrev.length < 1 ) var $oneParent = $one.parent();

  var $twoPrev = $two.prev();
  if( $twoPrev.length < 1 ) var $twoParent = $two.parent();
  
  if( $onePrev.length > 0 ) $onePrev.after( $two );
    else $oneParent.prepend( $two );
    
  if( $twoPrev.length > 0 ) $twoPrev.after( $one );
    else $twoParent.prepend( $one );

});

...随意将内部代码包装在一个函数中:)

小提琴示例附带了额外的单击事件,以演示事件保存...
示例小提琴: https : //jsfiddle.net/ewroodqa/

...将适用于各种情况-甚至例如:

<div>
  <div id="one">ONE</div>
</div>
<div>Something in the middle</div>
<div>
  <div></div>
  <div id="two">TWO</div>
</div>

2

这是我在父元素内上下移动多个子元素的解决方案。适用于在列表框(<select multiple></select>)中移动所选选项

提升:

$(parent).find("childrenSelector").each((idx, child) => {
    $(child).insertBefore($(child).prev().not("childrenSelector"));
});

下移:

$($(parent).find("childrenSelector").get().reverse()).each((idx, child) => {
    $(opt).insertAfter($(child).next().not("childrenSelector"));
});


1

我想要一种解决方案,女巫不使用clone(),因为它会对附加事件产生副作用,这就是我最终要做的事情

jQuery.fn.swapWith = function(target) {
    if (target.prev().is(this)) {
        target.insertBefore(this);
        return;
    }
    if (target.next().is(this)) {
        target.insertAfter(this);
        return
    }

    var this_to, this_to_obj,
        target_to, target_to_obj;

    if (target.prev().length == 0) {
        this_to = 'before';
        this_to_obj = target.next();
    }
    else {
        this_to = 'after';
        this_to_obj = target.prev();
    }
    if (jQuery(this).prev().length == 0) {
        target_to = 'before';
        target_to_obj = jQuery(this).next();
    }
    else {
        target_to = 'after';
        target_to_obj = jQuery(this).prev();
    }

    if (target_to == 'after') {
        target.insertAfter(target_to_obj);
    }
    else {
        target.insertBefore(target_to_obj);
    }
    if (this_to == 'after') {
        jQuery(this).insertAfter(this_to_obj);
    }
    else {
        jQuery(this).insertBefore(this_to_obj);
    }

    return this;
};

不得与包含多个DOM元素的jQuery对象一起使用


1

如果每个元素都有多个副本,则需要自然地循环执行某些操作。我最近有这种情况。我需要切换的两个重复元素具有类和容器div:

<div class="container">
  <span class="item1">xxx</span>
  <span class="item2">yyy</span>
</div> 
and repeat...

以下代码使我可以遍历所有内容并反向操作...

$( ".container " ).each(function() {
  $(this).children(".item2").after($(this).children(".item1"));
});

1

我已经用这个片段完成了

// Create comments
var t1 = $('<!-- -->');
var t2 = $('<!-- -->');
// Position comments next to elements
$(ui.draggable).before(t1);
$(this).before(t2);
// Move elements
t1.after($(this));
t2.after($(ui.draggable));
// Remove comments
t1.remove();
t2.remove();

1

我做了一个表,用于更改使用.after().before()的数据库中obj的顺序,所以这是根据我的实验得出的。

$(obj1).after($(obj2))

是在obj2之前插入obj1并

$(obj1).before($(obj2)) 

反之亦然。

因此,如果obj1在obj3之后,而obj2在obj4之后,并且如果您要更改放置obj1和obj2,则可以这样做

$(obj1).before($(obj4))
$(obj2).before($(obj3))

顺便说一句,如果您还没有某种索引,可以使用.prev()和.next()查找obj3和obj4。


这不只是被接受答案的a窃,它包含严重的语法错误。您复制了这个吗?
clockw0rk '18

嗯不?这是我在以前的公司工作的工作代码。另外,我确保指出如何使用以及在哪种情况下使用。为什么我需要撕掉某人?我还给出了如何获取对象的索引,这就是为什么接受的答案被评论为未完成的原因。
Tran Vu Dang Khoa

那为什么是after $()而不是after($())
clockw0rk '18

哦,是的,我没看到它,谢谢。我是从内存中输入的,离开公司后无权保留该代码
Tran Vu Dang Khoa

0

如果nodeA和nodeB是兄弟姐妹,就像两个兄弟姐妹<tr>一样<tbody>,则可以使用$(trA).insertAfter($(trB))$(trA).insertBefore($(trB))交换它们,这对我有用。而且您无需先致电$(trA).remove(),否则您需要重新绑定一些点击事件$(trA)


0
$('.five').swap('.two');

创建一个像这样的jQuery函数

$.fn.swap = function (elem) 
{
    elem = elem.jquery ? elem : $(elem);
    return this.each(function () 
    {
        $('<span></span>').insertBefore(this).before(elem.before(this)).remove();
    });
};

由于亚尼克吉尼斯https://jsfiddle.net/ARTsinn/TVjnr/



-2

我认为您可以轻松完成。例如,假设您具有下一个结构:...

<div id="first">...</div>
<div id="second">...</div>

结果应该是

<div id="second">...</div>
<div id="first">...</div>

jQuery的:

$('#second').after($('#first'));

希望对您有所帮助!


2
除非两个元素彼此紧挨着,否则已经指出了该解决方案并且不起作用。
BernaMariano
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.