4

I have a page with a large number of jquery UI draggables and a large number of droppables. I wanted some custom logic to determine if a droppable would accept a certain draggable so i coded up some logic in the "accept" event of the droppable. It worked but one thing i noticed is that this event fires right when you start dragging an item and it fires for all droppables at that time.

That seems very inefficient. I was thinking it would right for a single droppable right when you hover but that doesn't seem to be the case. Is there any reason why you think its coded this way (as it seems very inefficent as if i have 100 droppables, its firing 100 times even if if the user only tries to drag into a single droppable) and also, is there any way to have the behavior that i want .

I thought of putting logic into the drop event to simple do my check and delete the draggable item (to simulate not accepting it) but the issue there is that you don't get any of that nice "revert' animation so it looks a bit inconsistent (compared the what you would see if you based on the accept event)

Any thoughts on how I can still plug in some custom logic but not waste the cycles to fire on every single droppable right when i move something (and still get the revert animation) ?

leora
  • 188,729
  • 360
  • 878
  • 1,366
  • 4
    I see an accept option for the droppable but no accept event as you describe. – j08691 Nov 27 '13 at 23:37
  • Isn't the accept "event" just a filter to determine if some item should be draggable? – megawac Nov 27 '13 at 23:40
  • @megawac - yes, but if i have 100 droppables, it fires 100 times when i drag one draggable . . maybe i am overestimating the overhead of that – leora Nov 27 '13 at 23:43
  • 2
    Can you create a small fiddle that demonstrates the event that gets fired over and over? I am not aware of the `accept` event your describing. Thanks – Trevor Nov 28 '13 at 01:57
  • @Trevor - its not firing over and over for one droppable but rather one time for each droppable – leora Nov 28 '13 at 02:38
  • 2
    Can you provide a [jsfiddle](http://jsfiddle.net)? – Dom Nov 28 '13 at 03:27
  • did you try `scope` as described [here](http://api.jqueryui.com/droppable/#option-accept) – SoftSan Nov 28 '13 at 07:21
  • I know this is old, but just wanted to mention that its doing that in order to highlight the droppables that can accept the current element (see `activeClass` option). – riv Mar 27 '15 at 09:52

2 Answers2

2

I don't think the overhead is that great, but if you do want an alternative solution: Grab the start position of the draggable element on start and save it in data() of the element. In the drop routine do the logic check. If it failed, animate the element back to the startPosition. This will ensure the events are specific to only the element being dragged and the droppable.

http://jsfiddle.net/samnunnally/sgy29/2/

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>droppable demo</title>
<link rel="stylesheet"
    href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
<style>

#notAllowed {
    width: 100px;
    height: 100px;
    background: #ccc;
}

#allowed {
    width: 100px;
    height: 100px;
    background: red;
}

#droppable {
    position: absolute;
    left: 250px;
    top: 0;
    width: 125px;
    height: 125px;
    background: #999;
    color: #fff;
    padding: 10px;
}
</style>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
</head>
<body>
    <div id="droppable" >Drop here</div>
    <div id="notAllowed" >not allowed to drop</div>
    <div id="allowed">allowed to drop</div>
    <script>

        function capturePosition(event, ui) {

            ui.helper.data( 'startPosition', ui.position);
        }
$( document ).ready(function() {

        $("#notAllowed").draggable({
            start : capturePosition
        });

        $("#allowed").draggable({
            start : capturePosition
        });

        $("#droppable").droppable({
            drop : function(event, ui) {
                //do logic to determine if it can be dropped
                if(ui.draggable.attr('id') == 'notAllowed'){
                   var startPosition = ui.draggable.data('startPosition');
                   ui.draggable
                    .animate({ 
                    'left': 
                    '+='+ (startPosition.left - ui.draggable.offset().left + $('body').offset().left) + 'px' }, 
                    'slow' )

                    .animate({
                    'top': 
                    '+='+ (startPosition.top - ui.draggable.offset().top + + $('body').offset().top) + 'px' }, 
                    'slow' );
                }
            }
        });
}
    </script>
</body>
</html>
Sam Nunnally
  • 2,291
  • 2
  • 19
  • 30
1

perhaps this can help :

See this fiddle code (simulate not accepting or accepting)

http://jsfiddle.net/penjepitkertasku/VYQcW/8/

This will ensure the events are specific to only the element being dragged and the droppable

$(function() {

    //global
    var dname = "";

    //position
    var d1left = $("#droppable1").position().left + 20;
    var d1top = $("#droppable1").position().top + 30;
    $("#draggable1").css({'position': 'absolute', 'left': (d1left*1) + 'px', 'top': ((d1top*1)+15) + 'px'});
    $("#draggable2").css({'position': 'absolute', 'left': (d1left*1) + 'px', 'top': ((d1top*2)+20) + 'px'});

    //checkbox
    $(".checkbox").change(function(){
        $(this).next().text(this.checked === true ? 'invalid' : 'valid');
    });

    //drag
    $("#draggable1").draggable({ revert: 'valid' });
    $("#draggable2").draggable({ revert: 'invalid' });

    //event
    $(".draggable").mousedown(function(){
        dname = this.id;
    });

    //drop
    $("#droppable1, #droppable2").droppable({
        activeClass: 'ui-state-hover',
        hoverClass: 'ui-state-active',
        drop: function(event, ui) {
            console.log(dname);
            var _checboxid = $("#" + dname + ' input[type=checkbox]');
            var _revert = _checboxid.is(':checked') ? 'invalid' : 'valid';
            $("#" + dname).draggable("option", "revert", _revert);
        },
        out: function( event, ui ) {
            $("#" + dname).draggable("option", "revert", "invalid");
        }
    });
});