I ran into the excellent jstree jQuery UI plug in this morning. In a word - great! It is easy to use, easy to style & does what it says on the box. The one thing I have not yet been able to figure out is this - in my app I want to ensure that only one node is expanded at any given time. i.e. when the user clicks on the + button and expands a node, any previously expanded node should silently be collapsed. I need to do this in part to prevent the container div for a rather lengthy tree view from creating an ugly scrollbar on overflow and also to avoid "choice overload" for the user.
I imagine that there is some way of doing this but the good but rather terse jstree documentation has not helped me to identify the right way to do this. I would much appreciate any help.
jsTree is great but its documentation is rather dense. I eventually figured it out so here is the solution for anyone running into this thread.
Firstly, you need to bind the open_node event to the tree in question. Something along the lines of
$("tree").jstree({"themes":objTheme,"plugins":arrPlugins,"core":objCore}).
bind("open_node.jstree",function(event,data){closeOld(data)});
i.e. you configure the treeview instance and then bind the open_node event. Here I am calling the closeOld function to do the job I require - close any other node that might be open. The function goes like so
function closeOld(data)
{
var nn = data.rslt.obj;
var thisLvl = nn;
var levels = new Array();
var iex = 0;
while (-1 != thisLvl)
{
levels.push(thisLvl);
thisLvl = data.inst._get_parent(thisLvl);
iex++;
}
if (0 < ignoreExp)
{
ignoreExp--;
return;
}
$("#divElements").jstree("close_all");
ignoreExp = iex;
var len = levels.length - 1;
for (var i=len;i >=0;i--) $('#divElements').jstree('open_node',levels[i]);
}
This will correctly handle the folding of all other nodes irrespective of the nesting level of the node that has just been expanded.
A brief explanation of the steps involved
The above answer will construct tree again and again. The below code will open the node and collapse which are already opened and it does not construct tree again.
.bind("open_node.jstree",function(event,data){
closeOld(data);
});
and closeOld function contains:
function closeOld(data)
{
if($.inArray(data.node.id, myArray)==-1){
myArray.push(data.node.id);
if(myArray.length!=1){
var arr =data.node.id+","+data.node.parents;
var res = arr.split(",");
var parentArray = new Array();
var len = myArray.length-1;
for (i = 0; i < res.length; i++) {
parentArray.push(res[i]);
}
for (var i=len;i >=0;i--){
var index = $.inArray(myArray[i], parentArray);
if(index==-1){
if(data.node.id!=myArray[i]){
$('#jstree').jstree('close_node',myArray[i]);
delete myArray[i];
}
}
}
}
}
Yet another example for jstree 3.3.2. It uses underscore lib, feel free to adapt solution to jquery or vanillla js.
$(function () {
var tree = $('#tree');
tree.on('before_open.jstree', function (e, data) {
var remained_ids = _.union(data.node.id, data.node.parents);
var $tree = $(this);
_.each(
$tree
.jstree()
.get_json($tree, {flat: true}),
function (n) {
if (
n.state.opened &&
_.indexOf(remained_ids, n.id) == -1
) {
grid.jstree('close_node', n.id);
}
}
);
});
tree.jstree();
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With