Drag & Drop Sortable Lists with JavaScript and CSS


In Web applications I've seen numerous — and personally implemented a few — ways to rearrange items in a list. All of those were indirect interactions typically involving something like up/down arrows next to each item. The most heinous require server roundtrips for each modification...boo.

Then I came across Simon Cozens' example of rearranging a list via drag & drop. I was so inspired I had to try it out myself.

Example: A Basic List

Essentially Simon Cozen's example with some subtle enhancements. It's not obvious, but go ahead and rearrange the items:

In Firefox you can also drag the bullet to move an item. Keen.

Saving the reorderd list is possible by inspecting the DOM. All the sortable lists on this page retain their order via cookies (try rearranging a list and then reloading the page). Read a description of the technique on my blog.

Example: Add Some Style

I added some styling and cursor hinting in an attempt to make the dragability more obvious (see in-place editing for an example with drag handles).

  • alpha
  • bravo
  • charlie
  • alpha
  • bravo
  • charlie
  • alpha
  • bravo
  • charlie
no margin on list items 4px bottom margin on list items. Firefox exhibits bug when dragging. Firefox workaround: -4px top margin on list, 4px top margin on list items.

This next list is intentionally long to see how well the technique scales up and uncover other interaction issues.

You'll notice if part of this list is below the fold, it requires at least 2 drags to move an item from the beginning to the end (technically 3 drags if you count the one on your browser's scrollbar). Automatic scrolling, like in Word or Excel, is a well established solution to this problem. Adding that is a work in progress.

Example: Sorting in two dimensions

With sorting vertically oriented items under our belt, onto the next challenge: sorting floated, wrapped list items. Earlier versions of my code had separate scripts for vertical, horizontal, and wrapped lists. Now they are unified into one script that does it all. Amazing!


A previous version determined when to swap based on the position of the top-left corner of the item being dragged. It was this example and the slide arranger example which illustrated that the better interaction is to base this on the position of the cursor, which is how the script works now.

Example: Sortable links or buttons

Sortable items containing links. Links are 'display: block' so the entire item is clickable (except this isn't so in IE). As buttons go these aren't very good; they lack button affordances and behavior. But I think you get the general idea.

 

Very few people keep the mouse perfectly still when clicking on something. To avoid accidental drags these buttons have a drag threshold, as do all the examples on this page.

Additional examples

TODO: Example: Drag between two lists

Benjamin Levy is credited with the first version (that was based on my code) of dragging between two lists. Eventually this will be supported in the ToolMan DHTML Library.

TODO: Example: Sorting within nested lists

Steps:

  1. Complete “Drag between two lists”
  2. ???
  3. Profit

TODO: sortable table cells or cell contents

This will take more work than I initially thought. That's because the sorting script currently makes the assumption that sortable elements are immediate siblings of the same parent element.

I may start by cloning the existing dragsort.js and customizing it for table cells, then see if the two can be generalized without making either a pain to use.

TODO