24

I am trying to write a function that opens specific nodes on a jsTree but I am having a problem where the function is executed before my base tree is loaded from the ajax call. How can I tell if my jstree data has been loaded and wait until it is done loading. Below is the function I am trying to use.

function openNodes(tree, nodes) {
    for (i in nodes) {
        $('#navigation').jstree("open_node", $(nodes[i]));
    }
}

I am loading my initial tree with the following command

$("#navigation").jstree({
    "json_data": {
        "ajax": {
            "url": function(node) {
                var url;
                if (node == -1) {
                    url = "@Url.Action("BaseTreeItems", "Part")";
                } else {
                    url = node.attr('ajax');
                }
                return url;
            },
            "dataType": "text json",
            "contentType": "application/json charset=utf-8",
            "data": function(n) { return { id: n.attr ? n.attr("id") : 0, ajax: n.attr ? n.attr("ajax") : 0 }; },
            "success": function() {
            }
        }
    },
    "themes": { "theme": "classic" },
    "plugins": ["themes", "json_data", "ui"]
});
PlTaylor
  • 7,345
  • 11
  • 52
  • 94
  • You set that jsTree data type is json and will be loaded via ajax. To load the jsTree the data needs to be loaded first. What you can go is trigger the data load on opening the node. Will it solve your question? – Radek Jul 12 '12 at 22:45

8 Answers8

38

Before you call .jstree() on an element, you can bind your callbacks to before.jstree and loaded.jstree events:

$(selector)
.bind('before.jstree', function(e, data) {
    // invoked before jstree starts loading
})
.bind('loaded.jstree', function(e, data) {
    // invoked after jstree has loaded
    $(this).jstree("open_node", $(nodes[i]));
})
.jstree( ... )
craftsman
  • 15,133
  • 17
  • 70
  • 86
  • Will this postpone the .jstree(....) until after jstree loads or until after the initial ajax data loads? – PlTaylor Jul 12 '12 at 13:43
  • It will not postpone .jstree() anyway. Only a couple of events are being hooked here. Those callbacks will be invoked when their corresponding events will be triggered at specific moments by jstree. – craftsman Jul 12 '12 at 17:08
  • I'm really looking to postpone the .jstree("open_node", $(nodes[i])); until after the ajax data has been loaded. – PlTaylor Jul 12 '12 at 17:17
  • You need to put .jstree("open_node", $(nodes[i])); in the loaded callback function. I have updated the answer for you. – craftsman Jul 13 '12 at 06:54
  • @craftsman Hi, I am using the old jstree v 1.0 and am using the bind 'loaded.jstree' event not exactly the same order as above but like this: $("#selector") .jstree({ themes: { // themes declared here}, plugins: ["themes", "html_data", "ui"], core: {} }).bind("loaded.jstree", function (event, data) { alert('called loaded jstree ');// statements to be executed after this}); But the alert(); is not called. Any idea why? I guess the binding must happen before invoking the jstree on the selector.(?) – raikumardipak Mar 02 '17 at 10:21
  • here some triggers: `init.jstree, loading.jstree, loaded.jstree, ready.jstree, changed.jstree` – Junior Mayhé Dec 20 '17 at 20:57
16

In the more recent versions of jstree, you may need to wait until all the nodes have finished loading before interacting with them. To do this you need:

ready.jstree

So:

$(selector)
    .bind('ready.jstree', function(e, data) {
        // invoked after jstree has loaded
     })
...
LennonR
  • 1,433
  • 18
  • 35
7

I used setInterval and clearInterval:

var interval_id = setInterval(function(){
     // $("li#"+id).length will be zero until the node is loaded
     if($("li#"+id).length != 0){
         // "exit" the interval loop with clearInterval command
         clearInterval(interval_id)
         // since the node is loaded, now we can open it without an error
         $("#tree").jstree("open_node", $("li#"+id))
      }
}, 5);

JStree's ".loaded" callback only works for root nodes; "._is_loaded" may work instead of checking the node's length, but I haven't tried it. Either way, the animation settings cause nodes that are deeper in the tree to be loaded a few milliseconds later. The setInterval command creates a timed loop that exits when your desired node(s) is loaded.

bensiu
  • 24,660
  • 56
  • 77
  • 117
oracleoptimizer
  • 102
  • 1
  • 2
  • 1
    I am facing the same problem as OP, but I cannot get your solution to work. If all nodes are initially closed, any sub nodes beneath closed nodes cannot be found with the $("li#"+id) selector because they don't exist in the DOM yet (inspecting the DOM via expanding the elements in debug mode doesn't find any additional li items underneath the parent DOM when it is closed). Any thoughts / ideas? – Petteri Pertola Jul 23 '14 at 08:51
4

You had better call $.ajax() function to get data you want at first, you can call $().jstree() function in the success: field like bellow my code.

    var fullTree;
$.ajax({
  url :"./php/select_tree.php",
  data : fullTree,
  method : "GET",
  dataType : "json",
  success : function(fullTree){
    $("#jstree").jstree({
      "core" : {
        "data" :fullTree,
        "check_callback" : true
       },
       "plugins" : [ "contextmenu", "dnd", "changed", "wholerow" ]
    });
  }
})
;
koji Izawa
  • 41
  • 1
3

I use refresh.jstree event in my case (I need to dynamically change and refresh the nodes in jstree very often).

According to the docoment, it

triggered when a refresh call completes

jsTree Doc (Events)

Sample code:

$.post( [url], ... ).done(function(){ $jstree.jstree().settings.core.data = result; $jstree.jstree().refresh(); }); $(selector).on('refresh.jstree', function(){ // do something });

Ben
  • 780
  • 7
  • 10
2

You don't have to add an interval. The plugin sets a class on the node that it's loading via ajax.

.one("reselect.jstree", function (evt, data) {
        if ($("#MYTREEID").find(".jstree-loading").length == 0) {
            alert('my ajax tree is done loading");
        }
    })
jnoreiga
  • 2,156
  • 19
  • 26
2

[Disclaimer: this does not apply to the OP since the state plugin is not listed in the setup code.]

If you are using the state plugin, use the state_ready.jstree event instead of the ready.jstree or loaded.jstree events.

$(selector).on('state_ready.jstree', function () {
    // open desired node
})
Dave Pile
  • 5,559
  • 3
  • 34
  • 49
djm
  • 153
  • 1
  • 10
0

Since i found no solution, that is working for the current version of JSTree (3.3.7) i will publish my work aproach here.

First i bind on the select_node and tell jstree to open the parent, whenever i select i node.

var strIdentifierJsTree  = "#your-whatever-id-to-jstree";
var strNode2Select = "#your-whatever-id-to-your-node-which-parents-has-to-be-showed";

$( strIdentifierJsTree ).on("select_node.jstree", function (e, data) {
       $(strIdentifierJsTree).jstree(true).open_node( data.node.parent );
});

Then i bind to the load-Function:

$( strIdentifierJsTree ).bind('loaded.jstree', function(e, data) {
       $(strIdentifierJsTree).jstree(true).select_node( strNode2Select );
});

Now it will select my Node after initialisation of JSTree (when fully loaded) and also open all the parents, whatever the depth of nesting is. The single open-node approach, like mentioned in other answers did not work for me.

Zephil
  • 13
  • 3