title: jQuery nestedSortable - how to make first item root and subsequent items children of root
body: <p>I have a form that you can generate a sortable menu structure with. I want to make the first item the parent and all subsequent items it's children. I have achieved this visually but I need to also ensure the code structure is correct.</p>
  
  <p>In my options I noticed that protectRoot and rootID are not working but I don't think it's impacting on anything in the code.</p>
  
  <p>jsFiddle
  <a href="http://jsfiddle.net/clintongreen/EazX7/" rel="nofollow">http://jsfiddle.net/clintongreen/EazX7/</a></p>
  
  <p><strong>JS</strong></p>
  
  <pre><code>// Nested Sortable Plugin
  /*
   * jQuery UI Nested Sortablefdfv
  * v 1.3.4 / 28 apr 2011
  * http://mjsarfatti.com/sandbox/nestedSortable
  *
  * Depends:
  *     jquery.ui.sortable.js 1.8+
  *
  * License CC BY-SA 3.0
  * Copyright 2010-2011, Manuele J Sarfatti
  */
  
  (function($) {
  
  $.widget("ui.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
  
      options: {
          tabSize: 20,
          disableNesting: 'ui-nestedSortable-no-nesting',
          errorClass: 'ui-nestedSortable-error',
          listType: 'ol',
          maxLevels: 0,
          noJumpFix: 0
      },
  
      _create: function(){
          if (this.noJumpFix == false)
              this.element.height(this.element.height());
          this.element.data('sortable', this.element.data('nestedSortable'));
          return $.ui.sortable.prototype._create.apply(this, arguments);
      },
  
  
  
      _mouseDrag: function(event) {
  
          //Compute the helpers position
          this.position = this._generatePosition(event);
          this.positionAbs = this._convertPositionTo("absolute");
  
          if (!this.lastPositionAbs) {
              this.lastPositionAbs = this.positionAbs;
          }
  
          //Do scrolling
          if(this.options.scroll) {
              var o = this.options, scrolled = false;
              if(this.scrollParent[0] != document &amp;&amp; this.scrollParent[0].tagName != 'HTML') {
  
                  if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY &lt; o.scrollSensitivity)
                      this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
                  else if(event.pageY - this.overflowOffset.top &lt; o.scrollSensitivity)
                      this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
  
                  if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX &lt; o.scrollSensitivity)
                      this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
                  else if(event.pageX - this.overflowOffset.left &lt; o.scrollSensitivity)
                      this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
  
              } else {
  
                  if(event.pageY - $(document).scrollTop() &lt; o.scrollSensitivity)
                      scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
                  else if($(window).height() - (event.pageY - $(document).scrollTop()) &lt; o.scrollSensitivity)
                      scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
  
                  if(event.pageX - $(document).scrollLeft() &lt; o.scrollSensitivity)
                      scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
                  else if($(window).width() - (event.pageX - $(document).scrollLeft()) &lt; o.scrollSensitivity)
                      scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
  
              }
  
              if(scrolled !== false &amp;&amp; $.ui.ddmanager &amp;&amp; !o.dropBehaviour)
                  $.ui.ddmanager.prepareOffsets(this, event);
          }
  
          //Regenerate the absolute position used for position checks
          this.positionAbs = this._convertPositionTo("absolute");
  
          //Set the helper position
          if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
          if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
  
          //Rearrange
          for (var i = this.items.length - 1; i &gt;= 0; i--) {
  
              //Cache variables and intersection, continue if no intersection
              var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
              if (!intersection) continue;
  
              if(itemElement != this.currentItem[0] //cannot intersect with itself
                  &amp;&amp;    this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
                  &amp;&amp;    !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
                  &amp;&amp; (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
                  //&amp;&amp; itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
              ) {
  
                  this.direction = intersection == 1 ? "down" : "up";
  
                  if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
                      this._rearrange(event, item);
                  } else {
                      break;
                  }
  
                  // Clear emtpy ul's/ol's
                  this._clearEmpty(itemElement);
  
                  this._trigger("change", event, this._uiHash());
                  break;
              }
          }
  
          var parentItem = (this.placeholder[0].parentNode.parentNode &amp;&amp; $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length) ? $(this.placeholder[0].parentNode.parentNode) : null;
          var level = this._getLevel(this.placeholder);
          var childLevels = this._getChildLevels(this.helper);
          var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
          if (previousItem != null) {
              while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0]) {
                  if (previousItem[0].previousSibling) {
                      previousItem = $(previousItem[0].previousSibling);
                  } else {
                      previousItem = null;
                      break;
                  }
              }
          }
  
          newList = document.createElement(o.listType);
  
          this.beyondMaxLevels = 0;
  
          // If the item is moved to the left, send it to its parent level
          if (parentItem != null &amp;&amp; this.positionAbs.left &lt; parentItem.offset().left) {
              parentItem.after(this.placeholder[0]);
              this._clearEmpty(parentItem[0]);
              this._trigger("change", event, this._uiHash());
          }
          // If the item is below another one and is moved to the right, make it a children of it
          else if (previousItem != null &amp;&amp; this.positionAbs.left &gt; previousItem.offset().left + o.tabSize) {
              this._isAllowed(previousItem, level+childLevels+1);
              if (!previousItem.children(o.listType).length) {
                  previousItem[0].appendChild(newList);
              }
              previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
              this._trigger("change", event, this._uiHash());
          }
          else {
              this._isAllowed(parentItem, level+childLevels);
          }
  
          //Post events to containers
          this._contactContainers(event);
  
          //Interconnect with droppables
          if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
  
          //Call callbacks
          this._trigger('sort', event, this._uiHash());
  
          this.lastPositionAbs = this.positionAbs;
          return false;
  
      },
  
      _mouseStop: function(event, noPropagation) {
  
          // If the item is in a position not allowed, send it back
          if (this.beyondMaxLevels) {
              var parent = this.placeholder.parent().closest(this.options.items);
  
              for (var i = this.beyondMaxLevels - 1; i &gt; 0; i--) {
                  parent = parent.parent().closest(this.options.items);
              }
  
              this.placeholder.removeClass(this.options.errorClass);
              parent.after(this.placeholder);
              this._trigger("change", event, this._uiHash());
          }
  
          $.ui.sortable.prototype._mouseStop.apply(this, arguments);
  
      },
  
      serialize: function(o) {
  
          var items = this._getItemsAsjQuery(o &amp;&amp; o.connected);
          var str = []; o = o || {};
  
          $(items).each(function() {
              var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
              var pid = ($(o.item || this).parent(o.listType).parent('li').attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
              if(res) str.push((o.key || res[1]+'['+(o.key &amp;&amp; o.expression ? res[1] : res[2])+']')+'='+(pid ? (o.key &amp;&amp; o.expression ? pid[1] : pid[2]) : 'root'));
          });
  
          if(!str.length &amp;&amp; o.key) {
              str.push(o.key + '=');
          }
  
          return str.join('&amp;');
  
      },
  
      toHierarchy: function(o) {
  
          o = o || {};
          var sDepth = o.startDepthCount || 0;
          var ret = [];
  
          $(this.element).children('li').each(function() {
              var level = _recursiveItems($(this));
              ret.push(level);
          });
  
          return ret;
  
          function _recursiveItems(li) {
              var id = ($(li).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
              if (id != null) {
                  var item = {"id" : id[2]};
                  if ($(li).children(o.listType).children('li').length &gt; 0) {
                      item.children = [];
                      $(li).children(o.listType).children('li').each(function () {
                          var level = _recursiveItems($(this));
                          item.children.push(level);
                      });
                  }
                  return item;
              }
          }
      },
  
      toArray: function(o) {
  
          o = o || {};
          var sDepth = o.startDepthCount || 0;
          var ret = [];
          var left = 2;
  
          ret.push({"item_id": 'root', "parent_id": 'none', "depth": sDepth, "left": '1', "right": ($('li', this.element).length + 1) * 2});
  
          $(this.element).children('li').each(function () {
              left = _recursiveArray(this, sDepth + 1, left);
          });
  
          function _sortByLeft(a,b) {
              return a['left'] - b['left'];
          }
          ret = ret.sort(_sortByLeft);
  
          return ret;
  
          function _recursiveArray(item, depth, left) {
  
              right = left + 1;
  
              if ($(item).children(o.listType).children('li').length &gt; 0) {
                  depth ++;
                  $(item).children(o.listType).children('li').each(function () {
                      right = _recursiveArray($(this), depth, right);
                  });
                  depth --;
              }
  
              id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
  
              if (depth === sDepth + 1) pid = 'root';
              else {
                  parentItem = ($(item).parent(o.listType).parent('li').attr('id')).match(o.expression || (/(.+)[-=_](.+)/));
                  pid = parentItem[2];
              }
  
              if (id != null) {
                      ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
              }
  
              return left = right + 1;
          }
  
      },
  
      _clear: function(event, noPropagation) {
  
          $.ui.sortable.prototype._clear.apply(this, arguments);
  
          // Clean last empty ul/ol
          for (var i = this.items.length - 1; i &gt;= 0; i--) {
              var item = this.items[i].item[0];
              this._clearEmpty(item);
          }
          return true;
  
      },
  
      _clearEmpty: function(item) {
  
          if (item.children[1] &amp;&amp; item.children[1].children.length == 0) {
              item.removeChild(item.children[1]);
          }
  
      },
  
      _getLevel: function(item) {
  
          var level = 1;
  
          if (this.options.listType) {
                  var list = item.closest(this.options.listType);
                  while (!list.is('.ui-sortable')/* &amp;&amp; level &lt; this.options.maxLevels*/) {
                          level++;
                          list = list.parent().closest(this.options.listType);
                  }
          }
  
          return level;
      },
  
      _getChildLevels: function(parent, depth) {
          var self = this,
              o = this.options,
              result = 0;
          depth = depth || 0;
  
          $(parent).children(o.listType).children(o.items).each(function (index, child) {
                  result = Math.max(self._getChildLevels(child, depth + 1), result);
          });
  
          return depth ? result + 1 : result;
      },
  
      _isAllowed: function(parentItem, levels) {
          var o = this.options
          // Are we trying to nest under a no-nest or are we nesting too deep?
          if (parentItem == null || !(parentItem.hasClass(o.disableNesting))) {
              if (o.maxLevels &lt; levels &amp;&amp; o.maxLevels != 0) {
                  this.placeholder.addClass(o.errorClass);
                  this.beyondMaxLevels = levels - o.maxLevels;
              } else {
                  this.placeholder.removeClass(o.errorClass);
                  this.beyondMaxLevels = 0;
              }
          } else {
              this.placeholder.addClass(o.errorClass);
              if (o.maxLevels &lt; levels &amp;&amp; o.maxLevels != 0) {
                  this.beyondMaxLevels = levels - o.maxLevels;
              } else {
                  this.beyondMaxLevels = 1;
              }
          }
      }
  }));
  
  $.ui.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.ui.nestedSortable.prototype.options);
  })(jQuery);
  
  // Create Div
  $(".add_menu_item").live('click', function() {
  var value = $(this).prev().val();
  if (value.length) {
      var newDiv = $('&lt;div id="toggleshow' + "0" + numToggle++ +'" class="div_menu_button"&gt;&lt;/div&gt;');
      var showDiv = $('&lt;div id="show'+ "0" + numShow++ +'" class="menu_button_info"&gt;Haywood Tjibloumi&lt;/div&gt;');
      $('#created_buttons_list').append(
          newDiv.val(value).text(value) );
          newDiv.wrap("&lt;li&gt;&lt;/li&gt;");
          newDiv.append(showDiv);
  }
  
  });
      var numToggle = 0;
      var numShow = 0;
  
  
     // Nested Divs Options
      $('ol#created_buttons_list').nestedSortable({
          disableNesting: 'no-nest',
          protectRoot: true,
          rootID: '#theroot',
          forcePlaceholderSize: true,
          handle: 'div',
          helper: 'clone',
          items: 'li:not(.dontsortme)',
          maxLevels: 10,
          opacity: .6,
          placeholder: 'placeholder',
          revert: 250,
          tabSize: 25,
          tolerance: 'pointer',
          toleranceElement: '&gt; div'
  });
  
  
      // Toggle Hide/SHow
      $('#toggleshow').live('click', function() {$('#show').slideToggle('slow', function() {});});
      $('#toggleshow00').live('click', function() {$('#show00').slideToggle('slow', function() {});});
      $('#toggleshow01').live('click',function() {$('#show01').slideToggle('slow', function() {});});
      $('#toggleshow02').live('click',function() {$('#show02').slideToggle('slow', function() {});});
      $('#toggleshow03').live('click',function() {$('#show03').slideToggle('slow', function() {});});
      $('#toggleshow04').live('click',function() {$('#show04').slideToggle('slow', function() {});});
      $('#toggleshow05').live('click',function() {$('#show05').slideToggle('slow', function() {});});
  
  // Indent first item
  $("input.add_menu_item").live('click', function(){
      $("#created_buttons_list li:first").css('margin-left','0px').addClass('dontsortme');
  });
  </code></pre>
  
  <p><strong>CSS</strong></p>
  
  <pre><code>form#create_menu_item{ width:920px; }
  form#create_menu_item input#new_menu_name{ padding:5px; float:left; margin:0 10px 10px 0; }
  form#create_menu_item input.add_menu_item{font-size:11px; width:60px; height:27px; display:block; -moz-border-radius:3px; -webkit-border-radius:3px; -o-border-radius:3px; border-radius:3px; background:#f5f5f5; background: -moz-linear-gradient(100% 100% 90deg, #eee, #fcfcfc); background: -webkit-linear-gradient(#fcfcfc, #eee) !important; background: -o-linear-gradient(#fcfcfc, #eee);border:1px solid #ccc;color:#666;padding-bottom:3px;float:left;}
  div.div_menu_button{font-size:12px;width:295px;height:16px;display:block;-moz-border-radius:3px; -webkit-border-radius:3px; -o-border-radius:3px; border-radius:3px;background:#f5f5f5; background: -moz-linear-gradient(100% 100% 90deg, #eee, #fcfcfc); background: -webkit-linear-gradient(#fcfcfc, #eee) !important; background: -o-linear-gradient(#fcfcfc, #eee);border:1px solid #ccc;color:#666;padding:10px;margin:0 0 10px 0;}
  div#created_buttons{ width:680px; border:1px solid #eee; min-height:400px; padding:10px;}
  .clear{ clear:both;}
  #show, #show00, #show01, #show02, #show03, #show04, #show05 { display:none;}
  .ui-nestedSortable-error { background:#fbe3e4; color:#8a1f11;}
  ol#created_buttons_list{ margin: 0; padding: 0; padding-left: 30px;}
  ol#created_buttons_list, ol#created_buttons_list ol { margin: 0 0 0 25px; padding: 0; list-style-type: none;}
  ol#created_buttons_list { margin: 10px 0 10px 0;}
  #created_buttons_list li { margin: 7px 0 0 0; padding: 0;}
  #created_buttons_list li div  { padding: 10px; margin: 0; cursor: move;}
  .placeholder { background-color: #f9f9f9; border:1px dashed #ccc;}
  #created_buttons li{margin-left: 25px; padding-left: 25px;}
  </code></pre>
  
  <p><strong>HTML</strong></p>
  
  <pre><code>      &lt;div&gt;
            &lt;form id="create_menu_item"&gt;
              &lt;ul class="add_remove_list"&gt;
                  &lt;li&gt;&lt;h4&gt;Add Menu Item&lt;/h4&gt;&lt;/li&gt;
                  &lt;li&gt;&lt;input id="new_menu_name" type="text" name="new_menu_name" placeholder="Please enter your menu title"&gt;
                      &lt;input class="add_menu_item" name="add_menu_item" value="Add" type="button"&gt;
                  &lt;/li&gt;
                  &lt;li class="clear"&gt;&lt;h4&gt;Menu Items&lt;/h4&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;div id="created_buttons"&gt; &lt;ol id="created_buttons_list"&gt; &lt;/ol&gt;  &lt;/div&gt;                        
                  &lt;/li&gt;
              &lt;/ul&gt;
                  &lt;/form&gt; 
          &lt;/div&gt;
  </code></pre>