使用了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>