Skip to content
Martin@MBP edited this page Jan 10, 2016 · 3 revisions

Vision

Fancytree should support a large number of flat nodes.

Status

This feature is not yet implemented.

Please discuss here: https://github.com/mar10/fancytree/issues/169

Requirements

Discussion

A large number of visible - i.e. rendered as DOM elements - nodes causes severe performance issues in current browsers.

Some grid controls and table enhancement plugins try to solve this, by using paging or 'endless scrolling'.

Trees allow to build categorized hierarchies in order to prevent such situations in the first place. Fancytree will defer creation DOM elements until the parent is expanded for the first time, so a tree with 10,000+ nodes perform well. (HTML markup may also be discarded as soon as the parent is collapsed again.)

If however 10.000 nodes are top-level, or all children of one single parent, they will be rendered all together and performance will degrade:

+ Cat 1
  + Node 1.1
  + Node 1.2
  + Node 1.3
+ Cat 2
  + Node 2.1
  + Node 2.2
  + Node 2.3
  + ...
  + Node 2.1000
+ Cat 3
  + Node 3.1
  + ...

Solution 1: Simple Paging

This approach is a variant of lazy loading, so the nodes are loaded on demand (and therefore the DOM elements are only created then). Only load the first N child nodes, and append a dummy Node called 'Load more...', which will trigger loading and appending of the next batch:

+ Cat 1
  + Node 1.1
  + Node 1.2
+ Cat 2
  + Node 2.1
  + Node 2.2
  + Node 2.3
  + ...
  + [More...]
+ Cat 3

Solution 2: Endless Scrolling

This would probably be much easier to implement if the nodes are already loaded (i.e. lazy-loading is off).
However the node markup is created for visible nodes only, and discarded otherwise.

+ Cat 1
  + Node 1.1
  + Node 1.2    v------- start of visible viewport
  + Node 1.3
+ Cat 2
  + Node 2.1
  + Node 2.2
  + Node 2.3    ^------- end of visible viewport
  + ...
  + Node 2.1000
+ Cat 3
  + Node 3.1
  + ...

Since the element structure of plain trees is made of nested <ul> / <li> tags, paging / endless scrolling may be easier to implement using the ext-table variant: the <tr> elements appear as a flat list and can be removed without affecting the structure.

Resources

Known Problems / Things to consider

First of all: do we need this feature?
Displaying a flat list of 10.000+ nodes is questionable usability and may not necessarily be a thing that a tree control is optimized for, since hierarchy could be used instead.
Existing grid controls may already handle this better.

Also:

  1. A solution should play with the filter plugin.
  2. A solution should play with the dnd plugin.
  3. A solution should play with the persist plugin.
  4. A solution should play with the edit plugin.
  5. A solution should play with the table plugin.
  6. What happens if we have an unsaved edit control, that is scrolled outside the viewport?
  7. Discarding and re-recreating nodes may require to re-bind events or re-initialize embedded widgets (for example flipwsiwtch plugins in table rows)
  8. Even collapsing a node might trigger reload, because now some space is freed up that can be filled by sibling nodes.

Proposal

None yet, but open to disussion or reference implementations.

Current Specification

'Paging' nodes are status nodes of type 'paging' and can serve as a placeholder for missing data. Typically we add some additional information that we can use later to load the missing nodes.

[
  {title: "Item 1", key: "node1"},
  {title: "Folder 2", folder: true, expanded: true, key: "node2"},
  {title: "Lazy error", key: "node3", lazy: true},
  {title: "Lazy empty", key: "node4", lazy: true},
  {title: "Lazy paging", key: "node5", lazy: true},
  {title: "More...", statusNodeType: "paging", icon: false, url: "get_children?parent=4321&start=5&count=10"}
]

It is also possible to create paging nodes using the API, for example in the loadChildren event:

  data.node.addPagingNode({
    title: "More...",
    url: "get_children?parent=4321&start=5&count=10"
  });

Paging nodes generate special events instead of plain activate. A common implementation would issue a load request and replace the 'More...' entry with the result:

$("#tree").fancytree({
  ...
  clickPaging: function(event, data) {
    data.node.replaceWith({url: data.node.data.url}).done(function(){
      // The paging node was replaced with the next bunch of entries.
    });
  }