Monday, June 8, 2009

jQuery UI Sortable with TABLE

Drag and drop sorting on a web application? This thought is so far fetched several years ago - and now everybody is or can do that easily with jQuery. Looking at the tutorials and documentation in jQuery's website, it lays out a simple method to call to make our list to become sortable.

     $(function() {
        $("#sortable").sortable();
        $("#sortable").disableSelection();
    });
With our corresponding HTML:
     <ul id=sortable>
        <li>one</li>
        <li>two</li>
        <li>three</li>
        <li>four</li>
    </ul>
Here is a demo on how that works.
Now what if we are using TABLE instead of UL or OL??Easy - Use TBODY tag. So using the example above, let's convert the list into a table:
     <table id=anothersortable>
        <tbody class=content> 
            <tr><td>one</td></tr>
            <tr><td>two</td></tr>
            <tr><td>three</td></tr>
            <tr><td>four</td></tr>
        </tbody>
    </table>
Then our jQuery to be as such:
     $(function() {
        $("#anothersortable tbody.content").sortable();
        $("#anothersortable tbody.content").disableSelection();
    });
Click for demo for the simple table.You can even make this having sub-sort - or with children sorting. Like this:
     <table id=subsortsortable>
        <tbody class=content>
            <tr><td>one</td></tr>
            <tr><td>two</td></tr>
            <tr><td>
                <table><tbody class=subcontent>
                    <tr><td>three.one</td></tr>
                    <tr><td>three.two</td></tr>
                </tbody></table>
            </td></tr>
            <tr><td>four</td></tr>
        </tbody>
    </table>
Adjust our jQuery to be as such:
     $(function() {
        $("#subsortsortable tbody.content").sortable();
        $("#subsortsortable tbody.content").disableSelection();
        $("tbody.subcontent").sortable();
        $("tbody.subcontent").disableSelection();
    });
Click for demo of this one. UPDATE: Viewer Coolboy in his comment below pointed out that viewing in IE7 may sometimes introduce unexpected behavior in the y axis. I can replicate the problem and have found a solution or a work around for it by using "handler". Using handler, you basically designating a "dragging" point, instead of using the whole row as to drag. So in using handler, our code changes a little bit in both HTML and javascript. Here is the updated HTML:
     <table id=subsortsortable>
        <tbody class=content>
            <tr><td><label class="levelonehandle">X</label></td><td>one</td></tr>
            <tr><td><label class="levelonehandle">X</label></td><td>two</td></tr>
            <tr><td><label class="levelonehandle">X</label></td><td>
                <table><tbody class=subcontent>
                    <tr><td><label class="leveltwohandle">X</label></td><td>three.one</td></tr>
                    <tr><td><label class="leveltwohandle">X</label></td><td>three.two</td></tr>
                </tbody></table>
            </td></tr>
            <tr><td><label class="levelonehandle">X</label></td><td>four</td></tr>        </tbody>
    </table>
Then our javascript as such:
     $(function() {
        $("#subsortsortable tbody.content").sortable({
                handle: ".levelonehandle"
            });
        $("#subsortsortable tbody.content").disableSelection();
        $("tbody.subcontent").sortable({
                handle: ".leveltwohandle"
            });
        $("tbody.subcontent").disableSelection();
    });

32 comments:

tommy.vu said...

Nice post! I will definitely be using this technique. You should consider having a "live" example of some of this stuff, it helps to see this kind of thing in action.

Johannes Setiabudi said...

Demos are added.

jarik said...

Thanks a lot! Supeperb post! :)

Siddhesh said...

yaaah!!! nice post!!
it saved my time!!!!

Unknown said...

Thank you!

Anonymous said...

The main issue with ui.sortables which i'm sure you have noticed is that it breaks really bad in IE when you start working with Nested Lists or Tables. I really really hope they can get that fixed because code-wise ui.sortables is really light weight and awesome!

Anonymous said...

This trick breaks if you put 2 or more td fields, on firefox and ie. Search on the web tablednd plugin. It solve the problem.

Johannes Setiabudi said...

I have tried this with several TDs, it works pretty well for me, in IE, FF, Chrome.

Here is an example with 2 TDs - http://setiabud.googlepages.com/sortableSubTable_2.htm

Sergei Udalov said...

Thank You very much!!! :) Where did You get this solution?

Johannes Setiabudi said...

@Sergey: lots of trial and error.

doxotron said...

thanks you very much... :D

Unknown said...

Great post.
Having some problems with IE7. When you drag a row down the target y-coordinates gets way off.
:(

This only happens when using sortable in a table. Not when I'm using it with UL/LI.

Verified this on your, and some other guy's, demo-page.

Johannes Setiabudi said...

@coolboy thanks! In my experience IE7 does have some quirks. You can work around this by adding a "handler".

I will update my post with this.

ShachMaT said...

Hello
I would like to point that if you want more nesting (WORKS WITH UL! IE7/8) levels you just need to define unique LI handler for every UL you nest that fixes IE7/8 issue with braking while drag.

Anonymous said...

Not sure if I'm doing something wrong, but when I use a table like this, when I call .sortable('serialise') I get an empty string returned even when each tr has an id of the form "set_id".

Johannes Setiabudi said...

@Grant: Are you sure that you put "serialize" instead of "serialise"?

Anonymous said...

Joe, I want to sort td's within a table of 2 X 2 layout. Your example sorts sort, how td's can be sorted.

Johannes Setiabudi said...

@anonymous I am not sure I understand what you are saying/asking. Let me ask to clarify: you want to sort TDs within a TR or within a whole TABLE?

Johan van der Kuijl said...

Nice example, i got it working too. However, the Serialize function returns an empty list. When I use a simple ul / li list it works fine.

Johannes Setiabudi said...

@Johan The serialize is giving empty string because I did not put any ids in the sortable elements. Here is a quote from jquery's website about serialize method for sortable: If serialize returns an empty string, make sure the id attributes include an underscore. They must be in the form: "set_number" For example, a 3 element list with id attributes foo_1, foo_5, foo_2 will serialize to foo[]=1&foo[]=5&foo[]=2. You can use an underscore, equal sign or hyphen to separate the set and number. For example foo=1 or foo-1 or foo_1 all serialize to foo[]=1.

شايان داورزني said...

thats great!!!

thank you

Unknown said...

doing this on a table worked like a charm, until IE9 came ...

Johannes Setiabudi said...

@Tom - I am working on a solution. As a work around, you can use this metatag within your "head" element:

meta http-equiv="x-ua-compatible" content="IE=8"

to force render it using IE8 compatibility mode.

Johannes Setiabudi said...

@Tom - upgrading your jQuery UI to the latest (mine is using 1.8.11) should fix the IE9 problem.

R. Nurse said...

Nice post! Works great!

Andy said...

great post.
i forgot the underscode (_) in the ID-attribute, so the serialize()-method just responsed with a undefined values.

now everythingworkds fine :-)
Thanks

Ville said...

Thank you, this saved me a lot time and hassle :)

David said...

This works pretty good, but now, i need to safe it in a cookie, can you tell me how it works? Thanks

Johannes Setiabudi said...

David -

If you already have a method to handle the sortable event, you can add/edit the cookie inside it.

Here is a crude example:
$(function() {
$("#subsortsortable tbody.content").sortable({
handle: ".levelonehandle",
create: function (event, ui) {
var co = document.cookie.split(';');
for(var i = 0;
i < co.length; i++) {
var c = co[i];
while (c.charAt(0)==' ')
c = c.substring(1,c.length);
if (c.indexOf("sort=") == 0)
sortOrder = c.substring("sort=".length, c.length);
}
// display sortables appropriately based on saved sort order
}
update: function (event, ui) {
document.cookie = "sort=" + ($(this).sortable("toArray"));
}
});
});

Silvio Fernandes said...

Very good!

Silvio Fernandes said...

Very good, helped me a lot.

Kulwinder Sandal said...

Very nice.... Thank uu..!!!!!!