jQuery Sortable and Droppable jQuery Sortable and Droppable jquery jquery

jQuery Sortable and Droppable


Here's a simplified version of Colin's solution.

The biggest difference is that this solution does not store the entire html of the sortable elements, but only the currently dragged item. It is then inserted into it's original position if the item is dropped on the droppable area.

Anyway, here's the code:

<!doctype html><html><head>    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.5/jquery-ui.js"></script><script type="text/javascript">$(document).ready(function() {    var dropped = false;    var draggable_sibling;    $("#sortable").sortable({        start: function(event, ui) {            draggable_sibling = $(ui.item).prev();        },        stop: function(event, ui) {            if (dropped) {                if (draggable_sibling.length == 0)                    $('#sortable').prepend(ui.item);                draggable_sibling.after(ui.item);                dropped = false;            }        }    });    $(".droppable").droppable({        activeClass: 'active',        hoverClass:'hovered',        drop:function(event,ui){            dropped = true;            $(event.target).addClass('dropped');        }    });});</script><style type="text/css">#sortable li{    clear:both;    float:left;}.droppable {    clear:both;    height:300px;    width:400px;    background-color:#CCC;}.droppable.active { background: #CFC; }.droppable.hovered { background: #CCF; }.dropped { background: #f52; }</style></head><body><ul id="sortable">    <li id="one">One</li>    <li id="two">Two</li>    <li id="three">Three</li>    <li id="four">Four</li>    <li id="five">Five</li>    <li id="six">Six</li></ul><div class="droppable">Drop Here</div><div class="droppable">Drop Here</div></body></html>

It still suffers the same problem as Colin's solution if an item is dragged into a new position in the list and then dropped outside both the <ul> and the droppable <div>. The item will then not reset but take the last position in the list (tested in Google Chrome). Anyway, this is easily solved with some mouse over detection or it may even be desirable behaviour.


As far as I could tell, the issue with the previous code I posted is that the drop function of the droppable was getting called before the mouseup of the sortable, and since I rewrote the list the sortable was getting angry (for lack of better terms). That's somewhat a guess.

Looking at the final code:

<html><head>    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.js"></script>    <script type="text/javascript">var dropped = false;var templateHtml;$(document).ready(function(){    setSortable();    $("#droppable").droppable({        activeClass: 'active',        hoverClass:'hovered',        accept:".drop",        drop:function(event,ui){            dropped = true;            //alert(ui.draggable.text());        }    });});function setSortable(){    $("#sortable").sortable({        stop:function(event,ui){            if(dropped){                $("#sortable").sortable('destroy').html(templateHtml);                dropped = false;                setSortable();            }        }    });    $("#sortable li").addClass("drop").bind('mousedown',function(){        templateHtml = $("#sortable").html();    });}    </script>    <style type="text/css">    #sortable li{        clear:both;        float:left;    }    #droppable {        clear:both;        height:300px;        width:400px;        background-color:#CCC;    }    #droppable.active {        background-color:#CFC;    }    #droppable.hovered {        background-color:#CCF;    }    </style></head><body><ul id="sortable"><li id="one">One</li><li id="two">Two</li><li id="three">Three</li><li id="four">Four</li><li id="five">Five</li><li id="six">Six</li></ul><div id="droppable">Drop Here</div></body>

Tons of quirks are involved here.

I tried to save the #sortable html on the start event of sortable, but that get's called after all of the ui-css gets applied, and ended up placing list elements in crazy places. So, I needed to use the mousedown function on the LIs.

The setSortable is in a function because the stop event rewrites the sortable, which is "recursive" I guess. Not sure the exact terminology here, but we can go with annoyingly confusing.

Fortunately, the droppable's drop function gets called before the sortables stop function, so I was able to set a global "dropped" variable that was used to see if the element was dropped.

I'm still surprised this wasn't easier to do, I thought jQuery would have functions to handle it a lot better. Perhaps I'm missing something?


Not exactly what you asked for, but I needed an initial container where the items could be dragged to another sortable container - and thought I should share it. This was how I achieved it (jQuery 1.8.3):

$("#sortable-list").sortable();$("#initial-list div").draggable({  revert: true,  revertDuration: 0});$("#initial-list").droppable({  accept: ".item-selected",  drop: function(e, ui) {    var dropped = ui.draggable;    var droppedOn = $(this);    var itemInOtherList = $(this).children("#" + dropped.attr('id'));    itemInOtherList.removeClass('ui-state-disabled').draggable({ disabled: false });    dropped.remove();  }});$("#sortable-list").droppable({  accept: ".item-initial",  drop: function(e, ui) {    var dropped = ui.draggable;    var droppedOn = $(this);    droppedOn.append(dropped.clone()      .removeAttr('style')      .removeClass("item-initial")      .addClass("item-selected"));    dropped.animate(dropped.data().origPosition, {      complete: function() {        $(this).addClass('ui-state-disabled').draggable({ disabled: true });      }    });  }});

JSFiddle: http://jsfiddle.net/Muskar/dwz4mvxa/8/