Concepts - mar10/fancytree GitHub Wiki
Design goals and main concepts.
Fancytree is a refactored version of Dynatree. Read WhatsNew for details and migration hints.
- Performant and efficient handling of big data structures.
- Robust, consistent handling of parallel, asynchronous behavior.
- Refactored to a more MVC-style design, so new features - like rendering as tree-grid becomes easier.
- Allow to develop extensions as separate modules.
- Compliance with the current jQuery.UI widget style guide.
- Good test coverage.
Typically, jQuery Plugins wrap around an existing HTML element, tweak the appearance and add behavior. Fancytree (as well as DynaTree) takes a different approach:
We have a tree data model as the backbone, i.e. an instance of the
Fancytree
class that contains a hierarchical structure of FancytreeNode
objects.
A node may be active, selected, focused, and/or hovered. These states are independent, so one node can have all, some, or none of these states at the same time. See FAQ 'What statuses can a node have?'.
This structure is initialized on startup from a JavaScript data structure, an
Ajax JSON response or a generator function.
It is also possible to pass a reference to a <ul>
/<li>
element, which then will
be parsed into the equivalent data model.
The tree's data model can be accessed and modified using an extensive
object oriented API like tree.getNodeByKey()
or node.setExpanded()
.
Most of these API methods will eventually call hook functions, which do the
real work.
Hook functions are members of the Fancytree class, with a name prefixed
'tree...' or 'node...', for example treeClear()
or nodeRenderTitle()
.
Hook functions may be overloaded by extension modules, in order to implement new or customized functionality. Some of the standard functionality is implemented and delivered as extension modules, for example the table view, drag-and-drop, or persistence using cookies.
Some API functions are potentially asynchronous. For example node.setExpanded()
on a lazy node may have to issue an Ajax request, wait for its response and then
start the expand-animation which also takes some time.
These functions generally return a $.promise
,
so handling deferred responses is easy:
node.setExpanded().done(function(){
alert("expand animation has finished");
});
The HTML markup is rendered on demand. If a large tree with 10,000
nodes is partly collapsed, only the visible nodes will have DOM elements.
Reducing the number of DOM elements allows to hold large data structures in the
browser, but also has some implications. For example it might not be possible to
access all nodes using a jQuery selector, because they simply don't exist:
Use tree API functions instead.
For the same reason it is not possible to bind events to all node elements directly.
However this is rarely necessary, since Fancytree offers event handlers like
click
, dblclick
, and keypress
. Use event delegation otherwise.
Starting with v2.31 a tree grid may be set up to display in a viewport.
A viewport defines a sub-range (start and count) of rows from the model that
will be visible (expanded or not).
This concept allows to store a huge data model (100k+ nodes) in the frontend,
while only having a few <tr>
elements materialized in the DOM.
As a side effect, scrolling needs to be implemented by shifting the start
parameter, instead of relying on the browser. Also, the table header remains
fixed (demo).
A tree may be set up for lazy loading:
For one thing, the tree initialization may be delayed to an asynchronous Ajax
request. This will result in a fast page load, and an empty tree displaying a spinner
icon until the data arrives.
Additionally, single child nodes may be marked 'lazy'. These nodes will generate
Ajax request when expanded for the first time.
Lazy loading allows to present hierarchical structures of infinite size in an
efficient way. But since neither all DOM elements nor even all node data is
available, API functions like tree.getNodeByKey()
or node.findAll()
may
not work as expected.