13

On ui draggables ( http://jqueryui.com/demos/droppable/#revert ) is it possible to revert a div if its inside one div and if not inside another one? for example like this

$( "#draggable" ).draggable({ revert: "valid", revert:"invalid" });

but that wouldn't work because of obvious reasons.. can I condition that though?.. for when its inside this droppable and not inside of that droppable?

Logan
  • 10,649
  • 13
  • 41
  • 54
  • Are the 'valid' and 'invalid' divs somehow nested? Because otherwise, surely it couldn't be in both divs simultaneously... :) – chrisfrancis27 May 20 '11 at 11:50
  • imagine a big box, and there are small boxes inside them. the small boxes are draggable and if i take them outside the big box they get reverted back, if i take them on to another small box it should also get reverted- but that's the part i can't do. I've tried making all the small boxes another droppable but since they are also inside the big box, it registers as valid so no revert occurs. Also tried greedy, which I couldn't make it work. – Logan May 20 '11 at 11:58
  • Assuming the `revert` attribute can take any selector, could you use something like `revert: ".outside, .valid > div.invalid"` ? Assuming the element that wraps 'valid' has a class of `outside`. – chrisfrancis27 May 20 '11 at 12:06
  • actually its either true or false (valid or invalid) so i guess there's no way of doing that like that. Do you know if there's a way around excluding the small boxes' area from the big box'es area and calling that valid? – Logan May 20 '11 at 12:15
  • Ah, I'm afraid not... Never actually used droppables! :) – chrisfrancis27 May 20 '11 at 12:17

1 Answers1

35

Your thinking was correct, you have to make the small boxes greedy droppables and handle the drop event on them. The tricky part is to cancel the drag operation.

By default, your draggables should start as revert:'invalid'. You don't have to do anything if they are dragged inside the big box, which in my example uses tolerance:'fit', so the small boxes must be completely inside to be accepted.

I have made the small boxes greedy droppables with tolerance:'touch', so if the dragged small box touches another small box, it will call the drag handler on it.

To cancel the drag operation from a drag handler, you can do a workaround of setting the dragged item to revert:true, which forces it to revert even though it was dropped on an accepting droppable. To make sure you can drag that small box again, on its drag stop event you have to reset revert:'invalid'. The stop event will fire on every successful drop and if it's reverting, it will fire after reverting has completed.

You can try out a live demo here: http://jsfiddle.net/htWV3/1/

HTML:

<div class="drop">
    <div class="drag"></div>
    <div class="drag"></div>
    <div class="drag"></div>
    <div class="drag"></div>
    <div class="drag"></div>
</div>

CSS:

.drop { display:inline-block; width:300px; height:200px; border:1px solid silver; background-color:whitesmoke; padding:10px; }

.drag { display:inline-block; width:30px; height:30px; border:1px solid silver; background-color:white; }

Javascript:

$('.drop').droppable({
    tolerance: 'fit'
});

$('.drag').draggable({
    revert: 'invalid',
    stop: function(){
        $(this).draggable('option','revert','invalid');
    }
});

$('.drag').droppable({
    greedy: true,
    tolerance: 'touch',
    drop: function(event,ui){
        ui.draggable.draggable('option','revert',true);
    }
});
DarthJDG
  • 16,511
  • 11
  • 49
  • 56
  • 1
    You are just gorgeous! Thank you so much! – Logan May 23 '11 at 06:03
  • Thanks for that. Is it possible to do it somehow with `helper: 'clone'` on the draggable element? – mreq Nov 14 '11 at 15:02
  • 1
    @PetrMarek: I'm not sure how you want it to work, in case you want the draggable to just snap in place of its clone on a valid drop, you can just add a drop handler to the main droppable container and set `ui.draggable`'s position to `ui.position`, where it was released. Here's a demo: http://jsfiddle.net/htWV3/231/ – DarthJDG Nov 14 '11 at 17:16
  • Hi sorry to bring this back up but how can you make this work better with a grid layout? for example my draggable snaps to `grid: [ 60, 60 ]` but when it reverts after being dropped it doesn't revert back to the grid position it is always off by a bit. – ChristopherStrydom Apr 12 '13 at 20:05
  • I just realized this only happens after the 2nd drag of the same element – ChristopherStrydom Apr 12 '13 at 20:17
  • Is it possible to make these divs resizable as well combined with the reverting functionality of drag & drop ? – Pieter_Daems Nov 15 '13 at 10:10
  • 1
    first of all thank you so much. i learned a lot from your answer, which was linked from a different question where the accepted answer was to use another plugin. i'll add that when using a grid i find it worked better for me to set the `tolerance` of the `greedy` droppable to `fit`. otherwise it wouldn't let me drop into the grid spots which touched the greedy droppable. – r14n Sep 05 '14 at 19:50