Follow my updates with RSS

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:

?View Code JAVASCRIPT
 
    $(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

VN:F [1.4.6_730]
Rating: 4.2/5 (22 votes cast)

Tags: ,

19 Responses to “Sortable table rows with jQuery.”

  1. Jack F chips in with:

    That’s pretty damn cool! Nice tutorial Luke :D

    I <3 jQuery :D

    VA:F [1.4.6_730]
    Rating: 4.5/5 (2 votes cast)
  2. Japh chips in with:

    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 :P
    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 :D

    Great stuff!! Post more! (says me… I should post more too…)

    VA:F [1.4.6_730]
    Rating: 5.0/5 (1 vote cast)
  3. Rakuli chips in with:

    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.

    VN:F [1.4.6_730]
    Rating: 5.0/5 (1 vote cast)
  4. Sortable table rows with jQuery - Draggable rows | Luke Dingle . com chips in with:

    [...] Scripts Luke Dingle . com Turn over a playful leaf on web design « Sortable table rows with jQuery. [...]

  5. Japh chips in with:

    My pleasure ;) Silly WP plugin.

    VA:F [1.4.6_730]
    Rating: 5.0/5 (1 vote cast)
  6. gerard chips in with:

    how aplic to php and mysql

    ????

    VA:F [1.4.6_730]
    Rating: 5.0/5 (1 vote cast)
  7. danjan chips in with:

    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?

    VA:F [1.4.6_730]
    Rating: 5.0/5 (1 vote cast)
  8. Rakuli chips in with:

    @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;});

    VN:F [1.4.6_730]
    Rating: 5.0/5 (1 vote cast)
  9. links for 2009-07-29 - magnum blog chips in with:

    [...] Sorting table rows using jQuery | Luke Dingle . com (tags: jquery grid table tables sorting tutorial) [...]

  10. Eugene Flores chips in with:

    How would you change your script to account for multiple tables?

    VA:F [1.4.6_730]
    Rating: 0.0/5 (0 votes cast)
  11. mark chips in with:

    This one is a life saver. Thanks very much!!

    VA:F [1.4.6_730]
    Rating: 0.0/5 (0 votes cast)
  12. kk chips in with:

    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

    VA:F [1.4.6_730]
    Rating: 5.0/5 (3 votes cast)
  13. Rakuli chips in with:

    @Eugene — For multiple tables, just give each an id and change the jQuery selector to access multiple tables.

    @Mark – Cool :D 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.

    VN:F [1.4.6_730]
    Rating: 0.0/5 (0 votes cast)
  14. Chris chips in with:

    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

    VA:F [1.4.6_730]
    Rating: 0.0/5 (0 votes cast)
  15. Eugene chips in with:

    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)

    VA:F [1.4.6_730]
    Rating: 0.0/5 (0 votes cast)
  16. KK chips in with:

    $(’#depositTbl tr’).each(function() {
    if($(this).find(”td”).eq(3).html() == ‘LNLN’)
    {
    $(this).hide();

    }

    VA:F [1.4.6_730]
    Rating: 0.0/5 (0 votes cast)
  17. KK chips in with:

    @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

    VA:F [1.4.6_730]
    Rating: 5.0/5 (2 votes cast)
  18. KK chips in with:

    $(’.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

    VA:F [1.4.6_730]
    Rating: 5.0/5 (2 votes cast)
  19. anusuya chips in with:

    Hai ,
    this code is not working for me.
    I got error in and $(document).ready( function () {

    VA:F [1.4.6_730]
    Rating: 0.0/5 (0 votes cast)

Leave a Reply