21
votes

I have two lists #sortable1 and #sortable 2 which are connected sortables, as shown in this example.

You can drag and drop list items from sortable1 to sortable 2. However, if an item in sortable 1 contains the class "number", I want to prevent the drop on Sortable2 and thus make the dragged item drop back into sortable 1.

I have used the following on sortable2:

receive: function (event, ui) {
            if ($(ui.item).hasClass("number")) {
                $(ui.item).remove();
            }

but it deletes the list item from both tables altogether. Any help will be appreciated.

8

8 Answers

19
votes

You can use a combination of the stop and sortable('cancel') methods to validate the item being moved. In this example, upon an item being dropped, I check if the item is valid by:

  1. Checking if the item has the class number
  2. and checking if the list item was dropped in list2

This is slightly more hard-coded that I'd like, so alternatively what you could do is check the parent of the dropped item against this, to check if the lists are different. This means that you could potentially have an item of number in list1 and list2, but they're not interchangeable.

jsFiddle Example

$(function() {
    $('ul').sortable({
        connectWith: 'ul',
        stop: function(ev, ui) {
            if ($(ui.item).hasClass('number') && $(ui.placeholder).parent()[0] != this) {
                $(this).sortable('cancel');
            }
        }
    });        
});​
30
votes

For anyone reading this in future, as mentioned by briansol in comments for the accepted answer, it throws error

Uncaught TypeError: Cannot read property 'removeChild' of null

The the documentation particularly says

cancel()

Cancels a change in the current sortable and reverts it to the state prior to when the current sort was started. Useful in the stop and receive callback functions.

Canceling the sort during other events is unreliable, So it's better use the receive event as shown in Mj Azani's answer or use the stop event as follows:

$('#list1').sortable({
  connectWith: 'ul',
  stop: function(ev, ui) {
    if(ui.item.hasClass("number"))
      $(this).sortable("cancel");
   }
}); 

$('#list2').sortable({
   connectWith: 'ul',
});  

Demo

14
votes

Try this example

$('#list1').sortable({
    connectWith: 'ul'
});    

$('#list2').sortable({
    connectWith: 'ul',
    receive: function(ev, ui) {
        if(ui.item.hasClass("number"))
          ui.sender.sortable("cancel");
    }
});    
1
votes

After a few experiments, I've found that by far the easiest method is to use the remove event, that essentially only fires when you try to drop an item into a new sortable (that was previously made available as a target using connectWith).

Simply add this to your sortable call:

remove:function(e,ui) {
    if(ui.item.hasClass('your_restricted_classname')) return false;
},
0
votes

If you don't need to be able to drag the items with class "number" at all, you can also restrict the whole drag-and-drop functionality to items that don't have the class "number":

$("#sortable1, #sortable2").sortable({
    connectWith: ".connectedSortable",
    items: "li:not(.number)"
});

You can try it out here: http://jsfiddle.net/60gwjsgb/1/

0
votes
beforeStop: function(ev, ui) {
                if ($(ui.item).hasClass('number') && 
                    $(ui.placeholder).parent()[0] != this) {
                    $(this).sortable('cancel');
                }
            }

try this.

0
votes

If a) you only have just these 2 lists, and b) you don't care that your "number" actually be dragged and then drop back, you can simply prevent it from getting dragged by this:

 sort: function(event, ui) {
if(ui.item.hasClass('number')) return false;
}
0
votes

Here is my 2 cents. If you don't wish to allow sorting in one of the connected list you can remove it from the containers in the instance object when start even is triggered, so the sorting won't happen at all, and if you need to restore the sorting functionality afterwards, you can recreate the sortable once again or add back removed elements in container array. It would look like this:

    start: function(e, ui){
        if(ui.item.hasClass('uniqToSortableItem')){
            var instance = $(this).sortable( "instance" );
            var newContainers = [];
            instance.containers.forEach((el)=> {
                if(!el.element.hasClass('sortableToDisable')){
                    newContainers.push(el);
                } 
            });
            // in case you need to save initial array just attach it to ui.helper
            ui.helper.initialContainersArray = instance.containers;
            instance.containers = newContainers;
        }
    },
    stop: function(e, ui){
        // returning containers array to previous state if necessary
        if(ui.helper.initialContainersArray){
            $(this).sortable( "instance" ).containers = ui.helper.initialContainersArray;
        }
    }