I have managed to create an Ext.tree.TreePanel that loads child nodes dynamically, but I'm having a difficult time clearing the tree and loading it with new data. Can someone help me with the code to do this?
From the wonderful blog of Saki an ExtJS guru.
while (node.firstChild) {
node.removeChild(node.firstChild);
}
http://blog.extjs.eu/know-how/how-to-remove-all-children-of-a-tree-node/
In Ext JS 4:
if you want to reload the data of the tree panel, you need to reload the tree store:
getCmp('treeId').getStore().load();
where treeId is the id of the tree. If you have a store id, you may directly use load() on store id.
to remove all child nodes:
getCmp('treeId').getRootNode().removeAll();
However, removing child nodes is not necessary for reloading the tree nodes from its store.
In my case, my Ext tree has a hidden root node of type AsyncTreeNode. If I want to clear the tree and repopulate from the server, it's pretty simple:
tree.getRootNode().reload();
I finally found an answer in their forums. For anyone interested it is here:
if (tree)
{
var delNode;
while (delNode = tree.root.childNodes[0])
tree.root.removeChild(delNode);
}
you can simply use node.removeAll() to remove all child nodes from this node.
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.data.NodeInterface-method-removeAll
I ran into a similar problem and the solution i came up with was to 'tag' the node has having not loaded when it was collapsed thus forcing a reload when it was re-expanded.
listeners: {
collapsenode: function(node){
node.loaded = false;
},
if (tree) { var delNode; while (delNode = tree.root.childNodes[0]) tree.root.removeChild(delNode); }
I don't know Ext, but I'm guessing that they have DOM abstraction that might make that easier. An equivalent in Prototype would be something like:
tree.root.immediateDescendants().invoke('remove'); // or
tree.root.select('> *').invoke('remove');
Unless tree.root refers to a collection object rather than the tree's root DOM node, but is borrowing DOM API method names? That seems really unlikely, especially for a modern JS library.
Related
jQuery holds references to DOM nodes in its internal cache until I explicitly call $.remove(). If I use a framework such as React which removes DOM nodes on its own (using native DOM element APIs), how do I clean up jQuery's mem cache?
I'm designing a fairly large app using React. For those unfamiliar, React will tear down the DOM and rebuild as needed based on its own "shadow" DOM representation. The part works great with no memory leaks.
Flash forward, we decided to use a jQuery plugin. After React runs through its render loop and builds the DOM, we initialize the plugin which causes jQuery to hold a reference to the corresponding DOM nodes. Later, the user changes tabs on the page and React removes those DOM elements. Unfortunately, because React doesn't use jQuery's $.remove() method, jQuery maintains the reference to those DOM elements and the garbage collector never clears them.
Is there a way I can tell jQuery to flush its cache, or better yet, to not cache at all? I would love to still be able to leverage jQuery for its plugins and cross-browser goodness.
jQuery keeps track of the events and other kind of data via the internal API jQuery._data() however due to this method is internal, it has no official support.
The internal method have the following signature:
jQuery._data( DOMElement, data)
Thus, for example we are going to retrieve all event handlers attached to an Element (via jQuery):
var allEvents = jQuery._data( document, 'events');
This returns and Object containing the event type as key, and an array of event handlers as the value.
Now if you want to get all event handlers of a specific type, we can write as follow:
var clickHandlers = (jQuery._data(document, 'events') || {}).click;
This returns an Array of the "click" event handlers or undefined if the specified event is not bound to the Element.
And why I speak about this method? Because it allow us tracking down the event delegation and the event listeners attached directly, so that we can find out if an event handler is bound several times to the same Element, resulting in memory leaks.
But if you also want a similar functionality without jQuery, you can achieve it with the method getEventHandlers
Take a look at this useful articles:
getEventHandlers
getEventListeners - chrome
getEventListeners - firebug
Debugging
We are going to write a simple function that prints the event handlers and its namespace (if it was specified)
function writeEventHandlers (dom, event) {
jQuery._data(dom, 'events')[event].forEach(function (item) {
console.info(new Array(40).join("-"));
console.log("%cnamespace: " + item.namespace, "color:orangered");
console.log(item.handler.toString());
});
}
Using this function is quite easy:
writeEventHandlers(window, "resize");
I wrote some utilities that allow us keep tracking of the events bound to DOM Elements
Gist: Get all event handlers of an Element
And if you care about performance, you will find useful the following links:
Leaking Memory in Single Page Apps
Writing Fast, Memory-Efficient JavaScript
JavaScript Memory Profiling
I encourage anybody who reads this post, to pay attention to memory allocation in our code, I learn the performance problems ocurrs because of three important things:
Memory
Memory
And yes, Memory.
Events: good practices
It is a good idea create named functions in order to bind and unbind event handlers from DOM elements.
If you are creating DOM elements dynamically, and for example, adding handlers to some events, you could consider using event delegation instead of keep bounding event listeners directly to each element, that way, a parent of dynamically added elements will handle the event. Also if you are using jQuery, you can namespace the events ;)
//the worse!
$(".my-elements").click(function(){});
//not good, anonymous function can not be unbinded
$(".my-element").on("click", function(){});
//better, named function can be unbinded
$(".my-element").on("click", onClickHandler);
$(".my-element").off("click", onClickHandler);
//delegate! it is bound just one time to a parent element
$("#wrapper").on("click.nsFeature", ".my-elements", onClickMyElement);
//ensure the event handler is not bound several times
$("#wrapper")
.off(".nsFeature1 .nsFeature2") //unbind event handlers by namespace
.on("click.nsFeature1", ".show-popup", onShowPopup)
.on("click.nsFeature2", ".show-tooltip", onShowTooltip);
Circular references
Although circular references are not a problem anymore for those browsers that implement the Mark-and-sweep algorithm in their Garbage Collector, it is not a wise practice using that kind of objects if we are interchanging data, because is not possible (for now) serialize to JSON, but in future releases, it will be possible due to a new algorithm that handles that kind of objects. Let's see an example:
var o1 = {};
o2 = {};
o1.a = o2; // o1 references o2
o2.a = o1; // o2 references o1
//now we try to serialize to JSON
var json = JSON.stringify(o1);
//we get:"Uncaught TypeError: Converting circular structure to JSON"
Now let's try with this other example
var freeman = {
name: "Gordon Freeman",
friends: ["Barney Calhoun"]
};
var david = {
name: "David Rivera",
friends: ["John Carmack"]
};
//we create a circular reference
freeman.friends.push(david); //freeman references david
david.friends.push(freeman); //david references freeman
//now we try to serialize to JSON
var json = JSON.stringify(freeman);
//we get:"Uncaught TypeError: Converting circular structure to JSON"
PD: This article is about Cloning Objects in JavaScript. Also this gist contain demos about cloning objects with circular references: clone.js
Reusing objects
Let's follow some of the programming principles, DRY (Don't Repeat Yourself) and instead of creating new objects with similar functionality, we can abstract them in a fancy way. In this example I will going to reuse an event handler (again with events)
//the usual way
function onShowContainer(e) {
$("#container").show();
}
function onHideContainer(e) {
$("#container").hide();
}
$("#btn1").on("click.btn1", onShowContainer);
$("#btn2").on("click.btn2", onHideContainer);
//the good way, passing data to events
function onToggleContainer(e) {
$("#container").toggle(e.data.show);
}
$("#btn1").on("click.btn1", { show: true }, onToggleContainer);
$("#btn2").on("click.btn2", { show: false }, onToggleContainer);
And there are a lot of ways to improve our code, having an impact on performance, and preventing memory leaks. In this post I spoke mainly about events, but there are other ways that can produce memory leaks. I suggest read the articles posted before.
Happy reading and happy coding!
If your plugin exposes a method to programatically destroy one of its instances (i.e. $(element).plugin('destroy')), you should be calling that in the componentWillUnmount lifecycle of your component.
componentWillUnmount is called right before your component is unmounted from the DOM, it's the right place to clean up all external references / event listeners / dom elements your component might have created during its lifetime.
var MyComponent = React.createClass({
componentDidMount() {
$(React.findDOMNode(this.refs.jqueryPluginContainer)).plugin();
},
componentWillUnmount() {
$(React.findDOMNode(this.refs.jqueryPluginContainer)).plugin('destroy');
},
render() {
return <div ref="jqueryPluginContainer" />;
},
});
If your plugin doesn't expose a way to clean up after itself, this article lists a few ways in which you can try to dereference a poorly thought out plugin.
However, if you are creating DOM elements with jQuery from within your React component, then you are doing something seriously wrong: you should almost never need jQuery when working with React, since it already abstracts away all the pain points of working with the DOM.
I'd also be wary of using refs. There are only few use cases where refs are really needed, and those usually involve integration with third-party libraries that manipulate/read from the DOM.
If your component conditionally renders the element affected by your jQuery plugin, you can use callback refs to listen to its mount/unmount events.
The previous code would become:
var MyComponent = React.createClass({
handlePluginContainerLifecycle(component) {
if (component) {
// plugin container mounted
this.pluginContainerNode = React.findDOMNode(component);
$(this.pluginContainerNode).plugin();
} else {
// plugin container unmounted
$(this.pluginContainerNode).plugin('destroy');
}
},
render() {
return (
<div>
{Math.random() > 0.5 &&
// conditionally render the element
<div ref={this.handlePluginContainerLifecycle} />
}
</div>
);
},
});
How about do this when the user exits the tab:
for (x in window) {
delete x;
}
This is much better to do, though:
for (i in $) {
delete i;
}
I want to associate each node in my tree with a domain object. I was passing HTML data and manually storing the domain object in jQuery data:
$('li node description').data('obj', my_domain_object);
However, it seems that jsTree clears that data out during $('#jstree_div').jstree();.
So later, $('li node description').data('obj') is undefined.
What's the best practice to do this? (I'm guessing the principle would be the same for either HTML or JSON data)
jsTree keeps the data intact, but moves it for optimization reasons [1]. So in the callback, it must be accessed via data.node.data.some_key. In my example, the magic incantation was:
$('#jsTree_div').on('select_node.jstree', function (e, data) {
data.node.data.obj //... (instead of $('#node_id').data('obj'))
});
[1] "This is done because of speed. Attaching the data using jQuery's '.data()' every time a node is redrawn proved to be rather slow." - Ivan Bozhanov, jsTree creator https://groups.google.com/d/msg/jstree/w97E8uG_Bd0/enYklH-B1-cJ
I want to save my workFlow and then load it, For this I have to save all the sources and targets for all connections. Can anyone tell me how can I get all sources and targets against one node.
Thankx buddy but I found a link that is very simple in syntax wise. Also that worked for me.
I'm going to share this for future references...
Can we export a JsPlumb flowchart as a JSON or XML?
For this you need to get all the connections first and then save each connection source and target id in an array. While restoring first you need to create DOM elements with same id's and then use the array to restore connections:
var con=jsPlumb.getAllConnections();
var list=[];
for(var i=0;i<con.length;i++)
{
list[i]=new Array(2);
list[i][0]=con[i].sourceId;
list[i][1]=con[i].targetId;
}
For connecting elements based on id's make use of syntax:
jsPlumb.connect({source:list[1][0], target:list[1][1]});
In a tree built with jsTree, I have the text within the <a> tag sitting in a variable. I would like to check that node. How can I do so?
I am currently finding that node, using jQuery, and altering its class. However, this does not repair the parent node by making the parent undetermined in its class. I tried doing $('.colors').jstree("checkbox_repair"), but that didn't seem to do anything.
It would be great if someone could actually answer both those questions, since they are related to the same problem.
Here is a jsFiddle, illustrating the issue--> http://jsfiddle.net/thapar/5XAjU/
In js_tree there are .check_node ( node ) and .uncheck_node ( node ) functions, i think this is what you are asking for. Soe the documentation here: http://www.jstree.com/documentation/checkbox
This is an excerpt from the documentation in the link above, "how to perform an operation":
/* METHOD ONE */
jQuery("some-selector-to-container-node-here")
.jstree("operation_name" [, argument_1, argument_2, ...]);
/* METHOD TWO */
jQuery.jstree._reference(needle)
/* NEEDLE can be a DOM node or selector for the container or a node within the container */
.operation_name([ argument_1, argument_2, ...]);
So I think this syntax should work
$.jstree._reference(".colors").check_node('li#tree_3');
Also i am not sure you should be using a class to reference your tree. Probably use an ID to reference your tree, and then use this syntax:
$.jstree._reference("#colors").check_node('li#tree_3');
//EDIT: Please keep in mind that the newest version of jsTree doesn't have a function called _reference anymore. It got renamed to reference (without the leading underscore). (Last checked 24/08/2015 15:45 by #mkli90)
Link: https://www.jstree.com/api/#/?f=$.jstree.reference(needle)
If you want to check jsTree nodes on load for example like this:
$(document).ready(function()
{
$.jstree._reference('#menu').check_node('#pih2');
});
it does not work. For me works following:
$(function () {
$('#mainMenu1').bind('loaded.jstree', function(e, data){ //waiting for loading
$.jstree._reference('#menu').check_node('#pih2'); //check node with id pih2
$.jstree._reference('#menu').check_node('#pih6'); //check node with id pih6
});
});
I use jsTree 1.0-rc3 and JQuery 1.7.1.
Aloe
In current versions of jstree the following syntax works:
$("#my_tree").jstree("check_node", node_id);
I would like to add this solution, the select_node function does the same function as check_node. we can use it as follows;
$('#jstree_id').on('loaded.jstree', function() {
$("#jstree_id").jstree("select_node", ["list of nodes go here"]);
});
You can use it to select multiple nodes in an array or a single node.
Building a browsergame I came from PHP to JavaScript, which I now also want to use at the server side.
As I'm going to require Users to have JavaScript either way, I'm going to take extensive use of it. I want to use in in a object-oriented way though.
Considering MVC, Models will be used on both client and server side. Views are only used at the client side.
The interface is split into multiple parts: a main sidemenu, main content and some widgets. I'll take the part I've already done as example:
The menu is split into three categories with multiple entries. Each entry is a link with an attached action (like switching the content).
// menuview:
var self = new View();
var generalMenu = new MenuCategory('generalmenu')
.addEntry(new MenuEntry('overview', new Action()))
.addEntry(new MenuEntry('buildings'))
.addEntry(new MenuEntry('resources'))
// [..more categories..]
self.toData = function() {
return {
id: this.id,
cat: [generalMenu.toData(), infosMenu.toData(), userMenu.toData()]
};
};
At the moment View is a compositum with a toData() method to create data for the template parser(selfmade, simple but supporting iteration). And the actions get attached after creation. I use jQuery as framework:
self.show = function(callback) {
$tpl(this.tpl).parse(this.toData()).lang('main').toHTML(function(html) {
var el = $(html);
el.find('a').click(function (e) {
MenuEntry.actionHandler.execAction(e.target.id);
return false;
});
el.appendTo('#'+self.target);
callback && callback();
});
return this;
};
I have declared an actionhandler to avoid iterating over the links.
I'm not feeling well with this solution, it's not flexible enough. I'd like to treat a view like a real compositum, not with a lot of strange dependencies. Also, I have to reparse the whole View if I change a part. Well, in this example this is not obvious, because the menu wont change while runningtime, but other parts of the interface will.
Now, to finally get to my question: Is there a better solution?
Like having dom references spread over the view, each menuentry having it's own reference and directly attached action? If I'm not using templates anymore, what kind of flexiblity am I losing?
I decided to go without template parser. Each view stores it's node and is able to manipulate it directly if it gets informed to update the data.