开发者

jQuery UI - Droppable only accept one draggable

开发者 https://www.devze.com 2023-01-20 09:33 出处:网络
I\'m making an app, that is using one droppable div and a few draggable divs. How can I make the droppable to not accept more than one draggable div? I Googled, but didn\'t find any workaroun开发者_如

I'm making an app, that is using one droppable div and a few draggable divs. How can I make the droppable to not accept more than one draggable div? I Googled, but didn't find any workaroun开发者_如何学God.


A workaround came up in mi mind. How can i check is there's dropped element in this droppable div? If it's busy then revert this draggable, which is trying to be dropped


OK found a nice solution for this, essentially on 'drop' I set the droppable to only accept the item which has been dragged into it.

When you 'disable', the 'out' event that you need to re-initialize isn't available anymore, so instead I just switched the eligible items around.

Then it's possible for me to use the OUT event to re-accept all draggable items and because nothing else is accepted the OUT won't be triggered by other draggables:

$(".drop-zone").droppable({
    drop: function(event, ui) { 
        $(this).droppable('option', 'accept', ui.draggable);
    },
    out: function(event, ui){
        $(this).droppable('option', 'accept', '.drag-item');
        }   
    });
});


You can destroy the .droppable() widget after the first drop, like this:

$(".droppable").droppable({
    drop: function( event, ui ) {
        $(this).droppable("destroy");
    }
});

You can try out a demo here.


Easy Peasey. Just enable all the .drop-zone's when hovered over them, and then check if the currently hovered .drop-zone contains a draggable element

$('.drop-zone').droppable({
  over: function(event, ui) {

    // Enable all the .droppable elements
    $('.droppable').droppable('enable');

    // If the droppable element we're hovered over already contains a .draggable element, 
    // don't allow another one to be dropped on it
    if($(this).has('.draggable').length) {
        $(this).droppable('disable');
    }
  }
});


I spend many hours to figure it out and finally it works for me like this:

$( ".drop-zone" ).droppable({
    classes: {
        "ui-droppable-active": "ui-state-active",
        "ui-droppable-hover": "ui-state-hover"
    },
    accept: function( draggable ){
        if (!$(this).hasClass('dropped') || draggable.hasClass('dropped')){
            return true;
        }
        return false;
    },
    drop: function( event, ui ) {
        $(this).addClass('dropped');
        ui.draggable.addClass('dropped');
    },
    out: function( event, ui ){
        $(this).removeClass('dropped');
        ui.draggable.removeClass('dropped');
    }
});


This solution solves a major bug in Likwid_T's answer.

$('.draggable').draggable({
  start: function(ev, ui) {  
    $('.ui-droppable').each(function(i, el) {
      if (!$(el).find('.ui-draggable').length) $(el).droppable('enable');
    });
  }
});

$('.droppable').droppable({
  drop: function(ev, ui) {
    $(ev['target']).droppable('disable');
  }
});


How about this:

$(".drop-zone").droppable({
    accept: function(draggable) {
        return $(this).find("*").length == 0;
    });
});

This way the accept funcion return true only when no elements have been dropped yet.


To enable it, use the option: $(".selector").droppable({ disabled: **false** });


You could also do it the other way around, by reverting the draggable when the droppable has a certain class or attribute (building on this example: https://stackoverflow.com/a/3418306/1005334).

So for example, using the rel attribute (you could also use class or something else), for the droppable:

$('.drop-zone').droppable({
    drop: function () {
        drop.attr('rel', 'filled');
    }
});

And the draggable:

$('.draggable').draggable({
    revert: function (droppable) {

        if (droppable.attr('rel') == 'filled') {
            return true;
        }
    }
});


My solution is similar to Likwid_T's, except it uses the droppable drop event as well as maintaining the links between draggables and droppables instead of droppable's out event. I think the problem with using out is that it is fired even when a draggable is dragged over an already "full" droppable and then "out" of it.

droppable({
  drop: function(event, ui) {
    var $droppable = $(this);
    var $draggable = ui.draggable;

    // If the draggable is moved from another droppable, unlink it from the old droppable
    var oldDropped = $draggable.data('dropped');
    if(oldDropped) {
      $draggable.data('dropped', null);
      oldDropped.data('dragged', null);
    }

    // Link the draggable and droppable
    $draggable.data('dropped', $droppable);
    $droppable.data('dragged', $draggable);
  },
  accept: function() {
    // Only accept if there is no draggable already associated
    return !$(this).data('dragged');
  }
});

A related feature is that one dragging one item over a droppable that already has a draggable, the old one would get replaced and revert to its initial position. This is how I do it:

droppable({
  drop: function(event, ui) {
    var $droppable = $(this);
    var $draggable = ui.draggable;

    // Reset position of any old draggable here
    var oldDragged = $droppable.data('dragged');
    if(oldDragged) {
      // In the CSS I have transitions on top and left for .ui-draggable, so that it moves smoothly
      oldDragged.css({top: 0, left: 0});
      oldDragged.data('dropped', null);
    }

    // If the draggable is moved from another droppable, unlink it from the old droppable
    var oldDropped = $draggable.data('dropped');
    if(oldDropped) {
      $draggable.data('dropped', null);
      oldDropped.data('dragged', null);
    }

    // Link the draggable and droppable
    $draggable.data('dropped', $droppable);
    $droppable.data('dragged', $draggable);
  },
});
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号