Skip to content
Martin Wendt edited this page May 29, 2022 · 86 revisions

An introduction to Fancytree.

NOTE:
Fancytree is considered feature-complete. The code is still maintained and bugfixes will be commited. However do not expect new major features.
Have a look at the Wunderbaum incubator for a potential successor.

Main Features

Some highlights:

  • Rich object oriented API
  • Extensible modular design with AMD support
  • Lazy loading and efficient and performant handling of large data sets
  • Support for viewports, i.e. rendering only required DOM elements while maintaining huge data models.
  • Full featured table view support (aka tree grid)
  • Keyboard navigation
  • WAI-ARIA compliant
  • (Hierarchical) selection and checkboxes
  • Drag and drop (html5 or jQuery UI based)
  • Inline editing
  • Searching and filtering
  • Persistence of expansion, selection, and active state
  • Themable (comes with WinXP, Win7, Win8, OS X Lion, and Glyph samples)
  • The tree behaves like a single form control, i.e. it is 'tabbable'
  • and more... see the list of available extensions

Try the Demos

Have a look at the Example Browser to play with live demos. Click the View source link at the bottom of every page to see what's going on.

Understand the Core Concepts

This is not required for standard use cases, but you might want to come back to read about the Main Concepts later on.

Getting Started

There are several ways to get the latest stable Fancytree release:

  • Use your favorite package manager, e.g.:
    npm: $ npm install --save jquery.fancytree,
    yarn: $ yarn add jquery.fancytree, or
    bower: $ bower install fancytree
    will download the latest stable release into your project folder.

  • Include specific versions directly from the cloud using a CDN source, e.g.: jsDelivr, cdnjs, or UNPKG.

  • Open a release from the release list and download the complete source tree with documentation, tests, etc. by clicking Source code (zip). You will find the production library and CSS files in the dist/ folder.

Access Development Sources

There are many different options to access the latest unstable development snapshot:

Note that the dist/ folder still contains the latest released version. You have to run npm install and grunt make_dist locally to create current minified files in the dist/ folder.
See HowtoContribute for details on how to install Fancytree for debugging and contributing.

Project Structure

Currently we deliver this directory structure (only showing the most important files):

jquery.fancytree/
├─ demo/                                 Example browser
├─ src/                                  Current development code ('nightly build')
├─ test/                                 Unit tests and triage
└─ dist/                                 Latest released version:
   ├─ modules/                             All single modules with AMD support
   |  ├─ jquery.fancytree.js                 The core module. This is also what you get with
   |  |                                      `require('jquery.fancytree')`
   |  ├─ jquery.fancytree.EXT.js, ...        Extension modules
   |  └─ jquery.fancytree.ui-deps.js         Implicitly loaded dependencies
   ├─ skin-NAME/                           LESS and CSS styles and images for the NAME theme
   |  ├─ icons.gif, ...                      Icon sprite
   |  ├─ ui.fancytree.css                    Compiled CSS
   |  ├─ ui.fancytree.min.css                Minified CSS
   |  └─ ui.fancytree.less                   LESS definition (includes ../skin-common)
   ├─ skin-custom-1/                       Starting point for a custom theme
   ├─ jquery.fancytree.min.js              Core library (no extensions), minified
   ├─ jquery.fancytree-all[.min].js        Bundle with core and most extensions.
   │                                         The AMD wrapper references ui-widgets.
   │                                         Use this in production ONLY IF you plan
   │                                         to include jQuery UI separately.
   ├─ jquery.fancytree-all-deps[.min].js   Bundle with core, most extensions, and
   │                                         required jQuery UI widgets.
   │                                         This is the recommended source file for
   |                                         production (unless you use a module loader).
   └─ skin-common.less                     Shared styles, used by stock themes

Requirements and Dependencies

  • Fancytree requires jQuery 1.9+ (3.x normal build recommended) as an external dependency.
    Note: If a package manager or module loader is used, jQuery is automatically included with the Fancytree core module.
    Note: This does not mean that you actually need to use jQuery in your project.

  • Fancytree internally also uses a few jQuery UI widgets.
    However those are transparently part of the distribution (jquery.fancytree-all-deps.min.js), or if a module loader is used.
    Note: The jQuery UI themes are not required.

  • Optionally, if your project is already including jQuery UI (1.12+ recommended) anyway, use jquery.fancytree-all.min.js instead of jquery.fancytree-all-deps.min.js.
    If a custom download is used, at least the following components should be selected:
    'UI Core',
    'Effects': 'Effects Core', 'Blind Effect'
    The ext-dnd extension also requires 'Interactions': 'Draggable', 'Droppable', however the ext-dnd5 extension does not have this dependencies.

Embed Fancytree on a Web Page

A simple example:

<head>
  [...]
  <!-- Include jQuery -->
  <script src="assets/jquery/dist/jquery.min.js"></script>
  <!-- Include Fancytree skin and library -->
  <link href="assets/jquery.fancytree/dist/skin-win8/ui.fancytree.min.css" rel="stylesheet">
  <script src="assets/jquery.fancytree/dist/jquery.fancytree-all-deps.min.js"></script>
  <!-- Initialize the tree when page is loaded -->
  <script type="text/javascript">
    $(function(){  // on page load
      // Create the tree inside the <div id="tree"> element.
      $("#tree").fancytree({
        extensions: ["edit", "filter"],
        source: {...},
         ...
      });
      // Note: Loading and initialization may be asynchronous, so the nodes may not be accessible yet.
    });
  </script>
</head>
<body>
  [...]
  <!-- Define the targel element for the tree -->
  <div id="tree"></div>
  [...]
</body>
</html>

Or include directly from CDN sources:

<script src="//code.jquery.com/jquery-3.6.0.min.js"></script>
<link href="//cdn.jsdelivr.net/npm/[email protected]/dist/skin-win8/ui.fancytree.min.css" rel="stylesheet">
<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/jquery.fancytree-all-deps.min.js"></script>

See also the Integration Guideline for details.

Use a Module Loader

Note: available with v2.25+.

When using a module bundler like webpack, we can add Fancytree

$ npm install --save jquery.fancytree

and then use require() or import. Example:

// Import LESS or CSS:
import 'jquery.fancytree/dist/skin-lion/ui.fancytree.less'

const $ = require('jquery');

const fancytree = require('jquery.fancytree');
require('jquery.fancytree/dist/modules/jquery.fancytree.edit');
require('jquery.fancytree/dist/modules/jquery.fancytree.filter');

console.log(fancytree.version);

$(function(){
  $('#tree').fancytree({
    extensions: ['edit', 'filter'],
    source: {...},
    ...
  });
  const tree = fancytree.getTree('#tree');
  // Note: Loading and initialization may be asynchronous, so the nodes may not be accessible yet.
})

The require() calls return the $.ui.fancytree object, which brings some helpful static properties and methods like .version, .createTree(), .eventToString(),
.getNode(), .getTree(), etc. (check the API docs for details).

If we want to avoid the $, we can also do without:

import 'jquery.fancytree/dist/skin-lion/ui.fancytree.less';  // CSS or LESS
import {createTree} from 'jquery.fancytree';
import 'jquery.fancytree/dist/modules/jquery.fancytree.edit';
import 'jquery.fancytree/dist/modules/jquery.fancytree.filter';

const tree = createTree('#tree', {
  extensions: ['edit', 'filter'],
  source: {...},
  ...
});
// Note: Loading and initialization may be asynchronous, so the nodes may not be accessible yet.

See also the Integration Guideline for details and configuration tips for Angular, webpack, require.js, etc.

Define the Tree Data

There are several ways to define the actual node structure:

  1. Use the source option to pass a data structure, i.e. an array of nested objects:

    $("#tree").fancytree({
      source: [
        {title: "Node 1", key: "1"},
        {title: "Folder 2", key: "2", folder: true, children: [
          {title: "Node 2.1", key: "3"},
          {title: "Node 2.2", key: "4"}
        ]}
      ],
      ...

    See also the complete list of data properties.

  2. Use the source option to load the data via Ajax.

    $("#tree").fancytree({
      source: {
        url: "/getTreeData",
        cache: false
      },
      ...

    The Ajax service is expected to return valid JSON data:

    [{"title": "Node 1", "key": "1"},
     {"title": "Folder 2", "key": "2", "folder": true, "children": [
        {"title": "Node 2.1", "key": "3"},
        {"title": "Node 2.2", "key": "4"}
      ]}
    ]
  3. Define a <ul>/<li> markup structure inside the tree's <div> tag. (Note that method 1. and 2. are preferred.)

    $("#tree").fancytree();

    HTML:

    <div id="tree">
      <ul id="treeData" style="display: none;">
        <li id="1">Node 1
        <li id="2" class="folder">Folder 2
          <ul>
            <li id="3">Node 2.1
            <li id="4">Node 2.2
          </ul>
      </ul>
    </div>
      ...

For more information see also TutorialLoadData and the online demo.

Lazy Loading

Fancytree supports loading nodes on demand, i.e. only load data when a node is expanded for the first time.

In order to enable this, we can mark nodes as lazy.

  $("#tree").fancytree({
    // Initial node data that sets 'lazy' flag on some leaf nodes
    source: [
      {title: "Child 1", key: "1", lazy: true},
      {title: "Folder 2", key: "2", folder: true, lazy: true}
    ],
    lazyLoad: function(event, data) {
      var node = data.node;
      // Issue an Ajax request to load child nodes
      data.result = {
        url: "/getBranchData",
        data: {key: node.key}
      }
    }
  });

For more information see TutorialLoadData.

Configure Options

Additional options are passed to Fancytree during initialization:

$("#tree").fancytree({
  source: {
    url: "ajax-tree-plain.json"
  },
  checkbox: true,
  [...]
});

Options can also be set after initialization using this syntax:

var tree = $.ui.fancytree.getTree("#tree");

tree.setOption("checkbox", true);
// Alternative jQuery style:
// $("#tree").fancytree("option", "checkbox", true);

For more information see also the complete list of available options and the Option Configurator.

Dynamic Options

Some node options can be defined in a flexible way using a dynamic pattern.

Consider for example the checkbox option, which may be true, false, or "radio". If omitted, it will default to false. Globally enabling checkboxes for all nodes can be configured like so:

$("#tree").fancytree({
  checkbox: true,
  source: {url: "/mySource"},
  ...

This global setting may be overridden per node by the concrete source data, if a property of the same name is present:

[{"title": "Node 1"},
 {"title": "Node 2", "checkbox": false},
 {"title": "Node 3", "checkbox": "radio"}
 ]

If the global setting is a callback, it will be called for every node, thus allowing to dynamically define option values:

$("#tree").fancytree({
  checkbox: function(event, data) {
    // Hide checkboxes for folders
    return data.node.isFolder() ? false : true;
  },
  tooltip: function(event, data) {
    // Create dynamic tooltips
    return data.node.title + " (" + data.node.key + ")";
  },
  icon: function(event, data) {
    var node = data.node;
    // Create custom icons
    if( node.data.refType === "foo" ) {
      return "foo-icon-class";
    }
    // Exit without returning a value: continue with default processing.
  },
  ...

Currently the following options are evaluated as dynamic options: checkbox, icon, iconTooltip, tooltip, unselectable, unselectableIgnore, unselectableStatus.

Event Handling

Functionality can be added (and modified) by defining event handlers (i.e. callback functions).

Every event handler is passed a data argument, that contains information about the event target.

  $("#tree").fancytree({
    ...
    activate: function(event, data){
      // A node was activated: display its title:
      var node = data.node;
      $("#echoActive").text(node.title)
    },
    beforeSelect: function(event, data){
      // A node is about to be selected: prevent this, for folder-nodes:
      if( data.node.isFolder() ){
        return false;
      }
    }
  });

An alternative way to define event handlers is to bind them later to an initialized tree. Note that the event name must be converted to lower case and prefixed with 'fancytree':

  $("#tree").on("fancytreebeforeselect", function(event, data){
    if( data.node.isFolder() ){
      return false;
    }
  });

For more information see also TutorialEvents, the online demo, the complete list of available events, and a description of the 'data' object.

API Access

Fancytree exposes an extensive, object oriented interface to query and manipulate the data model:

  var tree = $.ui.fancytree.getTree("#tree"),
      activeNode = tree.getActiveNode();

  // Sort children of active node:
  activeNode.sortChildren();
  // Expand all tree nodes
  tree.visit(function(node){
    node.setExpanded(true);
  });
  // Append a new child node
  activeNode.addChildren({
    title: "Document using a custom icon",
    icon: "customdoc1.gif"
  });

For more information see TutorialApi.

Selection and Checkboxes

Fancytree supports three modes:

  1. selectMode: 1: single selection
    Only one node is selected at any time.
  2. selectMode: 2: multiple selection (default)
    Every node may be selected independently.
  3. selectMode: 3: hierarchical selection
    (De)selecting a node will propagate to all descendants. Mixed states will be displayed as partially selected using a tri-state checkbox.

While the selected state of a node is independant of a checkbox icon, we would typically enable checkboxes for the tree using the checkbox: true option. (The special value "radio" turns checkoxes into radio buttons.)

Propagation in select mode 3 can be controlled using unselectable, unselectableStatus, and unselectableIgnore options.
The node option radiogroup enables single-select for its child nodes.

See the online demo and details.

Tree Grid

One major feature is Fancytree's ability to render a tree as a table (aka tree grid) and support keyboard navigation in a grid with embedded input controls.

This is implemented as ext-table extension.

Theming

Some skins are part of the distribution (Win-XP, Windows 7, Windows 8, OS X Lion, ...). Use the Skin combobox on the online demo, to try them out.

Some more info here.

Extensions

Fancytree is extensible using extensions. The standard distribution already contains some extra functionality in this way, such as table-support, inline editing, filtering, etc.

Read more about