zoukankan      html  css  js  c++  java
  • javascript 树形控件

    学了点Ext,确实很酷很强大,但学习起来有点复杂,大部分功能都要依赖CSS,不使用Ext的Resources就用不了。于是能不能自己写一个不用CSS虽然不好看但也能用的控件。下面就是树形控件Tree的测试和实现(使用了prototype)。

    HTML测试页。有两个按钮,一个是生成两棵树,另一个是显示当前选择的节点的标签(显示在树上的字符串)。树的数据和树的HTML是分开的,数据是具有树状结构的对象,这里使用的是这种结构:{label: '...', children:[childtree, childtree, ...]}。数据是随机生成的,包括标签、树的深度、子树的数量都是在一定范围内随机生成的。

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Tree 2</title>
    <style type="text/css">
    	.selected {
    		font-weight: bolder;
    	}
    </style>
    <script type="text/javascript" src="js/prototype.js"></script>
    <script type="text/javascript" src="js/tree2.js"></script>
    <script type="text/javascript">
    var trees = new Array(2);
    
    Event.observe(window, 'load', function() {
    	// 初始化两个按钮事件
    	$('btnTree').observe('click', function() {
    		trees[0] = showTree($('dJSON'), $('dTree'));
    		trees[1] = showTree($('dJSON2'), $('dTree2'));
    	});
    
    	$('btnShow').observe('click', function(e) {
    		var next = e.element().next();
    		var content = trees[0].selectedNode ? trees[0].selectedNode.innerHTML : ''; 
    		next.update(content);
    	});
    });
    
    //生成并显示树。divJSON:显示JSON格式的数据;divTree:树所在的DIV。
    function showTree(divJSON, divTree) {
    	divTree.update('');
    	var data = randomData(3);
    	divJSON.update(Object.toJSON(data));
    	var tree = new Tree(divTree, {data: data, onNodeClick: onnc, selectedClass: 'selected'});
    	tree.load();
    	return tree;
    
    	function onnc(e) {
    		var content = e.element().innerHTML;
    		$('btnTree').next().update(content);
    	}
    }
    
    //随机生成数据。level:树的深度。
    function randomData(level) {
    	var CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789';
    	var CHARS_LENGTH = CHARS.length;
    
    	return createData(level);
    
    	// 真正干活的函数。
    	function createData(level) {
    		var o = {};
    		o.label = randomString(10);
    
    		if (level > 0) {
    			var seed = randomInt(0, 10);
    			if (seed > 2) {
    				o.children = new Array(randomInt(2, 4));
    				for (var i = 0, n = o.children.length; i < n; ++i) {
    					o.children[i] = createData(level - 1);
    				}
    			}
    		}
    		return o;
    	}
    	
    	// 随机整数,[bottom, top)
    	function randomInt(bottom, top) {
    		return Math.floor(Math.random() * (top - bottom)) + bottom;
    	}
    
    	// 使用CHARS生成随机字符串。length:字符串长度,返回的字符串一定是这个长度。用数组join的方法实现,高效简洁。
    	function randomString(length) {
    		var a = new Array(length);
    		for (var i = 0; i < length; ++i) {
    			a[i] = CHARS.charAt(randomInt(0, CHARS_LENGTH));
    		}
    		return a.join('');
    	}
    }
    </script>
    </head>
    <body>
    
    <div id="dJSON"></div><div id="dTree"></div>
    
    <div id="dJSON2"></div><div id="dTree2"></div>
    
    <div><input id="btnTree" type="button" value="tree" /><span></span></div>
    <div><input id="btnShow" type="button" value="show"/><span></span></div>
    </body>
    </html>
    

    树形控件。初始化使用两个参数:一是控件所在的父标签。二是配置对象,配置对象中必须设置的是data,如果数据对象有children或label属性,也不用设置getChildren和getLabel函数,getChildren与getLabel这两个函数以数据对象为参数,分别返回节点的子树数组和节点的标签字符串;树可用按钮折叠,disableCollapse属性可以取消折叠的功能;selectedClass是设置当前选择的节点的CSS Class,这里不设置在外观上就看不出来区别,这里还是使用CSS最为合理;onNodeClick为点击节点的事件函数,这里可以设计得更好一些能处理多种和多个事件。树的HTML结构使用UL和LI实现,在逻辑上LI是一棵树,树的子节点放在LI下的UL中。LI的子节点最多有三个:第一个是折叠按钮;第二个是标签;第三个是UL,若没有子树则省略UL。

    (function() {
    	var Tree = Class.create({
    		initialize: function(element, config) {
    			this.element = element;
    			
    			this.data = config.data;
    			this.disableCollapse = config.disableCollapse;
    			this.selectedClass = config.selectedClass;
    			
    			this.getChildren = config.getChildren ? config.getChildren : function(o) {
    				return Object.isArray(o.children) ? o.children : [];
    			};
    			this.getLabel = config.getLabel ? config.getLabel : function(o) {
    				return Object.isUndefined(o.label) ? 'unknown' : o.label;
    			};
    			
    			this.onNodeClick = config.onNodeClick ? config.onNodeClick : function(){};
    			
    			this.root = new Element('ul');
    			this.element.appendChild(this.root);
    		},
    		// 载入数据,创建树。分离此步骤为更灵活地把握创建的时刻。
    		load: function() {
    			if (!this.data) return;
    			
    			var _createNode = this._createNode;
    			var _setNode = this._setNode;
    			var getLabel = this.getLabel;
    			
    			var root_li = this._traverseData(this.data, 0, function(data, level, nodes) {
    				var li = _createNode();
    				_setNode(li, level, '[-]', getLabel(data));
    				var length = nodes.length;
    				if (length > 0) {
    					var ul = new Element('ul');
    					for (var i = 0; i < length; ++i) {
    						ul.appendChild(nodes[i]);
    					}
    					li.appendChild(ul);
    				}
    				return li;
    			});
    			this.root.update('');
    			this.root.appendChild(root_li);
    			
    			if (!this.disableCollapse) {
    				var me = this;
    				this._traverseNode(root_li, 0, function(children, results) {
    					var anchor = children[0];
    					anchor.observe('click', collapse);
    					
    					var label = children[1];
    					label.observe('click', me.onNodeClick);
    					label.observe('click', function(e) {
    						if (me.selectedClass) {
    							if (me.selectedNode) 
    								me.selectedNode.removeClassName(me.selectedClass);
    							me.selectedNode = e.element();
    							me.selectedNode.addClassName(me.selectedClass);
    						}
    					});
    				});
    			}
    			function collapse(e) {
    				var a = e.element();
    				var next = a.next(1);
    				if (next) {
    					a.update(a.innerHTML == '[-]' ? '[+]' : '[-]');
    					next.toggle();
    				}
    			}
    		},
    		// 创建节点,虽然这里封装了,但其他地方深入了节点的内部结构,这块没有做好设计。
    		_createNode: function() {
    			var li = new Element('li');
    			li.appendChild(new Element('a', {href: 'javascript:void(0)'}));
    			li.appendChild(new Element('span'));
    			return li;
    		},
    		// 设置节点的属性(数据)
    		_setNode: function(node, level, button, label) {
    			var children = node.childElements();
    			if (!Object.isUndefined(button)) children[0].update(button);
    			if (!Object.isUndefined(label)) children[1].update(label);
    		},
    		// 后序遍历数据,适合某一个数据及其子树数据的综合计算。data:数据对象;level:树深;callback:回调函数,格式如下                
    		// callback(data, level, results):返回回调的结果,通常是这个节点及其子树的综合值;data:数据对象;level:树深;results:子树的计算结果数组。
    		_traverseData: function(data, level, callback) {
    			if (!data) return;
    			
    			var children = this.getChildren(data);
    			var n = children.length;
    			var results = new Array(n);
    			for (var i = 0; i < n; ++i) {
    				results[i] = this._traverseData(children[i], level + 1, callback);
    			}
    			
    			return callback(data, level, results);
    		},
    		// 后序遍历结构,适合某一个节点及其子树节点的综合计算。data:数据对象;level:树深;callback:回调函数,格式如下                
    		// callback(children, results):返回回调的结果,通常是这个节点及其子树的综合值;results:数据对象;results:子树的计算结果数组。(这里忘了level……)
    		_traverseNode: function(linode, level, callback) {
    			var children = linode.childElements();
    			var length = children.length;
    			var results;
    			if (length > 2) {
    				var ul = children[2];
    				var lis = ul.childElements();
    				var n = lis.length;
    				results = new Array(n);
    				for (var i = 0; i < n; ++i) {
    					results[i] = this._traverseNode(lis[i], level + 1, callback);
    				}
    			} else {
    				results = [];
    			}
    			return callback(children, results);
    		}
    	});
    	
    	window.Tree = Tree;
    })();
    
  • 相关阅读:
    转载(腾讯云社区)——详解django-apscheduler的使用方法
    pipenv——python包管理工具
    xx系统需求进度02
    xx系统需求进度01
    Hbase简介
    第七周总结
    《软件需求十步走》阅读笔记一
    第六周总结
    HDFS
    金字塔表达方法
  • 原文地址:https://www.cnblogs.com/ajeyone/p/1786600.html
Copyright © 2011-2022 走看看