acorello
acorello

Reputation: 4653

Prevent drop event to propagate to the parent if an inner droppable does not accept the draggable

I have a droppable div#space and a collection of draggable li.element. When I drag an li.element in the div#space a new ul.group is created, the li.element is appended to the ul.group and finally the ul.group is made droppable.

Given

I would like these elements to behave as follows:

  1. when an li.element is dropped in the ul.group that does not contain it, the li.element will be added to the ul.group
  2. when an li.element is dropped in the ul.group that does contain it, the li.element will be reverted to it's original position
  3. when an li.element is dropped in the div#space (outside a ul.group) an new group is created as mentioned above.

The problem is that when an element is dropped on an ul.group that does not accept it the parent div#space will catch the event and will create a new group.

The ul.grpup has an accept function that checks if the droppable is an element and if it's not already been dropped.

newULGroup.droppable({
    drop: ...
    accept: function(ui)
    {
        var isElement = ui.hasClass('element')
        var elementId = '.' + ui.data().id
        var isAlreadyPresent = $(this).find(elementId).length > 0
        return isElement && (!isAlreadyPresent)
    },
    greedy: true
});

How would you implement this behaviour if not by accepting everything and ignoring things in the drop handler? (I haven't tried but I guess that would work)


Edit

An attempt to fix it

I patched the jquery-ui-1.8.4.js to pass the event to the accept function. Now I can stop the propagation with $(event).stopPropagation(), but I have to animate manually the draggable helper back to its original position to simulate the revert, then remove it from the dom.

accept: function(ui, event)
{
    var isCriteria = ui.hasClass('criteria')
    var criteriaId = '.' + ui.data().id
    var isAlreadyPresent = $(this).find(criteriaId).length > 0
    var accepted = isCriteria && (!isAlreadyPresent)
    if (event && !accepted)
    {
        $(event.target).animate(ui.offset(), {complete: function() {$(this).remove()}})
        $(event).stopPropagation()
    }
    return accepted
}

Unfortunately it seems to destroy the draggable behaviour from the original element as well. Why, oh why?

Upvotes: 1

Views: 2569

Answers (1)

achekh
achekh

Reputation: 1106

I managed to achieve similar behavior, although using undocumented jQuery.ui feature.

If you look into the jquery.ui.draggable implementation (at least for v1.8.16) the revert option can also be a function. It has this being the draggable element, single parameter which is the droppable who accepted the drop, and return value is a boolean to indicate whether to revert the draggable or not.

So in your case you need to

  1. have your div#space and ul.group always accept drops and be greedy
  2. do the check in the ul.group drop handler and processes item only if it's not in the group yet
  3. do similar check in the draggable revert function and revert itself if it's already in the group.

Upvotes: 1

Related Questions