views:

3127

answers:

7

Can I easily swap two elements with jQuery?

I'm looking to do it 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

+11  A: 

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.

Paolo Bergantino
is it necessary to clone them?
Juan Manuel
Pretty sure, but I'd be interested if anyone can achieve the same effect without cloning them.
Paolo Bergantino
Perhaps you could write them directly using html() ?
Ed Woodcock
@Ed Woodcock If you do, you will lose bound events on those elements I'm pretty sure.
alex
+1  A: 

It's not a matter of jQuery but JavaScript: you cannot swap DOM elements in a single instruction.

However, Paolo's answer is a great plugin ;)

Seb
+4  A: 

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);
}
bobince
http://docs.jquery.com/Clone - passing it "true" clones the events too. I tried without cloning it first but it was doing what yours currently is: it's swapping the first one with the 2nd one and leaving the first one as is.
Paolo Bergantino
What do you mean by "as is"?
Juan Manuel
if i have <div id="div1">1</div><div id="div2">2</div> and call swapNodes(document.getElementById('div1'), document.getElementById('div2')); i get <div id="div1">1</div><div id="div2">1</div>
Paolo Bergantino
Ah, there's a corner case where a's next sibling is b itself. Fixed in the above snippet. jQuery was doing the exact same thing.
bobince
+1  A: 

The best option is to clone them with clone() method.

+5  A: 

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.

Matthew Wilcoxson
The problem with this, and Paolo's, is that they cannot swap elements with an ID as IDs must be unique so cloning does not work. Bobince's solution does work in this case.
Ruud v A
A: 

take a look at jQuery plugin "Swapable"

http://plugins.jquery.com/project/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.

vadimk
A: 

If you're wanting to swap two items selected in the jQuery object, you can use this method

http://www.vertstudios.com/blog/swap-jquery-plugin/

Joseph McCullough