Using jQuery UI Sortables To Move Items From One List To Another

JavaScript , Web development , jQuery Add comments

During my most recent project, my clients asked me to build a web-based tool that would help them place volunteers into various standing committees. Placements would be made based on the preferences of the volunteers (who were asked to choose up to three committees they would like to serve on), the vacancies created in each committee by outgoing members, and the desire to have a diversity of units and divisions represented in each committee.

I decided pretty quickly that the most natural way to represent this placement process on a web page would be to let them "physically" move a volunteer into a committee. I had built similar tools before (even before I started using jQuery), but never with more than two lists or collections, so I went to the jQuery UI site to see what my options were.

I started with the most obvious place to start, the Droppable interaction, but soon realized that a better choice for this task was the Sortables interaction.

The primary focus of the Sortable interaction is to let you reorder a collection of DOM elements by dragging them up-and-down through the collection. It's extremely easy to implement in its most basic form. If you wanted to make all of the <li> elements in a <ul> with an "id" attribute "listA" sortable, you can do it in one line:

$(document).ready() {
  $("#listA").sortable();
});

...you can see that code in action on the home page of the Sortables interaction.

What I discovered was that it was almost as easy to connect one Sortable list to another, so that in addition to being able to move reorder items within each list, you could drag items from one list to the other, simply by using the "connectWith" option:

$(document).ready() {
  $("#listA").sortable({
    connectWith: ['#listB']
   });

$("#listB").sortable({
   connectWith: ['#listA']
  });

});

...In the code above, the first sortable() function call makes listA sortable and uses the "connectWith" option to allow items from listA to be dragged into listB. The second sortable() function call lets you sort items in listB and drag items from listB over to listA (even items that originally belonged to listA). If you wanted the movement to only go in one direction (from listA to listB but not back again), you could leave out the "connectWith" option for listB.

Again, the jQuery UI site has a nice ready-made demo of this.

All well and good, but right now all this code does is create the visual effect of moving an item from one list to another. Actually recording the fact that a particular item was moved from one list to the other requires more code, code that is invoked whenever such a move takes place. Being the smart guys and gals that they are, the jQuery UI team built a couple of custom events into the Sortables interaction so you can run additional functions when a certain event has taken place. For my purposes, I only needed to utilize two of these events: receive and remove:

$(document).ready({
  $("#listB").sortable({
    connectWith: ['#listA'],
    receive: function(event, ui) {
       //Run this code whenever an item is dragged and dropped into this list 
       var itemText= ui.item.text();
alert(itemText + " just joined this list");
}, remove: function(event, ui){ //Run this code whenever an item is dragged and dropped out of this list var itemText= ui.item.text(); alert(itemText + " just left this list"); } }); });

...The code is, for the most part, self-explanatory, save for one line (repeated twice): var itemText= ui.item.text()

The ui object is a "prepared" object created by jQuery UI that holds a number of objects and data associated with the event that just took place. The item object within the ui object represents the item that was moved in or out of the list, so I can treat it as a jQuery object and retrieve the text of the item using the standard text() function. You can find a full list of the data contained in the ui object by clicking on the "Overview" tab at the bottom of the main Sortables interaction web page.

I created my own demo page to illustrate how this code works (with a homage to the Fox TV show "Fringe"):

http://www.swartzfager.org/blog/demoFiles/connectedSortables/connectedSortableEvents.cfm

Two things worth mentioning at this point:

  • If one of your lists starts off empty, or if there's any chance that a user might remove all of the items from a list and then try to put items back into the now-empty list, you need to set a minimum height for that <ul>, so that even when empty, the <ul> is large enough to accomodate a single list element. In Firefox and Safari, you can set the minimum height using the min-height CSS style, but if you have to support IE 7, you'll have to add two additional height styles (like so):
    • min-height:50px;
      height:auto !important;
      height:50px;

  • In my demo, you'll notice that as you drag a person out of the Candidates list, the list item's width gets shrunk to the width of the longest unbroken string of text in the list item. I'm not sure why it does that, but you can negate that effect by either defining a set width for the <li> elements, or by defining a "helper" with a set width (a helper is a visual representation of the item being moved, using something graphical like an icon).

So, armed with this knowledge about the Sortables interaction, I was able to build the tool required by my clients. Every time a volunteer was moved into or out of a committee, the receive or remove event would make an AJAX call to update the volunteer's record (either providing the id of the committee they were placed in or removing it), and it would run a function that updated the vacancy count for that committee specific to that type of person and counted the overall number of vacancies for that committee (to determine if the committee had been "filled"). I also added a few toggles allowing them to hide extraneous information when they only wanted to see the data pertinent to doing placements for a particular committee. The final challenge was to scale everything so that the tool could be viewed with a projector, so that the members of the group responsible for making the placements could work on it collaboratively.

I wasn't comfortable sharing an exact copy of the tool I created, but I have posted a facsimile that's fairly close to it, minus any real-life data and any AJAX calls to save the placements between page reloads. You can view it here (Note: it doesn't work in IE 7):

http://www.swartzfager.org/blog/demoFiles/connectedSortables/volunteerPlacement.cfm

...I provided instructions at the top, but quite honestly I think most IT-inclined folks could figure it out without them.

It's just one example of some of the really cool (yet practical) user interfaces made possible with jQuery and jQuery UI.

8 responses to “Using jQuery UI Sortables To Move Items From One List To Another”

  1. Todd Rafferty Says:
    Great demo. I'll definitely have to play around with this sometime.
  2. Jon Says:
    Could this work as a heiarchy somehow?
  3. Brian Swartzfager Says:
    @Jon: I just did a REALLY quick test of that (where you have a parent &lt;ul&gt; with an &lt;li&gt; element that contains another &lt;ul&gt; list, and use the connectWith setting to connect the &lt;ul&gt; lists to each other). It let me move items from the parent list to the child list, but not from the child list to the parent list.
  4. Jon Says:
    That's a shame. I am looking for something where my clients can decide what items to have in their front end menus and I thought that would be a good approach allowing them to create heirarchies. Not sure how else to do it.
  5. Brian Swartzfager Says:
    @Jon: It didn't seem right to me that it wouldn't work, so I tried it again this morning.

    Turns out I was wrong: it will let you move items from the child list to the parent list...but I had to drag the child item around in the parent list for a while before jQuery would react and shift the parent items to make room for the child element (which is why I thought it wasn't working before).

    So while it does work, it doesn't seem to work too well: I don't think a user is going to be willing to wait to be allowed to drop that child item.

    But I'd say try it out for yourself, and see if maybe there's some trick to getting it to work right: maybe see if adding borders around the list elements will help provide a visual cue for where an item needs to be dragged to get it to drop.
  6. Jason Campbell Says:
    Would it be difficult to treat one list as a master and rather than remove the item from that master list when adding to the second list make a copy of that item instead, also confining the interaction to be a one way interaction Master -> New list and begin with "New list" being an empty list?

    As an aside, I am unable to get your demo links to perform the drag and drop in Safari 4.0.4 in OSX 10.6.2

    Thanks,
    J.
  7. Brian Swartzfager Says:
    @Jason: As far as I know, the Sortable function in jQuery UI doesn't give you the option to copy an item from one list to another as a result of the move/drag and drop. That's not to say that you couldn't write your own code for copying an item from the master list to the second/child list, but you probably wouldn't want to "drag" said item, as that implies you are moving the item from one place to the other.

    I checked the demos, and it looks like something happened to the core jQuery library file such that jQuery never initialized, and therefore the demos wouldn't work regardless of browser. I've replaced the jQuery library file and the demos seem to work fine now (I don't have Snow Leopard, but I did test it on my Safari on my Mac).
  8. Arnaldo Says:
    wonderful!

Leave a Reply