jQuery draggable + droppable: how to snap dropped element to dropped-on element
Asked Answered
C

7

31

I have my screen divided into two DIVs. In the left DIV I have a few 50x50 pixel DIVs, in the right DIV I have an empty grid made of 80x80 LIs. The DIVs on the left are draggable, and once dropped on a LI, they should snap to center of that LI.

Sounds simple, right? I just don't know how to get this done. I tried by manipulating the dropped DIV's top and left CSS properties to match those of the LI they're dropped into, but the left and top properties are relative to the left DIV.

How can I best have the dropped element snap to the center of the element it's dropped into? That's gotta be simple, right?

Edit: I'm using jQuery UI 1.7.2 with jQuery 1.3.2.

Edit 2: For whoever else has this problem, this is how I fixed it:

I used Keith's solution of removing the dragged element and placing it inside the dropped-on element in the drop callback of the droppable plugin:

function gallerySnap(droppedOn, droppedElement)
{
    $(droppedOn).html('<div class="drop_styles">'+$(droppedElement).html()+'</div>' );
    $(droppedElement).remove();
}

I don't the dropped element to be draggable again, but if you do, just bind draggable to it again.

For me this method also solved the problem I had when positioning the dropped elements (which would be relative to the left DIV) and scrolling inside the second DIV. (Elements would remain fixed on page, now they scroll along).

I did play with the snap options to make it look good while dragging, so thanks to karim79 for that suggestion.

I probably won't win any Awesome Code prizes with this, so if you see room for improvement, please share!

Congius answered 10/8, 2009 at 12:45 Comment(0)
S
16

I had a similar problem - I worked around it by manually removing the dragged element from its old parent and adding it to the dropped on element.

Stirling answered 10/8, 2009 at 12:55 Comment(1)
Thanks, I was thinking about this solution. The snap-to solution by karim79 isn't totally solving my problem so I'll give this a try.Congius
R
75

I found that Keith's method worked for me. Since his answer doesn't include an example implementation, I'll post mine:

$('.dropTarget').droppable({
    drop: function(ev, ui) {
        var dropped = ui.draggable;
        var droppedOn = $(this);
        $(dropped).detach().css({top: 0,left: 0}).appendTo(droppedOn);
    }
});

or, slightly more concisely:

$('.dropTarget').droppable({
    drop: function(ev, ui) {
        $(ui.draggable).detach().css({top: 0,left: 0}).appendTo(this);
    }
});
Retinite answered 14/5, 2011 at 17:50 Comment(1)
IMO this should be the default behaviour of draggable & droppable.Castanon
S
16

I had a similar problem - I worked around it by manually removing the dragged element from its old parent and adding it to the dropped on element.

Stirling answered 10/8, 2009 at 12:55 Comment(1)
Thanks, I was thinking about this solution. The snap-to solution by karim79 isn't totally solving my problem so I'll give this a try.Congius
N
11

Thanks for your post - it helped me in the right direction. I find it a bit cleaner to set the position properties of the draggable object instead of messing with the HTML code. This sets the position to the top left corner of the droppable object, but you can modify to have it centered as well.

drop: function(event, ui) {
   $(ui.draggable).css('top', $(this).position().top);
   $(ui.draggable).css('left', $(this).position().left);
}
Nealah answered 28/3, 2010 at 3:50 Comment(1)
+1 because the accepted answer will break functionality and layout in lots of cases. Especially if revert is used outside of the droppable area. While this is the right approach, position() won't do the job if the droppable element is not a sibling of the draggable. On complex websites with huge DOMs, the positioning will be wrong.Fieldsman
F
3

I found that when you do the drag, jQuery UI adds an inline to tell you where you dropped it. Below is a sample of the code that I used to snap it into place

$('.droppable').droppable({ drop: function(ev, ui) { 
    //Get Details of dragged and dropped
    var draggedclass = ui.draggable.attr('class'),
        droppedclass = 'class' + $(this).attr('name').toLowerCase();

    //update the classes so that it looks od.       
    ui.draggable.removeClass(draggedclass).addClass(droppedclass);  
    ui.draggable.removeAttr('style');
});
Flocculent answered 10/8, 2009 at 12:53 Comment(0)
C
3
$("form li").draggable({snap:'.ui-droppable', snapMode:'inner', revert:true});
$('.drop').droppable({drop:function(ev, ui)
                           {$(ui.draggable).appendTo($(this))
                                           .css({position:'static', left:'0px', top:'0px'})
                                           .draggable('option', 'disabled', false)
                                           .css({position:'relative'});
                           }
                     }
                    );
Chiekochien answered 20/12, 2010 at 8:16 Comment(0)
S
0

If the divs on the left are actually in the lis on the right (which you can confirm with Firebug), and if the lis are all in a ul (as they should be), try one or both of the following:

ul#right_div {
  text-align: center;
}

ul#right_div li {
  text-align: center;
}
Suburb answered 10/8, 2009 at 12:52 Comment(2)
I checked with FireBug, but the dropped DIVs are still part of the left DIV, they have not moved in the HTML or the DOM tree, it seems?Congius
Well, I'm pretty sure Firebug updates based on javascript events, but you may want to check me on that. I know the Web Dev toolbar has a "view generated source" option as well. Are you using JQuery UI to do this? or something else?Suburb
S
0

based on Barry's code, what if we d like to add an option with an "x" button the element to be detached again from the new parent and be reattached to the initial?

i thought sth like this, but didn't seem to work.. to make some sort of a variable that hold initial state

var flag;
$('.draggable-div').draggable({
    revert: 'invalid',
    stop: function(){
        $(this).draggable('option','revert','invalid');
        $(this).find('.undo').show();
    flag=$(this).parent();
    }
});


$('.draggable-div').find('.undo').click(function(i, e) {
    var $div = $(this).parent();
$($div).detach().appendTo(flag);
 }

sth is definately wrong but i don't know if you can get the concept... just being able to reverse whatever you have dropped to their initial state.

Skyrocket answered 4/4, 2013 at 14:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.