Bring jQuery drag object back to source container on droppable event

I have a draggable element that, if it does not fall into a droppable, will return. This works well until the user drops the item in the drop-down list. If they decide that they made a mistake at any time when they pulled out by dragging, it will return to droppable. I would prefer that turning off and turning off draggable back to the original container.

My code is below, but I provided a sample in jsFiddle .

HTML

<div id="origin"> <div id="draggable" class="ui-widget-content"> <p>I revert when I'm not dropped</p> </div> </div> <div id="droppable" class="ui-widget-header"> <p>Drop me here</p> </div> 

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 }; }); } }); }); 
+58
jquery jquery-ui
Apr 20 '11 at 19:00
source share
5 answers
 $(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(); }); 
+117
May 01 '11 at 13:52
source share

I'm not sure if this will work for your actual use, but it works in your test case - updated at http://jsfiddle.net/sTD8y/27/ .

I just made it so that the inline return is used only if the item has not been discarded before. If it has been reset, the return is manual. You can tweak this to spice up some calculated offset by checking the actual CSS properties, but I will let you play with it because a lot depends on the CSS of the dragged object and its DOM structure.

 $(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 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); } }); }); 
+7
Apr 25 '11 at 17:44
source share

If you want to return the element to its original position, if it did not fall inside the #droppable element, just save the original parent element of the drag and drop at the beginning of the script (instead of the position) and if you make sure that it does not fall into #droppable , then just restore the parent element #draggable for this source element.

So replace this:

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

with this:

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

Here you will have the original parent being dragged. Now you need to restore its parent at some point.

drop is called every time an item is pulled from a droppable, not at a stop. This way you add a lot of event callbacks. This is wrong because you never clear the mouseup event. A good place where you can intercept the callback and check if the element has been deleted inside or outside the #droppable element is revert , and you are doing it right now, so just remove the drop callback.

When an element is discarded and it needs to know whether to return it or not, you know for sure that you will not have any other interaction with the user until the start of a new drag and drop. Thus, using the same condition that you use to know whether to return it or to know, replace this alert piece of code that: restores the parent element to the original div and discards the originalPosition from the draggable internals. The originalPosition project is set during _mouseStart , so if you change the owner of an element, you must reset it so that the return animation returns to the right place. Thus, set the value {top: 0, left: 0} so that the animation moves to the starting point of the element:

 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; } 

And this! You can check it here: http://jsfiddle.net/eUs3e/1/

Note that if the revert or originalPosition behavior changes in any jQuery UI update, you need to update the code to work. Keep in mind that.

If you need a solution that does not use calls to internal ui.draggable elements, you can make your body element with the option of deleting with the greedy option defined as false . You will need to make sure your body elements are displayed in full screen.

Good luck

+3
Apr 25 '11 at 19:15
source share

In case anyone is interested, here is my solution to the problem. It works completely regardless of the Draggable objects, instead uses the events in the Droppable object. It works quite well:

 $(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'}); } 
+3
May 08 '13 at 20:07
source share

This is due to the return start: set the origin when the object is dragged: just use $(this).data("draggable").originalPosition = {top:0, left:0};

For example: I use like this

  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); } 
+1
Nov 11 '12 at 13:15
source share



All Articles