javascript树形控件第二版
使用了prototype的自定义事件;进一步抽象了树节点。数据还是用上个版本的随机数据。
都写在一个HTML中了。
<!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>Test Tree</title> <script type="text/javascript" src="js/prototype.js"></script> <script type="text/javascript"> var Tree = Class.create({ initialize: function(config) { this.parent = config.parent; this.data = config.data; this.getLabel = config.getLabel; this.getChildren = config.getChildren; this.parent.observe('tree:nodeclick', function(){}); this.parent.observe('tree:selectchanged', function(){}); }, observe: function(eventName, handler) { this.parent.observe(eventName, handler); }, load: function() { var me = this; this.root = this._postTraverseData(this.data, 0, makeNode); this._buildTree(this.root); this._postTraverseNode(this.root, 0, createBuildinBehaviour); function makeNode(o, level, children) { var node = me._buildTreeNode(me.getLabel(o), null, null, children, level); return node; } function createBuildinBehaviour(node) { node.treeButton.observe('click', function (e) { var button = e.findElement(); var tree = e.findElement('li'); if (tree.childTreesContainer) { tree.childTreesContainer.toggle(); button.toggleButton(); } }); node.treeLabel.observe('click', function(e) { var node = e.findElement('li'); if (me.selectedNode != node) { var old = me.selectedNode; me.selectedNode = node; me.parent.fire('tree:selectchanged', {old: old, current: node}); } me.parent.fire('tree:nodeclick', {node: node}); }); } }, _postTraverseData: function(o, level, callback) { var children = this.getChildren(o); var results = []; if (children) { for (var i = 0, n = children.length; i < n; ++i) { results.push(this._postTraverseData(children[i], level + 1, callback)); } } return callback(o, level, results); }, _postTraverseNode: function(node, level, callback) { var results = null; if (node.childTreesContainer) { results = []; var childTrees = node.childTreesContainer.childElements(); for (var i = 0, n = childTrees.length; i < n; ++i) { results.push(this._postTraverseNode(childTrees[i], level + 1, callback)); } } return callback(node, level, results); }, _buildTreeNode: function(label, button, toggleButton, childTrees, level) { var eNode = new Element('li'); var eButton; if (Object.isElement(button)) { eButton = button; } else { eButton = new Element('a').update('[-]'); eButton.href = 'javascript:void(0)'; } if (!eButton.toggleButton) { eButton.toggleButton = toggleButton ? toggleButton : function() { this.update(this.innerHTML == '[-]' ? '[+]' : '[-]'); } } eNode.appendChild(eButton); eNode.treeButton = eButton; var eLabel = Object.isElement(label) ? label : new Element('span').update(label.toString()); eNode.appendChild(eLabel); eNode.treeLabel = eLabel; if (Object.isArray(childTrees) && childTrees.length > 0) { var eChildTreesContainer = new Element('ul'); for (var i = 0, n = childTrees.length; i < n; ++i) { eChildTreesContainer.appendChild(childTrees[i]); } eNode.appendChild(eChildTreesContainer); eNode.childTreesContainer = eChildTreesContainer; } return eNode; }, _buildTree: function(root) { var ul = new Element('ul'); ul.appendChild(root); this.parent.appendChild(ul); } }); 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(''); } } Event.observe(window, 'load', function() { var t = new Tree({ parent: $('divTree'), data: randomData(3), getLabel: function(o) { return o.label; }, getChildren: function(o) { return o.children; } }); t.observe('tree:nodeclick', function(e) { $('txtLabel').setValue(e.memo.node.treeLabel.innerHTML); }); t.observe('tree:selectchanged', function(e) { var old = e.memo.old; var current = e.memo.current; $('divMessage').update((old ? old.treeLabel.innerHTML : 'null') + " -> " + current.treeLabel.innerHTML); }); t.load(); $('btnUpdate').observe('click', function() { if (t.selectedNode) { t.selectedNode.treeLabel.update($F('txtLabel')); } }); }); </script> </head> <body> <div id="divTree"></div> <input id="txtLabel" type="text" /> <input id="btnUpdate" type="button" value="Update" /> <div id="divMessage"></div> </body> </html>