Revertir un objeto arrastrable de jQuery a su contenedor original en el evento out de droppable

Tengo un elemento arrastrable que, si no se deja caer en un droppable, se revertirá. Esto funciona bien hasta que un usuario suelta un elemento en el droppable. Si deciden que han cometido un error cada vez que retiran el dispositivo de arrastre, vuelve al área de despliegue. Preferiría que fuera y desactivar el arrastrable regrese a su contenedor original.

Mi código está debajo pero he proporcionado una muestra en jsFiddle .

HTML

I revert when I'm not dropped

Drop me here

JavaScript

 $(function() { $("#draggable").draggable({ revert: function(dropped) { var dropped = dropped && dropped[0].id == "droppable"; if(!dropped) alert("I'm reverting!"); return !dropped; } }).each(function() { var top = $(this).position().top; var left = $(this).position().left; $(this).data('orgTop', top); $(this).data('orgLeft', left); }); $("#droppable").droppable({ activeClass: 'ui-state-hover', hoverClass: 'ui-state-active', drop: function(event, ui) { $(this).addClass('ui-state-highlight').find('p').html('Dropped!'); }, out: function(event, ui) { // doesn't work but something like this ui.draggable.mouseup(function () { var top = ui.draggable.data('orgTop'); var left = ui.draggable.data('orgLeft'); ui.position = { top: top, left: left }; }); } }); }); 

 $(function() { $("#draggable").draggable({ revert : function(event, ui) { // on older version of jQuery use "draggable" // $(this).data("draggable") // on 2.x versions of jQuery use "ui-draggable" // $(this).data("ui-draggable") $(this).data("uiDraggable").originalPosition = { top : 0, left : 0 }; // return boolean return !event; // that evaluate like this: // return event !== false ? false : true; } }); $("#droppable").droppable(); }); 

No estoy seguro de si esto funcionará para su uso real, pero funciona en su caso de prueba, actualizado en http://jsfiddle.net/sTD8y/27/ .

Acabo de hacerlo de manera que la reversión incorporada solo se use si el elemento no se ha caído antes. Si se ha eliminado, la reversión se realiza de forma manual. Podrías ajustar esto para animar a una compensación calculada verificando las propiedades de CSS reales, pero te dejaré jugar con eso porque mucho depende del CSS del arrastrable y de la estructura DOM que lo rodea.

 $(function() { $("#draggable").draggable({ revert: function(dropped) { var $draggable = $(this), hasBeenDroppedBefore = $draggable.data('hasBeenDropped'), wasJustDropped = dropped && dropped[0].id == "droppable"; if(wasJustDropped) { // don't revert, it's in the droppable return false; } else { if (hasBeenDroppedBefore) { // don't rely on the built in revert, do it yourself $draggable.animate({ top: 0, left: 0 }, 'slow'); return false; } else { // just let the built in revert work, although really, you could animate to 0,0 here as well return true; } } } }); $("#droppable").droppable({ activeClass: 'ui-state-hover', hoverClass: 'ui-state-active', drop: function(event, ui) { $(this).addClass('ui-state-highlight').find('p').html('Dropped!'); $(ui.draggable).data('hasBeenDropped', true); } }); }); 

Si desea revertir el elemento a la posición de origen si no se deja caer dentro de un elemento #droppable , simplemente guarde el elemento padre original del elemento que se puede arrastrar al inicio del script (en lugar de la posición), y si verifica que no es soltado en #droppable , luego simplemente restaure el padre de #draggable a este elemento original.

Entonces, reemplace esto:

 }).each(function() { var top = $(this).position().top; var left = $(this).position().left; $(this).data('orgTop', top); $(this).data('orgLeft', left); }); 

con este:

 }).each(function() { $(this).data('originalParent', $(this).parent()) }); 

Aquí, tendrá el elemento padre original del arrastrable. Ahora, debes restaurar su padre en un momento preciso.

drop se invoca cada vez que se arrastra el elemento desde el elemento desplegable, no desde el stop. Por lo tanto, está agregando una gran cantidad de devoluciones de llamadas de eventos. Esto es incorrecto, porque nunca se limpia el evento mouseup . Un buen lugar donde puede conectar una callback y verificar si el elemento se #droppable dentro o fuera del elemento #droppable , se revert , y lo está haciendo en este momento, por lo tanto, simplemente elimine la drop llamada perdida.

Cuando el elemento se descarta y necesita saber si debe revertirse o no, usted está seguro de que no tendrá ninguna otra interacción del usuario hasta que se inicie el nuevo arrastre. Entonces, usando la misma condición que está utilizando para saber si debería revertirse o saber, reemplacemos esta alert con un fragmento de código que: restaure el elemento padre al div original y restablezca la posición originalPosition de las draggable internas que se pueden draggable . La propiedad originalPosition configura en el momento de _mouseStart , por lo tanto, si cambia el propietario del elemento, debe restablecerlo para que la animación de reversión vaya al lugar adecuado. Entonces, establezcamos esto en {top: 0, left: 0} , haciendo que la animación vaya al punto de origen del elemento:

 revert: function(dropped) { var dropped = dropped && dropped[0].id == "droppable"; if(!dropped) { $(this).data("draggable").originalPosition = {top:0, left:0} $(this).appendTo($(this).data('originalParent')) } return !dropped; } 

¡Y eso es! Puede verificar esto trabajando aquí: http://jsfiddle.net/eUs3e/1/

Tenga en cuenta que, si en cualquier actualización de la interfaz de usuario de jQuery, el comportamiento de revert o la revert originalPosition cambia, deberá actualizar su código para que funcione. Manten eso en mente.

Si necesita una solución que no haga llamadas a las partes internas de ui.draggable, puede hacer que su body un elemento que se pueda eliminar con la opción greedy definida como false . Tendrás que asegurarte de que los elementos de tu body ocupen toda la pantalla.

¡Buena suerte!

En caso de que alguien esté interesado, esta es mi solución al problema. Funciona de forma completamente independiente de los objetos arrastrables, mediante el uso de eventos en el objeto desplegable en su lugar. Funciona bastante bien:

 $(function() { $(".draggable").draggable({ opacity: .4, create: function(){$(this).data('position',$(this).position())}, cursor:'move', start:function(){$(this).stop(true,true)} }); $('.active').droppable({ over: function(event, ui) { $(ui.helper).unbind("mouseup"); }, drop:function(event, ui){ snapToMiddle(ui.draggable,$(this)); }, out:function(event, ui){ $(ui.helper).mouseup(function() { snapToStart(ui.draggable,$(this)); }); } }); }); function snapToMiddle(dragger, target){ var topMove = target.position().top - dragger.data('position').top + (target.outerHeight(true) - dragger.outerHeight(true)) / 2; var leftMove= target.position().left - dragger.data('position').left + (target.outerWidth(true) - dragger.outerWidth(true)) / 2; dragger.animate({top:topMove,left:leftMove},{duration:600,easing:'easeOutBack'}); } function snapToStart(dragger, target){ dragger.animate({top:0,left:0},{duration:600,easing:'easeOutBack'}); } 

Está relacionado con el origen revertido: para establecer el origen cuando se arrastra el objeto: simplemente use $(this).data("draggable").originalPosition = {top:0, left:0};

Por ejemplo: uso así

  drag: function() { var t = $(this); left = parseInt(t.css("left")) * -1; if(left > 0 ){ left = 0; t.draggable( "option", "revert", true ); $(this).data("draggable").originalPosition = {top:0, left:0}; } else t.draggable( "option", "revert", false ); $(".slider-work").css("left", left); } 

He encontrado otra manera fácil de lidiar con este problema, solo necesitas el atributo “connectToSortable:” para arrastrarlo como el siguiente código:

 $("#a1,#a2").draggable({ connectToSortable: "#b,#a", revert: 'invalid', }); 

PD: más detalles y ejemplos
Cómo mover objetos arrastrables entre el área fuente y el área objective con jQuery