以下是 带日志JS多级目录树结构特效代码 的示例演示效果:
部分效果截图1:
部分效果截图2:
HTML代码(index.html):
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>带日志JS多级目录树结构特效</title>
<link rel="stylesheet" type="text/css" href="css/default.css">
<link rel="stylesheet" type="text/css" href="css/Aimara.css">
<link rel="stylesheet" type="text/css" href="css/Example.css">
<script src="lib/Aimara.js" type="text/javascript"></script>
<script type="text/javascript">
window.onload = function() {
//Initializing Tree
//Tree Context Menu Structure
var contex_menu = {
'context1' : {
elements : [
{
text : 'Node Actions',
icon: 'images/blue_key.png',
action : function(node) {
},
submenu: {
elements : [
{
text : 'Toggle Node',
icon: 'images/leaf.png',
action : function(node) {
node.toggleNode();
}
},
{
text : 'Expand Node',
icon: 'images/leaf.png',
action : function(node) {
node.expandNode();
}
},
{
text : 'Collapse Node',
icon: 'images/leaf.png',
action : function(node) {
node.collapseNode();
}
},
{
text : 'Expand Subtree',
icon: 'images/tree.png',
action : function(node) {
node.expandSubtree();
}
},
{
text : 'Collapse Subtree',
icon: 'images/tree.png',
action : function(node) {
node.collapseSubtree();
}
},
{
text : 'Delete Node',
icon: 'images/delete.png',
action : function(node) {
node.removeNode();
}
},
]
}
},
{
text : 'Child Actions',
icon: 'images/blue_key.png',
action : function(node) {
},
submenu: {
elements : [
{
text : 'Create Child Node',
icon: 'images/add1.png',
action : function(node) {
node.createChildNode('Created',false,'images/folder.png',null,'context1');
}
},
{
text : 'Create 1000 Child Nodes',
icon: 'images/add1.png',
action : function(node) {
for (var i=0; i<1000; i++)
node.createChildNode('Created -' + i,false,'images/folder.png',null,'context1');
}
},
{
text : 'Delete Child Nodes',
icon: 'images/delete.png',
action : function(node) {
node.removeChildNodes();
}
}
]
}
}
]
}
};
//Creating the tree
tree = createTree('div_tree','white',contex_menu);
div_log = document.getElementById('div_log');
//Setting custom events
tree.nodeBeforeOpenEvent = function(node) {
div_log.innerHTML += node.text + ': Before expand event<br/>';
}
tree.nodeAfterOpenEvent = function(node) {
div_log.innerHTML += node.text + ': After expand event<br/>';
}
tree.nodeBeforeCloseEvent = function(node) {
div_log.innerHTML += node.text + ': Before collapse event<br/>';
}
//Loop to create test nodes
for (var i=1; i<10; i++) {
node1 = tree.createNode('Level 0 - Node ' + i,false,'images/star.png',null,null,'context1');
for (var j=1; j<5; j++) {
node2 = node1.createChildNode('Level 1 - Node ' + j, false, 'images/blue_key.png',null,'context1');
for (var k=1; k<5; k++) {
node3 = node2.createChildNode('Level 2 - Node ' + k, false, 'images/monitor.png',null,'context1');
/*for (var l=1; l<5; l++) {
node4 = node3.createChildNode('Level 3 - Node ' + l, false, 'images/key_green.png',null,'context1');
for (var m=1; m<5; m++) {
node4.createChildNode('Level 4 - Node ' + m, false, 'images/file.png',null,'context1');
}
}*/
}
}
}
//Rendering the tree
tree.drawTree();
//Adding node after tree is already rendered
tree.createNode('Real Time',false,'images/leaf.png',null,null,'context1');
};
function expand_all() {
tree.expandTree();
}
function clear_log() {
document.getElementById('div_log').innerHTML = '';
}
function collapse_all() {
tree.collapseTree();
}
</script>
</head>
<body>
<div class="htmleaf-container">
<div class="htmleaf-content">
<h2>带日志JS多级目录树结构特效</h2>
<button onclick="expand_all()">扩大所有节点</button>
<button onclick="collapse_all()">所有节点崩溃</button>
<button onclick="clear_log()">清除日志</button>
<br/><br/>
<div id="div_log"></div>
<div id="div_tree"></div>
</div>
</div>
</body>
</html>
JS代码(Aimara.js):
///// Creating the tree component// p_div:ID of the div where the tree will be rendered;
// p_backColor:Background color of the region where the tree is being rendered;
// p_contextMenu:Object containing all the context menus. Set null for no context menu;
function createTree(p_div,p_backColor,p_contextMenu){
var tree ={
name:'tree',div:p_div,ulElement:null,childNodes:[],backcolor:p_backColor,contextMenu:p_contextMenu,selectedNode:null,nodeCounter:0,contextMenuDiv:null,rendered:false,///// Creating a new node// p_text:Text displayed on the node;
// p_expanded:True or false,indicating wether the node starts expanded or not;
// p_icon:Relative path to the icon displayed with the node. Set null if the node has no icon;
// p_parentNode:Reference to the parent node. Set null to create the node on the root;
// p_tag:Tag is used to store additional information on the node. All node attributes are visible when programming events and context menu actions;
// p_contextmenu:Name of the context menu,which is one of the attributes of the p_contextMenu object created with the tree;
createNode:function(p_text,p_expanded,p_icon,p_parentNode,p_tag,p_contextmenu){
v_tree = this;
node ={
id:'node_' + this.nodeCounter,text:p_text,icon:p_icon,parent:p_parentNode,expanded:p_expanded,childNodes:[],tag:p_tag,contextMenu:p_contextmenu,elementLi:null,///// Removing the node and all its childrenremoveNode:function(){
v_tree.removeNode(this);
}
,///// Expanding or collapsing the node,depending on the expanded valuetoggleNode:function(p_event){
v_tree.toggleNode(this);
}
,///// Expanding the nodeexpandNode:function(p_event){
v_tree.expandNode(this);
}
,///// Expanding the node and its children recursivelyexpandSubtree:function(){
v_tree.expandSubtree(this);
}
,///// Changing the node text// p_text:New text;
setText:function(p_text){
v_tree.setText(this,p_text);
}
,///// Collapsing the nodecollapseNode:function(){
v_tree.collapseNode(this);
}
,///// Collapsing the node and its children recursivelycollapseSubtree:function(){
v_tree.collapseSubtree(this);
}
,///// Deleting all child nodesremoveChildNodes:function(){
v_tree.removeChildNodes(this);
}
,///// Creating a new child node;
// p_text:Text displayed;
// p_expanded:True or false,indicating wether the node starts expanded or not;
// p_icon:Icon;
// p_tag:Tag;
// p_contextmenu:Context Menu;
createChildNode:function(p_text,p_expanded,p_icon,p_tag,p_contextmenu){
return v_tree.createNode(p_text,p_expanded,p_icon,this,p_tag,p_contextmenu);
}
}
this.nodeCounter++;
if (this.rendered){
if (p_parentNode==undefined){
this.drawNode(this.ulElement,node);
this.adjustLines(this.ulElement,false);
}
else{
var v_ul = p_parentNode.elementLi.getElementsByTagName("ul")[0];
if (p_parentNode.childNodes.length==0){
if (p_parentNode.expanded){
p_parentNode.elementLi.getElementsByTagName("ul")[0].style.display = 'block';
v_img = p_parentNode.elementLi.getElementsByTagName("img")[0];
v_img.style.visibility = "visible";
v_img.src = 'images/collapse.png';
v_img.id = 'toggle_off';
}
else{
p_parentNode.elementLi.getElementsByTagName("ul")[0].style.display = 'none';
v_img = p_parentNode.elementLi.getElementsByTagName("img")[0];
v_img.style.visibility = "visible";
v_img.src = 'images/expand.png';
v_img.id = 'toggle_on';
}
}
this.drawNode(v_ul,node);
this.adjustLines(v_ul,false);
}
}
if (p_parentNode==undefined){
this.childNodes.push(node);
node.parent=this;
}
elsep_parentNode.childNodes.push(node);
return node;
}
,///// Render the tree;
drawTree:function(){
this.rendered = true;
var div_tree = document.getElementById(this.div);
div_tree.innerHTML = '';
ulElement = createSimpleElement('ul',this.name,'tree');
this.ulElement = ulElement;
for (var i=0;
i<this.childNodes.length;
i++){
this.drawNode(ulElement,this.childNodes[i]);
}
div_tree.appendChild(ulElement);
this.adjustLines(document.getElementById(this.name),true);
}
,///// Drawing the node. This function is used when drawing the Tree and should not be called directly;
// p_ulElement:Reference to the UL tag element where the node should be created;
// p_node:Reference to the node object;
drawNode:function(p_ulElement,p_node){
v_tree = this;
var v_icon = null;
if (p_node.icon!=null)v_icon = createImgElement(null,'icon_tree',p_node.icon);
var v_li = document.createElement('li');
p_node.elementLi = v_li;
var v_span = createSimpleElement('span',null,'node');
var v_exp_col = null;
if (p_node.childNodes.length == 0){
v_exp_col = createImgElement('toggle_off','exp_col','images/collapse.png');
v_exp_col.style.visibility = "hidden";
}
else{
if (p_node.expanded){
v_exp_col = createImgElement('toggle_off','exp_col','images/collapse.png');
}
else{
v_exp_col = createImgElement('toggle_on','exp_col','images/expand.png');
}
}
v_span.ondblclick = function(){
v_tree.doubleClickNode(p_node);
}
;
v_exp_col.onclick = function(){
v_tree.toggleNode(p_node);
}
;
v_span.onclick = function(){
v_tree.selectNode(p_node);
}
;
v_span.oncontextmenu = function(e){
v_tree.selectNode(p_node);
v_tree.nodeContextMenu(e,p_node);
}
;
if (v_icon!=undefined)v_span.appendChild(v_icon);
v_a = createSimpleElement('a',null,null);
v_a.innerHTML=p_node.text;
v_span.appendChild(v_a);
v_li.appendChild(v_exp_col);
v_li.appendChild(v_span);
p_ulElement.appendChild(v_li);
var v_ul = createSimpleElement('ul','ul_' + p_node.id,null);
v_li.appendChild(v_ul);
if (p_node.childNodes.length > 0){
if (!p_node.expanded)v_ul.style.display = 'none';
for (var i=0;
i<p_node.childNodes.length;
i++){
this.drawNode(v_ul,p_node.childNodes[i]);
}
}
}
,///// Changing node text// p_node:Reference to the node that will have its text updated;
// p_text:New text;
setText:function(p_node,p_text){
p_node.elementLi.getElementsByTagName('span')[0].lastChild.innerHTML = p_text;
p_node.text = p_text;
}
,///// Expanding all tree nodesexpandTree:function(){
for (var i=0;
i<this.childNodes.length;
i++){
if (this.childNodes[i].childNodes.length>0){
this.expandSubtree(this.childNodes[i]);
}
}
}
,///// Expanding all nodes inside the subtree that have parameter 'p_node' as root// p_node:Subtree root;
expandSubtree:function(p_node){
this.expandNode(p_node);
for (var i=0;
i<p_node.childNodes.length;
i++){
if (p_node.childNodes[i].childNodes.length>0){
this.expandSubtree(p_node.childNodes[i]);
}
}
}
,///// Collapsing all tree nodescollapseTree:function(){
for (var i=0;
i<this.childNodes.length;
i++){
if (this.childNodes[i].childNodes.length>0){
this.collapseSubtree(this.childNodes[i]);
}
}
}
,///// Collapsing all nodes inside the subtree that have parameter 'p_node' as root// p_node:Subtree root;
collapseSubtree:function(p_node){
this.collapseNode(p_node);
for (var i=0;
i<p_node.childNodes.length;
i++){
if (p_node.childNodes[i].childNodes.length>0){
this.collapseSubtree(p_node.childNodes[i]);
}
}
}
,///// Expanding node// p_node:Reference to the node;
expandNode:function(p_node){
if (p_node.childNodes.length>0 && p_node.expanded==false){
if (this.nodeBeforeOpenEvent!=undefined)this.nodeBeforeOpenEvent(p_node);
var img=p_node.elementLi.getElementsByTagName("img")[0];
p_node.expanded = true;
img.id="toggle_off";
img.src = 'images/collapse.png';
elem_ul = img.parentElement.getElementsByTagName("ul")[0];
elem_ul.style.display = 'block';
if (this.nodeAfterOpenEvent!=undefined)this.nodeAfterOpenEvent(p_node);
}
}
,///// Collapsing node// p_node:Reference to the node;
collapseNode:function(p_node){
if (p_node.childNodes.length>0 && p_node.expanded==true){
var img=p_node.elementLi.getElementsByTagName("img")[0];
p_node.expanded = false;
if (this.nodeBeforeCloseEvent!=undefined)this.nodeBeforeCloseEvent(p_node);
img.id="toggle_on";
img.src = 'images/expand.png';
elem_ul = img.parentElement.getElementsByTagName("ul")[0];
elem_ul.style.display = 'none';
}
}
,///// Toggling node// p_node:Reference to the node;
toggleNode:function(p_node){
if (p_node.childNodes.length>0){
if (p_node.expanded)p_node.collapseNode();
elsep_node.expandNode();
}
}
,///// Double clicking node// p_node:Reference to the node;
doubleClickNode:function(p_node){
this.toggleNode(p_node);
}
,///// Selecting node// p_node:Reference to the node;
selectNode:function(p_node){
var span = p_node.elementLi.getElementsByTagName("span")[0];
span.className = 'node_selected';
if (this.selectedNode!=null && this.selectedNode!=p_node)this.selectedNode.elementLi.getElementsByTagName("span")[0].className = 'node';
this.selectedNode = p_node;
}
,///// Deleting node// p_node:Reference to the node;
removeNode:function(p_node){
var index = p_node.parent.childNodes.indexOf(p_node);
if (p_node.elementLi.className=="last" && index!=0){
p_node.parent.childNodes[index-1].elementLi.className += "last";
p_node.parent.childNodes[index-1].elementLi.style.backgroundColor = this.backcolor;
}
p_node.elementLi.parentNode.removeChild(p_node.elementLi);
p_node.parent.childNodes.splice(index,1);
if (p_node.parent.childNodes.length==0){
var v_img = p_node.parent.elementLi.getElementsByTagName("img")[0];
v_img.style.visibility = "hidden";
}
}
,///// Deleting all node children// p_node:Reference to the node;
removeChildNodes:function(p_node){
if (p_node.childNodes.length>0){
var v_ul = p_node.elementLi.getElementsByTagName("ul")[0];
var v_img = p_node.elementLi.getElementsByTagName("img")[0];
v_img.style.visibility = "hidden";
p_node.childNodes = [];
v_ul.innerHTML = "";
}
}
,///// Rendering context menu when mouse right button is pressed over a node. This function should no be called directly// p_event:Event triggered when right clicking;
// p_node:Reference to the node;
nodeContextMenu:function(p_event,p_node){
if (p_event.button==2){
p_event.preventDefault();
p_event.stopPropagation();
if (p_node.contextMenu!=undefined){
v_tree = this;
var v_menu = this.contextMenu[p_node.contextMenu];
var v_div;
if (this.contextMenuDiv==null){
v_div = createSimpleElement('ul','ul_cm','menu');
document.body.appendChild(v_div);
}
elsev_div = this.contextMenuDiv;
v_div.innerHTML = '';
var v_left = p_event.pageX-5;
var v_right = p_event.pageY-5;
v_div.style.display = 'block';
v_div.style.position = 'absolute';
v_div.style.left = v_left + 'px';
v_div.style.top = v_right + 'px';
for (var i=0;
i<v_menu.elements.length;
i++) (function(i){
var v_li = createSimpleElement('li',null,null);
var v_span = createSimpleElement('span',null,null);
v_span.onclick = function (){
v_menu.elements[i].action(p_node)}
;
var v_a = createSimpleElement('a',null,null);
var v_ul = createSimpleElement('ul',null,'sub-menu');
v_a.appendChild(document.createTextNode(v_menu.elements[i].text));
v_li.appendChild(v_span);
if (v_menu.elements[i].icon!=undefined){
var v_img = createImgElement('null','null',v_menu.elements[i].icon);
v_li.appendChild(v_img);
}
v_li.appendChild(v_a);
v_li.appendChild(v_ul);
v_div.appendChild(v_li);
if (v_menu.elements[i].submenu!=undefined){
var v_span_more = createSimpleElement('div',null,null);
v_span_more.appendChild(createImgElement(null,'menu_img','images/right.png'));
v_li.appendChild(v_span_more);
v_tree.contextMenuLi(v_menu.elements[i].submenu,v_ul,p_node);
}
}
)(i);
this.contextMenuDiv = v_div;
}
}
}
,///// Recursive function called when rendering context menu submenus. This function should no be called directly// p_submenu:Reference to the submenu object;
// p_ul:Reference to the UL tag;
// p_node:Reference to the node;
contextMenuLi:function(p_submenu,p_ul,p_node){
v_tree = this;
for (var i=0;
i<p_submenu.elements.length;
i++) (function(i){
var v_li = createSimpleElement('li',null,null);
var v_span = createSimpleElement('span',null,null);
v_span.onclick = function (){
p_submenu.elements[i].action(p_node)}
;
var v_a = createSimpleElement('a',null,null);
var v_ul = createSimpleElement('ul',null,'sub-menu');
v_a.appendChild(document.createTextNode(p_submenu.elements[i].text));
v_li.appendChild(v_span);
if (p_submenu.elements[i].icon!=undefined){
var v_img = createImgElement('null','null',p_submenu.elements[i].icon);
v_li.appendChild(v_img);
}
v_li.appendChild(v_a);
v_li.appendChild(v_ul);
p_ul.appendChild(v_li);
if (p_submenu.elements[i].p_submenu!=undefined){
var v_span_more = createSimpleElement('div',null,null);
v_span_more.appendChild(createImgElement(null,'menu_img','images/right.png'));
v_li.appendChild(v_span_more);
v_tree.contextMenuLi(p_submenu.elements[i].p_submenu,v_ul,p_node);
}
}
)(i);
}
,///// Adjusting tree dotted lines. This function should not be called directly// p_node:Reference to the node;
adjustLines:function(p_ul,p_recursive){
var tree = p_ul;
var lists = [];
if (tree.childNodes.length>0){
lists = [ tree ];
if (p_recursive){
for (var i = 0;
i < tree.getElementsByTagName("ul").length;
i++){
var check_ul = tree.getElementsByTagName("ul")[i];
if (check_ul.childNodes.length!=0)lists[lists.length] = check_ul;
}
}
}
for (var i = 0;
i < lists.length;
i++){
var item = lists[i].lastChild;
while (!item.tagName || item.tagName.toLowerCase() != "li"){
item = item.previousSibling;
}
item.className += "last";
item.style.backgroundColor = this.backcolor;
item = item.previousSibling;
if (item!=null)if (item.tagName.toLowerCase() == "li"){
item.className = "";
item.style.backgroundColor = 'transparent';
}
}
}
}
window.onclick = function(){
if (tree.contextMenuDiv!=null)tree.contextMenuDiv.style.display = 'none';
}
return tree;
}
// Helper Functions//Create a HTML element specified by parameter 'p_type'function createSimpleElement(p_type,p_id,p_class){
element = document.createElement(p_type);
if (p_id!=undefined)element.id = p_id;
if (p_class!=undefined)element.className = p_class;
return element;
}
//Create img elementfunction createImgElement(p_id,p_class,p_src){
element = document.createElement('img');
if (p_id!=undefined)element.id = p_id;
if (p_class!=undefined)element.className = p_class;
if (p_src!=undefined)element.src = p_src;
return element;
}
CSS代码(Example.css):
body{font-family:'Helvetica Neue',Helvetica,'Segoe UI',Arial,freesans;}
#div_tree{font:10px Verdana,sans-serif;display:inline-block;width:300px;}
#div_log{width:400px;height:400px;overflow:scroll;padding:10px;vertical-align:top;display:inline-block;border:1px solid gray;font:10px Verdana,sans-serif;line-height:16px;}