Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

Swap Items in jQuery Object

4.62/5 (5 votes)
27 Feb 2017CPOL4 min read 12.4K  
Swap positions of two elements in a jQuery object

Introduction

I happened to stumble upon a jQuery discussion forum, where a user, requesting for help, wanted to know how to swap two items in a jQuery object. The answers that tried to address the problem were not satisfactory in my opinion and with this article, I will present my solution to the issue.

First, let me present the original scenario. The user has a table with some rows (tr) and each row has up and down buttons. When the client clicks on a button, the user uses jQuery ($.insertAfter(), $.insertBefore()) to move that row up or down inside the table.

He also has a jQuery object that holds all the rows ($("table").find("tr")). What that user wanted was to swap places inside that jQuery object without selecting again. He argued that any re-selecting will result in the same jQuery object as before with the exception of two rows with swapped places. So any re-selecting will be redundant.

The various solutions, presented in that discussion forum, had some intrinsic problems with them. First, there's an assumption that the jQuery object holds DOM elements. Obviously from the way the scenario was presented, that assumption is correct, but it also prevented for a more generic solution to emerge. Most of the solutions used jQuery functions such as $.replaceWith(), $.detach(), $.insertAfter(), $.insertBefore(). What happens when the jQuery object holds numbers $([0, 1, 2, 3, 4, 5])? Second, some solutions used cloning ($.clone()). What they did, in some form or other, was to clone an item, then removed it and inserted the clone to a new location. The problem with cloning is that you get a whole new object with different jQuery id and if you're not too careful, you might lose any events attached to the original item.

Swap Items in Array Object

Before we proceed to understand how to swap items in jQuery object, we must be reminded how to swap items in JavaScript Array object. Array object has a function called splice. splice removes certain number of items (could be 0 items) from a certain index and them inserts new items, into the array, in that index. splice returns an array of removed items. You can read more about it in w3Schools JavaScript Array splice() Method. Here's an example of how to swap two items in an Array object.

JavaScript
var arr = [0, 1, 2, 3, 4, 5];
var index1 = 4;
var index2 = 1;

// item at index2
var item2 = arr[index2];

// remove item at index1 and insert item2 at index1
var item1 = arr.splice(index1, 1, item2)[0];

// remove item at index2 and insert item1 at index2
arr.splice(index2, 1, item1);

alert(arr); // 0, 4, 2, 3, 1, 5

And with a single line of code.

JavaScript
var arr = [0, 1, 2, 3, 4, 5];
var index1 = 4;
var index2 = 1;

// swap items
arr.splice(index2, 1, arr.splice(index1, 1, arr[index2])[0]);

alert(arr); // 0, 4, 2, 3, 1, 5

Swap Items in jQuery Object

We are going to leverage the function $.toArray() to retrieve an array with the elements that are contained in the jQuery object. Then use Array splice to swap items, and then wrap it again in jQuery. Here's the table that we'll use (nothing fancy).

HTML
<table>
  <tr><td>tr 0</td></tr>
  <tr><td>tr 1</td></tr>
  <tr><td>tr 2</td></tr>
  <tr><td>tr 3</td></tr>
  <tr><td>tr 4</td></tr>
  <tr><td>tr 5</td></tr>
</table>

And this is the code that swaps two tr in positions 1 and 4. I added a little alert at the end for readability.

JavaScript
var $trItems = $("tr");
var index1 = 4;
var index2 = 1;

// array of tr
var trArr = $trItems.toArray();

// swap items
trArr.splice(index2, 1, trArr.splice(index1, 1, trArr[index2])[0]);

// wrap with jQuery
$trItems = $(trArr);

var message = "";
$trItems.each(function(index, tr) {
    if (index > 0) message += "\n";
    message += $(tr).find("td").text();
});
alert(message); // tr 0, tr 4, tr 2, tr 3, tr 1, tr 5

swap Function

We want to generalize what we did before, add some validity checks and wrap it all in a function.

JavaScript
function swap($items, index1, index2) {

    // index1 validity check
    if (index1 < 0 || index1 >= $items.length)
        return $items;

    // index2 validity check
    if (index2 < 0 || index2 >= $items.length)
        return $items;

    // indices are equal. no swapping
    if (index1 == index2)
        return $items;

    // array of items
    var items = $items.toArray();

    // swap items
    items.splice(index2, 1, items.splice(index1, 1, items[index2])[0]);

    // wrap the new array with jQuery and return
    $items = $(items);
    return $items;
}

The Fly in the Ointment

There is one cavity with what we did so far. The jQuery object that we return from the swap function is different from the jQuery object that we passed as parameter. They will have different jQuery id. When we swap items in Array object, we swap them in-place, meaning inside the Array itself. However, this is not possible in jQuery. Every time that we try to change a jQuery object - not even change, but try to change - we get a new jQuery object. Its content may be different from or same as the original jQuery object, but it is a different object altogether. To illustrate this point, let's look at this code:

JavaScript
var obj1 = $([0, 1, 2, 3, 4, 5]);

alert(obj1.length); // 6

var obj2 = obj1.filter(function(index, element) {
  return element <= 3;
});

alert(obj1.length); // 6
alert(obj2.length); // 4

alert(obj1 === obj2); // false

The filter changes the jQuery object (in this case, reduces the number of elements) and returns a new jQuery object. To illustrate this point further more, let's look at a filter that doesn't filter at all.

JavaScript
var obj1 = $([0, 1, 2, 3, 4, 5]);

alert(obj1.length); // 6

var obj2 = obj1.filter(function(index, element) {
  return true;
});

alert(obj1.length); // 6
alert(obj2.length); // 6

alert(obj1 === obj2); // false

This filter returns true for all elements so no element is filtered out. The contents of obj1 and obj2 are the same but obj1 and obj2 are completely different jQuery objects.

Once we understand this, we can see where the swap function could cause our code to be error-prone. The swapping is not performed in-place the jQuery object. We must remember to update all references to the result that returns from swapping two items.

JavaScript
var trItems1 = $("tr");
var trItems2 = trItems1;

var index1 = 4;
var index2 = 1;
trItems1 = swap(trItems1, index1, index2); // update trItems1

alert(trItems1 === trItems2); // false

trItems2 = trItems1; // update trItems2

alert(trItems1 === trItems2); // true

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)