WhatsNew - mar10/fancytree GitHub Wiki

Design goals, new and changed features, compared to Dynatree 1.x.

Motivation

While DynaTree 1.x seems to work well for many users, bug-fixing and implementation of new features became increasingly difficult without breaking compatibility.

A new project name was chosen, so we can re-think and change the API, attribute names, events, default behavior, etc. - without confusing users that see references to 'DynaTree' on forums or stackoverflow.

Still most concepts and implementations are based on DynaTree, so we start off with release 2.0.

New Features

  • More robust on (parallel) asynchronous operations (using deferreds/promises)
  • Better maintainability using an extension-module pattern
    The final design was inspired by jsTree, which features a pretty cool plugin concept:
    credits to Ivan Bozhanov!
  • More flexible initialization (unified source option)
  • More consistent API, using naming conventions and best practice from jQuery UI widgets.
  • Table tree support (example)
  • Inline editsupport (example)
  • The tree now behaves more like a form control, i.e. it is 'tabbable' (example)
  • Filter support (example)
  • HTML markup changed (not using <a> tags anymore)
  • New skins (Windows 7, 8, [OS X] Lion, bootstrap, ...)
Other Improvements
Dropped features
  • Required minimum versions for jQuery have been lifted to jQuery 1.7+ and jQuery UI 1.8.6+

Migration from Dynatree 1.x

Please help to improve this document: send suggestions or edit this Wiki, to clarify things.

  • Most attribute names, event names and method signatures have changed.
// Fancytree 2.x            | DynaTree 1.x
// -------------------------+------------------------------
// Requirements
jQuery 3                      jQuery 1.4+  
jQuery UI 1.12                jQuery UI 1.7+

// Class names
Fancytree                     DynaTree
FancytreeNode                 DynaTreeNode

// Attributes
node.expanded                 node.bExpanded 
node.selected                 node.bSelected
node.children                 node.childList
node.key                      node.data.key
node.title                    node.data.title
node.extraClasses             node.data.addClass
node.lazy                     node.data.isLazy
node.folder                   node.data.isFolder
...
tree.root                     tree.tnToot
...

// Note: it is reccomended to use access methods instead:
node.isFolder()
tree.getRootNode()
...

// Methods
tree = $.ui.fancytree.getTree("#tree");
                              tree = $("#tree").dynatree("getTree");

node.toggleSelected()         node.toggleSelect() 
node.setSelected()            node.select()
node.setActive()              node.activate()
...

// Events
beforeExpand(event, data)     onQueryExpand(flag, node)
select(event, data)           onSelect(flag, node)
...

// Options
source                        initAjax, init
...

// <ul>/<li> initialization
<li class='expanded'>         <li class='expand'>

See the API Reference.

  • Some functionality is now implemented as extension and has to be enabled like this:
  $("#tree").fancytree({
    extensions: ["persist", "dnd"],
    [...]
  });

See the details.

  • New source option for generalized initialization:
  $("#tree").fancytree({
    // source may be Ajax options, a pointer to a <ul> element, a callback, ...
    source: {
      url: "/getTopLevelNodesAsJson"
    },
    lazyLoad: function(event, data){
      // return a source in 'data.result'
      data.result = {
        url: "/getChildrenAsJson",
        data: {key: data.node.key}
      };
    },
    [...]
  });

See the details.

  • All events have been renamed according to common jQuery widget style. Event arguments have been unified to data.
   activate: function(event, data) {
       var node = data.node;
       alert("activated #" + node.key);
   },
   beforeSelect: function(event, data) {
       logEvent(event, data, "current state=" + data.node.isSelected());
       // return false to prevent default behavior (i.e. selecting or deselecting)
       if( data.node.isFolder() ){
           return false;
       }
   },
   select: function(event, data) {
       logEvent(event, data, "current state=" + data.node.isSelected());
       var s = data.tree.getSelectedNodes().join(", ");
       $("#echoSelected").text(s);
   },

See the details.

  • Most asynchronous methods now return $.promise so handling deferred responses is easy:

    node.setExpanded().done(function(){
        alert("expand animation has finished");
    });
  • The generated markup has changed (structure and class names).
    Check your custom CSS.

More Examples
Fancytree 2.x DynaTree 1.x
node.setActive(true, {noEvents: true})
 node.activateSilently(); 
  $("#tree").fancytree("disable");
  tree.disable();
  tree.reload().done(function(){
    // Root node was reloaded
  }).fail(function(){
    // Reload failed
  });
  tree.reload(function(status){
    // Root node was reloaded
  });
  node.addChildren({
    title: "n.a.", 
    checkbox: false, 
    icon: false, 
    selected: true, 
    nodeStatus: "empty", 
    key: "_empty" 
  });
  rootNode.addChild({
    title: "n.a.", 
    hideCheckbox: true, 
    icon: false, 
    select: true, 
    nodeStatus: "empty", 
    key: "_empty" 
  });
$("#tree").fancytree({
  extensions: ["clones", "edit"],
  autoCollapse: true,
    // Pass an array of nodes (and child nodes)
    source: [
      {title: "Node 1", folder: true, lazy: true, 
        treeMode: "variant", 
        keyType:"root", key: "_famtree_", 
        refKey: "_famtree_", 
        extraClasses: "scioFamilyTree" },
      {title: "Node 2", folder: true, lazy: true, 
        treeMode: "struct", 
        keyType: "root", key: "_structtree_", 
        refKey: "_structtree_", 
        extraClasses: "scioStructureTree" }
  ],
  init: function(event, data){
  },
  beforeActivate: function(event, data) {
  },
  click: function(event, data) {
  },
  activate: function(event, data) {
    var node = data.node,
        isUserEvent = event.originalEvent 
                && event.originalEvent.type === "click";
    if( !node.data.isFolder ){
              node.setExpanded(true);
    }
  },
  deactivate: function(event, data) {
  },
  expand: function(event, data) {
  },
  lazyLoad: function(event, data){
      var node = data.node;
          data.result = $.getJSON("tree/getChildren", {
            mode: node.data.treeMode,
            keyType: node.data.keyType,
            key: node.refKey
          });
  },
  render: function(event, data) {
    var node = data.node;
  },
});
$("#tree").dynatree({
  fx: { height: "toggle", duration: 200 },
  autoCollapse: true,
  noLink: true,
    // Pass an array of nodes (and child nodes)
    children: [
      {title: "Node 1", isFolder: true, isLazy: true, mode: "variants", 
        keyType:"root", key: "_famtree_", addClass: "scioFamilyTree" },
      {title: "Node 2", isFolder: true, isLazy: true, mode: "struct", 
        keyType: "root", key: "_structtree_", addClass: "scioStructureTree" }
    ],
    onQueryActivate: function(dtnode) {
      return broadcastEvent("queryModelDeactivate") !== false;
    },
    onClick: function(dtnode, event) {
    },
    onActivate: function(dtnode) {
    },
    onDeactivate: function(dtnode) {
    },
    onExpand: function(flag, dtnode) {
    },
    onLazyRead: function(dtnode){
      dtnode.appendAjax({
        url: "tree/getChildren",
          data: {mode: dtnode.data.mode,
            keyType: dtnode.data.keyType,
            key: dtnode.data.key
          }
      });
    },
    onRender: function(node, span) {
    },
});
⚠️ **GitHub.com Fallback** ⚠️