It supports json data, loading via ajax and drag-and-drop.
JqTree is a jQuery widget for displaying a tree structure in html. It supports json data, loading via ajax and drag-and-drop.
The project is hosted on github.
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
});
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
});
}
);
Use cases or implementations of JqTree
This release drops support for very old browsers (like IE 11).
Small breaking change: this release changes the definition of getPreviousNode and getNextNode.
jquery
elementDetermine 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’.
Determine if text is autoescaped. The default is true.
Open nodes initially.
Open all nodes initially:
$('#tree1').tree({
data: data,
autoOpen: true
});
Open first level nodes:
$('#tree1').tree({
data: data,
autoOpen: 0
});
Set the position of the toggle button; can be true
(left) or false
(right). The default is true
.
$('#tree1').tree({
buttonLeft: false
});
A character or symbol to display on closed nodes. The default is ‘►’ (►)
The value can be a:
// 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" />') });
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});
label
instead of name
id
property is required if you use the multiple selection featureYou can also include other data in the objects. You can later access this data.
For example, to add an id:
{
name: 'node1',
id: 1
}
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'];
}
});
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'}
};
}
});
Turn on dragging and dropping of nodes.
Example: turn on drag and drop.
$('#tree1').tree({
data: data,
dragAndDrop: true
});
Enable or disable keyboard support. Default is enabled.
Example: disable keyboard support.
$('#tree1').tree({
keyboardSupport: false
});
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;
}
}
});
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;
}
}
});
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;
}
}
});
The function is called for each created node. You can use this to define extra html.
The function is called with the following parameters:
$('#tree1').tree({
data: data,
onCreateLi: function(node, $li, isSelected) {
// Add 'icon' span before title
$li.find('.jqtree-title').before('<span class="icon"></span>');
}
});
Function that is called when a node is dragged outside the tree. This function is called while the node is being dragged.
onDragStop
option.function handleMove(node: Node, e: JQueryEventObject) {
//
}
$tree.tree({
dragAndDrop: true,
onDragMove: handleMove,
});
Function that is called when a node is dragged outside the tree. This function is called when user stops dragging.
onDragMove
option.function handleStop(node: Node, e: JQueryEventObject) {
//
}
$tree.tree({
dragAndDrop: true,
onDragStop: handleMove,
});
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'));
}
});
When loading the data by ajax fails, then the option onLoadFailed is called.
$('#tree1').tree({
dataUrl: '/my/data/',
onLoadFailed: function(response) {
//
}
});
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)
li
elementul
element of the whole treeA character or symbol to display on opened nodes. The default is ‘▼’ (▼)
The value can be a:
// 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" />') });
Set the delay for opening a folder during drag-and-drop. The delay is in milliseconds. The default is 500 ms.
false
disables opening folders during drag-and-drop.$('#tree1').tree({
dataUrl: '/my/data/',
openFolderDelay: 1000
});
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>
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,
children: [
{
name: 'child1',
id: 124
}
]
}
$('#tree1').tree({
data: data,
saveState: true
});
Example: save state in key ‘tree1’:
$('#tree1').tree({
data: data,
saveState: 'tree1'
});
Turn on selection of nodes.
Example: turn off selection of nodes.
$('#tree1').tree({
data: data,
selectable: false
});
Example with option true:
const data = [
{
name: 'node1',
id: 123,
children: []
}
];
$('#tree1').tree({
data: data,
showEmptyFolder: true
});
Turn slide animation on or off. Default is true.
$('#tree1').tree({
slide: false
});
Sets the delay before drag-and-drop is started. The default is 300 milliseconds.
$tree.tree({
dragAndDrop: true,
startDndDelay: 3000,
});
Set the tabindex of the tree. The default is 0
.
Note that the tabindex is set to the selected node.
$('#tree1').tree({
tabIndex: 5
});
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.
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
);
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
);
function addNodeBefore(newNodeInfo, existingNode);
Add a new node before this existing node.
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
);
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);
function destroy();
Destroy the tree. This removes the dom elements and event bindings.
$('#tree1').tree('destroy');
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;
}
}
);
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);
function getNodeByHtmlElement(htmlElement);
Get a tree node by an html element. The html htmlElement should be:
li
element for the nodeli
. 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);
Get the selected node. Returns the row data or false.
var node = $tree.tree('getSelectedNode');
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.
function getTree();
Get the root node of the tree.
var treeData = $('#tree1').tree('getTree');
function isDragging();
Is currently a node being dragged for drag-and-drop? Returns True
or False
.
const isDragging = $('#tree1').tree('isDragging');
Return if this node is selected.
var node = $('#tree1').tree('getNodeById', 123);
var isSelected = $('#tree1').tree('isNodeSelected', node);
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);
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');
}
);
function moveDown()
Select the next node. This does the same as the down key.
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');
function moveUp()
Select the previous node. This does the same as the up key.
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);
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
);
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');
function reload();
function reload(onFinished);
Reload data from the server.
onFinished
when the data is loaded.$('#tree1').tree('reload');
$('#tree1').tree('reload', function() {
console.log('data is loaded');
});
function removeNode(node);
Remove node from the tree.
$('#tree1').tree('removeNode', node);
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
const node = $tree.tree('getNodeById', 123);
$tree.tree('selectNode', { mustSetFocus: false });
const node = $tree.tree('getNodeById', 123);
$tree.tree('selectNode', { mustToggle: false });
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);
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);
Set the state of the tree: which nodes are open and which one is selected?
See getState for more information.
function toggle(node);
function toggle(node, slide);
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);
function toJson();
Get the tree data as json.
// Assuming the tree exists
$('#tree1').tree('toJson');
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 }
]
}
);
Triggered when a tree node is clicked. The event contains the following properties:
// 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();
}
);
Called when a node is closed.
$('#tree1').on(
'tree.close',
function(e) {
console.log(e.node);
}
);
Triggered when the user right-clicks a tree node. The event contains the following properties:
// bind 'tree.contextmenu' event
$('#tree1').on(
'tree.contextmenu',
function(event) {
// The clicked node is 'event.node'
var node = event.node;
alert(node.name);
}
);
The tree.dblclick is fired when a tree node is double-clicked. The event contains the following properties:
$('#tree1').on(
'tree.dblclick',
function(event) {
// event.node is the clicked node
console.log(event.node);
}
);
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
}
);
Called after data is loaded using ajax.
$('#tree1').on(
'tree.load_data',
function(e) {
console.log(e.tree_data);
}
);
Called before and after data is loaded using ajax.
The event data looks like this:
Example code:
$('#tree1').on(
'tree.loading_data',
function(e) {
console.log(e.isLoading, e.node, e.$el);
}
);
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:
$('#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')});
}
);
The tree.refresh
event is triggered when the tree is repainted.
Examples when the tree.refresh
event is fired:
updateNode
method is calledCalled when a node is opened.
$('#tree1').on(
'tree.open',
function(e) {
console.log(e.node);
}
);
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
}
}
);
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.
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);
Get the selected nodes. Return an array of nodes
var node = $tree.tree('getSelectedNodes');
Remove this node from the selection.
var node = $('#tree1').tree('getNodeById', 123);
$('#tree1').tree('removeFromSelection', node);
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:
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];
}
function getData(includeParent = false);
Get the subtree of this node.
includeParent
var data = node.getData();
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();
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.
const nextNode = node.getNextNode();
Get the next sibling of this node. Returns a node or null.
const nextSibling = node.getNextSibling();
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.
const nextNode = node.getNextVisibleNode();
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.
const previousNode = node.getPreviousNode();
Get the previous sibling of this node. Returns a node or null.
const previousSibling = node.getPreviousSibling();
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.
const previousNode = node.getPreviousVisibleNode();
You can access the parent of a node using the parent property.
var parentNode = node.parent;