Introduction
JqTree is a jQuery widget for displaying a tree structure in html. It supports json data, loading via ajax and drag-and-drop.
Features
- Create a tree from JSON data
- Load data using ajax
- Drag and drop
- Saves the state
- Keyboard support
- Lazy loading
- Works on ie8+, firefox 3.6+, chrome and safari
- Written in Coffeescript
The project is hosted on github, has a test suite.
Demo
var data = [
{
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
},
{
label: 'node2',
children: [
{ label: 'child3' }
]
}
];
$('#tree1').tree({
data: data,
autoOpen: true,
dragAndDrop: true
});
Requirements
- jQuery 1.5+ or 2.0
Downloads
- All (version 0.21): jqTree.tar.gz
- Javascript: tree.jquery.js
- Css: jqtree.css
- Image: jqtree-circle.png
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">
Optionally, for saveState include jquery-cookie:
<script src="jquery.cookie.js"></script>
Create a div.
<div id="tree1"></div>
Create tree data.
var data = [
{
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
},
{
label: 'node2',
children: [
{ label: '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
- Example 1 - load json data
- Example 2 - load json data from the server
- Example 3 - drag and drop
- Example 4 - save the state
- Example 5 - load nodes on demand from the server
- Example 6 - autoEscape
- Example 7 - autoscroll
- Example 8 - multiple select
- Example 9 - custom html
- Example 10 - use icon toggle buttons
Changelog
0.21 (june 7 2014) version
- 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)
- 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
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
});
closedIcon
A character or symbol to display on closed nodes. The default is '►' (►)
$('#tree1').tree({
closedIcon: '+'
});
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 = [
{
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
},
{
label: 'node2',
children: [
{ label: 'child3' }
]
}
];
$('#tree1').tree({data: data});
- label: label of a node (required)
- children: array of child nodes (optional)
You can also include other data in the objects. You can later access this data.
For example, to add an id:
{
label: 'node1',
id: 1
}
dataFilter
Process the tree data from the server.
$('#tree1').tree({
url: '/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>
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(moved_node, target_node, position) {
if (target_node.is_menu) {
// 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.
$('#tree1).tree({
data: data,
onCreateLi: function(node, $li) {
// Add 'icon' span before title
$li.find('.jqtree-title').before('<span class="icon"></span>');
}
});
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({
url: '/my/data/',
onLoadFailed: function(response) {
//
}
});
openedIcon
A character or symbol to display on opened nodes. The default is '▼' (▼)
$('#tree1').tree({
openedIcon: '-'
});
openFolderDelay
Set the delay for opening a folder during drag-and-drop. The delay is in milliseconds. The default is 500 ms.
$('#tree1').tree({
url: '/my/data/',
openFolderDelay: 1000
});
saveState
Save and restore the state of the tree automatically. Saves in a cookie which nodes are opened and selected.
The state is saved in localstorage. In browsers that do not support localstorage, the state is saved in a cookie.
For this to work, please include jquery-cookie.
For this to work, you should give each node in the tree data an id field:
{
label: 'node1',
id: 123,
childen: [
label: 'child1',
id: 124
]
}
- true: save and restore state in a cookie
- false (default): do nothing
- string: save state and use this name to store in a cookie
$('#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
});
slide
Turn slide animation on or off. Default is true.
$('#tree1').tree({
slide: false
});
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
addNodeAfter
function addNodeAfter(new_node_info, existing_node);
Add a new node after this existing node.
var node1 = $('#tree1', 'getNodeByName', 'node1');
$('#tree1').tree(
'addNodeAfter',
{
label: 'new_node',
id: 456
},
node1
);
addNodeBefore
function addNodeBefore(new_node_info, existing_node);
Add a new node before this existing node.
appendNode
function appendNode(new_node_info, parent_node);
Add a node to this parent node. If parent_node is empty, then the new node becomes a root node.
var parent_node = $tree.tree('getNodeById', 123);
$tree.tree(
'appendNode',
{
label: 'new_node',
id: 456
},
parent_node
);
To add a root node, leave *parent_node* empty:
$tree.tree(
'appendNode',
{
label: 'new_node',
id: 456
}
);
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);
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);
getSelectedNode
Get the selected node. Returns the row data or false.
var node = $tree.tree('getSelectedNode');
getState
function getState();
Get the state of tree: which nodes are open and which one is selected?
getTree
function getTree();
Get the root node of the tree.
loadData
function loadData(data);
function loadData(data, parent_node);
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 new_data = [
{
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
},
{
label: 'node2',
children: [
{ label: 'child3' }
]
}
];
$('#tree1').tree('loadData', new_data);
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 = [
{ label: 'new node' },
{ label: 'another new node' }
];
$('#tree1').tree('loadData', data, node);
loadDataFromUrl
function loadDataFromUrl(url);
function loadDataFromUrl(url, parent_node);
function loadDataFromUrl(parent_node);
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, parent_node, on_finished);
function loadDataFromUrl(parent_node, on_finished);
$('#tree1').tree(
'loadDataFromUrl',
'/category/tree/123',
null,
function() {
alert('data is loaded');
}
);
moveNode
function moveNode(node, target_node, position);
Move a node. Position can be 'before', 'after' or 'inside'.
var node = $tree.tree('getNodeById', 1);
var target_node = $tree.tree('getNodeById', 2);
$tree.tree('moveNode', node, target_node, 'after');
openNode
function openNode(node);
function openNode(node, slide);
Open this node. The node must have child nodes.
Parameter slide: open the node using a slide animation (default is true).
// 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);
addParentNode
function addParentNode(new_node_info, existing_node);
Add a new node as parent of this existing node.
var node1 = $('#tree1', 'getNodeByName', 'node1');
$('#tree1').tree(
'addParentNode',
{
label: 'new_parent',
id: 456
},
node1
);
reload
function reload();
Reload data.
$('#tree1').tree('reload');
removeNode
function removeNode(node);
Remove node from the tree.
$('#tree1').tree('removeNode', node);
selectNode
function selectNode(node);
function selectNode(null);
Select this node.
You can deselect the current node by calling selectNode(null).
// create tree
var $tree = $('#tree1');
$tree.tree({
data: data,
selectable: true
});
var node = $tree.tree('getNodeById', 123);
$tree.tree('selectNode', node);
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
function setState(state);
Set the state of the tree: which nodes are open and which one is selected?
toggle
function toggle(node);
Open or close the tree node.
toJson
function toJson();
Get the tree data as json.
// Assuming the tree exists
$('#tree1').tree('toJson');
updateNode
function updateNode(node, label);
function updateNode(node, data);
Update the title of a node. You can also update the data.
Update the label:
var node = $tree.tree('getNodeById', 123);
$tree.tree('updateNode', node, 'new label');
Update the data (including the label)
var node = $tree.tree('getNodeById', 123);
$tree.tree(
'updateNode',
node,
{
label: 'new label',
other_property: 'abc'
}
);
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').bind(
'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').bind(
'tree.click',
function(event) {
event.preventDefault();
}
);
tree.close
Called when a node is closed.
$('#tree1').bind(
'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').bind(
'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').bind(
'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').bind(
'tree.init',
function() {
// initializing code
}
);
tree.move
Triggered when the user moves a node.
Event.move_info contains:
- moved_node
- target_node
- position: (before, after or inside)
- previous_parent
$('#tree1').tree({
data: data,
dragAndDrop: true
});
$('#tree1').bind(
'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').bind(
'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').bind(
'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').bind(
'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.open
Called when a node is opened.
$('#tree1').bind(
'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').bind(
'tree.select',
function(event) {
if (event.node) {
// node was selected
var node = event.node;
alert(node.name);
}
else {
// event.node is null
// a node was deselected
// e.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
var node = $('#tree1').tree('getNodeById', 123);
$('#tree1').tree('addToSelection', node);
getSelectedNodes
Return a list of selected nodes.
var nodes = $('#tree1').tree('getSelectedNodes');
isNodeSelected
Return if this node is selected.
var node = $('#tree1').tree('getNodeById', 123);
var is_selected = $('#tree1').tree('isNodeSelected', node);
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
Get the subtree of this node.
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();
getNextSibling
Get the next sibling of this node. Returns a node or null.
var level = node.getNextSibling();
getPreviousSibling
Get the previous sibling of this node. Returns a node or null.
var level = node.getPreviousSibling();
parent
You can access the parent of a node using the parent property.
var parent_node = node.parent;