Can I easily swap two elements with jQuery?
I'm looking to do this with one line if possible.
I have a select element and I have two buttons to move up or down the options, and I already have the selected and the destination selectors in place, I do it with an if, but I was wondering if there is an easier way.
This question is related to
jquery
swap
javascript
I did a table for changing order of obj in database used .after() .before(), so this is from what i have experiment.
$(obj1).after($(obj2))
Is insert obj1 before obj2 and
$(obj1).before($(obj2))
do the vice versa.
So if obj1 is after obj3 and obj2 after of obj4, and if you want to change place obj1 and obj2 you will do it like
$(obj1).before($(obj4))
$(obj2).before($(obj3))
This should do it BTW you can use .prev() and .next() to find obj3 and obj4 if you didn't have some kind of index for it already.
You shouldn't need two clones, one will do. Taking Paolo Bergantino answer we have:
jQuery.fn.swapWith = function(to) {
return this.each(function() {
var copy_to = $(to).clone(true);
$(to).replaceWith(this);
$(this).replaceWith(copy_to);
});
};
Should be quicker. Passing in the smaller of the two elements should also speed things up.
If you have multiple copies of each element you need to do something in a loop naturally. I had this situation recently. The two repeating elements I needed to switch had classes and a container div as so:
<div class="container">
<span class="item1">xxx</span>
<span class="item2">yyy</span>
</div>
and repeat...
The following code allowed me to iterate through everything and reverse...
$( ".container " ).each(function() {
$(this).children(".item2").after($(this).children(".item1"));
});
I have done it with this snippet
// 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();
I used a technique like this before. I use it for the connector list on 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');
No, there isn't, but you could whip one up:
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);
});
};
Usage:
$(selector1).swapWith(selector2);
Note this only works if the selectors only match 1 element each, otherwise it could give weird results.
take a look at jQuery plugin "Swapable"
http://code.google.com/p/jquery-swapable/
it's built on "Sortable" and looks like sortable (drag-n-drop, placeholder, etc.) but only swap two elements: dragged and dropped. All other elements are not affected and stay on their current position.
an other one without cloning:
I have an actual and a nominal element to swap:
$nominal.before('<div />')
$nb=$nominal.prev()
$nominal.insertAfter($actual)
$actual.insertAfter($nb)
$nb.remove()
then insert <div> before
and the remove
afterwards are only needed, if you cant ensure, that there is always an element befor (in my case it is)
Paulo's right, but I'm not sure why he's cloning the elements concerned. This isn't really necessary and will lose any references or event listeners associated with the elements and their descendants.
Here's a non-cloning version using plain DOM methods (since jQuery doesn't really have any special functions to make this particular operation easier):
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);
}
There are a lot of edge cases to this problem, which are not handled by the accepted answer or bobince's answer. Other solutions that involve cloning are on the right track, but cloning is expensive and unnecessary. We're tempted to clone, because of the age-old problem of how to swap two variables, in which one of the steps is to assign one of the variables to a temporary variable. The assignment, (cloning), in this case is not needed. Here is a jQuery-based solution:
function swap(a, b) {
a = $(a); b = $(b);
var tmp = $('<span>').hide();
a.before(tmp);
b.before(a);
tmp.replaceWith(b);
};
$('.five').swap('.two');
Create a jQuery function like this
$.fn.swap = function (elem)
{
elem = elem.jquery ? elem : $(elem);
return this.each(function ()
{
$('<span></span>').insertBefore(this).before(elem.before(this)).remove();
});
};
Thanks to Yannick Guinness at https://jsfiddle.net/ARTsinn/TVjnr/
if nodeA and nodeB are siblings, likes two <tr>
in the same <tbody>
, you can just use $(trA).insertAfter($(trB))
or $(trA).insertBefore($(trB))
to swap them, it works for me. and you don't need to call $(trA).remove()
before, else you need to re-bind some click events on $(trA)
This is my solution to move multiple children elements up and down inside the parent element.
Works well for moving selected options in listbox (<select multiple></select>
)
Move up:
$(parent).find("childrenSelector").each((idx, child) => {
$(child).insertBefore($(child).prev().not("childrenSelector"));
});
Move down:
$($(parent).find("childrenSelector").get().reverse()).each((idx, child) => {
$(opt).insertAfter($(child).next().not("childrenSelector"));
});
I've made a function which allows you to move multiple selected options up or down
$('#your_select_box').move_selected_options('down');
$('#your_select_boxt').move_selected_options('up');
Dependencies:
$.fn.reverse = [].reverse;
function swapWith() (Paolo Bergantino)
First it checks whether the first/last selected option is able to move up/down. Then it loops through all the elements and calls
swapWith(element.next() or 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);
}
Many of these answers are simply wrong for the general case, others are unnecessarily complicated if they in fact even work. The jQuery .before
and .after
methods do most of what you want to do, but you need a 3rd element the way many swap algorithms work. It's pretty simple - make a temporary DOM element as a placeholder while you move things around. There is no need to look at parents or siblings, and certainly no need to clone...
$.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) put the temporary div temp
before this
2) move this
before that
3) move that
after temp
3b) remove temp
Then simply
$(selectorA).swapWith(selectorB);
I think you can do it very simple. For example let's say you have next structure: ...
<div id="first">...</div>
<div id="second">...</div>
and the result should be
<div id="second">...</div>
<div id="first">...</div>
jquery:
$('#second').after($('#first'));
I hope it helps!
I wanted a solution witch does not use clone() as it has side effect with attached events, here is what I ended up to do
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;
};
it must not be used with jQuery objects containing more than one DOM element
The best option is to clone them with clone() method.
This is an answer based on @lotif's answer logic, but bit more generalized
If you append/prepend after/before the elements are actually moved
=> no clonning needed
=> events kept
.prev()
ious" => we can put the other target .after()
that..parent()
=> we can .prepend()
the other target to parent.This code could be done even shorter, but I kept it this way for readability. Note that prestoring parents (if needed) and previous elements is mandatory.
$(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 );
});
...feel free to wrap the inner code in a function :)
Example fiddle has extra click events attached to demonstrate event preservation...
Example fiddle: https://jsfiddle.net/ewroodqa/
...will work for various cases - even one such as:
<div>
<div id="one">ONE</div>
</div>
<div>Something in the middle</div>
<div>
<div></div>
<div id="two">TWO</div>
</div>
No need to use jquery for any major browser to swap elements at the moment. Native dom method, insertAdjacentElement
does the trick no matter how they are located:
var el1 = $("el1");
var el2 = $("el2");
el1[0].insertAdjacentElement("afterend", el2[0]);
If you're wanting to swap two items selected in the jQuery object, you can use this method
Source: Stackoverflow.com