jsTree - Closing nodes on a specific level - javascript

I have a jstree with 5 levels of nodes. I want to iterate through all nodes to close those nodes for which the level is 4 so that one doesn't see level 5 unless 4 is clicked to expand. If there is a better approach than iterating, I'm all ears!
$("#mytree").bind('ready.jstree', function(event, data) {
var $tree = $(this);
$($tree.jstree().get_json($tree, {
flat: true
}))
.each(function(index, value) {
var node = $("#mytree").jstree().get_node(this.id);
var lvl = node.parents.length;
if (lvl = 4) {
node.close_node(this, true);
}
});
});

I found an answer, in case anyone else is looking to do the same.
Instead of opening all and then closing some, I started with all closed and opened the ones I wanted:
$("#mytree").bind('ready.jstree', function(event, data) {
var $tree = $(this);
$($tree.jstree().get_json($tree, {
flat: true
}))
.each(function(index, value) {
var node = $("#mytree").jstree().get_node(this.id);
var lvl = node.parents.length;
if (lvl <= 3) {
$('#mytree').jstree().open_node({"id":node.id});
}
});
});

If you want to open the nodes of first level, you may try this.
$('#myTree li').each( function() {
var node=$("#myTree").jstree().get_node(this.id);
var level = node.parents.length;
if(level<=1){
$('#myTree').jstree().open_node({"id":node.id});
}
});
It works for me, when I want to display the folders of an azure container. In my case, container is the root node.

Related

jquery to json problems

I would really appreciate any help on this problem that I have spent many hours trying to solve. I have a jquery object that I created from elements on a web page:
var get_article = $('title, p, img');
When I console.log this, I get a nice, clean list of the elements and their index values. What I would like to do is add each element to a json object along with their index value and contents. I have tried this:
article = new Array();
$.each(get_article, function(){
$('title').each(function() {
var title_items = {
node: $(this).index(''),
html_element: "title",
content: $(this).text()
};
article.push(JSON.stringify(title_items));
});
$('p').each(function() {
var p_items = {
node: $(this).index(''),
html_element: "p",
content: $(this).text()
};
article.push(JSON.stringify(p_items));
});
$('img').each(function() {
var img_items = {
node: $(this).index(''),
html_element: "img",
content: $(this).attr("src")
};
article.push(JSON.stringify(img_items));
});
});
When I console.log the json object (article), all of the data is correct except the node values in the json object (article) are all over the place and they don't show the same as the index value in the original jquery object (get_article). Also, the data begins to repeat itself, so I have more nodes than was in the original jquery object (get_article). Please help!
Thanks all for your help. I was able to solve the problem using this code. Looking at your responses made me see that I needed to try another way. This is what worked for me:
for (i = 0; i < get_article.length; i++) {
if ($(get_article[i]).is('title')) {
var title_items = {
node: i,
html_element: "title",
content: $(get_article[i]).text()
};
article.push(JSON.stringify(title_items));
}
if ($(get_article[i]).is('p')) {
var p_items = {
node: i,
html_element: "p",
content: $(get_article[i]).text()
};
article.push(JSON.stringify(p_items));
}
if ($(get_article[i]).is('img')) {
var img_items = {
node: i,
html_element: "img",
content: $(get_article[i]).attr("src")
};
article.push(JSON.stringify(img_items));
}
};
You're not actually using get_article anywhere in your code.
$.each(get_article, function(){
$('title').each(function() {
var title_items = {
// ...
};
article.push(JSON.stringify(title_items));
});
// ....
});
is exactly the same as
$('title').each(function() {
var title_items = {
// ...
};
article.push(JSON.stringify(title_items));
});
It's hard to say for sure since you haven't provided enough details, but perhaps what you want is something like:
$.each(get_article, function(){
$(this).find('title').each(function() {
var title_items = {
// ...
};
article.push(JSON.stringify(title_items));
});
// ....
});
That's still not going to have the exact same effect as simply logging to the console, though. The order of the elements will be different because in the code you're first getting all the titles, then all the paragraphs, and then all the images. When you console.log the jQuery output, it's not segregating the elements by their type but simply showing them in their DOM order.
For example, if the DOM has
title 1
paragraph 1
title 2
the console.log will show
title 1
paragraph 1
title 2
while the (fixed) code will show
title 1
title 2
paragraph 1

Copy node from TreePanel to TreePanel keeping the structure

I have problem with script that will allow me to copy nodes from one Tree to another keeping the structure of folders.
Here's the script written in extjs 6.0
https://fiddle.sencha.com/#fiddle/1dr0
I am able to reconstruct structure from the source tree but unfortunately the copied element is duplicated and I don't know how to handle this. I am not sure if the way how I am doing this is correct so any tips will be helpfull.
The main logic is here:
beforedrop: function (node, data, overModel, dropPosition, dropFunction, eOpts) {
eOpts.trackedParentsNodeList = [];
createParentList(data.records[0], eOpts.trackedParentsNodeList);
var currentNode = node;
for (var i = eOpts.trackedParentsNodeList.length - 1; i >= 0; i--) {
var newNode = createCloneNode(eOpts.trackedParentsNodeList[i]);
currentNode.appendChild(newNode);
currentNode = currentNode.lastChild;
}
}
and here
createParentList = function(data, result){
result.push(data);
if(data.parentNode && data.parentNode.id !== "root")
createParentList(data.parentNode, result);
else return false;
}
createCloneNode = function(node){
return {
"text": node.data.text,
"leaf": node.isLeaf(),
"expanded": node.isExpanded()
};
}
ok, so I was managed to solve this problem and here's solution https://fiddle.sencha.com/#fiddle/1dr0.
Not sure if that's best way to do this, but it works.

JsTree v3.0 drag and drop plugin. Reference target node upon dropping

I use drag and drop plugin of jsTree library (ver. 3.0)
With the following code I can bind to the end of drag'n'drop action, but I can not see a way to get the reference to the target node (the node I'm dropping on).
$(document).on('dnd_stop.vakata', function(e, data) {
// how to get target_node here?
});
I had same problem. I found other solution than event dnd_stop.vakata, which returns old data before changed position.
This works:
$('#jstree_demo_div').on("move_node.jstree", function (e, data) {
//data.node, data.parent, data.old_parent is what you need
//console.log(data);
alert(data.node.id);
alert(data.parent);
});
Another solution is to use the get_node() function on the jstree object.
$(document).on('dnd_stop.vakata', function (e, data) {
ref = $('#jstree').jstree(true);
parents = ref.get_node(data.element).parent;
});
You can get all parents with:
all_parents = ref.get_node(data.element).parents;
I had the same problem and had to get the ID within the dnd_stop event, so I came up with this:
$(document).on('dnd_stop.vakata', function(e, data) {
var t = $(data.event.target);
var targetnode = t.closest('.jstree-node');
var nodeID = targetnode.attr("id");
});
That way I can get the ID of the targetnode, for example.
$(document).on('dnd_stop.vakata', function(e, data) {
var inst = $.jstree.reference('#jstree');
console.log("END DROP:");
var sourceID = data.data.nodes[0];
console.log("Source ID: " + sourceID);
var targetNode = inst.get_node(data.event.target, true);
var targetID = targetNode[0].id;
console.log("Target ID: " + targetID);
});
If you need to do this via the check_callback then you can access the target node via the more parameter.
'check_callback': function(operation, node, node_parent, node_position, more) {
// operation can be 'create_node', 'rename_node', 'delete_node', 'move_node' or 'copy_node'
// in case of 'rename_node' node_position is filled with the new node name
if (operation === "move_node") {
if (more.ref.data.type === "folder") {
return true;
}
}
return false; //allow all other operations
}
To get the target node you use the dnd_stop.vakata event. Once you get the node you can access to the different properties like id:
$(document).bind("dnd_stop.vakata",function(e, data) {
var targetNode = $('#jstree').jstree(true).get_node($(data.event.target));
var nodeId = targetNode.id;
});
you just need to call:
'check_callback': function(operation, node, node_parent, node_position, more) {
// operation can be 'create_node', 'rename_node', 'delete_node', 'move_node' or 'copy_node'
if (operation === "move_node") {
var node = more.origin.get_node('fiche-1');
return true;
}
return true; //allow all other operations
}
Bind the listener after document is ready:
$(document).ready(function() {
$(document).on('dnd_stop.vakata', function (e, data) {
let ref = $.jstree.reference("#jstree");
let nodes = data.data.nodes.map(node_id => ref.get_node(node_id));
let parent_node_id = nodes[0].parent;
let parent = ref.get_node(parent_node_id);
});
});
jstree has an internal listener for dnd_stop.vakata.jstree that performs the ui logic. It's setup inside a $(function() {...}) i.e. when the document is ready. If you bind your custom function before jstree, you get the parent before the ui logic is executed.

Underscore.js performance issue - Can i use _.defer

In an simple web app that i am building using underscore.js and jquery. For a list of all people ( js object ) I am filtering out list of all the places (js object) they visited. People list is a html table with a td having places image icon which on click displays list of all places they visited. Icon needs to be shown only for people who have visited at the least one place. The problem here is that people and places count comes around 2000, 100. So the code below executes 2000*100 combinations. The browser complains me of unresponsive script. Code is provided below
_.each(peopleList, function (person, index, list) {
//filter the respective places for people
var visitedPlaces = _.filter(places, function (place) {
return place.PeopleId == person.Id;
});
if (_.isEmpty(visitedPlaces)) {
$("a#" + place.PeopleId).remove();
}
});
Dead simple isn't it. For each person check if visited places has him tracked. How do i optimize the above code to unblocking and responsive. Tried putting in _.defer and _.delay at some places but no improvement
FWIW, here is how I would solve it in underscore.
function removeNonTravelers(people, visits) {
var travelers = _.pluck(visits, 'PeopleId'),
nonTravelers = _.reject(people, function (person) {
return _.contains(travelers, person.Id);
});
$(_.map(nonTravelers, document.getElementById)).remove();
}
http://jsfiddle.net/FWzeN/
My suggestion would be to drop underscore and use plain JS for this:
function removeNonTravelers(people, visits) {
var i, peopleId,
numPeople = people.length,
numVisits = visits.length,
index = {}, nonTravelers = [];
// index
for (i = 0; i < numVisits; i++) {
peopleId = visits[i].PeopleId;
if (!index.hasOwnProperty(peopleId)) {
index[peopleId] = 1;
} else {
index[peopleId]++;
}
}
// find HTML elements to remove
for (i = 0; i < numPeople; i++) {
peopleId = people[i].Id;
if (!index.hasOwnProperty(peopleId)) {
nonTravelers.push(document.getElementById(peopleId));
}
}
// remove them all at once
    $(nonTravelers).remove();
}
This is reasonably fast. If I didn't make any mistake, your test case (2000 people, 100 places) times at more than 700 operations per second on my rather outdated laptop (DOM operations excluded).
Try for yourself: http://jsperf.com/where-not-exists-in-javascript
var hashMap = {};
_.each(places, function(place) {
hashMap[place.PeopleId] = place;
});
_.each(peopleList, function (person, index, list) {
//filter the respective project documents
var visitedPlaces = hashMap[person.id];
if (visitedPlaces) {
$("a#" + place.PeopleId).remove();
}
});

Telerik RadTreeView And Client-Side Expand/Collapse

I'm following this approach to expanding and collapsing all nodes in client JavaScript: http://www.telerik.com/help/aspnet/treeview/tree_expand_client_side.html
However, it's taking a REALLY long time to process this, and after expanding then collapsing, I get the "script unresponsive" error, so I was wondering if there was a way to speed this up for a rather large tree? Is there a better way to parse it? Currently, the tree is 4 levels deep.
Thanks.
I got around the "script unresponsive" errors by expanding and collapsing the tree asynchronously. In addition, I expand from the bottom (so you can see the nodes expand) and collapse from the top, but only when it gets to the last node in each branch, so visually it's far more interesting to the user. They can actually see it happen, and if it's not fast (IE7 and before is particularly slow), it's at least entertaining while they wait.
var treeView, nodes;
function expandAllNodesAsynchronously() {
if (<%= expandedLoaded.ToString().ToLower() %>) {
treeView = $find("<%= tv.ClientID %>");
nodes = treeView.get_allNodes();
if (nodes.length > 1) {
doTheWork(expandOneNode, nodes.length);
}
return false;
} else
return true;
}
function expandOneNode(whichNode) {
var actualNode = nodes.length - whichNode;
if (nodes[actualNode].get_nextNode() == null) {
nodes[actualNode].get_parent().expand();
}
}
function collapseAllNodesAsynchronously() {
treeView = $find("<%= tv.ClientID %>");
nodes = treeView.get_allNodes();
if (nodes.length > 1) {
doTheWork(collapseOneNode, nodes.length);
}
}
function collapseOneNode(whichNode) {
if (nodes[whichNode].get_nextNode() == null && nodes[whichNode].get_parent() != nodes[0]) {
nodes[whichNode].get_parent().collapse();
}
}
function doTheWork(operation, cycles) { //, callback
var self = this, // in case you need it
cyclesComplete = 1,
batchSize = 10; // Larger batch sizes will be slightly quicker, but visually choppier
var doOneBatch = function() {
var c = 0;
while(cyclesComplete < cycles) {
operation(cyclesComplete);
c++;
if(c >= batchSize) {
// may need to store interim results here
break;
}
cyclesComplete++;
}
if (cyclesComplete < cycles) {
setTimeout(doOneBatch, 1); // "1" is the length of the delay in milliseconds
}
else {
// Not necessary to do anything when done
//callback(); // maybe pass results here
}
};
// kickoff
doOneBatch();
return null;
};
Start off getting your nodes with yourtreeViewInstance.get_nodes(), and then the child nodes as eachChildNode.get_nodes() and so on down the hierarchy.
Then you can expand each item by calling .set_expanded(true); on each node you want to expand.

Categories

Resources