Sorting of a list of items is a common task in any user interface. Personally I've been wanting to add sorting to a number of applications for ages, and some time ago I started down the path of building something of my own to handle this task on the client side with JavaScript. Ok, I failed - I got most of it almost working but there were always a few things that just didn't work quite right. I've also tried a few other components that do sorting and most are quirky or don't work well cross browser.
[updated 4.28.2008 - added code to show how to notify server of sort updates]
[updated 6.24.2008 - updated download samples for jQuery 1.2.6 and jQuery.ui 1.5 release]
jQuery's Sortable is a cool client side jQuery plugin that is part of the jQuery.ui plug in library that works well, looks very nice and is fairly self contained (2 scripts beyond jQuery). It lets lets you create sortable unordered/ordered lists or a plain group of contained DIV tags.
It's extremely easy to use in client code by simply pointing at the list and a few options to make the list sortable. Items can be dragged around and sorted and the items appear transparent when dragging (by default). It's also fairly customizable to let you control how sorting works and how the items are represented while dragging.
One place I used Sortable is in my photo album where I allow client side sorting of the images that are displayed. In my app the administrator can rearrange images in real time, make changes to captions and then when done save them back via an Ajax callback to the ASP.NET server app.
Here's what sorting operations look like (the real deal is a lot smoother - this is captured as an animated gif with CamTasia):
Items slide around as they are sorted and use a nice sliding into place effect when dragging is complete, or slide back into their original position if moved out of the main area. The plug in also handles scrolling the browser when bumping up against edges. The really cool thing about Sortable though is how easy it is to use: It's an extender, so all you do is point it at the result of a jQuery selector that is a list and add a few simple and optional settings and that's it.
To use Sortables you'll need jQuery plus a few of the jQuery.ui scripts:
<script src="/photoalbum/scripts/jquery.js" type="text/javascript"></script>
<script src="/photoalbum/scripts/ui.base.js" type="text/javascript"></script>
<script src="/photoalbum/scripts/ui.sortable.js" type="text/javascript"></script>
All the code required to make the above list sortable is:
$("#ulThumbnailList").sortable(
{ opacity: 0.7,
revert: true,
scroll: true ,
handle: $(".imagecontainer").add(".imagecontainer a img")
});
The settings parameter object is optional but allows configuration of the behavior for the sorting/dragging operation. Here I optionally apply opacity, and tell the sort to revert to original position on aborting and scrolling the browser window when bumping against the edges. In addition, I supply a selector to use as a drag handle - the item is dragged by grabbing the image container which is a DIV that wraps the image. The above control supports in-place text editing and selecting the entire control makes it hard to get the text editing to 'take' because the control will try to drag fairly quickly.
Notice the handle selector: You can apply a selector for the area that acts as the drag handle - ie. where the user can drag the item. Here I want it to be a DIV tag that wraps the image. Note that I also select the image itself - the drag handle is very explicit about which element is used so I make sure that both the container and the contained image can initiate drag operations. (this is a change from the previous rev I'd worked with).
Keep in mind that Sortable doesn't work with tables, but HTML lists or a set of Div tags, so if you need a table like layout you need to think of other ways to arrange the layout. You can use a Repeater or ListView in ASP.NET to create this sort of layout easily. The above HTML uses contained plain old <li> tags with a fixed width and float:left to provide the 'column' based layout of the photos.
The trickier part - for the photo list above at least though - is getting the CSS right. The above is rendered as an unordered list with float:left items which gives the effect of a table. Note that Sortable can sort elements in this way and does the right thing when sliding elements out of the way as the dragged item moves through the table - it's a very cool visual as well as practical effect.
The unordered list HTML above is rendered like this:
<ul id='ulThumbnailList'>
<li id='f0db40f3' class='photolistitem'>
<div class='imagecontainer'>
<a href="javascript:ShowImagePopup('f0db40f3');">
<img src='tb_sweatshop.jpg' class='thumbnailimage' id='f0db40f3Img' title='sweatshop.jpg' />
</a>
</div>
<div id='f0db40f3Notes'>Team programming
</div> <div class='deletethumbnail'>X</div>
</li>
<li id='64ac6f8c' class='photolistitem'>
<div class='imagecontainer'>
<a href="javascript:ShowImagePopup('64ac6f8c');">
<img src='tb_whalewhispers.jpg' class='thumbnailimage' id='64ac6f8cImg' title='whalewhispers.jpg' />
</a>
</div>
<div id='64ac6f8cNotes'>Can you hear me now? Really now?
</div> <div class='deletethumbnail'>X</div>
</li>
...
</ul>
with the photolistitem CSS class defined like this:
.photolistitem
{
position: relative;
display: block;
float: left;
list-style-type: none;
height: 220px;
width: 170px;
padding: 10px;
border: solid 1px khaki;
margin: 12px;
background: steelblue;
text-align: center;
}
Note the float:left and the fixed width and height which is a requirement if you want to use the unordered list as a table like display. Items need to be the same size or else the items ends up not aligning properly...
With this layout in place all that needs to happen is to apply the .Sortable() call against the top level <ul> tag and the plug in does the rest, nice UI effects and all.
Saving Sorted Data
Because Sortable is a client side component sort changes are not automatically visible on the server. This means you'll need to somehow let the server know of the sort order update and push the data to the server via an AJAX callback. In the example above I'm using jQuery to reselect all the sorted items (.photolistitem class) and then reading the content from the controls into an array of objects that contains the Id (a hash) and the caption. The array is then sent back to the server via an AJAX callback. The update is triggered off a Save button on the page.
Here's what the JavaScript code for the save operation looks like:
function SavePhotos()
{
var items = $(".photolistitem");
var photos = [];
for(var x=0; x<items.length; x++)
{
var photo = {}
photo.id = items[x].id;
photo.notes = textFromHtml( $(items[x]).find("div[id$=Notes]").html(),true);
photos.push(photo);
}
var photoList = {};
photoList.Photos = photos;
photoList.Title = $("#lblTitle").text();
photoList.Description = textFromHtml( $("#lblDescription").html(),true);
// *** Page JSON wrapper
httpJson("SaveList",photoList,
function(result) { showMessage("Changes have been saved...",msgTimeout); },
pageError);
return photos;
}
On the server side a CallbackMethod (part of West Wind Ajax Toolkit, but you can point this at WCF REST or ASMX Ajax service) the data gets picked up like this:
[CallbackMethod]
public string SaveList(PhotoList photoList)
{
// *** Write out sort order in increments of 10
// *** Highest sort order goes to the top
int sortCount = 10 * this.Album.Photos.Count;
Album.AlbumName = photoList.Title.Trim();
Album.AlbumDescription = photoList.Description.Trim();
foreach (PhotoListItem changedPhoto in photoList.Photos)
{
Photo photo = Album.Photos.GetPhotoByHashCode(changedPhoto.Id);
if (photo == null)
continue;
photo.Notes = changedPhoto.Notes.Trim(' ','\r','\n');
photo.Updated = DateTime.Now;
photo.SortOrder = sortCount;
sortCount -= 10;
}
// *** Re-sort the list to update the actual object layout
this.Album.Sort();
// *** Save the album to disk
this.Album.SaveAlbum(this.ConfigFile);
return "Ok";
}
The process is pretty straight forward. If your lists are large and push back lots of data you'd probably also implement some sort of IsDirty flag that determines whether an item has changed so only those items changed are sent to the server. However for sorting you definitely have to send all the items/ids so the server can properly reassemble the items and sort accordingly. It isn't automatic, but it ain't rocket science either.
jQuery UI - Wait for Version 1.5 or use the 1.5 Beta 2 now
When jQuery.ui 1.0 was first released I was pretty excited to see that there'd be a somewhat uniform UI library built ontop of jQuery. Unfortunately Version 1.0 was a really bad release with many of the components not even working out of the box. There were lots of issues with rendering and most of the components worked haphazardly with no consistency between the various control APIs. Most of the controls are based on third party plug-ins that have been drafted into the jQuery.ui API and in version 1.0 that really showed by way of the differing API interfaces.
Version 1.5 - which is not released yet, but available in beta form - fixes these issues with a complete overhaul of the jQuery.ui API. I've used quite a few of the 1.5b components and had no major problems. Many of the controls also have had a big face lift with much nicer UI and effects applied. Using the beta however requires that you also use an interim version of jQuery (1.2.4a). Prior to this release I'd been pulling jQuery out of the SVN repository, patching it together from the base source files, but with the beta 2 release there's now a synched version of jQuery included. If you want to use the beta you need to use this version or a newer one out of the SVN repository.
The current release of jQuery.ui and jQuery work well, but if you're squeamish about beta releases you might want to wait a bit for release.
[updated 4.28.2008 - jQuery.ui 1.5 was released earlier this month]
Online Sample:
http://www.west-wind.com/rick/photoalbum/demoMaui2006/Default.aspx?Admin=true
Click on the Configuration link for the admin portion that includes the sortable code and component.
jQuery Samples for Downloading:
You can also download the full photo album sample along with a host of other ASP.NET / jQuery samples from:
http://www.west-wind.com/files/conferences/jQuery.zip
Enjoy.
Other Posts you might also like