My solution is similar to Likwid_T's, except it uses the droppable drop
event as well as maintaining the links between draggables and droppables instead of droppable's out
event. I think the problem with using out
is that it is fired even when a draggable is dragged over an already "full" droppable and then "out" of it.
droppable({
drop: function(event, ui) {
var $droppable = $(this);
var $draggable = ui.draggable;
// If the draggable is moved from another droppable, unlink it from the old droppable
var oldDropped = $draggable.data('dropped');
if(oldDropped) {
$draggable.data('dropped', null);
oldDropped.data('dragged', null);
}
// Link the draggable and droppable
$draggable.data('dropped', $droppable);
$droppable.data('dragged', $draggable);
},
accept: function() {
// Only accept if there is no draggable already associated
return !$(this).data('dragged');
}
});
A related feature is that one dragging one item over a droppable that already has a draggable, the old one would get replaced and revert to its initial position. This is how I do it:
droppable({
drop: function(event, ui) {
var $droppable = $(this);
var $draggable = ui.draggable;
// Reset position of any old draggable here
var oldDragged = $droppable.data('dragged');
if(oldDragged) {
// In the CSS I have transitions on top and left for .ui-draggable, so that it moves smoothly
oldDragged.css({top: 0, left: 0});
oldDragged.data('dropped', null);
}
// If the draggable is moved from another droppable, unlink it from the old droppable
var oldDropped = $draggable.data('dropped');
if(oldDropped) {
$draggable.data('dropped', null);
oldDropped.data('dragged', null);
}
// Link the draggable and droppable
$draggable.data('dropped', $droppable);
$droppable.data('dragged', $draggable);
},
});