Sortable table rows with jQuery.
I was working on a web application today and found myself needing to do something I haven’t needed to do previously. It has probably been explained far better elsewhere and is more than likely a common task for most people but for me it was something new. I’ve done so much on the web and it’s always good to be reminded that there is plenty I haven’t touched yet.
What did I need to do?
What my desired result boiled down to was adding controls inside a table to move rows up and down. If you click the “Up” contol, the TR is moved up one and if you click “Down”, the TR moves down. The table has a table header row which needs to remain static and the table rows are zebra-striped meaning the stripes need to stay in the same position.
How did I do it with jQuery?
Coming into it, I thought it may have been quite tricky but it is actually incredibly easy (even with normal JavaScript). The script was for a site that uses jQuery so the situation soon became a cakewalk.
Sortable table rows with jQuery
It all started with a table:
<table>
<thead>
<tr>
<th>Position</th><th>Name</th><th>Colour</th><th>Controls</th>
</tr>
</thead>
<tbody>
<tr class="row1">
<td>1</td><td>Geraldo</td><td>Pink</td><td> <a href="#up" rel="up" class="control">Up</a> <a href="#down" rel="down" class="control">Down</a></td>
</tr>
<tr class="row2">
<td>2</td><td>Lorenzo</td><td>Green</td><td> <a href="#up" rel="up" class="control">Up</a> <a href="#down" rel="down" class="control">Down</a></td>
</tr>
<tr class="row1">
<td>3</td><td>Flamingo</td><td>Blue</td><td> <a href="#up" rel="up" class="control">Up</a> <a href="#down" rel="down" class="control">Down</a></td>
</tr>
<tr class="row2">
<td>4</td><td>Denya</td><td>Red</td><td> <a href="#up" rel="up" class="control">Up</a> <a href="#down" rel="down" class="control">Down</a></td>
</tr>
</tbody>
</table> |
Obviously, a table is nothing special. I included the thead and tbody so there is a container in the table holding the content rows (not the head rows). The links in the table have a class of control with a rel attribute defining whether it will move the row up or down.
Now the code is simplicity itself:
$(document).ready( function () {
/**
* I am using the jQuery live binding method as the tables I deal with will be loaded via Ajax in most cases.
* If you use this script and the page is static, rather than full of dynamically loaded content, you can just bind
* click event like normal.
*/
$('table tbody a.control').live('click', function (e) {
e.preventDefault();
// To allow the control links to be buried anywhere in the table (rather than at two deep)
// The following uses the jQuery parent() function to cycle up through parents until the TR is found
// To avoid an infinite loop (due to HTML error or other misnomer), there is a max iteration count set of 100
var tr = $(this); // Start with this link and work upwards from there, this allows the TR element itself to be the control if desired
var iterations = 0;
while(tr.attr('tagName') != 'TR') {
tr = tr.parent();
iterations += 1;
if (iterations == 100) {
return false; // Bail out, surely something is wrong or there is lots and lots of html in there
}
}
// Now that the TR tag has been stored in the tr variable, check the rel attribute of the link that has been clicked ( $(this) ) to see what direction to move
// To fine the previous sibling inside the <tbody> tag, the jQuery prev() function is used and to find the next element,
// surprisingly enough, the jQuery next() element is used.
// The TR element will only be moved if there IS a sibling , otherwise the function is exited
if ($(this).attr('rel') == 'up' && tr.prev().length)
// This next part is here mainly for show. The fading in and out isn't required. To simply move up
// or down, just use tr.insertBefore(tr.prev()) or tr.insertAfter(tr.next())
// The code below uses the jQuery fadeTo function to fade to 0.1 opacity (not fadeOut as that causes jerkiness when
// the element is hidden after fading out), then does the move inside a callback function before fading back in.
tr.fadeTo('medium', 0.1, function () {
tr.insertBefore(tr.prev()).fadeTo('fast', 1);
// If you want to do any more to the table after the move put it here, I am running the reorder function (defined below)
reorder();
});
else if ($(this).attr('rel') == 'down' && tr.next().length)
// Same as above only this is for moving elements down instead of up
tr.fadeTo('fast', 0.1, function () {
tr.insertAfter(tr.next()).fadeTo('fast', 1);
// If you want to do any more to the table after the move put it here, I am running the reorder function (defined below)
reorder();
});
else
//Can't do anything with these
return false;
return false;
});
// If you need nothing more than just to move rows up and down, you can leave it at that!
// This next part cycles through all of the TR elements putting the zebra-striping back in the correct places
// It also renumbers the positions so they are in the correct sequential order (this was a requirement for my task)
function reorder () {
var position = 1;
$('table tbody tr').each(function () {
// Change the text of the first TD element inside this TR
$('td:first', $(this)).text(position);
//Now remove current row class and add the correct one
$(this).removeClass('row1 row2').addClass( position % 2 ? 'row1' : 'row2');
position += 1;
});
}
});
</tbody> |
So. There you have it, incredibly easy. Take my comments out and this little piece of code can have many uses. In the application I added this script to, I use these controls to reorder the TR elements, renumber the positions and also update some hidden form fields with these positions. This can then be submitted to, and saved on, the server retaining the order.
Would you like a demo?
Of course you would. Everyone likes a demo.
| Position | Name | Colour | Controls |
|---|---|---|---|
| 1 | Geraldo | Pink | Up Down |
| 2 | Lorenzo | Green | Up Down |
| 3 | Flamingo | Blue | Up Down |
| 4 | Denya | Red | Up Down |
Tags: JavaScript, jQuery

June 21st, 2009 at 3:57 am
That’s pretty damn cool! Nice tutorial Luke
I <3 jQuery
June 21st, 2009 at 1:02 pm
Hey Luke, great tutorial! It’s funny how sometimes when you come across something new you think “ooo… could be tricky”, and it turns out not to be as tricky as you thought!
Also, I can’t help but point out two small typographical errors
1)
“Now that the TR tag has been stored in the tr variable, check its rel attribute to see what direction to move”, you actually then proceed to check the rel attribute of the “a.control” with a $(this), not the rel attribute of the TR as it sounds in the comment.2) You left a </tbody> in your javascript code snippet.
Not being pedantic, just letting you know
Great stuff!! Post more! (says me… I should post more too…)
June 21st, 2009 at 4:56 pm
Thanks Japh and Jack.
I was quite please at its simplicity and I know this will form the base of many interfaces to come.
@Japh — glad you’re always there to proof read my posts
I fixed those problems except the tbody keeps getting put back by wordpress. grr I hate that. I have no idea how to get rid of it.
June 23rd, 2009 at 12:38 am
[...] Scripts Luke Dingle . com Turn over a playful leaf on web design « Sortable table rows with jQuery. [...]
June 24th, 2009 at 10:33 am
My pleasure
Silly WP plugin.
July 17th, 2009 at 5:12 pm
how aplic to php and mysql
????
July 17th, 2009 at 10:09 pm
Great code, thanks for sharing.
Is there any way of preventing/dealing double mouse clicks? The code exits if a sibling is not found, but a user rapidly double/treble clicking a row that is positioned second to last will execute the code multiple times even though it can only move down once.
Not sure where else this would cause a problem, but for me its a problem as as I run a second function, where the script allows, which records the new order to database. This function is called multiple times under these circumstances, causing me problems. Is there a simple way of dealing with this in the script?
July 17th, 2009 at 10:20 pm
@gerard – This doesn’t have anything to do with PHP, you would simply output the table as HTML like normal and then apply the JavaScript to the table.
@danjan – you could add this line inside the ready() function:
$(’table tbody a.control’).bind(’dblclick’, function (e) { e.preventDefault(); return false;});
July 30th, 2009 at 10:05 am
[...] Sorting table rows using jQuery | Luke Dingle . com (tags: jquery grid table tables sorting tutorial) [...]
August 13th, 2009 at 5:22 pm
How would you change your script to account for multiple tables?
August 15th, 2009 at 1:16 am
This one is a life saver. Thanks very much!!
August 22nd, 2009 at 6:05 am
jQuery prev() function is giving its previous sibling, but how to get the previous/next visible sibling? My table has few hidden rows and using this approach it is sorting and showing the hidden rows also. But I need to sort only the visible rows. Please suggest me a solution.
Thanks,
kk
August 23rd, 2009 at 4:44 am
@Eugene — For multiple tables, just give each an id and change the jQuery selector to access multiple tables.
@Mark – Cool
Glad it helped you.
@KK — You’d just have to filter out based on its visibility
use prev(’:visible’) — this will only select siblings that are currently visible.
August 24th, 2009 at 7:08 am
Amazing script, thanks very much – I’m only just beginning to explore the wonders of jquery!
Quick question – I’ve been playing with the code trying to output an array which stores the order of the rows by their id, and then changes when they are re-ordered (so that I can save the order when the page is submitted).
What’s the best way to go about that?
Cheers, Chris
August 24th, 2009 at 12:25 pm
Thanks Rakuli, just a follow up question if the tables ids are generated dynamically (e.g. id=”100″ id=”101″.. and so on..) How do you change the jQuery selector to account for the multiple tables? (sorry, I’m new to jQuery and the tutorials on jquery.com didn’t provide me with enough insight)
August 25th, 2009 at 12:40 am
$(’#depositTbl tr’).each(function() {
if($(this).find(”td”).eq(3).html() == ‘LNLN’)
{
$(this).hide();
}
August 25th, 2009 at 12:42 am
@Rakuli and all–
I treid using prev(’tr:visible’) to get the prev tr but it is not working. In IE it is still getting the previous hidden tr and in mozilla it is not getting anything. Somewhere I saw ‘tr:visible’ wont work in IE so I used prev(’tr:not(:hidden)’), but still no luck it is not getting any row and its lengh is zero. Below is the code I am using to swap two rows on click and their result when tried to swap when hidden row is present for ‘tr:visible’ and ‘tr:not(:hidden)’:
Thanks,
KK
August 25th, 2009 at 12:48 am
$(’.upArrow’).live(’click’, function(){
var parent = getParent(this)
var prev = parent.prev(’tr:visible’);
alert(’prev.length :’ + prev.length);
if(prev.length == 1){ swap(prev, parent); }
})
with ‘tr:visible’ onclick result:
for IE: prev.length :1 // but it is swapping the hidden row
for Moz: prev.length :0 // Not swapping
with ‘tr:not(:hidden)’ onclick result:
for IE and Moz : prev.length :0 // Not swapping
February 25th, 2010 at 11:15 pm
Hai ,
this code is not working for me.
I got error in and $(document).ready( function () {