Learning jQuery - Basic Alphabetical Sorting
Now let's perform a sort on the Title column of the table. We'll need a class on the table header cell so that we can select it properly:
-
<thead>
</thead>
-
<tr>
</tr>
-
<th> </th>
-
<th class="sort-alpha"> </th>
Title -
<th> </th>
Author(s) -
<th> </th>
Publish Date -
<th> </th>
Price -
-
%0D%0A%3Cthead%3E%0D%0A%3C%2Fthead%3E%0D%0A%0A%0D%0A%3Ctr%3E%0D%0A%3C%2Ftr%3E%0D%0A%0A%0D%0A%3Cth%3E%20%3C%2Fth%3E%0D%0A%0A%0D%0A%3Cth%20class%3D%22sort-alpha%22%3E%20%3C%2Fth%3E%0D%0ATitle%0A%0D%0A%3Cth%3E%20%3C%2Fth%3E%0D%0AAuthor%28s%29%0A%0D%0A%3Cth%3E%20%3C%2Fth%3E%0D%0APublish%20Date%0A%0D%0A%3Cth%3E%20%3C%2Fth%3E%0D%0APrice%0A%0D%0A%0A%0D%0A
To perform the actual sort, we can use JavaScript's built in .sort() method. It does an in‑place sort on an array, and can take a function as an argument. This function compares two items in the array and should return a positive or negative number depending on the result. Our initial sort routine looks like this:
$(document).ready(function() { $ ('table.sortable'). each(function() { var $table = $(this); $ ('th', $table). each(function(column ) { if ($(this).is('.sort-alpha')) { $(this).addClass('clickable').hover(function() { $(this).addClass('hover'); }, function() { $(this).removeClass('hover'); }).click(function() { var rows = $table.find('tbody > tr').get(); rows. sort(function(a, b ) { var keyA = $(a).children('td').eq(column).text().toUpperCase(); var keyB = $(b).children('td').eq(column).text().toUpperCase(); if (keyA < keyB) return -1; if (keyA > keyB) return 1; return 0; }); $. each(rows, function(index, row ) { $table.children('tbody').append(row); }); }); } }); }); });
%24%28document%29.ready%28function%28%29%20%7B%0A%0D%0A%24%28%27table.sortable%27%29.each%28function%28%29%20%7B%0A%0D%0Avar%20%24table%20%3D%20%24%28this%29%3B%0A%0D%0A%24%28%27th%27%2C%20%24table%29.each%28function%28column%29%20%7B%0A%0D%0Aif%20%28%24%28this%29.is%28%27.sort-alpha%27%29%29%20%7B%0A%0D%0A%24%28this%29.addClass%28%27clickable%27%29.hover%28function%28%29%20%7B%0A%0D%0A%24%28this%29.addClass%28%27hover%27%29%3B%0A%0D%0A%7D%2C%20function%28%29%20%7B%0A%0D%0A%24%28this%29.removeClass%28%27hover%27%29%3B%0A%0D%0A%7D%29.click%28function%28%29%20%7B%0A%0D%0Avar%20rows%20%3D%20%24table.find%28%27tbody%20%3E%20tr%27%29.get%28%29%3B%0A%0D%0Arows.sort%28function%28a%2C%20b%29%20%7B%0A%0D%0Avar%20keyA%20%3D%20%24%28a%29.children%28%27td%27%29.eq%28column%29.text%28%29.toUpperCase%28%29%3B%0A%0D%0Avar%20keyB%20%3D%20%24%28b%29.children%28%27td%27%29.eq%28column%29.text%28%29.toUpperCase%28%29%3B%0A%0D%0Aif%20%28keyA%20%3C%20keyB%29%20return%20-1%3B%0A%0D%0Aif%20%28keyA%20%3E%20keyB%29%20return%201%3B%0A%0D%0Areturn%200%3B%0A%0D%0A%7D%29%3B%0A%0D%0A%24.each%28rows%2C%20function%28index%2C%20row%29%20%7B%0A%0D%0A%24table.children%28%27tbody%27%29.append%28row%29%3B%0A%0D%0A%7D%29%3B%0A%0D%0A%7D%29%3B%0A%0D%0A%7D%0A%0D%0A%7D%29%3B%0A%0D%0A%7D%29%3B%0A%0D%0A%7D%29%3B
The first thing to note is our use of the .each() method to make iteration explicit. Even though we could bind a click handler to all headers with the sort-alpha class just by calling $('table.sortable th.sort-alpha').click(), this wouldn't allow us to easily capture a crucial bit of information—the column index of the clicked header. Because .each() passes the iteration index into its callback function, we can use it to find the relevant cell in each row of the data later
Once we have found the header cell, we retrieve an array of all of the data rows. This is a great example of how .get() is useful in transforming a jQuery object into an array of DOM nodes; even though jQuery objects act like arrays in many respects, they don't have any of the native array methods available, such as .sort().
With .sort() at our disposal, the rest is fairly straightforward. The rows are sorted by comparing the textual contexts of the relevant table cell. We know which cell to look at because we captured the column index in the enclosing .each() call. We convert the text to uppercase because string comparisons in JavaScript are case-sensitive and we wish our sort to be case-insensitive. Finally, with the array sorted, we loop through the rows and reinsert them into the table. Since .append() does not clone nodes, this moves them rather than copying them. Our table is now sorted.
This is an example of progressive enhancement's counterpart, graceful degradation. Unlike with the AJAX solution discussed earlier, we cannot make the sort work without JavaScript, as we are assuming the server has no scripting language available to it in this case. The JavaScript is required for the sort to work, so by adding the "clickable" class only through code, we make sure not to indicate with the interface that sorting is even possible unless the script can run. The page degrades into one that is still functional, albeit without sorting available.
We have moved the actual rows around, hence our alternating row colors are now out of whack:

We need to reapply the row colors after the sort is performed. We can do this by pulling the coloring code out into a function that we call when needed:
$(document).ready(function() { var alternateRowColors = function($table) { $('tbody tr:odd', $table).removeClass('even').addClass('odd'); $('tbody tr:even', $table).removeClass('odd').addClass('even'); }; $ ('table.sortable'). each(function() { var $table = $(this); alternateRowColors($table); $ ('th', $table). each(function(column ) { if ($(this).is('.sort-alpha')) { $(this).addClass('clickable').hover(function() { $(this).addClass('hover'); }, function() { $(this).removeClass('hover'); }).click(function() { var rows = $table.find('tbody > tr').get(); rows. sort(function(a, b ) { var keyA = $(a).children('td').eq(column).text().toUpperCase(); var keyB = $(b).children('td').eq(column).text().toUpperCase(); if (keyA < keyB) return -1; if (keyA > keyB) return 1; return 0; }); $. each(rows, function(index, row ) { $table.children('tbody').append(row); }); alternateRowColors($table); }); } }); }); });
%24%28document%29.ready%28function%28%29%20%7B%0A%0D%0Avar%20alternateRowColors%20%3D%20function%28%24table%29%20%7B%0A%0D%0A%24%28%27tbody%20tr%3Aodd%27%2C%20%24table%29.removeClass%28%27even%27%29.addClass%28%27odd%27%29%3B%0A%0D%0A%24%28%27tbody%20tr%3Aeven%27%2C%20%24table%29.removeClass%28%27odd%27%29.addClass%28%27even%27%29%3B%0A%0D%0A%7D%3B%0A%0D%0A%0A%0D%0A%24%28%27table.sortable%27%29.each%28function%28%29%20%7B%0A%0D%0Avar%20%24table%20%3D%20%24%28this%29%3B%0A%0D%0AalternateRowColors%28%24table%29%3B%0A%0D%0A%24%28%27th%27%2C%20%24table%29.each%28function%28column%29%20%7B%0A%0D%0Aif%20%28%24%28this%29.is%28%27.sort-alpha%27%29%29%20%7B%0A%0D%0A%24%28this%29.addClass%28%27clickable%27%29.hover%28function%28%29%20%7B%0A%0D%0A%24%28this%29.addClass%28%27hover%27%29%3B%0A%0D%0A%7D%2C%20function%28%29%20%7B%0A%0D%0A%24%28this%29.removeClass%28%27hover%27%29%3B%0A%0D%0A%7D%29.click%28function%28%29%20%7B%0A%0D%0Avar%20rows%20%3D%20%24table.find%28%27tbody%20%3E%20tr%27%29.get%28%29%3B%0A%0D%0Arows.sort%28function%28a%2C%20b%29%20%7B%0A%0D%0Avar%20keyA%20%3D%20%24%28a%29.children%28%27td%27%29.eq%28column%29.text%28%29.toUpperCase%28%29%3B%0A%0D%0Avar%20keyB%20%3D%20%24%28b%29.children%28%27td%27%29.eq%28column%29.text%28%29.toUpperCase%28%29%3B%0A%0D%0Aif%20%28keyA%20%3C%20keyB%29%20return%20-1%3B%0A%0D%0Aif%20%28keyA%20%3E%20keyB%29%20return%201%3B%0A%0D%0Areturn%200%3B%0A%0D%0A%7D%29%3B%0A%0D%0A%24.each%28rows%2C%20function%28index%2C%20row%29%20%7B%0A%0D%0A%24table.children%28%27tbody%27%29.append%28row%29%3B%0A%0D%0A%7D%29%3B%0A%0D%0AalternateRowColors%28%24table%29%3B%0A%0D%0A%7D%29%3B%0A%0D%0A%7D%0A%0D%0A%7D%29%3B%0A%0D%0A%7D%29%3B%0A%0D%0A%7D%29%3B
This corrects the row coloring after the fact, fixing our issue:

Trackback(0)
|