jQuery Sortable and Droppable Puzzle

I’m having a hard time getting jQuery’s Sortable and Droppable to work together. The goal is to have a sortable list of elements, that can also be dragged away to a droppable div. If an element is dragged to the droppable div, the list should return to the position it was in before the element was clicked.

Below is code that makes it work (easy save as .html to see in your browser) but it throws an error in the error console. I’d prefer to have code that doesn’t throw any errors, any suggestions?


<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 templateHtml;
$(document).ready(function(){

	$("#sortable li").addClass("drop").bind('mousedown',function(){
		templateHtml = $("#sortable").html();
	});
	$("#sortable").sortable();
	$("#droppable").droppable({
		activeClass: 'active',
		hoverClass:'hovered',
		accept:".drop",
		drop:function(event,ui){
			$("#sortable").sortable('destroy').html(templateHtml);
			$("#sortable").sortable();
			$("#sortable li").addClass("drop").bind('mousedown',function(){
				templateHtml = $("#sortable").html();
			});
			alert(ui.draggable.text());
		}
	});
	
});

	</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>

I’d call that a bug in jqueryui.

It looks like a delayed mouseup event is responsible.

Google Chrome allows you to place a breakpoint on the appropriate line, so that you can investigate the call stack and scope variables.

The line that’s causing the issue is:


$("#sortable").sortable('destroy').html(templateHtml);

Woooo, all sorts of confusing getting this set up, but I figured it out.

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?

e39m5