10

RESOLVED

So I studied sortable() with the connectWith attribute a bit more and found the solution was simpler than I expected, but I was thinking about it sort of backwards because of draggable() and droppable().

Here's the fiddle: http://jsfiddle.net/DKBU9/12/


Original question follows:

This is an extension of a previous SO question that I felt should be kept separate.

The original question is here: jQuery UI - Clone droppable/sortable list after drop event. It regards reinitializing the droppable handler on cloned elements.

On top of that though, I'm trying to allow the elements that have been dragged from the initial list to be sorted within the droppable lists.

Here is a fiddle essentially illustrating the current behaviour as a result of the original question: http://jsfiddle.net/DKBU9/7/

And the required code because SO likes to complain and bloat these posts:

HTML

<ul id="draggables">

    <li>foo1</li>
    <li>foo2</li>
    <li>foo3</li>

</ul>

<ul class="droppable new">

</ul>

JS

$(function() {

    $('#draggables > li').draggable({
        appendTo: 'document',
        revert: 'invalid'
    });

    $('.droppable > li').draggable({
        appendTo: 'document',
        revert: 'invalid'
    });

    $('#draggables').droppable({
        accept: '.droppable > li',
        drop: function(event, ui) {
            ui.draggable.detach().css({top: 0,left: 0}).appendTo($(this));
            cleanUp();
        }
    });

    initDrop($('.droppable'));

});

function initDrop($element) {

    $element.droppable({
        accept: function(event, ui) {
            return true;
        },
        drop: function(event, ui) {
            if($(this).hasClass('new')) {
                var clone = $(this).clone();                
                $(this).after(clone);
                $(this).removeClass('new');
                initDrop( clone );
            }
            ui.draggable.detach().css({top: 0,left: 0}).appendTo($(this));  
            cleanUp();
        }
    }).sortable({
        items: 'li',
        revert: false,
        update: function() {
            cleanUp();   
        }
    });

}

function cleanUp() {

    $('.droppable').not('.new').each(function() {
        if($(this).find('li').length == 0) {
            $(this).remove();
        }
    });

}

Reference question: Jquery UI combine sortable and draggable

I've been trying to use this SO question to resolve my issue as the result is exactly what I'm trying to achieve, specifically the last fiddle provided in the comments of the accepted answer, but I can't seem to figure out how to adapt it to my code considering the minor differences. For example, the fiddle in that question clones the draggable rather than dragging the actual element and unlike mine doesn't allow elements to be dragged back into the initial list.

So any help to try to get that result for my code would be greatly appreciated.

Also, in that example, the accept attribute is set as a function to return true. What is the reason for this? Doesn't that just mean it will accept anything, or any draggable element?

EDIT: I read a couple answers that just used sortable() with the connectWith attribute, but for some reason I didn't think it did what I needed to, partly because I didn't want the initial list to be sortable as well. However, using sortable() alone seems to get the functionality I'm after but I just haven't yet adapted all the event handlers.

2
  • My answer will allow the initial list not to be sortable as per your edit, but if you are OK with it being sortable, then I think you should go with yours as it is simpler. Commented Jan 8, 2014 at 8:10
  • Yeah, I really appreciate your time and effort, but I will stick with this as it isn't really a big deal that the initial list is sortable. Commented Jan 8, 2014 at 8:32

1 Answer 1

5

See this fiddle.

It will make the items droppable and sortable in the new lists, but not in the initial (as per your edit).

There are a few tricks:

1) Starting with the draggable, I added a new function because we need to leave a placeholder (use a clone) so that the containers don't jump about during drag and we need to initialise the new element as draggable.

function initDraggable($element)
{
    $($element).draggable({
        connectToSortable: '.droppable',     
        revert: function(droppableObj)
        {
            if(droppableObj === false)
             {                
                $(this).removeClass('dragging');
                return true;
             }
             else
             {              
                return false;
             }
        },
        helper: 'clone',
        start: function(e, ui)
        {           
            $draggingParent = $(this).parent();
           $(this).addClass('dragging');
        }
    });
}

Note the connectToSortable: with .droppable (As a cleanup, I think you should change droppable to sortable);

Note also revert: - this gives us an opportunity to remove the 'dragging' class (which makes the original (cloned) element invisible) on revert.

Finally, there is start which adds the 'dragging' class which makes the original (cloned) element invisible during drag. It also sets the $draggingParent which is used to check whether the item is being dragged from #draggables or .droppables.

2) Then there is the droppable() for the #draggables:

 $("#draggables").droppable({    
    drop: function(ev, ui) {
        if($draggingParent[0] === $(ui.helper).parent()[0])
        {
            //dragged from draggables to draggables
            $(ui.draggable).removeClass('dragging');
        }
        else
        {            
            if(ui.draggable.parent())
            var $clone = $(ui.draggable).clone();
            $(ui.draggable).remove();
            $clone.removeClass();
            $clone.removeAttr('style');
            initDraggable($clone);
            $(this).append($clone);        
        }
    }

Note that the drop handler checks whether this element is dropped from #draggables or .droppables and acts accordingly.

3) Finally, as you already mentioned, we really want the droppable to be sortable:

function initDroppableSort($element) {       
    $element.sortable({
       items: 'li',
        connectWith: ".droppable,#draggables",
        revert: true,
       stop: function(event, ui) {
           $(ui.item).removeClass('dragging');
            $('.dragging').remove();
            if($(this).hasClass('new')) {
                var clone = $(this).clone();                
                clone.empty();
                $(this).after(clone);
                $(this).removeClass('new');
                initDroppableSort( clone );
            }            
            cleanUp();
        }
    });

}

Note the connectWith and specifically that it refers to both .droppable and #draggables.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.