jQuery.ui Sortable
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 topint 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
The Voices of Reason
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
http://blog.snapleague.com/screencasts/LinksPreview/
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
http://www.isocra.com/2008/02/table-drag-and-drop-jquery-plugin/
# re: jQuery.ui Sortable
Here is a little article I wrote some time back in which I showed how to persist the information into the database using Drag and Drop operations using JQuery.
http://www.gridviewguy.com/ArticleDetails.aspx?articleID=381_Drag_And_Drop_With_Persistence_Using_JQuery
# re: jQuery.ui Sortable
I've updated the post to show the save code.
@David - yes sortables are pretty common in JavaScript libraries. I had played with the Scriptalicious one a long time ago, but it didn't work very well at the time. That's probably been addressed. I started with Prototype and Scriptalicious, but Scriptalicious was always more work than it should have been and the lack of decent documentation really killed it for me. Again that may have changed - I haven't looked back since going the jQuery route.
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
It gave me the idea to add a function into my base MasterPage class which allows the changing of full-client libraries on the fly.
Why? Why not. I think the ability to master multiple libraries to perform similar or exactly the same operations can only strengthen you as a developer and assist in dispelling the myth that choosing ASP.NET secludes someone to MS technologies.
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
$(".photolistitem").each(function(){
photos.push({
id: this.id,
notes: textFromHtml( $(this).find("div[id$=Notes]").html(),true)
});
});
And, if all you need is the text from the $=Notes child element, you could simply even more:
$(".photolistitem").each(function(){
photos.push({
id: this.id,
notes: $(this).find("div[id$=Notes]").text()
});
});
# re: jQuery.ui Sortable
OTOH, the .each() function is one I don't really see much reason to use. Just to cut out a for loop and item[x] syntax doesn't buy much IMHO. Just not sure if it's worth replacing a for loop with a delegate call both in terms of readibility and even more so for performance.
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
I also really loved the "Status bar" for the notifications in the demo.
Could you post the code for that? It would be really great.
Thanks
# re: jQuery.ui Sortable
nice article as all i read from you so far.
i am pretty excited about Jquery too and JSON also.
butu i have no experience on it..never had the time to get to it.
but i do now.
does this example works only with AJAX, i dont really want to getajax.
i think JSON is a better solution for asyncronous stuff.
what you think?, somehow i always like the idea to know exactly what i am doing and with AJAX it seems to get lost in translation..
my question?... if you please could release the example code in JSON and what i your thoughts on it.
Thanks rick,
keep them coming, cos we love them
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
I'll update the samples when I get a chance, but at the moment I don't have the time. There are a few other samples in that downlaod that require the same changes so it's not as simple as updating just this one file.
# re: jQuery.ui Sortable
http://www.shopdev.co.uk/blog/sortable-lists-using-jquery-ui
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
Hey, BTW, for extra user-friendliness, why don't you add the 'cursor: move;' artribute to your div and img? That way, the user gets a nice multi-directional arrow cursor to indicate that the thing is movable!!
# re: jQuery.ui Sortable
How to use this code using a table instead of unordered list ?
Currently I have table that looks like
<table cellspacing="10" id="columns">
<tr>
<td id="col1" class="portlet"></td>
<td id="col2" class="portlet"></td>
<td id="col3" class="portlet"></td>
</tr>
</table>
And my jQuery is
jQuery('#columns td').sortable(
{
accept: 'portlet',
helperclass: 'sort_placeholder',
handle: 'div.portlet_topper',
axis: 'float',
revert: true,
opacity: 0.7,
tolerance: 'pointer',
fx: 200,
bla bla
}
)
I can drag and drop the items inside a same cell but I can't move them to another cell.
I mean move an Item from col1 to col3.
Any help ?
Stan
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
stop: SavePhotos
# re: jQuery.ui Sortable
To allow dragging between columns in UI 1.5.2, call sortable() on each of the column containers, and pass an array of IDs of the *other* column containers. It's a bit of a pain to do this in a generic way, but here's what I'm using now, where my column containers are siblings:
$('div.widget-column').each(function(){
var siblings = $(this).siblings('.widget-column');
siblings = $.map(siblings, function(n){return "#" + n.id;});
$(this).sortable({connectWith:siblings});
});I'm convinced that this can be easier...anyone have suggestions on how?
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
is there an option that i can use that is called before the sorting starts, where i can make an ajax call....and if needed, prevent the update callback from triggering?
i have tried them all it seems, and update is always triggered
thanks so much!
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
$(function() {
$("#sortable").sortable({
stop: function() {
items = $("#sortable li");
photos = '';
for(var x=0; x<items.length; x++){
photos += items[x].id +';';
}
$("#num").load("/users/test5/" + photos);
}
});
$("#sortable").disableSelection();
});and then after you just have to implode the array and do the thing you want on the server like saving the order on your database for example... ;)
# re: jQuery.ui Sortable
# re: jQuery photo album create new album
# re: jQuery.ui Sortable
for example:
the order was 1,2,3
and we display them 3,2,1 now
thx in advance
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
Excellent post. I also found this post: http://www.wil-linssen.com/extending-the-jquery-sortable-with-ajax-mysql/ which explains how you can use the $('selector').sortable('serialize'); to get the new order. Thought I'd put it here for someone that might need it, it really helped me.
Pieter
# re: jQuery.ui Sortable
I like it so much. But I have a problem, when i run it on IE 6, it work not good.
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
There will be two divs ListA and ListB where elements in ListA should be able to drag and drop on target ie ListB.
I should be able to sort elements in ListA supporting multiple elements at a time to sort and drag .
Please suggest me how to do in jQuery and show demo site if available
thanks in advance,
sri..
# re: jQuery.ui Sortable
I am trying to setup nested sortable tabs in JQUERY. I got it working somewhat, but I get draggable errors int eh draggable stop "data(...).options is null".
Here is my POC:
<html>
<head>
<link href="themes/light-blue/ui.all.css" rel="stylesheet" type="text/css" />
<title>Multilevel Menu</title>
<style type="text/css">
ul.top-nav li span.parent-li
{
/*--Drop down trigger styles--*/
padding: .5em 1em;
float: left;
}
ul.top-nav li span.subhover
{
background-position: center bottombottom;
cursor: pointer;
}
/*--Hover effect for trigger--*/ul.top-nav li ul.sub-nav
{
list-style: none;
position: absolute; /*--Important - Keeps sub-nav from affecting main navigation flow--*/
left: 0;
top: 28px;
margin: 0;
padding: 0;
display: none;
float: left;
width: 170px;
}
ul.top-nav li ul.sub-nav li
{
margin: 0;
padding: 0;
border-top: 1px solid #252525; /*--Create bevel effect--*/
border-bottom: 1px solid #444; /*--Create bevel effect--*/
clear: both;
width: 170px;
}
html ul.top-nav li ul.sub-nav li a
{
float: left;
width: 145px;
padding-left: 20px;
}
html ul.top-nav li ul.sub-nav li a:hover
{
}
</style>
<script src="scripts/JqueryLib/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="scripts/JqueryLib/plugins/ui/jquery-ui-1.7.2.custom.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
var $tabs = $("#tabs").tabs();
var sorts = $("#tabs").find(".ui-tabs-nav");
var tabUpdate = function(e, ui) {
try {
var itemToRemove = $($tabs).data('itemInTransit');
var parent = $($tabs).data('parent');
parent.removeClass('ui-not-sortable');
var t = $(ui.item);
$(itemToRemove).remove();
$(ui.item).draggable({
helper: function(event, element) {
var clone = $(this).clone();
$($tabs).data('itemInTransit', $(this));
return clone;
},
opacity: 0.8,
connectToSortable: '.ui-tabs-nav',
cursor: 'move'
});
$(ui.item).find("a").click(function(e) {
var parent = $(this).parent().parent().parent();
if (parent.is("li") === true && parent.find('ul').size() > 0) {
$($tabs).data('parent', parent);
$(parent).addClass('ui-not-sortable');
}
});
}
catch (err) {
var e = err;
}
}
$(sorts).sortable({
//items: "li:not(.ui-not-sortable)",
cursor: 'move',
helper: 'clone',
connectWith: '.ui-tabs-nav',
cancel: '.ui-not-sortable',
update: function(e, ui) {
tabUpdate(e, ui);
}
});
$("ul.top-nav li ul li").draggable({
helper: function(event, element) {
var clone = $(this).clone();
$($tabs).data('itemInTransit', $(this));
return clone;
},
opacity: 0.8,
connectToSortable: '.ui-tabs-nav',
cursor: 'move'
});
$("ul.top-nav li span.parent-li").toggle(
function() {
$(this).parent().find("ul.sub-nav").show();
},
function() {
$(this).parent().find("ul.sub-nav").hide();
}
);
$("ul.top-nav li ul li a").click(function(e) {
var parent = $(this).parent().parent().parent();
$($tabs).data('parent', parent);
parent.addClass('ui-not-sortable');
});
});
</script>
</head>
<body>
<div id="tabs">
<ul class="ui-tabs-nav top-nav">
<li><a href="#fragment-1"><span>One</span></a></li>
<li><a href="#fragment-2"><span>Two</span></a></li>
<li><a href="#fragment-3"><span>Three</span></a></li>
<li><span class="parent-li">Four</span>
<ul class="ui-tabs-nav sub-nav">
<li><a href="#fragment-4"><span>fragment-4</span></a></li>
<li><a href="#fragment-5"><span>fragment-5</span></a></li>
</ul>
</li>
</ul>
<div id="fragment-1">
<p>
First tab is active by default:</p>
$('#example').tabs();
</div>
<div id="fragment-2">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh
euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt
ut laoreet dolore magna aliquam erat volutpat.
</div>
<div id="fragment-3">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh
euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt
ut laoreet dolore magna aliquam erat volutpat. Lorem ipsum dolor sit amet, consectetuer
adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat.
</div>
<div id="fragment-4">
Lorem
</div>
<div id="fragment-5">
Lorem ipsum dolor sit amet,
</div>
</div>
</body>
</html>
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
i have one div container that contain one html table with multiple rows
i am trying to drag of rows using jquery , its working fine for me
but the problem is if the selected row is coming at most bottom of div then the vertical
scroll is not working automatically.
i want while dragging rows the vertical scroll should work automatically
please help me
thanks
irfan
# re: jQuery.ui Sortable
Great post. I was trying to do something similar. Sorting out the divs.
I dwnloaded the code and was trying to search for the implementation of the photo album.
I sould not find that. Am i missing something.
If possible can you please post the code for the photoalbum.
Thanks
# re: jQuery.ui Sortable
Is there a way to add a menu?
# re: jQuery.ui Sortable
I was wondering if it was possible to get the code for the photoalbum. I could not located it in the zip file.
Or if anyone else has it could you put it some where i could access it.
thanks
# re: jQuery.ui Sortable
thanQ........
But, how does this value persist on postback?
where are you getting back the values from database?
Does it work for postback?
# re: jQuery.ui Sortable
I am doing a igoogle panel implementation in a website….
I would like to know how Sortable works on Postback?
I have placed 8 datagrids(ASP.Net) in 8 portlets(div) and 2 columns(div), like this
And the sortable is working fine…….
I am trying to implement the Igoogle panels, where the user can
save his settings, but using a save button……
So when i randomize the order and save it i am able to get the sorted order
and saving userid and preferences(sort string) to the database.....
I already have the sorted string with me,
say the original order is div1,div2,div3,div4…..
Sorted order is div3,div2,div1,div4……
I saved this sorted order into a string and saved it to the database,
but when the page reloads, the order reverts back to original order
i.e, div1,div2,div3,div4……
I also have the sorted string on postback, but I dont know how to
call the sortable function using the list div3,div2,div1,div4……
How to call the sortable with this string?
How can I pass the arguments to sortable?
# re: jQuery.ui Sortable
Thanks
# re: jQuery.ui Sortable
I am looking for your excellent photoalbum solution. But couldn't find it within the link http://www.west-wind.com/files/conferences/jQuery.zip. Could you please send me the photoalbum solution.
Thanks,
Niloy
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
# re: jQuery.ui Sortable
The sample at the url you provided is really awesome.
http://www.west-wind.com/rick/photoalbum/demoMaui2006/Default.aspx?Admin=true
Can you share the code used for the photoalbum page you put on your site. i want to implement similar sort of UI for my project at school. I am really new to JQuery so it would be great if i could get some ideas from your code.
Thanks in advance.
-Arvind
# re: jQuery.ui Sortable
I am searching for multi selectable drag and drop sortable plugin..
Please let me know if ur plugin can meets that requirement.Also i would like if u can help me getting one.
# re: jQuery.ui Sortable
Just came across this article and think this is awesome. I have photo details stored in a database and display the images in using standard ListView in asp.net. I am new to jQuery.
I would like to display an album and then to rearrange the order or the photos; change the captions and/or delete the photos just like the example http://www.west-wind.com/rick/photoalbum/demoMaui2006/Default.aspx?Admin=true
Could you please provide the steps that I need to follow in order to achieve the following:
- Firstly to display the photos from the Photo table in the database
- Rearrange the order; change caption and/or delete the photo(s)
- Final to update the changes in the Photo table
Any help would be much appreciated.
Thanks
# re: jQuery.ui Sortable