JQuery UI: Cancel Sortable upon Droppable Drop

musefan picture musefan · Oct 20, 2011 · Viewed 10.1k times · Source

I am using JQuery 1.5.1 and JQuery UI 1.8.11.

I have added sortable for a number of items - the task here is to allow drag to sort, this all works fine.

But I also want to incorporate droppable, so that the item can be dropped onto a "copy me" area - the task there will be to duplicate the item (I will work that bit out later)

Problem is the droppable target is at the bottom of the sortable list (I do not want to move this) and once the drop occurs the sortable item moves to the bottom of the list.

What I want to do is cancel this sort when the drop event fires.

You can see my problem in action here (just drag "Item 1" onto the "Drop to Copy Item" area and you will see the sort does not get cancelled)

As you can see I have tried the following in the droppable "drop" event (suggested from JQuery UI Docs) but it does not seem to work...

$(this).sortable('cancel');

I am also open to any other recommendations on how to achieve this "drop to copy" effect I am looking for.

Thanks

Answer

musefan picture musefan · Oct 20, 2011

OK, so I have worked out a solution which does the job.

the cancel code does work if you have it in the "stop" event of the sortable function. However, it will only apply once the "revert" has completed. The problem is that I was trying to copy/revert the element from the droppable "drop" event and this was too early.

The solution is to wait for the "stop" event to complete, and to achieve this I had to create a "awaiting copy" flag, to be checked in the "stop" event.

Here is an example

It still doesn't feel right (UX-wise) but it works correct, and you could always set revert to false on the sortable function to get a slightly better feel.

The code from the example is as follows...

var itemCount = 3;
var awaitingCopy = false;

$(init);

function init() {
    $("#Items").sortable({
        revert: true,
        placeholder: "ItemPlaceHolder",
        opacity: 0.6,
        start: StartDrag,
        stop: StopDrag
    });

    $("#CopyItem").droppable({
        hoverClass: "CopyItemActive",
        drop: function(event, ui) {
            awaitingCopy = true;
        }
    });

    $("#NewItem").click(function(e) {
        e.preventDefault();
        itemCount++;
        var element = $("<div class='Item'>Item " + itemCount + "</div>");
        $("#Items").append(element);
        element.hide().slideDown(500);
    });
}

function CopyItem(element) {
    awaitingCopy = false;
    var clone = element.clone();
    $("#Items").append(clone);
    clone.hide().slideDown(500);
}

function StartDrag() {
    $("#NewItem").hide();
    $("#CopyItem").show();
}

function StopDrag(event, ui) {
    if (awaitingCopy) {
        $(this).sortable('cancel');
        CopyItem($(ui.item));
    }
    $("#NewItem").show();
    $("#CopyItem").hide();
}

Anyway, hopefully this will help others who want the same kind of effect... no stealing my design though ;)