jQuery nested sortable list

jQuery Sortable for nested lists, where the connected list as source has no nested features.

by Rolf Friedrich

HTML

<label>Cursor At:</label>
<label id="cursor"></label>
<h3>
Targetlist
</h3>
<ul class="items">
    <li class="item unsortable">Unsortable Item </li>
    <li class="item">Item 1</li>
    <li class="item nested">Item 2</li>   
    <li class="item nested">Item 3</li>        
    <li class="item">Item 4</li>    
    <li class="item">Item 5</li>    
</ul>

<h3>
Sourcelist
</h3>
<ul class="items secondlist">
    <li class="item">Source item</li>
    <li class="item">Source item</li>
    <li class="item">Source item</li>
    <li class="item">Source item</li>  
</ul>

CSS

ul,
.items,
.secondlist {
    list-style: none outside none;
    margin-bottom: 20px;
    padding: 0;
    width: 100%;
    position: relative;
}
.item {
    background: #fff;
    border: 1px solid #ddd;
    cursor: move;
    /* height: 50px; */
    line-height: 50px;
    padding: 0 10px;
    width: 50%;
}

.item.unsortable {
  cursor: default;
  background: #fff;
}


.item:hover{
  background: #d8ffdf;
}
.item:has(ol):hover{
  background: #fff;
}

.item.unsortable:hover{
   cursor: disabled;
  background: #ffb7b7;
}

.nested {
    margin-left: 20px;
    width: 50%;
}
.dragHover {
    border-color: blue;
    color: blue;
}
.placeholder {
    background: #fff;
    border: 1px dashed #ccc;
    height: 50px;
	width: 50%;
}

.placeholder-nested {
    background: #fff;
    border: 1px dashed #ccc;
    height: 50px;
	width: 480px;
    margin-left: 20px;
}

.secondlist{
  min-height: 4rem;
}

JavaScript

$(".items").sortable({
    items :".item:not(.unsortable)",
    connectWith: ".items",
    placeholder: "placeholder",
    targetList : null,
    update: function(event, ui) {
        // update
	},
    start: function(event, ui) {
        
        if(ui.helper.hasClass('nested')){
            ui.placeholder.removeClass('placeholder');
            ui.placeholder.addClass('placeholder-nested');
        }
        else{ 
            ui.placeholder.removeClass('placeholder-nested');
            ui.placeholder.addClass('placeholder');
        }
    },
	  sort: function(event, ui) {
        var pos;    
        
        if(ui.helper.hasClass('nested') ){
            pos = ui.position.left+20; 
            $('#cursor').text(ui.position.left+20);
        }
        else{
            pos = ui.position.left; 
            $('#cursor').text(ui.position.left);    
        }
        
          if( ( pos >= 32 && !ui.helper.hasClass('nested') ) ){
              ui.placeholder.removeClass('placeholder');
              ui.placeholder.addClass('placeholder-nested');
              ui.helper.addClass('nested');
          }
          else if( (pos < 25 && ui.helper.hasClass('nested')) ){
              ui.placeholder.removeClass('placeholder-nested');
              ui.placeholder.addClass('placeholder');
              ui.helper.removeClass('nested');
          }
        
    },
    stop: function(event, ui ){     
        
        if( ui.helper == null && ui.item[0].closest('ul').classList.contains('secondlist') ){
        	ui.item.removeClass('nested');
        }            
        
    },
    remove: function(event, ui) {
				var secondList = ui.item[0].closest('ul').classList.contains('secondlist');
       
        if( secondList ) {
          ui.item[0].classList.remove('nested');
        } 
    }
});