jqTree is a jQuery widget for displaying a tree

It supports json data, loading via ajax and drag-and-drop.

Introduction

JqTree is a jQuery widget for displaying a tree structure in html. It supports json data, loading via ajax and drag-and-drop.

NPM version

Features

  • Create a tree from JSON data
  • Load data using ajax
  • Drag and drop
  • Saves the state
  • Keyboard support
  • Lazy loading
  • Works on all modern browsers
  • Written in Typescript

The project is hosted on github.

Demo

var data = [
    {
        name: 'node1', id: 1,
        children: [
            { name: 'child1', id: 2 },
            { name: 'child2', id: 3 }
        ]
    },
    {
        name: 'node2', id: 4,
        children: [
            { name: 'child3', id: 5 }
        ]
    }
];
$('#tree1').tree({
    data: data,
    autoOpen: true,
    dragAndDrop: true
});

Requirements

Downloads

Tutorial

Include jQuery

<script src="jquery.min.js"></script>

Include tree.jquery.js:

<script src="tree.jquery.js"></script>

Include jqtree.css:

<link rel="stylesheet" href="jqtree.css">

Create a div.

<div id="tree1"></div>

Create tree data.

var data = [
    {
        name: 'node1',
        children: [
            { name: 'child1' },
            { name: 'child2' }
        ]
    },
    {
        name: 'node2',
        children: [
            { name: 'child3' }
        ]
    }
];

Create tree widget.

$(function() {
    $('#tree1').tree({
        data: data
    });
});

Alternatively, get the data from the server.

$.getJSON(
    '/some_url/',
    function(data) {
        $('#tree1').tree({
            data: data
        });
    }
);

Examples

Use cases

Use cases or implementations of JqTree

With AngularJS and FireBase
With CakePHP and OpenShift
With Spring MVC and Google App Engine

Changelog

1.8.8 (november 17 2024)

  • Issue #858: load on demand with empty children (thanks to Ivan Kolesov)

1.8.7 (november 2 2024)

  • Issue #853: fix dnd to first child (thanks to Oskar von Dungern)

1.8.6 (october 26 2024)

  • Issue #840: using the up and down keys scrolls the page
  • Issue #846: attach html element before calling onCreateLi

1.8.5 (september 28 2024)

  • Update packages

1.8.4 (july 19 2024)

  • Issue #819: update braces package to fix security issue

1.8.3 (may 7 2024)

  • Issue #805: fix injection vulnerability because of lodash.template
  • Issue #808: make the npm package smaller

1.8.2 (march 24 2024)

  • Issue #751: fix dnd regression (thanks to Tom De Ford)

1.8.1 (march 16 2024)

  • Issue #791: fix onCanMoveTo (thanks to Christophe Caron)

1.8.0 (november 26 2023)

This release drops support for very old browsers (like IE 11).

  • Issue #735: compile to es6. This decreases the file size to 49K (from 62K). This is the minified (not gzipped) version.
  • Issue #766: open parent nodes in the addToSelection method (thanks to Tmgarcia)

1.7.5 (october 21 2023)

  • Issue #734: fix autoscroll issue (thanks to WriterStat)
  • issue #736: fix empty folder for prepend

1.7.4 (september 24 2023)

  • Issue #675: support toggler on the right with multiline (thanks to Odungern)
  • Issue #751: fix inconsistent/incorrect drag & drop node highlighting (thanks to Tom De Ford)

1.7.3 (september 16 2023)

  • Issue #746: set jQuery as a peer dependency
  • Issue #747: fix eslint import error

1.7.2 (september 14 2023)

  • Issue #732: improve types of closedIcon and openedIcon
  • Issue #736: use the bundle in the package.json (thanks to TivoSoho)

1.7.1 (august 6 2023)

  • Issue #724: improve accessibility

1.7.0 (december 23 2022)

Small breaking change: this release changes the definition of getPreviousNode and getNextNode.

  • Issue #704: dispatch the tree.load_data after initializing the tree
  • Issue #706: change the functions for getting the next and previous nodes.
    • Rename getPreviousNode to getPreviousVisibleNode and getNextNode to getNextVisibleNode.
    • Add new getPreviousNode and getNextNode functions that ignores if a node is visible.

1.6.3 (august 7 2022)

  • Issue # 692: openFolderDelay is false disables opening folders during drag and drop

1.6.2 (december 15 2021)

  • Issue #662: fix left alignment for option buttonLeft: false (thanks to Odungern)
  • Issue #669: restore focus after load on demand (thanks to Jaccoky)

1.6.1 (june 24 2021)

  • Issues #652 and #655: add filter example
  • Issue #655: add refresh method

1.6.0 (february 9 2021)

  • Issue #647: improve performance of internal node lookup
    • Changed: getNodeById doesn’t convert strings to numbers anymore
  • Issue #648: fix drag-and-drop on ie11
1.5.3 (january 12 2021)
  • Issue #640: add startDndDelay option (thanks to Erik van Eykelen)
1.5.2 (october 25 2020)
  • Issue #621: decrease build size using Rollup
  • Issue #624: updateNode: check if the focus must be reset (thanks to Erik van Eykelen)
  • Issue #626: improve autoScroll when using the onCanMoveTo option (thanks to Dmytro Tarianyk)
1.5.1 (september 23 2020)
  • Issue #615: fix XSS vulnerability (thanks to Aimeos)
  • Issue #618: fix autoescape (thanks to Jorge Casas)
1.5.0 (september 6 2020)
  • Issue #603: improve performance of title rendering (thanks to Bekefi Gergely)
1.4.12 (november 11 2019)
  • Issue #579: have icon for empty folders (thanks to Andrew Hinde)
  • Issue #583: improve performance of deleting a node (thanks to maltmann-muc)
1.4.11 (july 22 2019)
  • Issue #571: add options to selectNode
1.4.10 (april 6 2019)
  • Issue #554: fix wrapping of long node titles (thanks to Damian Kulp)
  • Issue #557: devserver serves from root (thanks to Aaron Gray)
  • Issue #558: check for null parameters
1.4.9 (september 9 2018)
  • Issue #532: add tree.loading_data event (thanks to Theo Bot)
1.4.8 (july 23 2018)
  • Issue #537: SVG xlink as openedIcon/closedIcon disappear after click (thanks to Terry Ouyang)
  • Issue #538: add typescript type definitions
1.4.7 (june 16 2018)
  • Issue #531: updateNode must trigger tree.refresh (thanks to TivoSoho)
1.4.6 (may 8 2018)
  • Issue #525: add debug build (tree.jquery.debug.js) (thanks to Yurayko)
  • Issue #526: correctly load on demand if dataUrl is an object (thanks to Yurayko)
1.4.5 (march 14 2018)
  • Issue #515: do not use deprecated jquery functions
  • Issue #520: add mustSetFocus parameter to addToSelection (thanks to Romario Costegillio)
1.4.4 (december 21 2017)
  • Issue #506: use jQuery alias to fix conflict with Prototype.js
  • Issue #508: handle horizontal scrolling (thanks to Troels Holstein Kaa)
1.4.3 (october 22 2017)
  • Issue #503: add animationSpeed option
1.4.2 (july 15 2017)
  • Issue #489: make it possible to use nested element for icon (thanks to terryoy)
  • Issue #492: handle empty string for icon options
  • Issue #493: add tabIndex option (thanks to Aimeos)
  • Issue #494: remove tabindex if a node is deselected (thanks to Aimeos)
1.4.1 (june 5 2017)
  • Issue #487: setState must clear the selection (thanks to terryoy)
  • Issue #488: add is_selected parameter to onCreateLi
1.4.0 (may 12 2017)
  • Issue #481: fixed error that occured when opening an invalid node (thanks to Luciano Deriu)
  • Issue #482: changed keyboard logic (thanks to bnsblue)
    • Set focus to the selected node.
    • The keyboard is active if a tree node has the focus.
1.3.8 (april 12 2017)
  • Issue #471: use webpack instead of browserify
  • Issue #472: use ‘jquery.on’ instead of deprecated ‘bind’
  • Issue #478: use typescript instead of coffeescript
  • Issue #480: support jquery 3.2.x
1.3.7 (january 11 2017)
  • Issue 462: add on_finished parameter to openNode function
  • Issue 467: fix autoscroll when tree position changes after tree init (thanks to Themre)
1.3.6 (november 5 2016)
  • Issue 453: make it possible to initialize the tree without data
  • Issue 455: isNodeSelected must return boolean (thanks to Joshua Harrison)
1.3.5 (september 15 2016)
  • Issue 437: autoEscape option applies to dragged elements
  • Issue 448: calling updateNode with an empty children array should remove the children
1.3.4 (july 18 2016)
  • Issue 433: Added getNodeByHtmlElement function
  • Issue 435: Added getNodeByCallback function
  • Issue 437: Fix XSS issue in drag and drop
1.3.3 (may 30 2016)
  • Issue 420: Set the correct aria-level after updating a node (thanks to Fermin Gallego)
  • Issue 426 and 427: Make it possible to append a subtree using appendNode
    • also works for addNodeAfter, addNodeBefore, prependNode and updateNode
1.3.2 (march 3 2016)
  • Issue 412: Set ajax parameters in dataUrl option (thanks to Atul Bajpai)
  • Issue 415: Add onLoading callback parameter
1.3.0 (november 8 2015)
  • Issue 234: Added onDragMove and onDragStop callbacks
  • Issue 394: Label displays ‘null’ in UI when the label key is null (thanks to Carlos Ponce)
  • Issue 396: Trigger init.tree correctly for load on demand (thanks to jmashore)
  • Issue 397: Aria accessibility support (thanks to Rich Caloggero)
  • Issue 398: Functions should return the jquery element
  • Issue 400: Added on_finished parameter to reload function
  • Issue 402: Fix autoscroll when tree element is created outside the dom (thanks to hbaptiste)
1.2.1 (july 14 2015)
  • Issue 48: Added right-to-left support (thanks to Mohamed Ouederni, Ashraf Fayad and Udi Oron)
  • Issue 371: Added include_parent parameter to getData function
  • Issue 375: Fix conflict with prototype.js (thanks to Henry de Guzman)
1.1.0 (april 6 2015)
  • Issue 348: Circle for drag-and-drop is not positioned correctly for content box-sizing
  • Issue 356: Fixed drag-and-drop after nodeUpdate (thanks to Iker Vázquez)
  • Issue 360: Add a trailing semicolon to tree.jquery.js (thanks to David Thenon)
1.0.0 (january 11 2015)
  • Issues 273 and 304: autoOpen and saveState now works for nodes that are loaded on demand
  • Issue 283: Added getNodesByProperty function (thanks to Neeraj)
  • Issue 332: openNode should open parent nodes (thanks to Kitano Yoshitomo)
  • Issue 335: Add cloneNode missing argument for Gecko <13.0 (thanks to Tvinky)
  • Issue 337: Added functions for moving up and down
  • Issue 341: Fixed drag-and-drop border when nodes have padding (thanks to Alex Musayev)
0.22 (september 25 2014)
  • Issue 291: Dragging Item - do not open node if you don’t stay over it (thanks to Roman Klos)
  • Issue 300: Toggle function should get default slide value from options (thanks to Tazle)
  • Issue 303: Correctly set selected_node parameter in query string
  • Issue 315: Fix for issue when moving node over closed folder (thanks to terrybr)
  • Issue 320: Fixed error in drag and drop (thanks to Jerry Wu)
0.21 (june 7 2014)
  • Issue 263: Improve styling of toggle button
  • Issue 266: Make it possible to use html for toggle buttons
  • Issue 262: updateNode on first level makes node disappear (thanks to Miloš Đekić)
  • Issue 260: Exempt ‘select’ elements from keyboard navigation (thanks to Eli Flanagan)
  • Issue 270: Fixed error ‘selected_single_node is not defined’ (thanks to Bryan Smith)
  • Issue 279: .jqtree-moving removed when loading subtree (thanks to Marc-Stefan Cassola)
  • Issue 280: CSS3 Circle and style optimization (thanks to Marc-Stefan Cassola)
  • Issue 283: Added function getNodesByProperty (thanks to Cedrik Vanderhaegen)
  • Issue 292: Save state if multiple nodes are selected (thanks to MykhailoP)
  • Issue 294: Handle click on input element in tree (thanks to Naeco33)
0.20 (march 9 2014)
  • Issue 235: Added setOption function
  • Issue 241: Prevent duplicate event call after re-initalization
  • Issue 246: Check if folder must be opened while moving node (thanks to Dave Gardner)
  • Issue 247: Improve performance of updateNode (thanks to Gordon Woodhull)
  • Issue 250: Improve performance of creating dom elements (thanks to Carlos Scheidegger)
  • Issue 252: BorderDropHint has wrong height for border-box box-sizing (thanks to simshaun)
  • Issue 253: Added reload function
  • Issue 256: Toggler button is underlined
  • Issue 257: Make it possible to open a lazily loaded folder using the keyboard
  • Issue 258: Correctly unselect children if a node is reloaded
0.19 (december 8 2013)
  • Issue 225: Fixes TypeError when removing nodes without ids that aren’t selected (thanks to Marcus McCurdy)
  • Issue 222: scrollToNode does not consider direct parent
  • Issue 228: add property click_event to tree.click and tree.dblclick events (thanks to Gordon Woodhull)
  • Issue 78: Added option openFolderDelay: the delay for opening a folder during drag-and-drop (thanks to Jason Diamond)
0.18 (september 17 2013)
  • Issue 132: Skip keyboard handling if focus is on input element (thanks to Bingeling)
  • Issue 179 and 180: Added dataFilter option to filter the returned data from jQuery.ajax (thanks to Cheton Wu and Tony Dilger)
  • Issue 184: If the node id is 0, the id mapping is incorrect (thanks to Ika Wu)
  • Issue 190: The function selectNode should not toggle (thanks to Gordon Woodhull)
  • Issue 192: Added keyboardSupport option (thanks to Ika Wu)
  • Issue 181: Added tree.dblclick event (thanks to eskaigualker)
  • Issue 196: localStorage doesn’t work in Safari private browsing (thanks to thebagg)
  • Issue 203: Adding deselected_node attribute to event object of tree.select event (thanks to tedtoer)
0.17 (july 14 2013)
  • Issue 132: Added keyboard support
  • Issue 154: Calling loadDataFromUrl should not trigger tree.init event (thanks to Davide Bellini)
  • Issue 158: Index not updated on updateNode (thanks to Sam Mousa)
  • Issue 159: Cannot reselect node after unselecting it (thanks to Comanche)
  • Issue 162: Added getPreviousSibling and getNextSibling functions (thanks to Dimaninc)
  • Issue 169: Added touch support (thanks to Comanche)
  • Issue 171: Added functions getState and setState
  • Issue 175: Make it possible to install jqTree using bower (thanks to Adam Miskiewicz)
0.16 (may 17 2013)
  • Issue 62: Added functions for multiple select
  • Issue 125: Add option for overriding TRIANGLE_RIGHT and TRIANGLE_DOWN (thanks to Sam D)
  • Issue 126: Event tree.open event fires after first tree.select on top level node
  • Issue 129: Allow native context menu to be display (thanks to Charles Bourasseau)
  • Issue 130: Selectable not implemented (thanks to Sam D)
  • Issue 133: loadDataFromUrl doesn’t work when only parent_node and callback are passed in (thanks to Simone Deponti)
  • Issue 134: selectNode from inside tree.init breaks on loadData (thanks to Sam D)
  • Issue 145: Auto-open nodes with drag n drop when drag not enabled for that node (thanks to Daniel Powell)
  • Issue 146: Added function scrollToNode (thanks to Davide Bellini)
0.15 (march 16 2013)
  • Issue 100: Clicking on the jqtree-element element will trigger click event
  • Issue 102: Add original event to tree.move event
  • Issue 103: Added getLevel function to Node class
  • Issue 104: The addNodeBefore method must return the new node
  • Issue 105: Added nodeClass option
  • Issue 112: Fix call to iterate in removeNode (thanks to Ingemar Ådahl)
  • Issue 113: Added onLoadFailed option (thanks to Shuhei Kondo)
  • Issue 118: Deselect a node when click and already selected
  • Issue 119: Make it easier to reload a subtree
  • Issue 121: Unselect node if it’s reloaded by loadDataFromUrl
0.14 (december 2 2012)
Api changes
  • Removed parameter must_open_parents from function selectNode.
  • Changed slide parameter in functions openNode and closeNode.
Issues
  • Issue 80: Support more options for loading data from the server. E.g. the ‘post’ method (thanks to Rodrigo Rosenfeld Rosas)
  • Issue 81: getSelectedNode must return false if node is removed
  • Issue 82: Autoscroll for drag-and-drop
  • Issue 84: Fix correct type param in $.ajax() (thanks to Rodrigo Rosenfeld Rosas)
  • Issue 85: Option to turn slide animation on or off
  • Issue 86: The openNode function must automatically open parents
  • Issue 87: Remove the must_open_parents parameter from the selectNode function
  • Issue 88: selectNode must also work if selectable option is false
  • Issue 89: Clicking in title with img or em does not work
  • Issue 96: Added jqtree_common class to avoid css clashes (thanks to Yaniv Iny)
0.13 (october 10 2012)
  • Issue 54: Added tree.select event
  • Issue 63: Fixed contextmenu event
  • Issue 67: Use unicode characters for triangle buttons (thanks to Younès)
  • Issue 70: Load data from the server using the loadData function
  • Issue 78: Drag and drop is trigger happy
0.12 (august 14 2012)
  • Issue 46: Added tree.refresh event
  • Issue 47: Function ‘selectNode’ must properly open the parent nodes
  • Issue 49: Make sure that widget functions can be called in the ‘tree.init’ event
  • Issue 50: Add namespace to css classes
  • Issue 51: closeNode to collapse tree doesn’t work
  • Issue 55: Load-on-demand from the server
  • Issue 58: Added updateNode function
  • Issue 59: Added moveNode function
  • Issue 60: Use native JSON.stringify function
0.11 (july 8 2012)
  • Autoescape text
  • Added autoEscape option
  • Issue 33: appendNode does not correctly refresh the tree
  • Issue 34: unset internal pointer to previously selected node on DOM deselect
  • Issue 38: Correctly check if browser has support for localstorage
  • Issue 41: Open nodes are not displayed correctly in ie7
0.10 (june 10 2012)
  • Optimized getNodeById
  • Issue #18 and #26: Made comparison in getNodeById less strict
  • Added function prependNode
  • Added ‘data-url’ option
  • Added removeNode function
  • Issue #24: Tree with jquery ui Dialog: expand causes resize and move problem
  • Added Travis ci support
  • Added addNodeAfter, addNodeBefore and addParentNode
  • Renamed icons.png to jqtree-icons.png
  • selectNode with empty node deselects the current node
0.9 (may 9 2012)
  • Issue 15: ‘tree.open’ event is not triggered when dragging nodes
  • Issue 18: Allow moveNode to be canceled through ev.preventDefault()
  • Use sprite for images
  • Added function closeNode
  • Added support for localstorage
  • Implemented alternative data format
0.8 (april 18 2012)
  • Replace jquery.ui widget with SimpleWidget
  • Added ‘previous_parent’ to ‘tree.move’ event
  • Add posibility to load subtree
  • Added ‘tree.open’ and ‘tree.close’ events

Tree options

animationSpeed

Determine the speed of the slide animation. The value can be a number in millseconds, or a string like ‘slow’ or ‘fast’. The default is ‘fast’.

autoEscape

Determine if text is autoescaped. The default is true.

autoOpen

Open nodes initially.

  • true: open all nodes.
  • false (default): do nothing
  • n: open n levels

Open all nodes initially:

$('#tree1').tree({
    data: data,
    autoOpen: true
});

Open first level nodes:

$('#tree1').tree({
    data: data,
    autoOpen: 0
});

buttonLeft

Set the position of the toggle button; can be true (left) or false (right). The default is true.

$('#tree1').tree({
    buttonLeft: false
});

closedIcon

A character or symbol to display on closed nodes. The default is ‘&#x25ba;’ (►)

The value can be a:

  • string. E.g. a unicode character or a text.
    • The text is escaped.
  • html element. E.g. for an icon
  • JQuery element. Also for an icon
// String
$('#tree1').tree({ closedIcon: '+' });

// Html element
const icon = document.createElement("span");
icon.className = "icon test";
$('#tree1').tree({ closedIcon: icon });

// JQuery element
$('#tree1').tree({ closedIcon: $('<span class="icon test" />') });

data

Define the contents of the tree. The data is a nested array of objects. This option is required.

It looks like this:

var data = [
    {
        name: 'node1',
        children: [
            { name: 'child1' },
            { name: 'child2' }
        ]
    },
    {
        name: 'node2',
        children: [
            { name: 'child3' }
        ]
    }
];
$('#tree1').tree({data: data});
  • name: name of a node (required)
    • Note that you can also use label instead of name
  • children: array of child nodes (optional)
  • id: int or string (optional)
    • Must be an int or a string
    • Must be unique in the tree
    • The id property is required if you use the multiple selection feature

You can also include other data in the objects. You can later access this data.

For example, to add an id:

{
    name: 'node1',
    id: 1
}

dataFilter

Process the tree data from the server.

$('#tree1').tree({
    dataUrl: '/my/data/',
    dataFilter: function(data) {
        // Example:
        // the server puts the tree data in 'my_tree_data'
        return data['my_tree_data'];
    }
});

dataUrl

Load the node data from this url.

$('#tree1').tree({
   dataUrl: '/example_data.json'
});

You can also set the data-url attribute on the dom element:

<div id="tree1" data-url="/example_data.json"></div>
<script>
    $('#tree1').tree();
</script>

You can set additional jquery ajax options in an object:

$('#tree1').tree({
   dataUrl: {
       url: '/example_data.json',
       headers: {'abc': 'def'}
   }
});

Or you can use a function:

$('#tree1').tree({
   dataUrl: function(node) {
       return {
           url: '/example_data.json',
           headers: {'abc': 'def'}
       };
   }
});

dragAndDrop

Turn on dragging and dropping of nodes.

  • true: turn on drag and drop
  • false (default): do not allow drag and drop

Example: turn on drag and drop.

$('#tree1').tree({
    data: data,
    dragAndDrop: true
});

keyboardSupport

Enable or disable keyboard support. Default is enabled.

Example: disable keyboard support.

$('#tree1').tree({
    keyboardSupport: false
});

onCanMove

You can override this function to determine if a node can be moved.

$('#tree1').tree({
    data: data,
    dragAndDrop: true,
    onCanMove: function(node) {
        if (! node.parent.parent) {
            // Example: Cannot move root node
            return false;
        }
        else {
            return true;
        }
    }
});

onCanMoveTo

You can override this function to determine if a node can be moved to a certain position.

$('#tree1').tree({
    data: data,
    dragAndDrop: true,
    onCanMoveTo: function(movedNode, targetNode, position) {
        if (targetNode.isMenu) {
            // Example: can move inside menu, not before or after
            return (position == 'inside');
        }
        else {
            return true;
        }
    }
});

onCanSelectNode

You can set a function to override if a node can be selected. The function gets a node as parameter, and must return true or false.

For this to work, the option ‘selectable’ must be ‘true’.

// Example: nodes with children cannot be selected
$('#tree1').tree({
    data: data,
    selectable: true
    onCanSelectNode: function(node) {
        if (node.children.length == 0) {
            // Nodes without children can be selected
            return true;
        }
        else {
            // Nodes with children cannot be selected
            return false;
        }
    }
});

onCreateLi

The function is called for each created node. You can use this to define extra html.

The function is called with the following parameters:

  • node: Node element
  • $li: Jquery li element
  • isSelected: is the node selected (true/false)
$('#tree1').tree({
    data: data,
    onCreateLi: function(node, $li, isSelected) {
        // Add 'icon' span before title
        $li.find('.jqtree-title').before('<span class="icon"></span>');
    }
});

onDragMove

Function that is called when a node is dragged outside the tree. This function is called while the node is being dragged.

  • Also see the onDragStop option.
  • The function signature is function(node, event);
function handleMove(node: Node, e: JQueryEventObject) {
    //
}

$tree.tree({
    dragAndDrop: true,
    onDragMove: handleMove,
});

onDragStop

Function that is called when a node is dragged outside the tree. This function is called when user stops dragging.

  • Also see the onDragMove option.
  • The function signature is function(node, event);
function handleStop(node: Node, e: JQueryEventObject) {
    //
}

$tree.tree({
    dragAndDrop: true,
    onDragStop: handleMove,
});

onIsMoveHandle

You can override this function to determine if a dom element can be used to move a node.

$('#tree1').tree({
    data: data,
    onIsMoveHandle: function($element) {
        // Only dom elements with 'jqtree-title' class can be used
        // as move handle.
        return ($element.is('.jqtree-title'));
    }
});

onLoadFailed

When loading the data by ajax fails, then the option onLoadFailed is called.

$('#tree1').tree({
    dataUrl: '/my/data/',
    onLoadFailed: function(response) {
        //
    }
});

onLoading

The onLoading parameter is called when the tree data is loading. This gives us the opportunity to display a loading signal.

Callback looks like this:

function (isLoading, node, $el)
  • isLoading: boolean
    • true: data is loading
    • false: data is loaded
  • node:
    • Node: if a node is loading
    • null: if the tree is loading
  • $el:
    • if a node is loading this is the li element
    • if the tree is loading is the ul element of the whole tree

openedIcon

A character or symbol to display on opened nodes. The default is ‘&#x25bc;’ (▼)

The value can be a:

  • string. E.g. a unicode character or a text.
    • The text is escaped.
  • html element. E.g. for an icon
  • JQuery element. Also for an icon
// String
$('#tree1').tree({ openedIcon: '-' });

// Html element
const icon = document.createElement("span");
icon.className = "icon test";
$('#tree1').tree({ openedIcon: icon });

// JQuery element
$('#tree1').tree({ openedIcon: $('<span class="icon test" />') });

openFolderDelay

Set the delay for opening a folder during drag-and-drop. The delay is in milliseconds. The default is 500 ms.

  • Setting the option to false disables opening folders during drag-and-drop.
$('#tree1').tree({
    dataUrl: '/my/data/',
    openFolderDelay: 1000
});

rtl

Set right-to-left support (true / false). Default is false.

$('#tree1').tree({
    rtl: true
});

You can also set the option using data-rtl.

    <div id="tree1" data-url="/data" data-rtl></div>

saveState

Save and restore the state of the tree automatically. The state is saved in localstorage.

For this to work, you should give each node in the tree data an id field:

{
    name: 'node1',
    id: 123,
    childen: [
        name: 'child1',
        id: 124
    ]
}
  • true: save and restore state in localstorage
  • false (default): do nothing
  • string: save state and use this name to store
$('#tree1').tree({
    data: data,
    saveState: true
});

Example: save state in key ‘tree1’:

$('#tree1').tree({
    data: data,
    saveState: 'tree1'
});

selectable

Turn on selection of nodes.

  • true (default): turn on selection of nodes
  • false: turn off selection of nodes

Example: turn off selection of nodes.

$('#tree1').tree({
    data: data,
    selectable: false
});

showEmptyFolder

  • true: A node with empty children is considered a folder. Meaning: the node has a ‘children’ attribute, but it’s an empty array.
    • Show folder icon
    • Folder can be opened and closed
  • false (default): A node with empty children is considered a child node

Example with option true:

const data = [
    {
        name: 'node1',
        id: 123,
        children: []
    }
];

$('#tree1').tree({
    data: data,
    showEmptyFolder: true
});

slide

Turn slide animation on or off. Default is true.

$('#tree1').tree({
    slide: false
});

startDndDelay

Sets the delay before drag-and-drop is started. The default is 300 milliseconds.

$tree.tree({
  dragAndDrop: true,
  startDndDelay: 3000,
});

tabIndex

Set the tabindex of the tree. The default is 0.

Note that the tabindex is set to the selected node.

$('#tree1').tree({
    tabIndex: 5
});

useContextMenu

Bind the context menu event (true/false).

true (default)

A right mouse-click will trigger a tree.contextmenu event. This overrides the native contextmenu of the browser.

false

A right mouse-click will trigger the native contextmenu of the browser.

Functions

addParentNode

function addParentNode(newNodeInfo, existingNode);

Add a new node as parent of this existing node.

var node1 = $('#tree1').tree('getNodeByName', 'node1');
$('#tree1').tree(
    'addParentNode',
    {
        name: 'new_parent',
        id: 456
    },
    node1
);

addNodeAfter

function addNodeAfter(newNodeInfo, existingNode);

Add a new node after this existing node.

var node1 = $('#tree1').tree('getNodeByName', 'node1');
$('#tree1').tree(
    'addNodeAfter',
    {
        name: 'new_node',
        id: 456
    },
    node1
);

addNodeBefore

function addNodeBefore(newNodeInfo, existingNode);

Add a new node before this existing node.

appendNode

function appendNode(newNodeInfo, parentNode);

Add a node to this parent node. If parentNode is empty, then the new node becomes a root node.

var parentNode = $tree.tree('getNodeById', 123);

$tree.tree(
    'appendNode',
    {
        name: 'new_node',
        id: 456
    },
    parentNode
);

To add a root node, leave parent_node empty:

$tree.tree(
    'appendNode',
    {
        name: 'new_node',
        id: 456
    }
);

It’s also possible to append a subtree:

$tree.tree(
    'appendNode',
    {
        name: 'new_node',
        id: 456,
        children: [
            { name: 'child1', id: 457 },
            { name: 'child2', id: 458 }
        ]
    },
    parentNode
);

closeNode

function closeNode(node);

function closeNode(node, slide);

Close this node. The node must have child nodes.

Parameter slide: close the node using a slide animation (default is true).

var node = $tree.tree('getNodeById', 123);
$tree.tree('closeNode', node);

To close the node without the slide animation, call with slide parameter is false.

$tree.tree('closeNode', node, false);

destroy

function destroy();

Destroy the tree. This removes the dom elements and event bindings.

$('#tree1').tree('destroy');

getNodeByCallback

function getNodeByCallback(callback);

Get a tree node using a callback. The callback should return true if the node is found.

var node = $('#tree1').tree(
  'getNodeByCallback',
  function(node) {
      if (node.name == 'abc') {
          // Node is found; return true
          return true;
      }
      else {
          // Node not found; continue searching
          return false;
      }
  }
);

getNodeById

function getNodeById(id);

Get a tree node by node-id. This assumes that you have given the nodes in the data a unique id.

var $tree = $('#tree1');
var data = [
    { id: 10, name: 'n1' },
    { id: 11, name: 'n2' }
];

$tree.tree({
    data: data
});
var node = $tree.tree('getNodeById', 10);

getNodeByHtmlElement

function getNodeByHtmlElement(htmlElement);

Get a tree node by an html element. The html htmlElement should be:

  • The li element for the node
  • Or, an element inside the li. For example the span for the title.
var element = document.querySelector('#tree1 .jqtree-title');

var node = $('#tree1').tree('getNodeByHtmlElement', element);

console.log(node);

The element can also be a jquery element:

var $element = $('#tree1 .jqtree-title');

var node = $('#tree1').tree('getNodeByHtmlElement', $element);

console.log(node);

getSelectedNode

Get the selected node. Returns the row data or false.

var node = $tree.tree('getSelectedNode');

getState

Get the state of tree: which nodes are open and which one is selected?

Returns a javascript object that contains the ids of open nodes and selected nodes:

{
    open_nodes: [1, 2, 3],
    selected_node: [4, 5, 6]
}

If you want to use this function, then your tree data should include an id property for each node.

You can use this function in combination with setState to save and restore the tree state.

getTree

function getTree();

Get the root node of the tree.

var treeData = $('#tree1').tree('getTree');

isDragging

function isDragging();

Is currently a node being dragged for drag-and-drop? Returns True or False.

const isDragging = $('#tree1').tree('isDragging');

isNodeSelected

Return if this node is selected.

var node = $('#tree1').tree('getNodeById', 123);
var isSelected = $('#tree1').tree('isNodeSelected', node);

loadData

function loadData(data);

function loadData(data, parentNode);

Load data in the tree. The data is array of nodes.

You can replace the whole tree or you can load a subtree.

// Assuming the tree exists
var newData = [
    {
        name: 'node1',
        children: [
            { name: 'child1' },
            { name: 'child2' }
        ]
    },
    {
        name: 'node2',
        children: [
            { name: 'child3' }
        ]
    }
];
$('#tree1').tree('loadData', newData);

Load a subtree:

// Get node by id (this assumes that the nodes have an id)
var node = $('#tree1').tree('getNodeById', 100);

// Add new nodes
var data = [
    { name: 'new node' },
    { name: 'another new node' }
];
$('#tree1').tree('loadData', data, node);

loadDataFromUrl

function loadDataFromUrl(url);

function loadDataFromUrl(url, parentNode);

function loadDataFromUrl(parentNode);

Load data in the tree from an url using ajax. You can replace the whole tree or you can load a subtree.

$('#tree1').tree('loadDataFromUrl', '/category/tree/');

Load a subtree:

var node = $('#tree1').tree('getNodeById', 123);
$('#tree1').tree('loadDataFromUrl', '/category/tree/123', node);

You can also omit the url. In this case jqTree will generate a url for you. This is very useful if you use the load-on-demand feature:

var $tree = $('#tree1');

$tree.tree({
    dataUrl: '/my_data/'
});

var node = $tree.tree('getNodeById', 456);

// jqTree will load data from /my_data/?node=456
$tree.tree('loadDataFromUrl', node);

You can also add an on_finished callback parameter that will be called when the data is loaded:

function loadDataFromUrl(url, parentNode, onFinished);

function loadDataFromUrl(parentNode, onFinished);

$('#tree1').tree(
    'loadDataFromUrl',
    '/category/tree/123',
    null,
    function() {
        alert('data is loaded');
    }
);

moveDown

function moveDown()

Select the next node. This does the same as the down key.

moveNode

function moveNode(node, targetNode, position);

Move a node. Position can be ‘before’, ‘after’ or ‘inside’.

var node = $tree.tree('getNodeById', 1);
var targetNode = $tree.tree('getNodeById', 2);

$tree.tree('moveNode', node, targetNode, 'after');

moveUp

function moveUp()

Select the previous node. This does the same as the up key.

openNode

function openNode(node);

function openNode(node, slide);

function openNode(node, onFinished);

function openNode(node, slide, onFinished);

Open this node. The node must have child nodes.

Parameter slide (optional): open the node using a slide animation (default is true). Parameter onFinished (optional): callback when the node is opened; this also works for nodes that are loaded lazily

// create tree
var $tree = $('#tree1');
$tree.tree({
    data: data
});

var node = $tree.tree('getNodeById', 123);
$tree.tree('openNode', node);

To open the node without the slide animation, call with slide parameter is false.

$tree.tree('openNode', node, false);

Example with on_finished callback:

function handleOpened(node) {
    console.log('openende node', node.name);
}

$tree.tree('openNode', node, handleOpened);

prependNode

function prependNode(newNodeInfo, parentNode);

Add a node to this parent node as the first child. If parentNode is empty, then the new node becomes a root node.

var parentNode = $tree.tree('getNodeById', 123);

$tree.tree(
    'prependNode',
    {
        name: 'new_node',
        id: 456
    },
    parentNode
);

refresh

function refresh();

Refresh the rendered nodes. In most cases you will not use this, because tree functions will rerender automatically. E.g. The functions openNode and updateNode rerender automatically.

$('#tree1').tree('refresh');

reload

function reload();

function reload(onFinished);

Reload data from the server.

  • Call onFinished when the data is loaded.
$('#tree1').tree('reload');
$('#tree1').tree('reload', function() {
    console.log('data is loaded');
});

removeNode

function removeNode(node);

Remove node from the tree.

$('#tree1').tree('removeNode', node);

selectNode

function selectNode(node);

function selectNode(null);

function selectNode(node, { mustToggle, mustSetFocus });

Select this node.

You can deselect the current node by calling selectNode(null).

// create tree
const $tree = $('#tree1');
$tree.tree({
    data: data,
    selectable: true
});

const node = $tree.tree('getNodeById', 123);
$tree.tree('selectNode', node);

Options

  • mustSetFocus:
    • true (default): set the focus to the node; only do this on selection, not deselection
    • false: do not set the focus
  • mustToggle:
    • true (default): toggle; deselected if selected and vice versa
    • false: select the node, never deselect
 const node = $tree.tree('getNodeById', 123);
 $tree.tree('selectNode', { mustSetFocus: false });
 
 const node = $tree.tree('getNodeById', 123);
 $tree.tree('selectNode', { mustToggle: false });
 

scrollToNode

function scrollToNode(node);

Scroll to this node. This is useful if the tree is in a container div and is scrollable.

var node = $tree.tree('getNodeById', 1);
$tree.tree('scrollToNode', node);

setOption

function setOption(option, value);

Set a tree option. These are the same options that you can set when creating the tree.

$('#tree1').tree('setOption', 'keyboardSupport', false);

setState

Set the state of the tree: which nodes are open and which one is selected?

See getState for more information.

toggle

function toggle(node);

function toggle(node, slide);

  • slide: true / false

Open or close the tree node.

Default: toggle with slide animation:

var node = $tree.tree('getNodeById', 123);
$tree.tree('toggle', node);

Toggle without animation:

$tree.tree('toggle', node, false);

toJson

function toJson();

Get the tree data as json.

// Assuming the tree exists
$('#tree1').tree('toJson');

updateNode

function updateNode(node, name);

function updateNode(node, data);

Update the title of a node. You can also update the data.

Update the name:

var node = $tree.tree('getNodeById', 123);

$tree.tree('updateNode', node, 'new name');

Update the data (including the name)

var node = $tree.tree('getNodeById', 123);

$tree.tree(
    'updateNode',
    node,
    {
        name: 'new name',
        id: 1,
        otherProperty: 'abc'
    }
);

It is also possible to update the children. Note that this removes the existing children:

$tree.tree(
    'updateNode',
    node,
    {
        name: 'new name',
        id: 1,
        children: [
            { name: 'child1', id: 2 }
        ]
    }
);

Events

tree.click

Triggered when a tree node is clicked. The event contains the following properties:

  • node: the node that is clicked on
  • click_event: the original click event
// create tree
$('#tree1').tree({
    data: data
});

// bind 'tree.click' event
$('#tree1').on(
    'tree.click',
    function(event) {
        // The clicked node is 'event.node'
        var node = event.node;
        alert(node.name);
    }
);

The default action is to select the node. You can prevent the selection by calling preventDefault:

$('#tree1').on(
    'tree.click',
    function(event) {
        event.preventDefault();
    }
);

tree.close

Called when a node is closed.

$('#tree1').on(
    'tree.close',
    function(e) {
        console.log(e.node);
    }
);

tree.contextmenu

Triggered when the user right-clicks a tree node. The event contains the following properties:

  • node: the node that is clicked on
  • click_event: the original click event
// bind 'tree.contextmenu' event
$('#tree1').on(
    'tree.contextmenu',
    function(event) {
        // The clicked node is 'event.node'
        var node = event.node;
        alert(node.name);
    }
);

tree.dblclick

The tree.dblclick is fired when a tree node is double-clicked. The event contains the following properties:

  • node: the node that is clicked on
  • click_event: the original click event
$('#tree1').on(
    'tree.dblclick',
    function(event) {
        // event.node is the clicked node
        console.log(event.node);
    }
);

tree.init

Called when the tree is initialized. This is particularly useful when the data is loaded from the server.

$('#tree1').on(
    'tree.init',
    function() {
        // initializing code
    }
);

tree.load_data

Called after data is loaded using ajax.

$('#tree1').on(
    'tree.load_data',
    function(e) {
        console.log(e.tree_data);
    }
);

tree.loading_data

Called before and after data is loaded using ajax.

The event data looks like this:

  • isLoading: true / false
  • node:
    • null; when loading the whole tree
    • a node; when a node is loaded on demand
  • $el: dom element
    • whole tree; when loading the whole tree
    • dom element of node; when a node is loaded on demand

Example code:

$('#tree1').on(
    'tree.loading_data',
    function(e) {
        console.log(e.isLoading, e.node, e.$el);
    }
);

tree.move

Triggered when the user moves a node.

Note that this event is called before the node is moved. See note about do_move below.

Event.move_info contains:

  • moved_node
  • target_node
  • position: (before, after or inside)
  • previous_parent
$('#tree1').tree({
    data: data,
    dragAndDrop: true
});

$('#tree1').on(
    'tree.move',
    function(event) {
        console.log('moved_node', event.move_info.moved_node);
        console.log('target_node', event.move_info.target_node);
        console.log('position', event.move_info.position);
        console.log('previous_parent', event.move_info.previous_parent);
    }
);

You can prevent the move by calling event.preventDefault()

$('#tree1').on(
    'tree.move',
    function(event) {
        event.preventDefault();
    }
);

You can later call event.move_info.move_info.do_move() to move the node. This way you can ask the user before moving the node:

$('#tree1').on(
    'tree.move',
    function(event) {
        event.preventDefault();

        if (confirm('Really move?')) {
            event.move_info.do_move();
        }
    }
);

Note that if you want to serialise the tree, for example to POST back to a server, you need to let tree complete the move first:

$('#tree1').on(
    'tree.move',
    function(event)
    {
        event.preventDefault();
        // do the move first, and _then_ POST back.
        event.move_info.do_move();
        $.post('your_url', {tree: $(this).tree('toJson')});
    }
);

tree.refresh

The tree.refresh event is triggered when the tree is repainted.

Examples when the tree.refresh event is fired:

  • after the first draw of the tree
  • after a node is moved
  • after the updateNode method is called

tree.open

Called when a node is opened.

$('#tree1').on(
    'tree.open',
    function(e) {
        console.log(e.node);
    }
);

tree.select

Triggered when a tree node is selected or deselected.

If a node is selected, then event.node contains the selected node.

If a node is deselected, then the event.node property is null.

$('#tree1').on(
    'tree.select',
    function(event) {
        if (event.node) {
            // A node was selected
            const node = event.node;
            alert(node.name);
        }
        else {
            // event.node is null
            // A node was deselected
            // event.previous_node contains the deselected node
        }
    }
);

Multiple selection

Jqtree has some functions that can help you to implement multiple selection. See Example 8 - multiple select.

In order for multiple selection to work, you must give the nodes an id.

addToSelection

Add this node to the selection. Also set the focus to the node.

function addToSelection(node, mustSetFocus = true);

Parameter mustSetFocus: set the focus to the node (default true).

var node = $('#tree1').tree('getNodeById', 123);
$('#tree1').tree('addToSelection', node);

Without setting the focus:

$('#tree1').tree('addToSelection', node, false);

getSelectedNodes

Get the selected nodes. Return an array of nodes

var node = $tree.tree('getSelectedNodes');

removeFromSelection

Remove this node from the selection.

var node = $('#tree1').tree('getNodeById', 123);
$('#tree1').tree('removeFromSelection', node);

Node functions

You can access a node using for example getNodeById function:

var node = $('#tree1').tree('getNodeById', 123);

The Node object has the following properties and functions:

children

You can access the children of a node using the children property.

for (var i=0; i < node.children.length; i++) {
    var child = node.children[i];
}

getData

function getData(includeParent = false);

Get the subtree of this node.

includeParent

  • true: include node and children
  • false: only include children (default)
var data = node.getData();

getLevel

Get the level of a node. The level is distance of a node to the root node.

var node = $('#tree1').tree('getNodeById', 123);

// result is e.g. 2
var level = node.getLevel();

getNextNode

Get the next node in the tree. This is the next sibling, if there is one. Or, if there is no next sibling, a node further down in the tree.

  • Returns a node or null.
const nextNode = node.getNextNode();

getNextSibling

Get the next sibling of this node. Returns a node or null.

const nextSibling = node.getNextSibling();

getNextVisibleNode

Get the next visible node in the tree. Does the same as using the down key.

This is the previous sibling, if there is one. Or, if there is no previous sibling, a node further up in the tree that is visible.

  • Returns a node or null.
  • A node is visible if all its parents are open.
const nextNode = node.getNextVisibleNode();

getPreviousNode

Return the previous node in the tree. This is the previous sibling, if there is one. Or, if there is no previous sibling, a node further up in the tree.

  • Returns a node or null.
const previousNode = node.getPreviousNode();

getPreviousSibling

Get the previous sibling of this node. Returns a node or null.

const previousSibling = node.getPreviousSibling();

getPreviousVisibleNode

Get the previous visible node in the tree. Does the same as using the up key.

This is the previous sibling, if there is one. Or, if there is no previous sibling, a node further up in the tree that is visible.

  • Returns a node or null.
  • A node is visible if all its parents are open.
const previousNode = node.getPreviousVisibleNode();

parent

You can access the parent of a node using the parent property.

var parentNode = node.parent;