Search code examples
jqueryjquery-uijquery-ui-sortablejquery-mobile-collapsible

How to drop a jQuery sortable item into a collapsed list?


TEST CODE - http://jsfiddle.net/reGb3/

I am using jQuery sortable lists for which I would like to drag an item from one list to another. I have that working (see test code) when the main categories are expanded, but if the main list is collapsed, I can not get dropped item to successfully populate the hidden list. I get part way there by making the main category list items "droppable" and then expanding the list, but then I need to do the drag and drop operation again. Any way to make this happen with either the main category collapsed (preferred) or with the extra expand step?

HTML:

<ul data-role="listview" class="sortable-list" data-filter="true">

  <li data-role="collapsible" data-iconpos="right" data-inset="false">
    <h2 class="droppable"   id="cat1">EMPTY</h2>
    <ul data-role="listview" data-theme="b" class="sortable-list1" >

    </ul>
  </li>    
  <li data-role="collapsible" data-iconpos="right" data-inset="false" >
    <h2 class="droppable" id="cat2">Birds</h2>
    <ul data-role="listview" data-theme="b" class="sortable-list1">
      <li id="id1"><a href="#">Condor</a></li>
      <li id="id2"><a href="#">Eagle</a></li>
      <li id="id3"><a href="#">Sparrow</a></li>
    </ul>
  </li>
  <li data-role="collapsible" data-iconpos="right" data-inset="false" >
    <h2 class="droppable" id="cat3">Fish</h2>
    <ul data-role="listview" data-theme="b" class="sortable-list1">
      <li id="id4"><a href="#">Salmon</a></li>
      <li id="id5"><a href="#">Pollock</a></li>
      <li id="id6"><a href="#">Trout</a></li>
    </ul>
  </li>
</ul>

JavaScript:

$(document).ready(function() {
    $('.sortable-list').sortable({
      connectWith: '.sortable-list',
      dropOnEmpty: true,
      update: function(event, ui) {

      }
    });

    $(".droppable").droppable({
        drop: function( event, ui ) {           
            var collapsedList = $(this).parent() ;
            if ($(collapsedList).collapsible( "option", "collapsed" )){
                $(collapsedList).collapsible( "expand" );
            }
          }
    });

    var sortupdate = function (event, ui) {

    };

    $('.sortable-list1').on("sortupdate", sortupdate);
    $('.sortable-list1').sortable({
      connectWith: '.sortable-list1',
      dropOnEmpty: true      
    });
});

CSS:

.ui-listview>.ui-li-static, .ui-listview>.ui-li-divider, .ui-listview>li>a.ui-btn {
overflow: visible !important;
}

.sortable-list1 {
    min-height:50px;
    padding:10px
}

Thanks!


Solution

  • Normally an item from a sortable widget can only be moved to another sortable widget via the connectWith option. In this case you actually move an item from a sortable list to a normal header (droppable) not a sortable list. So you have to handle the event drop. Looks like we can simply append the draggable item (accessed via the ui.draggable) to the list associated with the droppable item in this drop event handler, but it's not such simple. This is not the right place to append the draggable item, because there are some stages after that may append the draggable item back to the original list when the dropping target is not a valid sortable list, so the appending will fail. I think we have to find a workaround. In the drop event handler, we can just save a reference to the list to which the draggable item should be appended. Then we need to handle the sortstop event, check if the reference to the target list is valid (not null) before appending the dragged item (can be accessed via ui.item) to that list. Here is the edited code:

    var receiver;
    $(".droppable").droppable({
        drop: function( event, ui ) {           
              var collapsedList = $(this).parent() ;
              if ($(collapsedList).collapsible( "option", "collapsed" )){
                $(collapsedList).collapsible( "expand" );
              }            
                  //save the target list to receiver for later appending
                  receiver = $(this).parent().find('.sortable-list1');
            }
    });
    
    $('.sortable-list1').sortable({
            connectWith: '.sortable-list1',
            dropOnEmpty: true,
            //handle the sortstop event
            stop: function(e,ui){
                  //check if receiver is valid (not null)
                  if(receiver){
                    receiver.append(ui.item);
                    receiver = null; //remember to reset it to null
                  }
                }
    });
    

    Demo.