Note
My initial response was simplistic and did not work in 100% of cases. I then made a rather complex implementation, but I have not yet been satisfied, as well as the other answers.
I am considering only the general solutions, that is, that apply to the exchange of any elements at different levels and positions.
Analysis of the proposed solutions
The simplest and most direct is the @mbigsonbr, but I was left with one foot behind because of the creation and insertion of an element unnecessarily.
The response of @Zuul cloning is also simple and interesting, but also seems even heavier.
My answer seems very complex. Does it take all that?
The test of performance
I then decided to apply the different techniques and do a performance test. The results confirmed my suspicions:
- Cloning is too slow
- My solution was discreetly faster than the others when the elements are at the same level, but due to the complexity and excessive use of the jQuery API, when they are at different DOM levels, the performance was equivalent.
Not yet satisfied, I have come to the conclusion that the @mgibsonbr could be easily converted to pure Javascript. I ran the test again and it seems I finally found something interesting.
Let’s see the result in the chart below:
The legend is as follows:
- Dark blue: my algorithm with nodes of the same level
- Red: my algorithm with nodes of different levels
- Yellow: the original algorithm of @mgibsonbr with us of the same level
- Dark green: the original algorithm of @mgibsonbr with nodes of different levels
- Purple: the modified algorithm of @mgibsonbr with us of the same level
- Light blue: the modified algorithm of @mgibsonbr with nodes of different levels
- Pink: the algorithm of @Zuul with us of the same level
- Light green: the algorithm of @Zuul with nodes of different levels
My algorithm
(function ($) {
$.fn.swap = function(anotherElement) {
var sameParentStrategy = function(one, another) {
var oneIndex = one.index();
var anotherIndex = another.index();
var swapFunction = function(first, second, firstIndex, secondIndex) {
if (firstIndex == secondIndex - 1) {
first.insertAfter(second);
} else {
var secondPrevious = second.prev();
second.insertAfter(first);
first.insertAfter(secondPrevious);
}
}
if (oneIndex < anotherIndex) {
swapFunction(one, another, oneIndex, anotherIndex);
} else {
swapFunction(another, one, anotherIndex, oneIndex);
}
};
var differentParentsStrategy = function(one, another) {
var positionStrategy = function(e) {
var previous = e.prev();
var next = e.next();
var parent = e.parent();
if (previous.length > 0) {
return function(e) {
e.insertAfter(previous);
};
} else if (next.length > 0) {
return function(e) {
e.insertBefore(next);
};
} else {
return function(e) {
parent.append(e);
};
}
}
var oneStrategy = positionStrategy(one);
var anotherStrategy = positionStrategy(another);
oneStrategy(another);
anotherStrategy(one);
return this;
};
//check better strategy
var one = $(this);
var another = $(anotherElement);
if (one.parent().get(0) == another.parent().get(0)) {
console.log('sameParentStrategy');
sameParentStrategy(one, another);
} else {
console.log('differentParentsStrategy');
differentParentsStrategy(one, another);
}
};
}(jQuery));
The modified algorithm of @mgibsonbr
(function ($) {
$.fn.swap = function(anotherElement) {
var a = $(this).get(0);
var b = $(anotherElement).get(0);
var swap = document.createElement('span');
a.parentNode.insertBefore(swap, a);
b.parentNode.insertBefore(a, b);
swap.parentNode.insertBefore(b, swap);
swap.remove();
}
}(jQuery));
Mode of Use
$(elementSelector).swap(anotherElementSelector);
Who just arrived, ignore the downvotes and take a look at mine reply to see a method that exchanges elements independently of the structure.
– utluiz
I can’t believe no one’s even suggested classic solution...
– mgibsonbr