Note
My initial response was simplistic and did not work in 100% of cases. I then made a somewhat complex implementation, but I was still not satisfied, as with the other answers.
I am only considering the general solutions, that is, they apply to changing any elements at different levels and positions.
Analysis of the proposed solutions
The simplest and most direct is @mbigsonbr , but I was left behind because of the creation and insertion of an element unnecessarily.
The @Zuul response of cloning is also simple and interesting, but it also seems even heavier.
My answer seems very complex. Is it all necessary?
The performance test
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 slightly faster than the others when the elements are at the same level, but due to the complexity and overuse of the jQuery API, when they are at different levels of the DOM, performance was equivalent. >
Still unsatisfied, I came to the conclusion that the @mgibsonbr solution could easily be converted to pure Javascript. I ran the test again and it looks like I finally found something interesting.
Let's look at the result in the chart below:
Thecaptionisasfollows:
- Darkblue:myalgorithmwithpeernodes
- Red:myalgorithmwithdifferentlevelnodes
- Yellow:theoriginal@mgibsonbralgorithmwithpeernodes
- DarkGreen:theoriginal@mgibsonbralgorithmwithdifferentlevelnodes
- Purple:themodified@mgibsonbralgorithmwithpeernodes
- Lightblue:themodified@mgibsonbralgorithmwithdifferentlevelnodes
- Pink:@Zuulalgorithmwithpeernodes
- Lightgreen:@Zuulalgorithmwithdifferentlevelnodes
Test on jsperf
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));
How to Use
$(elementSelector).swap(anotherElementSelector);