fuelux tree配置项(动态数据+字体图标)

1,057 阅读7分钟

在web项目开发中,我们不免会遇到各种树的信息展示,我也遇到同样的 问题,不过目前的前端树展示千奇百怪,其中 ztree(功能最强,样式騒丑) 还有bootstrap 的 Treeview也不错,今天,讲的是 fuelux tree 中的 树 插件,这个插件的好处 就是树的节点图标可自定义(需要改代码,后面会展示),什么bootstrap 的glyphicon字体font-awesome的 fa 字体,话不多说,直接上代码

fuelux tree 的HTML:

<div class="content">
	<div id="MyTree" class="tree tree-folder-select" role="tree">
		<!--包含有节点的 根节点-->
		<div class="tree-branch hide tree-folder" data-template="treebranch" role="treeitem" aria-expanded="false">
			<div class="tree-branch-header tree-folder-header">
				<div class="tree-branch-name">
					<span class="icon-folder glyphicon glyphicon-plus-sign"></span>
					<span class="tree-label"></span>
				</div>
			</div>
			<div class="tree-branch-children tree-folder-content" role="group"></div>
			<div class="tree-loader" role="alert">Loading...</div>
		</div>
		
		<!--子节点-->
		<div class="tree-item hide" 
			data-template="treeitem" role="treeitem">
			<div class="tree-item-name">
				<!--配置节点选中时的图标 不要 可注释掉-->
				<span class="glyphicon icon-item fueluxicon-bullet"></span>
				<span class="tree-label"></span>
			</div>
		</div>
	</div>
</div>

配置 非无穷树(实际开发中):

$(function() {
	var treeDataSource = new StaticTreeDataSource(folders);

	$('#MyTree').tree({
		dataSource: treeDataSource.getData,
		multiSelect: false,   	//能否多选节点
		folderSelect: false,	//根节点能否选中
        //注:配置 节点  首先 必须HTML 中的图标和 自定义的图标统一
        //否则 不统一可能 节点 能展开 但图标没有切换 或者 不能展开
		iconFolderOpen: "glyphicon-minus-sign",		//节点展开图标
		iconFolderClose: "glyphicon-plus-sign"		//节点关闭图标
	});
})

//初始化树的信息
function StaticTreeDataSource(treeData) {
	var nodes = treeData;

	this.getData = function(options, callback) {
		console.log(JSON.stringify(options))
		//数据第一次初始化时 此时的 options 没有信息
		if($.isEmptyObject(options)) {
			nodes = nodes;
		} else {
			//获取当前点击节点的 options
			if(options.children == null || options.children == undefined) {
				nodes = {};
			} else {
				nodes = options.children;
			}
		}
		//方法回调  添加当前选中节点的子节点
		callback({
			data: nodes
		});
	}
}

var folders = [{
		"name": "Aquire",
		"type": "item"
	},
	{
		"name": "Onboard",
		"type": "item"
	},
	{
		"name": "Engage",
		"type": "folder",
		"attr": {
			"id": "Engage",
			"data-icon": "glyphicon glyphicon-folder-open",
			"style": "color: rebeccapurple;"
		},
		"children": [{
				"name": "Abandoned Cart",
				"type": "folder",
				"children": [{
					"name": "Archive",
					"type": "item"
				}]
			},
			{
				"name": "Birthday",
				"type": "item"
			},
			{
				"name": "Browse Retargeting",
				"type": "item"
			},
			{
				"name": "Loyalty",
				"type": "item"
			},
			{
				"name": "Newsletter",
				"type": "item"
			},
			{
				"name": "Post-Purchase",
				"type": "item"
			},
			{
				"name": "Promotional",
				"type": "item"
			},
			{
				"name": "Transactional",
				"type": "folder",
				"children": [{
					"name": "Archive",
					"type": "item"
				}]
			},
			{
				"name": "Wish List",
				"type": "item"
			}
		]
	},
	{
		"name": "Retain",
		"type": "item",
		"attr": {
			"id": "sky-and-water-i",
			"style": "color: rebeccapurple;" //自定义节点颜色
		}
	}
];

配置 循环树(官网API):

function staticDataSource(parentData, callback) {
	childNodesArray = [{
			"name": "Ascending and Descending",
			"type": "folder",
			"attr": {
				"id": "ascending-and-descending"
			}
		},
		{
			"name": "Sky and Water I",
			"type": "item",
			"attr": {
				"id": "sky-and-water-i",
				"data-icon": "glyphicon glyphicon-file",
				"style": "color: rebeccapurple;"
			}
		},
		{
			"name": "Drawing Hands",
			"type": "folder",
			"attr": {
				"id": "drawing-hands"
			}
		},
		{
			"name": "Waterfall",
			"type": "item",
			"attr": {
				"id": "waterfall",
				"data-icon": "glyphicon glyphicon-cloud",
				"style": "color: rebeccapurple;"
			}
		},
		{
			"name": "Belvedere",
			"type": "folder",
			"attr": {
				"id": "belvedere"
			}
		},
		{
			"name": "Relativity",
			"type": "item",
			"attr": {
				"id": "relativity",
				"data-icon": "glyphicon glyphicon-picture",
				"style": "color: rebeccapurple;"
			}
		},
		{
			"name": "House of Stairs",
			"type": "folder",
			"attr": {
				"id": "house-of-stairs"
			}
		},
		{
			"name": "Convex and Concave",
			"type": "item",
			"attr": {
				"id": "convex-and-concave",
				"data-icon": "glyphicon glyphicon-tags",
				"style": "color: rebeccapurple;"
			}
		}
	];

	callback({
		data: childNodesArray
	});
}

//初始化 树信息
$('#MyTree').tree({
	dataSource: staticDataSource,
	multiSelect: false,
	folderSelect: false,
	iconFolderOpen: "glyphicon-minus-sign",
	iconFolderClose: "glyphicon-plus-sign"
}); 

Fuel UX Tree 树的js (动过手脚使其能自定义图标)

这里用的的是 FuelUX 3.0

/*
 * Fuel UX Tree 3.0
 * https://github.com/ExactTarget/fuelux
 *
 * Copyright (c) 2014 ExactTarget
 * Licensed under the BSD New license.
 */

// -- BEGIN UMD WRAPPER PREFACE --

// For more information on UMD visit:
// https://github.com/umdjs/umd/blob/master/jqueryPlugin.js

(function (factory) {
	if (typeof define === 'function' && define.amd) {
		// if AMD loader is available, register as an anonymous module.
		define(['jquery'], factory);
	} else if (typeof exports === 'object') {
		// Node/CommonJS
		module.exports = factory(require('jquery'));
	} else {
		// OR use browser globals if AMD is not present
		factory(jQuery);
	}
}(function ($) {
	// -- END UMD WRAPPER PREFACE --

	// -- BEGIN MODULE CODE HERE --

	var old = $.fn.tree;

	// TREE CONSTRUCTOR AND PROTOTYPE

	var Tree = function Tree(element, options) {
		this.$element = $(element);
		this.options = $.extend({}, $.fn.tree.defaults, options);

		if (this.options.itemSelect) {
			this.$element.on('click.fu.tree', '.tree-item', $.proxy(function (ev) {
				this.selectItem(ev.currentTarget);
			}, this));
		}

		this.$element.on('click.fu.tree', '.tree-branch-name', $.proxy(function (ev) {
			this.toggleFolder(ev.currentTarget);
		}, this));

		// folderSelect default is true
		if (this.options.folderSelect) {
			this.$element.addClass('tree-folder-select');
			this.$element.off('click.fu.tree', '.tree-branch-name');
			this.$element.on('click.fu.tree', '.icon-caret', $.proxy(function (ev) {
				this.toggleFolder($(ev.currentTarget).parent());
			}, this));
			this.$element.on('click.fu.tree', '.tree-branch-name', $.proxy(function (ev) {
				this.selectFolder($(ev.currentTarget));
			}, this));
		}

		this.render();
	};

	Tree.prototype = {
		constructor: Tree,

		deselectAll: function deselectAll(nodes) {
			// clear all child tree nodes and style as deselected
			nodes = nodes || this.$element;
			var $selectedElements = $(nodes).find('.tree-selected');
			$selectedElements.each(function (index, element) {
				styleNodeDeselected( $(element), $(element).find( '.glyphicon' ) );
			});
			return $selectedElements;
		},

		destroy: function destroy() {
			// any external bindings [none]
			// empty elements to return to original markup
			this.$element.find("li:not([data-template])").remove();

			this.$element.remove();
			// returns string of markup
			return this.$element[0].outerHTML;
		},

		render: function render() {
			this.populate(this.$element);
		},

		populate: function populate($el, isBackgroundProcess) {
			var self = this;
			var $parent = ($el.hasClass('tree')) ? $el : $el.parent();
			var loader = $parent.find('.tree-loader:eq(0)');
			var treeData = $parent.data();
			isBackgroundProcess = isBackgroundProcess || false;	// no user affordance needed (ex.- "loading")

			if (isBackgroundProcess === false) {
				loader.removeClass('hide hidden'); // hide is deprecated
			}
			this.options.dataSource(treeData ? treeData : {}, function (items) {
				loader.addClass('hidden');

				$.each(items.data, function (index, value) {
					var $entity;

					if (value.type === 'folder') {
						$entity = self.$element.find('[data-template=treebranch]:eq(0)').clone().removeClass('hide hidden').removeData('template'); // hide is deprecated
						$entity.data(value);
						$entity.find('.tree-branch-name > .tree-label').html(value.text || value.name);
					} else if (value.type === 'item') {
						$entity = self.$element.find('[data-template=treeitem]:eq(0)').clone().removeClass('hide hidden').removeData('template'); // hide is deprecated
						$entity.find('.tree-item-name > .tree-label').html(value.text || value.name);
						$entity.data(value);
					}

					// Decorate $entity with data or other attributes making the
					// element easily accessable with libraries like jQuery.
					//
					// Values are contained within the object returned
					// for folders and items as attr:
					//
					// {
					//     text: "An Item",
					//     type: 'item',
					//     attr = {
					//         'classes': 'required-item red-text',
					//         'data-parent': parentId,
					//         'guid': guid,
					//         'id': guid
					//     }
					// };
					//
					// the "name" attribute is also supported but is deprecated for "text".

					// add attributes to tree-branch or tree-item
					var attr = value.attr || value.dataAttributes || [];
					$.each(attr, function (key, value) {
						switch (key) {
							case 'cssClass':
							case 'class':
							case 'className':
								$entity.addClass(value);
								break;

							// allow custom icons
							case 'data-icon':
								$entity.find('.icon-item').removeClass().addClass('icon-item ' + value);
								$entity.attr(key, value);
								break;

							// ARIA support
							case 'id':
								$entity.attr(key, value);
								$entity.attr('aria-labelledby', value + '-label');
								$entity.find('.tree-branch-name > .tree-label').attr('id', value + '-label');
								break;

							// style, data-*
							default:
								$entity.attr(key, value);
								break;
						}
					});

					// add child nodes
					if ($el.hasClass('tree-branch-header')) {
						$parent.find('.tree-branch-children:eq(0)').append($entity);
					} else {
						$el.append($entity);
					}
				});

				// return newly populated folder
				self.$element.trigger('loaded.fu.tree', $parent);
			});
		},

		selectTreeNode: function selectItem(clickedElement, nodeType) {
			var clicked = {};	// object for clicked element
			clicked.$element = $(clickedElement);

			var selected = {}; // object for selected elements
			selected.$elements = this.$element.find('.tree-selected');
			selected.dataForEvent = [];

			// determine clicked element and it's icon
			if (nodeType === 'folder') {
				// make the clicked.$element the container branch
				clicked.$element = clicked.$element.closest('.tree-branch');
				clicked.$icon = clicked.$element.find('.icon-folder');
			}
			else {
				clicked.$icon = clicked.$element.find('.icon-item');
			}
			clicked.elementData = clicked.$element.data();

			// the below functions pass objects by copy/reference and use modified object in this function
			if ( this.options.multiSelect ) {
				multiSelectSyncNodes(this, clicked, selected);
			}
			else {
				singleSelectSyncNodes(this, clicked, selected);
			}

			// all done with the DOM, now fire events
			this.$element.trigger(selected.eventType + '.fu.tree', {
				target: clicked.elementData,
				selected: selected.dataForEvent
			});

			clicked.$element.trigger('updated.fu.tree', {
				selected: selected.dataForEvent,
				item: clicked.$element,
				eventType: selected.eventType
			});
		},

		discloseFolder: function discloseFolder(el) {
			var $el = $(el);

			var $branch = $el.closest('.tree-branch');
			var $treeFolderContent = $branch.find('.tree-branch-children');
			var $treeFolderContentFirstChild = $treeFolderContent.eq(0);

			//take care of the styles
			$branch.addClass('tree-open');
			$branch.attr('aria-expanded', 'true');
			$treeFolderContentFirstChild.removeClass('hide hidden'); // hide is deprecated
			
			//DIY  配置 节点展开图标  首先 必须HTML 中的图标和 自定义的图标统一
			$branch.find('> .tree-branch-header .icon-folder').eq(0)
				.removeClass(this.options.iconFolderClose)
				.addClass(this.options.iconFolderOpen);
				
			/*//原本的 节点 关闭 切换	
			$branch.find('> .tree-branch-header .icon-folder').eq(0)
				.removeClass('glyphicon-folder-close')
				.addClass('glyphicon-folder-open');	*/

			//add the children to the folder
			if (!$treeFolderContent.children().length) {
				this.populate($treeFolderContent);
			}

			this.$element.trigger('disclosedFolder.fu.tree', $branch.data());
		},

		closeFolder: function closeFolder(el) {
			var $el = $(el);
			var $branch = $el.closest('.tree-branch');
			var $treeFolderContent = $branch.find('.tree-branch-children');
			var $treeFolderContentFirstChild = $treeFolderContent.eq(0);

			//take care of the styles
			$branch.removeClass('tree-open');
			$branch.attr('aria-expanded', 'false');
			$treeFolderContentFirstChild.addClass('hidden');
			
			
			// DIY 配置 节点 关闭 切换图标
			$branch.find('> .tree-branch-header .icon-folder').eq(0)
				.removeClass(this.options.iconFolderOpen)
				.addClass(this.options.iconFolderClose);

			/*	//原本的 节点关闭切换 
			 * $branch.find('> .tree-branch-header .icon-folder').eq(0)
				.removeClass('glyphicon-folder-open')
				.addClass('glyphicon-folder-close');*/
				
			// remove chidren if no cache
			if (!this.options.cacheItems) {
				$treeFolderContentFirstChild.empty();
			}

			this.$element.trigger('closed.fu.tree', $branch.data());
		},

		toggleFolder: function toggleFolder(el) {
			var $el = $(el);

			/**
			 * DIY 修改 判断节点 是否展开
			 */
			if ($el.parent(".tree-branch-header").parent(".tree-branch").hasClass("tree-open")) {
				this.closeFolder(el);
			} else{
				this.discloseFolder(el);
			}
			
			/**  //原本的  节点展开判断
			if ($el.find('.glyphicon-folder-close').length) {
				this.discloseFolder(el);
			} else if ($el.find('.glyphicon-folder-open').length) {
				this.closeFolder(el);
			} */
		},

		selectFolder: function selectFolder(el) {
			if (this.options.folderSelect) {
				this.selectTreeNode(el, 'folder');
			}
		},

		selectItem: function selectItem(el) {
			if (this.options.itemSelect) {
				this.selectTreeNode(el, 'item');
			}
		},

		selectedItems: function selectedItems() {
			var $sel = this.$element.find('.tree-selected');
			var data = [];

			$.each($sel, function (index, value) {
				data.push($(value).data());
			});
			return data;
		},

		// collapses open folders
		collapse: function collapse() {
			var self = this;
			var reportedClosed = [];

			var closedReported = function closedReported(event, closed) {
				reportedClosed.push(closed);

				// hide is deprecated
				if (self.$element.find(".tree-branch.tree-open:not('.hidden, .hide')").length === 0) {
					self.$element.trigger('closedAll.fu.tree', {
						tree: self.$element,
						reportedClosed: reportedClosed
					});
					self.$element.off('loaded.fu.tree', self.$element, closedReported);
				}
			};

			//trigger callback when all folders have reported closed
			self.$element.on('closed.fu.tree', closedReported);

			self.$element.find(".tree-branch.tree-open:not('.hidden, .hide')").each(function () {
				self.closeFolder(this);
			});
		},

		//disclose visible will only disclose visible tree folders
		discloseVisible: function discloseVisible() {
			var self = this;

			var $openableFolders = self.$element.find(".tree-branch:not('.tree-open, .hidden, .hide')");
			var reportedOpened = [];

			var openReported = function openReported(event, opened) {
				reportedOpened.push(opened);

				if (reportedOpened.length === $openableFolders.length) {
					self.$element.trigger('disclosedVisible.fu.tree', {
						tree: self.$element,
						reportedOpened: reportedOpened
					});
					/*
					* Unbind the `openReported` event. `discloseAll` may be running and we want to reset this
					* method for the next iteration.
					*/
					self.$element.off('loaded.fu.tree', self.$element, openReported);
				}
			};

			//trigger callback when all folders have reported opened
			self.$element.on('loaded.fu.tree', openReported);

			// open all visible folders
			self.$element.find(".tree-branch:not('.tree-open, .hidden, .hide')").each(function triggerOpen() {
				self.discloseFolder($(this).find('.tree-branch-header'));
			});
		},

		/**
		* Disclose all will keep listening for `loaded.fu.tree` and if `$(tree-el).data('ignore-disclosures-limit')`
		* is `true` (defaults to `true`) it will attempt to disclose any new closed folders than were
		* loaded in during the last disclosure.
		*/
		discloseAll: function discloseAll() {
			var self = this;

			//first time
			if (typeof self.$element.data('disclosures') === 'undefined') {
				self.$element.data('disclosures', 0);
			}

			var isExceededLimit = (self.options.disclosuresUpperLimit >= 1 && self.$element.data('disclosures') >= self.options.disclosuresUpperLimit);
			var isAllDisclosed = self.$element.find(".tree-branch:not('.tree-open, .hidden, .hide')").length === 0;


			if (!isAllDisclosed) {
				if (isExceededLimit) {
					self.$element.trigger('exceededDisclosuresLimit.fu.tree', {
						tree: self.$element,
						disclosures: self.$element.data('disclosures')
					});

					/*
					* If you've exceeded the limit, the loop will be killed unless you
					* explicitly ignore the limit and start the loop again:
					*
					*    $tree.one('exceededDisclosuresLimit.fu.tree', function () {
					*        $tree.data('ignore-disclosures-limit', true);
					*        $tree.tree('discloseAll');
					*    });
					*/
					if (!self.$element.data('ignore-disclosures-limit')) {
						return;
					}

				}

				self.$element.data('disclosures', self.$element.data('disclosures') + 1);

				/*
				* A new branch that is closed might be loaded in, make sure those get handled too.
				* This attachment needs to occur before calling `discloseVisible` to make sure that
				* if the execution of `discloseVisible` happens _super fast_ (as it does in our QUnit tests
				* this will still be called. However, make sure this only gets called _once_, because
				* otherwise, every single time we go through this loop, _another_ event will be bound
				* and then when the trigger happens, this will fire N times, where N equals the number
				* of recursive `discloseAll` executions (instead of just one)
				*/
				self.$element.one('disclosedVisible.fu.tree', function () {
					self.discloseAll();
				});

				/*
				* If the page is very fast, calling this first will cause `disclosedVisible.fu.tree` to not
				* be bound in time to be called, so, we need to call this last so that the things bound
				* and triggered above can have time to take place before the next execution of the
				* `discloseAll` method.
				*/
				self.discloseVisible();
			} else {
				self.$element.trigger('disclosedAll.fu.tree', {
					tree: self.$element,
					disclosures: self.$element.data('disclosures')
				});

				//if `cacheItems` is false, and they call closeAll, the data is trashed and therefore
				//disclosures needs to accurately reflect that
				if (!self.options.cacheItems) {
					self.$element.one('closeAll.fu.tree', function () {
						self.$element.data('disclosures', 0);
					});
				}

			}
		},

		// This refreshes the children of a folder. Please destroy and re-initilize for "root level" refresh.
		// The data of the refreshed folder is not updated. This control's architecture only allows updating of children.
		// Folder renames should probably be handled directly on the node.
		refreshFolder: function refreshFolder($el) {
			var $treeFolder = $el.closest('.tree-branch');
			var $treeFolderChildren = $treeFolder.find('.tree-branch-children');
			$treeFolderChildren.eq(0).empty();

			if ($treeFolder.hasClass('tree-open')) {
				this.populate($treeFolderChildren, false);
			}
			else {
				this.populate($treeFolderChildren, true);
			}

			this.$element.trigger('refreshedFolder.fu.tree', $treeFolder.data());
		}

	};

	// ALIASES

	//alias for collapse for consistency. "Collapse" is an ambiguous term (collapse what? All? One specific branch?)
	Tree.prototype.closeAll = Tree.prototype.collapse;
	//alias for backwards compatibility because there's no reason not to.
	Tree.prototype.openFolder = Tree.prototype.discloseFolder;
	//For library consistency
	Tree.prototype.getValue = Tree.prototype.selectedItems;

	// PRIVATE FUNCTIONS

	function styleNodeSelected ($element, $icon) {
		$element.addClass('tree-selected');
		if ( $element.data('type') === 'item' && $icon.hasClass('fueluxicon-bullet') ) {
			$icon.removeClass('fueluxicon-bullet').addClass('glyphicon-ok'); // make checkmark
		}
	}

	function styleNodeDeselected ($element, $icon) {
		$element.removeClass('tree-selected');
		if ( $element.data('type') === 'item' && $icon.hasClass('glyphicon-ok') ) {
			$icon.removeClass('glyphicon-ok').addClass('fueluxicon-bullet'); // make bullet
		}
	}

	function multiSelectSyncNodes (self, clicked, selected) {
		// search for currently selected and add to selected data list if needed
		$.each(selected.$elements, function (index, element) {
			var $element = $(element);
			if ($element[0] !== clicked.$element[0]) {
				selected.dataForEvent.push( $($element).data() );
			}
		});

		if (clicked.$element.hasClass('tree-selected')) {
			styleNodeDeselected (clicked.$element, clicked.$icon);
			// set event data
			selected.eventType = 'deselected';
		}
		else {
			styleNodeSelected(clicked.$element, clicked.$icon);
			// set event data
			selected.eventType = 'selected';
			selected.dataForEvent.push(clicked.elementData);
		}
	}

	function singleSelectSyncNodes(self, clicked, selected) {
		// element is not currently selected
		if (selected.$elements[0] !== clicked.$element[0]) {
			var clearedElements = self.deselectAll(self.$element);
			styleNodeSelected(clicked.$element, clicked.$icon);
			// set event data
			selected.eventType = 'selected';
			selected.dataForEvent = [clicked.elementData];
		}
		else {
			styleNodeDeselected(clicked.$element, clicked.$icon);
			// set event data
			selected.eventType = 'deselected';
			selected.dataForEvent = [];
		}
	}


	// TREE PLUGIN DEFINITION

	$.fn.tree = function tree(option) {
		var args = Array.prototype.slice.call(arguments, 1);
		var methodReturn;

		var $set = this.each(function () {
			var $this = $(this);
			var data = $this.data('fu.tree');
			var options = typeof option === 'object' && option;

			if (!data) {
				$this.data('fu.tree', (data = new Tree(this, options)));
			}

			if (typeof option === 'string') {
				methodReturn = data[option].apply(data, args);
			}
		});

		return (methodReturn === undefined) ? $set : methodReturn;
	};

	$.fn.tree.defaults = {
		dataSource: function dataSource(options, callback) {},
		multiSelect: false,
		cacheItems: true,
		folderSelect: true,
		itemSelect: true,
		
		// DIY   配置 节点 默认 展开 关闭 图标
		//打开图标
		iconFolderOpen:"glyphicon-minus-sign",
		//关闭图标
		iconFolderClose:"glyphicon-plus-sign",
		
		/*
		* How many times `discloseAll` should be called before a stopping and firing
		* an `exceededDisclosuresLimit` event. You can force it to continue by
		* listening for this event, setting `ignore-disclosures-limit` to `true` and
		* starting `discloseAll` back up again. This lets you make more decisions
		* about if/when/how/why/how many times `discloseAll` will be started back
		* up after it exceeds the limit.
		*
		*    $tree.one('exceededDisclosuresLimit.fu.tree', function () {
		*        $tree.data('ignore-disclosures-limit', true);
		*        $tree.tree('discloseAll');
		*    });
		*
		* `disclusuresUpperLimit` defaults to `0`, so by default this trigger
		* will never fire. The true hard the upper limit is the browser's
		* ability to load new items (i.e. it will keep loading until the browser
		* falls over and dies). On the Fuel UX `index.html` page, the point at
		* which the page became super slow (enough to seem almost unresponsive)
		* was `4`, meaning 256 folders had been opened, and 1024 were attempting to open.
		*/
		disclosuresUpperLimit: 0
	};

	$.fn.tree.Constructor = Tree;

	$.fn.tree.noConflict = function () {
		$.fn.tree = old;
		return this;
	};


	// NO DATA-API DUE TO NEED OF DATA-SOURCE

	// -- BEGIN UMD WRAPPER AFTERWORD --
}));
// -- END UMD WRAPPER AFTERWORD --

配置 树的 样式

.content {
    margin: 0px auto;
    width: 400px;
    height: 400px;
    overflow: auto;
    padding: 10px;
    border: 1px solid #CCCCCC;
}

.tree {
    padding-left: 14px;
    overflow: auto;
    position: relative
}

.tree:before {
    width: 1px;
    z-index: 1;
    display: block;
    content: "";
    position: absolute;
    top: -21px;
    bottom: 16px;
    left: 0;
    border-left: 1px dotted #666
}

.tree.tree-solid-line {
    padding-left: 12px
}

.tree.tree-solid-line:before {
    border-left: 1px solid #999
}

.tree.tree-solid-line .tree-folder .tree-folder-content:before {
    border-left: 1px solid #999
}

.tree.tree-no-line {
    padding-left: 0
}

.tree.tree-no-line:before {
    display: none
}

.tree.tree-no-line .tree-folder .tree-folder-content:before {
    display: none
}

.tree .tree-folder {
    width: auto;
    min-height: 20px;
    cursor: pointer
}

.tree .tree-folder .tree-folder-header {
    position: relative;
    min-height: 20px;
    line-height: 20px;
    min-width: 100px
}

.tree .tree-folder .tree-folder-header:hover {
    background-color: #eee
}

.tree .tree-folder .tree-folder-header .tree-folder-name {
    margin-left: 2px
}

.tree .tree-folder img {
    margin-left: 4px
}

.tree .tree-folder .tree-folder-content {
    margin-left: 23px;
    position: relative
}

.tree .tree-folder .tree-folder-content:before {
    display: inline-block;
    content: "";
    position: absolute;
    width: 1px;
    z-index: 1;
    top: -9px;
    bottom: 16px;
    left: -12px;
    border-left: 1px dotted #666
}

.tree .tree-item {
    position: relative;
    min-height: 20px;
    line-height: 20px;
    min-width: 100px;
    cursor: pointer
}

.tree .tree-item:hover {
    background-color: #eee
}

.tree .tree-item .tree-item-name {
    margin-left: 2px
}

.tree .tree-folder,
.tree .tree-item {
    position: relative
}

.tree .tree-folder:before,
.tree .tree-item:before {
    display: inline-block;
    content: "";
    position: absolute;
    top: 14px;
    left: -13px;
    width: 18px;
    height: 0;
    border-top: 1px dotted #666;
    z-index: 1
}

.tree .tree-selected {
    background-color: #eee;
    color: #262626
}

.tree .tree-selected:hover {
    background-color: #e5e5e5
}

.tree .tree-folder .tree-folder-header .tree-folder-name,
.tree .tree-item .tree-item-name {
    display: inline;
    z-index: 2
}

.tree.tree-solid-line .tree-folder:before,
.tree.tree-solid-line .tree-item:before {
    border-top: 1px solid #999
}

.tree.tree-no-line .tree-folder:before,
.tree.tree-no-line .tree-item:before {
    display: none
}

.tree .tree-item,
.tree .tree-folder {
    border: 1px solid transparent
}

.tree .tree-item,
.tree .tree-folder .tree-folder-header {
    margin: 0;
    padding: 4px 5px 6px 5px;
    color: #333;
    -webkit-box-sizing: content-box;
    -moz-box-sizing: content-box;
    box-sizing: content-box
}

.tree .tree-item .tree-item-name>i,
.tree .tree-folder .tree-folder-header>i {
    color: #666;
    display: inline-block;
    font-family: FontAwesome;
    font-style: normal;
    font-weight: normal;
    line-height: 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale
}

.tree .tree-item.tree-selected i,
.tree .tree-item .tree-item-name i {
    margin-left: 2px;
    width: 14px;
    display: inline-block;
    text-align: center;
    margin-right: 1px;
    color: #262626
}

.tree.tree-plus-minus .tree-folder-header .fa-folder-open:before {
    height: 16px;
    width: 16px;
    line-height: 16px;
    vertical-align: middle;
    display: inline-block;
    background-position: 0 -21px;
    content: ""
}

.tree.tree-plus-minus .tree-folder-header .fa-folder:before {
    height: 16px;
    width: 16px;
    line-height: 16px;
    vertical-align: middle;
    display: inline-block;
    background-position: 0 2px;
    content: ""
}

.tree.tree-plus-minus .tree-folder-name {
    margin-left: 0 !important
}

.tree .tree-actions {
    display: none;
    position: absolute;
    margin-top: 1px;
    right: 4px
}

.tree .tree-item:hover .tree-actions,
.tree .tree-folder-header:hover .tree-actions {
    display: inline-block
}

.tree .tree-actions>i {
    font-weight: 300;
    padding: 1px 3px;
    text-align: center;
    font-size: 14px;
    color: #333;
    margin-right: 6px;
    margin-top: 0;
    display: inline-block
}

.tree .tree-actions>i:hover {
    font-weight: bold
}

.tree-loading {
    color: #2dc3e8;
    margin-left: 30px
}