Sunday, August 9, 2026
HomejQueryDrag and Drop Nested Tree for Bootstrap - BsNestedSortable

Drag and Drop Nested Tree for Bootstrap – BsNestedSortable


BsNestedSortable is a jQuery and Bootstrap plugin that turns flat parent-child information right into a draggable, collapsible, hierarchical tree construction. It is designed for class managers, menu builders, taxonomy editors, and different admin screens the place your customers reorder nested information and save the revised hierarchy as JSON.

The plugin rebuilds mum or dad IDs, ranges, sibling positions, ancestor lists, and descendant lists after a drag. Its serialization layer can write a number of tree representations to type fields, web page parts, the console, alerts, or international variables.

Options:

  • Converts flat mum or dad ID information into draggable nested branches.
  • Recalculates ranges, mother and father, kids, and sibling order after every drag.
  • Serializes tree state as flat information, hierarchy objects, or relationship maps.
  • Helps contact dragging via jQuery UI Contact Punch.
  • Department creation, modifying, deletion, collapse, and growth controls.
  • Maps customized file area names to the anticipated tree information mannequin.
  • Limits nesting depth via a configurable most degree.
  • Async save callbacks for reorder, add, edit, and delete actions.
  • Non-obligatory department photographs and descriptions.

Tips on how to use it:

1. Load the required CSS and JavaScript in your HTML doc. The plugin makes use of jQuery, jQuery UI Sortable, Bootstrap types, and its personal CSS and JavaScript information. The default icons use Font Superior.


<!-- Bootstrap tree types -->
<hyperlink rel="stylesheet" href="/path/to/cdn/bootstrap.min.css">

<!-- Font Superior Icons -->
<hyperlink rel="stylesheet" href="/path/to/cdn/all.min.css">

<!-- Plugin types -->
<hyperlink rel="stylesheet" href="/path/to/css/BsNestedSortable.css">

<!-- jQuery should load earlier than jQuery UI and Bootstrap JS -->
<script src="/path/to/cdn/jquerymin.js"></script>
<script src="/path/to/cdn/jquery-ui.min.js"></script>

<!-- Non-obligatory contact adapter -->
<script src="/path/to/cdn/jquery.ui.touch-punch.min.js"></script>

<!-- Bootstrap bundle helps the plugin's jQuery modal calls -->
<script src="/path/to/cdn/bootstrap.bundle.min.js"></script>

<!-- BsNestedSortable -->
<script src="/path/to/js/BsNestedSortable.js"></script>

2. Create an empty listing for the tree:


<div class="container py-4">
  <ul id="category-tree" class="list-group"></ul>
</div>

3. Initialize the plugin and cross a flat array with distinctive IDs, mum or dad IDs, titles, and order values:


var categoryRows = [
  {
    id: 101,
    parent_id: 0,
    title: 'Electronics',
    description: 'Devices and accessories',
    img: '',
    order: 1
  },
  {
    id: 102,
    parent_id: 101,
    title: 'Laptops',
    description: 'Portable computers',
    img: '',
    order: 1
  },
  {
    id: 103,
    parent_id: 101,
    title: 'Monitors',
    description: 'Desktop displays',
    img: '',
    order: 2
  },
  {
    id: 104,
    parent_id: 0,
    title: 'Office',
    description: 'Workspace products',
    img: '',
    order: 2
  }
];

$(perform () {
  $('#category-tree').BsNestedSortable({
    information: categoryRows,
    choices: {
      rootID: 0,
      maxLevel: 4
    }
  });
});

4. All plugin choices:

Prime-Stage Configuration

  • information (Array): Provides flat information for the tree. Every file wants an ID, a mum or dad reference, and a title. The order, picture, and outline fields are non-obligatory.
  • choices (Object): Teams tree selectors, information mappings, icons, modal selectors, depth limits, and show settings.
  • language (Object): Replaces labels for empty-tree creation, new information, edit actions, baby creation, and deletion.
  • serializeOption (Object): Prompts serialization and routes chosen output objects to DOM targets, console output, alerts, or international variables.
  • eventsOptions (Object): Registers async callbacks and area exclusions for change comparability.

Tree Choices

  • treeSelector (String): Selects the tree root. The jQuery wrapper replaces the default with the ID and lessons from the initialized component. Default: #tree.
  • branchSelector (String): Selects each department <li>. Default: .tree-branch.
  • branchPathSelector (String): Selects the visible connector beside a department. Default: .branch-path.
  • dragHandlerSelector (String): Selects the component that begins a drag motion. Default: .moveAble.
  • placeholderName (String): Units the jQuery UI Sortable placeholder class title. Enter the category title with no main dot. Default: sortable-placeholder.
  • childrenBusSelector (String): Selects the momentary container that carries descendants throughout a drag. Default: .children-bus.
  • collapseChildren (String): Selects the develop and collapse button. Default: .collapseChildren.
  • levelPrefix (String): Units the category prefix for department ranges. A second-level department receives a category equivalent to branch-level-2. Default: branch-level.
  • depth (Quantity): Units the horizontal indentation in pixels for every degree. Update the associated CSS degree guidelines after altering this worth. Default: 30.
  • maxHeight (Quantity): Units a connector top worth used throughout branch-path calculations. Default: 40.
  • minHeight (Quantity): Units the minimal further top used throughout branch-path calculations. Default: 10.
  • insertNewButton (String): Defines the button markup proven for an empty information set. The {insertText} token receives the configured language label.
  • imagesUrlPrefix (String): Provides a shared path earlier than every department picture worth. Default: an empty string.
  • maxLevel (Quantity): Caps the permitted nesting degree. Default: 3.
  • rootID (String or Quantity): Identifies top-level information. Default: 0.
  • icons (Object): Teams markup for take away, edit, add, develop, and collapse controls.
  • modal (Object): Teams selectors for create, edit, and delete dialogs and their fields.
  • dataAttributes (Object): Maps generated data-* attribute suffixes.
  • dataKeys (Object): Maps incoming file fields to the tree mannequin.

Icon Choices

  • icons.take away (String): Defines the delete button markup. Default: a Font Superior trash icon.
  • icons.edit (String): Defines the edit button markup. Default: a Font Superior edit icon.
  • icons.add (String): Defines the add-child button markup. Default: a Font Superior plus-square icon.
  • icons.develop (String): Defines the collapsed-state icon. Default: a Font Superior plus icon.
  • icons.collapse (String): Defines the expanded-state icon. Default: a Font Superior minus icon.

Modal Choices

  • modal.id (String): Selects the create and edit modal. Default: #myModal.
  • modal.ModalDelete (String): Selects the delete affirmation modal. Default: #myModalDelete.
  • modal.title (String): Selects the department title area. Default: #CatName.
  • modal.description (String): Selects the department description area. Default: #CatDescId.
  • modal.picture (String): Selects the department picture area. Default: #picture.

Knowledge Attribute Choices

  • dataAttributes.id (String): Units the suffix for the generated department ID attribute. Default: id.
  • dataAttributes.mum or dad (String): Units the suffix for the generated mum or dad attribute. Default: mum or dad.
  • dataAttributes.title (String): Defines a title attribute title within the configuration object. The renderer writes titles via .branch-title as an alternative of this setting. Default: title.

Knowledge Key Choices

  • dataKeys.id (String): Maps the file ID area. Default: id.
  • dataKeys.mum or dad (String): Maps the mum or dad ID area. Default: parent_id.
  • dataKeys.title (String): Maps the department title area. Default: title.
  • dataKeys.description (String): Maps the outline area. Default: description.
  • dataKeys.picture (String): Maps the picture path area. Default: img.
  • dataKeys.order (String): Maps the sibling order area. Default: order.

Language Choices

  • language.createTheFirstBranch (String): Units the empty-tree button textual content. Default: Insert New Department.
  • language.newItem (String): Units the fallback title for a brand new file. Default: New Merchandise.
  • language.editBranch (String): Units the edit button title. Default: Edit Department.
  • language.addBranch (String): Units the add-child button title. Default: Add a brand new baby.
  • language.removeBranch (String): Units the delete button title. Default: Take away Department.

Serialization Choices

  • serializeOption.serializeON (Boolean): Prompts initial-state monitoring and serialized output. Supplying serializeOption or eventsOptions turns it on mechanically.
  • serializeOption.methodology (String): Selects JSON, console, alert, or asVar. Default: JSON.
  • serializeOption.name (String): Selects html, val, or textual content for JSON output targets. Default: html.
  • serializeOption.outPuts (Object): Maps every serialized illustration to a goal selector.
  • serializeOption.outPuts.catObj (String): Receives the flat file assortment with mum or dad, degree, order, ancestor, and baby information.
  • serializeOption.outPuts.parentArr (String): Receives the direct mum or dad map for each department.
  • serializeOption.outPuts.childrenArr (String): Receives direct baby arrays grouped by mum or dad ID.
  • serializeOption.outPuts.Hierarchy (String): Receives the expanded recursive hierarchy.
  • serializeOption.outPuts.minHierarchy (String): Receives the compact recursive hierarchy with IDs and baby nodes.
  • serializeOption.outPuts.allParentsArr (String): Receives every department’s full ancestor listing.
  • serializeOption.outPuts.allChildrenArr (String): Receives every department’s full descendant listing.

Callback Choices

  • eventsOptions.onComplete (Perform): Runs after a accomplished drag modifications the extent or place.
  • eventsOptions.onDelete (Perform): Runs earlier than a leaf department is eliminated.
  • eventsOptions.onEdit (Perform): Runs earlier than an edited department updates its rendered content material.
  • eventsOptions.onAdd (Perform): Runs earlier than a brand new department enters the tree.
  • eventsOptions.excludedObjElms (Array): Lists file properties that the change comparator ought to ignore.

$('#category-tree').BsNestedSortable({
  choices: {
    depth: 30,
    treeSelector: '#tree',
    branchSelector: '.tree-branch',
    branchPathSelector: '.branch-path',
    dragHandlerSelector: '.moveAble',
    placeholderName: 'sortable-placeholder',
    childrenBusSelector: '.children-bus',
    collapseChildren: '.collapseChildren',
    levelPrefix: 'branch-level',
    maxHeight: 40, //px 
    minHeight: 10, // px
    insertNewButton: '<button sort="button" class="btn btn-primary btn-lg btn-block w-100">{insertText}</button>',
    imagesUrlPrefix: "",
    icons: {
      take away: '<i class="far fa-trash-alt"></i>',
      edit: '<i class="far fa-edit"></i>',
      add: '<i class="far fa-plus-square"></i>',
      develop: '<i class="fas fa-plus"></i>',
      collapse: '<i class="fas fa-minus"></i>',
    },

    modal: {
      id: "#myModal",
      ModalDelete: "#myModalDelete",
      title: "#CatName",
      description: "#CatDescId",
      picture: "#picture"
    },
    maxLevel: 3,
    rootID: 0,
    dataAttributes: {
      id: 'id',
      mum or dad: 'mum or dad',
      title: 'title',
    },
    dataKeys: {
      id: 'id',
      mum or dad: 'parent_id',
      title: 'title',
      description: 'description',
      picture: 'img',
      order: 'order'
    },
  },
  language: {
    createTheFirstBranch: "Insert New Department",
    newItem: "New Merchandise",
    editBranch: "Edit Department",
    addBranch: "Add a brand new baby",
    removeBranch: "Take away Department",
    exportSQL: "Export to SQL",
    exportTXT: "Export to TXT",
    exportSimple: "Easy Format",
    exportDetailed: "Detailed Format",
    exportHierarchical: "Hierarchical Format"
  },
  exportOptions: {
    enableExport: true,
    sqlTableName: 'nested_sortable',
    txtFormat: 'easy' // 'easy', 'detailed', or 'hierarchical'
  },
  serializeOption: {
    serializeON: false,
    methodology: "JSON",
    name: "html",
    outPuts: {
      catObj: "#catObj", // the locations which could have all classes
      parentArr: "#parentArr", // mother and father array
      childrenArr: "#childrenArr", // kids array
      Hierarchy: "#hierarchy", // expanded hierarchical array
      minHierarchy: "#minHierarchy", // minimized hierarchical array
      allParentsArr: "#allParentsArr", // all of oldsters
      allChildrenArr: "#allChildrenArr" // all of youngsters
    }
  },
  eventsOptions: {
    onComplete: async perform () { return true },
    onDelete: async perform () { return true },
    onEdit: async perform () { return true },
    onAdd: async perform () { return true },
    excludedObjElms: [],
  },
});

Superior Examples:

Save the Sorted Tree in Hidden Kind Fields

The plugin writes each hidden values throughout initialization and after drag operations. The built-in create, edit, and delete handlers replace their callback state. They don’t run the DOM output author. Persist these actions via onAdd, onEdit, and onDelete, or patch the accepted-action paths to name the inner serializer. Parse and validate each submitted worth on the server earlier than writing mum or dad IDs or order values.


<type id="catalog-form" methodology="submit" motion="/admin/catalog/save">
  <ul id="catalog-tree" class="list-group"></ul>

  <!-- Receives the flat file assortment -->
  <enter sort="hidden" id="catalog-records" title="catalog_records">

  <!-- Receives the compact recursive hierarchy -->
  <enter sort="hidden" id="catalog-hierarchy" title="catalog_hierarchy">

  <button sort="submit" class="btn btn-primary mt-3">Save Catalog</button>
</type>

<script>
var catalogRows = [
  { id: 1, parent_id: 0, title: 'Hardware', order: 1 },
  { id: 2, parent_id: 1, title: 'Keyboards', order: 1 },
  { id: 3, parent_id: 1, title: 'Pointing Devices', order: 2 },
  { id: 4, parent_id: 0, title: 'Software', order: 2 }
];

$('#catalog-tree').BsNestedSortable({
  information: catalogRows,
  serializeOption: {
    methodology: 'JSON',
    name: 'val',
    outPuts: {
      catObj: '#catalog-records',
      minHierarchy: '#catalog-hierarchy'
    }
  }
});
</script>

Map an Present Database Schema

Each mum or dad worth should match one other mapped ID or the configured root ID. The preparation step strikes orphaned information to the foundation. Duplicate IDs produce ambiguous department lookups and invalid serialization.


var departmentRows = [
  {
    node_key: 20,
    parent_key: 0,
    label: 'Operations',
    details: 'Internal operations',
    avatar_path: 'departments/operations.svg',
    sort_index: 1
  },
  {
    node_key: 21,
    parent_key: 20,
    label: 'Facilities',
    details: 'Office and building services',
    avatar_path: 'departments/facilities.svg',
    sort_index: 1
  }
];

$('#department-tree').BsNestedSortable({
  information: departmentRows,
  choices: {
    rootID: 0,
    maxLevel: 5,
    imagesUrlPrefix: '/media/',
    dataKeys: {
      id: 'node_key',
      mum or dad: 'parent_key',
      title: 'label',
      description: 'particulars',
      picture: 'avatar_path',
      order: 'sort_index'
    }
  }
});

Autosave Reordered Information By way of AJAX

A failed reorder request doesn’t restore the outdated DOM order. Reorder completion doesn’t substitute the inner comparability baseline both. Later diff objects could embody earlier drag modifications. Ship theLastSerializ because the authoritative snapshot, reload the information after a failed request, or patch the cease handler to retailer a brand new baseline after a profitable save.


$('#navigation-tree').BsNestedSortable({
  information: navigationRows,

  eventsOptions: {
    excludedObjElms: ['description', 'img'],

    onComplete: async perform (ui, diff) {
      if (!diff || !diff.modified.size) {
        return true;
      }

      $('#tree-save-status').textual content('Saving modifications...');

      strive {
        await $.ajax({
          url: '/admin/navigation/reorder',
          methodology: 'POST',
          contentType: 'utility/json',
          information: JSON.stringify({
            modified: diff.modified,
            tree: diff.theLastSerializ
          })
        });

        $('#tree-save-status').textual content('Saved');
      } catch (requestError) {
        $('#tree-save-status').textual content('Save failed. Reload the tree earlier than modifying once more.');
      }

      return true;
    }
  }
});

Join Customized Bootstrap Modals

Maintain the mounted hidden fields and motion lessons anticipated by the plugin. The create and edit dialog wants a .submit button and a .shut management. The delete dialog wants #IdOfDelete and a .Delete button. Bootstrap 5 tasks want native bootstrap.Modal calls within the plugin code.


$('#menu-tree').BsNestedSortable({
  information: menuRows,
  choices: {
    modal: {
      id: '#menu-item-modal',
      ModalDelete: '#menu-delete-modal',
      title: '#menu-item-label',
      description: '#menu-item-notes',
      picture: '#menu-item-icon'
    }
  }
});

Alternate options And Associated Sources:

FAQs:

Q: Does BsNestedSortable require jQuery UI?

A: Sure. The drag layer calls jQuery UI Sortable, and the plugin extends jQuery objects with its personal traversal helpers. Load jQuery first, then jQuery UI, then BsNestedSortable.

Q: Why do the add, edit, and delete modals fail in Bootstrap 5?

A: The plugin calls the eliminated jQuery .modal() interface. Use Bootstrap 4 for the unique circulation or substitute these calls with Bootstrap 5 bootstrap.Modal situations.

Q: Can the plugin initialize from AJAX information?

A: Sure. Fetch the array first, then name BsNestedSortable({ information: rows }) after the request completes. Reinitialization can duplicate handlers. Create the tree as soon as per web page load.

Q: Why does a department transfer to the foundation after initialization?

A: Its mum or dad worth doesn’t match one other file ID. Repair the mum or dad reference or change rootID to match the foundation worth utilized by the info supply.

Q: Does the generated tree help keyboard drag controls and ARIA tree semantics?

A: The renderer doesn’t add ARIA tree roles or keyboard reordering controls. Add a separate accessible move-up, move-down, indent, and outdent interface when keyboard operation types a part of the challenge requirement.


This superior jQuery plugin is developed by karamelikli. For extra Superior Usages, please test the demo web page or go to the official web site.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments