Unordered lists have a number of useful applications. They are commonly used to structure a site’s navigation. I have found, in a number of web applications, that the ability to reorder (sort) such lists would be extremely useful. So, how can this be achieved?
I’m a big fan of the jQuery library. Since most of the applications I build use jQuery, developing with this library would be the most logical approach. But wait… It looks like there are 2 jQuery plugins that allow elements to be sorted. These are the jQuery Interface plugin and jQuery UI.
At first, I started working with “Interface”. I soon ran into problems as I started working with styled lists. For some reason, the list elements lost their styling as they were being dragged. I just couldn’t quite put my finger on the problem - perhaps I missed something. I then started working with jQuery UI. This plugin retained the styling - perfect!
However, neither of these plugins provide a method to easily save and reload the list element order. Hence, should a user change the order of the list, these changes would be lost as the page is refreshed.
Saving the Reordered List
I determined that the easiest way to accomplish this is through the use of a cookie. The jQuery UI plugin already allows you to send the ID’s of the list elements (in order) to an array. Using Klaus Hart’s Cookie plugin, we can easily save the list element order to a cookie. But how do we retain/reload the element order saved to the cookie?
To restore the element order, I decided to remove all the sortable list elements from the DOM and then immediately append them to their parent element (in the order sepcified by the cookie). This is all done after the document has loaded.
The HTML Markup
<ul id="list1"> <li id="item-1">List Item 1</li> <li id="item-2">List Item 2</li> <li id="item-3">List Item 3</li> <li id="item-4">List Item 4</li> <li id="item-5">List Item 5</li> <li id="item-6">List Item 6</li> </ul>
The Javascript
///////////////////////////////////////////////////////////////// ///// EDIT THE FOLLOWING VARIABLE VALUES ////////////////////// ///////////////////////////////////////////////////////////////// // set the list selector var setSelector = "#list1"; // set the cookie name var setCookieName = "listOrder"; // set the cookie expiry time (days): var setCookieExpiry = 7; ///////////////////////////////////////////////////////////////// ///// YOU PROBABLY WON'T NEED TO EDIT BELOW /////////////////// ///////////////////////////////////////////////////////////////// // function that writes the list order to a cookie function getOrder() { // save custom order to cookie $.cookie(setCookieName, $(setSelector).sortable("toArray"), { expires: setCookieExpiry, path: "/" }); } // function that restores the list order from a cookie function restoreOrder() { var list = $(setSelector); if (list == null) return // fetch the cookie value (saved order) var cookie = $.cookie(setCookieName); if (!cookie) return; // make array from saved order var IDs = cookie.split(","); // fetch current order var items = list.sortable("toArray"); // make array from current order var rebuild = new Array(); for ( var v=0, len=items.length; v<len;> rebuild[items[v]] = items[v]; } for (var i = 0, n = IDs.length; i < n; i++) { // item id from saved order var itemID = IDs[i]; if (itemID in rebuild) { // select item id from current order var item = rebuild[itemID]; // select the item according to current order var child = $("ul.ui-sortable").children("#" + item); // select the item according to the saved order var savedOrd = $("ul.ui-sortable").children("#" + itemID); // remove all the items child.remove(); // add the items in turn according to saved order // we need to filter here since the "ui-sortable" // class is applied to all ul elements and we // only want the very first! You can modify this // to support multiple lists - not tested! $("ul.ui-sortable").filter(":first").append(savedOrd); } } } // code executed when the document loads $(function() { // here, we allow the user to sort the items $(setSelector).sortable({ axis: "y", cursor: "move", update: function() { getOrder(); } }); // here, we reload the saved order restoreOrder(); });
There are 2 functions to be concerned with in the above code. The first, “getOrder()”, writes the order of the list elements to a cookie. The second, “restoreOrder()”, fetches the order saved to the cookie and restores that order. We execute “getOrder()” each time the list order is changed. We execute “restoreOrder()” after the document has loaded.
Using the code provided above, you should only need to change the variables (at the very top) to get this working with your page. Just remember to include the jQuery library and jQuery UI plugin! I hope this is helpful!

July 8, 2008
Quote
Nice tutorial! I’m using every day a little more of jQuery. It’s fantastic to learn and very simple to use.
Thanks,
David Carreira
July 15, 2008
Quote
Pretty good script. Just one suggestion.
If you want your to contain more than just text you’ll have to set savedOrd to something like this:
var savedOrd = “” + $(’#’ + itemID).html() + “”;
There is probably a more elegant solution to this (so we don’t have to build the tag ourselves). I’m hoping someone replies with the answer. I’m guessing .children() has an issue when the child has children. This is a legacy function anyways (http://docs.jquery.com/API/1.1.1/DOM/Traversing).
July 18, 2008
Quote
Is there an object oriented way to use this script? In cases, such as when there are 2 columns of sortable items?
July 18, 2008
Quote
jQuery UI does allow you to connect 2 lists - so it certainly is possible. However, I would need to rework some of the code.
July 18, 2008
Quote
Thanks. To clarify — I’m trying to sort 2 columns separately. Items from Column 1 don’t need to mix with Column 2 items. Is it an easier to use this script with 2 separate columns?
Appreciate the help. New to JS and have been working hours on this one.
July 18, 2008
Quote
The code would still need to be reworked due to the selectors used. jQuery UI adds the “ui-sortable” class to each list. In the code, we select this class:
If there are 2 lists, the function will select both lists instead of working with each list separately. To get this working, you would probably need to pass the selector as a parameter of the function.
You would also need to write something like this for each list:
July 18, 2008
Quote
That was it! The code needed to know which ID to update. In the FOR loop, I changed 3 references to $(”ul.ui-sortable”) to $(”ul” + setSelector + “.ui-sortable”).
Not sure if this abstracts well, but the 2 columns are now sorting and remembering correctly. Appreciate the help.
August 25, 2008
Quote
Can this be used outside of a list? In my particular case, I’ve got
…stuff…
…stuff…
…stuff…
…stuff…
I know when I put it that way, it looks like it should be a list, but each .module holds a table and it’s really a bit much for a UL… and there may be constraints on the current structure I’m not aware of. Anyways, what I want is to order the .module divs.
August 25, 2008
Quote
August 25, 2008
Quote
Nevermind. I hadn’t put an id on the children. Feel free to delete comments.
August 25, 2008
Quote
Hi Baxter!
Is it working now?
August 27, 2008
Quote
Hi! FF3 Firebug throws me an error for the line:
for ( var v=0, len=items.length; v
Isn’t there a ) missing?
August 27, 2008
Quote
Hi Erik! Sorry about that. It looks like Wordpress has fiddled with the code. I’ll sort this out and post again when it’s fixed. If you need it now, check out the source code for the demo.
August 27, 2008
Quote
Hey! Works like a charm! Thx a lot for the very fast help!!!