欢迎访问:常州市武进区嘉泽中心小学网站 !今天是:
栏目列表
您现在的位置是:首页>>教师>>计算机技术>>网站制作技术>>文章内容
基于javascript的树形菜单
发布时间:2008-11-20   点击:   来源:本站原创   录入者:佚名
 

基于JavaScript的树形菜单

1 前言

显然,树形菜单在网络应用中是很重要的。我也到过国外专业的菜单开发网站看过,但是感觉可维护性很差。由于工作中的一些需要,我认为自己开发一个维护性好,接口简单的树形菜单是十分有用的。本文将详细介绍作者开发的基于JavaScript的树形菜单,并附上源代码。

 

2 基本功能

2.1 跨平台性能好

2.2 可维护性好

2.3 接口简单

2.4 能够表达子菜单

2.5 菜单显示时间不大于1ms/菜单项,响应时间亦如此。

 

3 基本分析

3.1 要求可维护性好,需采用完全的面向对象方法。

3.2 树形菜单完全由“菜单项”组成,“菜单项”由表达其类型(有无子菜单项)的开关图和表达其内容的文本,以及表达其事件响应的行为构成。开关图负责响应菜单项是否展开的事件。

3.3 树形菜单以及每个菜单项都使用table元素表示

 

4 设计

4.1 整个程序整合为一个treedefiniton.js文件,其中包含两个类(js称之为对象)。一个类是Tree,另一个是TreeItem,二者是一对多的关系。当然测试程序是一个test.htm文件。

4.2 程序的设计文档用reference.xml表示,版本信息(包含作者)用versions.xml表示。所有xml的结构尚未固定,仅有用于显示的reference.xsl以及versions.xsl.这些程序的源代码限于篇幅,未公布于此。

4.3 TreeItem设计 TreeItem TreeItem public methods TreeItem() addItem() public attributes text action children tree type id Description An instance of this class is corresponding to an menu item on a tree menu. public methods TreeItem(text,action) functions CONSTRUCTOR arguments text type: string description: to be shown action type: string description: The action will be performed when the item is clicked. It will be the onclick property of a html element. CAUTION:DON'T PUT CHAR ' TO IT. addItem() functions Let the menuitems specified by the arguments be the subitems of this menuitem. Their orders are determined by the orders of the correponding arguments. arguments type: TreeItem description: There is no limit on the number of arguments, but each argument should be an instance of Class MenuItem. public attributes 1 text string set by the first argument of CONSTRUCTOR. Only for Class Tree. 2 action string set by the second argument of CONSTRUCTOR. Only for Class Tree. 3 children Array all subitems, set by addChild() method. Only for Class Tree. 4 tree Tree the tree to which the item belongs. Only for Class Tree. 5 type string should be one of Tree.FILE_TYPE and Tree.FOLDER_TYPE. Only for Class Tree. 6 id string id property of the element corresponding to this item. Only for Class Tree.

4.4 Tree设计 Tree Tree CONSTANTS VERSION AUTHOR FILE_IMAGE FOLDER_IMAGE UNFOLD_IMAGE VERSIONS REFERENCE FILE_TYPE FOLDER_TYPE public methods Tree() getHtml() public attributes indent withSelfItem itemColor itemBackground selectedItemBackground root selfItem versionsItem referenceItem private attributes name html elementClicked private methods findItem findUnderlines click Description An instance of this class is corresponding to a tree menu. CONSTANTS 1 VERSION string "1.08" version number 2 AUTHOR string "liaomingxue" author name 3 FILE_IMAGE string "images/file.gif" relative path of an image file for menu item with no subitems 4 FOLDER_IMAGE string "images/fold.gif" relative path of an image file for menu item having visible subitems 5 UNFOLD_IMAGE string "images/unfold.gif" relative path of an image file for menu item with its invisible subitems 6 VERSIONS string "aboutTree/versions.xml" relative path of file used to show what progresses happened in all versions of this program. 7 REFERENCE string "aboutTree/class.xml" relative path of file used to introduce the design of the classes of this program. 8 FILE_TYPE string "FILE" an option for type atrribute of Class TreeItem 9 FOLDER_TYPE string "FOLDER" an option for type atrribute of Class TreeItem public methods Tree(instanceName,text,action) functions CONSTRUCTOR arguments instanceName type: string description: the name of the instance 'this' text type: string description: the text property of root item of Tree action type: string description: the action property of root item of Tree getHtml() functions to get html property of Tree public attributes 1 indent integer for indenting subitem, greater than 0 2 withSelfItem boolean if true, show selfItem 3 itemColor string text color of item, eg. "#888888" 4 itemBackground string background color of item 5 selectedItemBackground string background color of item clicked 6 root TreeItem root item 7 selfItem TreeItem for demonstrating information about this program 8 versionsItem TreeItem selfItem's subitem to tell information about all versions 9 referenceItem TreeItem selfItem's subitem to show Classes designing private attributes 1 name string set by argument 1 2 html string the content(in html format) to be shown 3 elementClicked string hold the id property of which item clicked private methods findItem(id) arguments id type: string description: the id of what item you want to find findUnderlines(str) arguments str type: string description: return type: integer description: return how many underlines '_' occur in the argument click(itemID) functions respond to item click event arguments itemID type: string description: id property of the clicked item

4.5 一些补充

4.5.1对于TreeItem的public attributes,显然是不能都作为public的,这里这样处理的原因是我认为程序的功能尚未达到需要严格定义的程度。

4.5.2 根菜单项。树形菜单只有一个根菜单项,其他所有菜单项都是它的子菜单项。

4.5.3 菜单项的id。根菜单项的id仅含一个下划线,其余类推之。

4.5.4 用到的图标资源为,类似于csdn,是我自己画的。

 

5 性能

 在菜单项达到48项时候,显示时间基本达到要求,响应时间好于要求。

 

6 总结

自己并不是专业做这个的,所以公布这个源代码,希望能够让大家做好这个东西。目前最新版本是1.12. 任何进一步改进该程序的程序员,我将在versions.xml中注明该程序员。原则上每次改进,版本号将增加0.01.限于个人能力,所有改进只能通过e-mail,且,所有改进都将在这篇文章中公布。我的email是mxliao@{域名已经过期}。暂时由我管理所有改进。如有朋友提供空间,我将感激不尽,并将适时采取其他程序管理方法。

 由于本程序的设计者和实现者都是我一人,以及时间有限,所以这里设计和实现的衔接可能有不完备或者不一致的地方,请见谅。

 

7 测试程序源代码片断(test.htm

var tree=new Tree("tree","Words Expert","");
tree.root.addChild((Add=new TreeItem("Add","")),
  (Recite=new TreeItem("Recite","")),
  (View=new TreeItem("View","")),
  (Help=new TreeItem("Help","")),
  (Config=new TreeItem("Config","")),
  (File=new TreeItem("File","")),
  (About=new TreeItem("About Words Expert","")));
Recite.addChild((seeChinese=new TreeItem("See Chinese","")),
             (seeEnglish=new TreeItem("See English","")),
  (lastOrder=new TreeItem("Last order","")));
seeChinese.addChild((reciteChineseAlpha=new TreeItem("Alpha","")),
                         (reciteChineseFamiliarity=new TreeItem("Familiarity","")),
                         (reciteChineseTime=new TreeItem("Time","")));
 reciteChineseAlpha.addChild(
  (new TreeItem("Alpha Order","")),
  (new TreeItem("Reverse Alpha Order","")));
 reciteChineseFamiliarity.addChild(
  (new TreeItem("Familiarity","")),
  (new TreeItem("Reverse Reverse Familiarity","")));
 reciteChineseTime.addChild(
  (new TreeItem("Time","")),
  (new TreeItem("Reverse Time","")));
 seeEnglish.addChild((reciteEnglishAlpha=new TreeItem("Alpha","")),
                         (reciteEnglishFamiliarity=new TreeItem("Familiarity","")),
                         (reciteEnglishTime=new TreeItem("Time","")));
 reciteEnglishAlpha.addChild(
  (new TreeItem("Alpha Order","")),
  (new TreeItem("Reverse Alpha Order","")));
 reciteEnglishFamiliarity.addChild(
  (new TreeItem("Familiarity","")),
  (new TreeItem("Reverse Reverse Familiarity","")));
 reciteEnglishTime.addChild(
  (new TreeItem("Time","")),
  (new TreeItem("Reverse Time","")));

 View.addChild(
  (lastOrder=new TreeItem("Last Order","")),
  (alpha=new TreeItem("Alpha","")),
  (familiarity=new TreeItem("Familiarity","")),
  (time=new TreeItem("Time","")));
 alpha.addChild(
  (new TreeItem("Alpha Order","")),
  (new TreeItem("Reverse Alpha Order","")));
 familiarity.addChild(
  (new TreeItem("Familiarity Order","")),
  (new TreeItem("Reverse Familiarity Order","")));
 time.addChild(
  (new TreeItem("Time Order","")),
  (new TreeItem("Reverse Time Order","")));
 
 File.addChild((new TreeItem("Save wordbook","")), (new TreeItem("Save config","")),(new TreeItem("Save All","")),(new TreeItem("Save All & Exit","")),
  (new TreeItem("Exit Without Saving","")),
  (new TreeItem("Backup wordbook","")));
tree.show();

 

8 TreeDefinition.js源代码(7K

function TreeItem(text,action)
{
 // public attributes
 this.text=text; 
 this.action=action; 
 this.children=new Array();
 this.tree=null;
 this.type="";
 this.id=""; 
 // public methods
 this.addChild=TreeItem_addChild;
}
function Tree(instanceName,text,action)
{
 // constants
 this.VERSION="1.12";
 this.AUTHOR="liaomingxue";
 this.FILE_IMAGE="images/file.gif";
 this.FOLDER_IMAGE="images/fold.gif";
 this.UNFOLD_IMAGE="images/unfold.gif";
 this.VERSIONS="aboutTree/versions.xml";
 this.REFERENCE="aboutTree/class.xml";
 this.FILE_TYPE="FILE";
 this.FOLDER_TYPE="FOLDER";
 
 // priavate attributes
 {域名已经过期}=instanceName;
 this.html="";
 this.elementClicked="";

 // public attributes
 this.indent=20;
 this.withSelfItem=true;
 this.itemColor="#000000";
 this.itemBackground="#aaafff";
 this.itemOverColor="#0000ff";
 this.selectedItemBackground="#ccaacc";
 this.root=new TreeItem(text,action);
  this.root.id="TREEMENU"+"_0";
  this.root.tree=this;
 this.selfItem=new TreeItem("ABOUT TREE MENU","");
 this.versionsItem=new TreeItem("versions","window.open(\""+this.VERSIONS+"\")");
 this.referenceItem=new TreeItem("programmer reference","window.open(\""+this.REFERENCE+"\")");

 // private methods
 this.findItem=Tree_findItem;
 this.findUnderlines=Tree_findUnderlines;
 this.click=Tree_click;

 // public methods
 this.show=TREE_show;
 this.getHtml=function getHtml(){return this.html;}
}
function Tree_findUnderlines(str)
{
 var i,j;
 for(i=0,j=0;i<str.length;i++) if(str.charAt(i)==='_') j++;
 return j;
}
function TreeItem_addChild()
{
 var child;
 var i;
 for(i=0;i<arguments.length;i++)
 {
  child=arguments[i];
  this.children[this.children.length]=child;
  this.type=this.tree.FOLDER_TYPE;
  child.id=this.id+"_"+(this.children.length-1);
  child.tree=this.tree;
  child.type=this.tree.FILE_TYPE;
 }
}

function TREE_show()
{
 this.html+="<table cellspacing=0 cellpadding=0 rules='cols' style='border-width:2;' height='100%'>";
 this.html+="<tr><td>";
 this.html+="<table cellspacing=0 cellpadding=0>";

 var n,i,top,stack;
 this.root.addChild(this.selfItem);
 this.selfItem.addChild(this.versionsItem,this.referenceItem);
 stack=new Array(); //初始化目录栈 
 stack[0]=this.root; //加入目录栈的栈顶目录
 while(stack.length>0)
 {
  /*弹出栈顶元素,并保存到top变量*/
  top=stack[stack.length-1];
  stack.length--;
  n=this.findUnderlines(top.id);
  /*将弹出的栈顶元素作为一个目录加入*/
  this.html+="<tr style='";
  if(n<3)  this.html+="  display:;'>";
  else  this.html+="  display:none;'>";
  this.html+="<td>";
  this.html+="  <table style='table-layout:fixed;' cellspacing=0 cellpadding=0 id='"+top.id+"'"; 
  this.html+="    <tr style='background:"+this.itemBackground+";'>";
  for(i=0;i<n-1;i++)
       this.html+="  <td width="+this.indent+">&nbsp;</td>";
  this.html+="       <td nowrap>";
  this.html+="          <img style='cursor:hand;' onclick='"+{域名已经过期}+".click(this.parentNode.parentNode.parentNode.parentNode.id);' border=0 src='";
  if(top.type==this.FOLDER_TYPE)
  if(n==1)
   this.html+=this.UNFOLD_IMAGE+"'>";
  else this.html+=this.FOLDER_IMAGE+"'>";
  else this.html+=this.FILE_IMAGE+"'>";
  this.html+=          "</img>";
  this.html+="          <a onmouseover='this.style.color=\""+this.itemOverColor+"\";'";
  this.html+="  onmouseout='this.style.color=\""+this.itemColor+"\";'";
  this.html+="  style='color:"+this.itemColor+";cursor=hand;font-family:times new roman;font-size:medium;font-weight:bold;''"
  this.html+="  onclick='"+top.action+"'>"+top.text;
  this.html+="          </a>";
  this.html+="       </td>";
  this.html+="    </tr>";
  this.html+="  </table>";
  this.html+="</tr></td>";

  /*假如弹出的栈顶目录有子目录,应该按逆序把所有子目录加到栈中*/
  for(i=top.children.length-1;i>=0;i--) stack[stack.length]=top.children[i];
 }
 this.html+="</table>";
 this.html+="</td></tr>";
 this.html+="<tr><td height='100%' style='background:"+this.itemBackground+";'>&nbsp;</td></tr>";
 this.html+="</table>";
 document.write(this.html);
}

function Tree_click(itemID)
{
    /* 首先找到被单击的html元素 */
 var e=window.event.srcElement;
 /*  找到该元素对应的目录对象 */
 var item=this.findItem(itemID);
 if(item!=null)
 {  
  /* 如果该目录的类型为文件夹,那么,当它已经展开时,应折叠,否则相反 */
  var i,s;
  //如果被单击的是文件夹类型的目录
  if(item.type==this.FOLDER_TYPE)
  {
   //如果该文件夹没有展开
   if(e.src.indexOf(this.FOLDER_IMAGE)>=0)
   {
    //首先展开它,也就是更换它的图像
    e.src=this.UNFOLD_IMAGE;
    var stack=new Array(); //初始化目录栈
    //先将所有一级子目录加到栈中
    for(i=item.children.length-1;i>=0;i--)  stack[stack.length]=item.children[i];
    var top;
    while(stack.length>0)
    {
     /*弹出一个目录*/
     top=stack[stack.length-1];stack.length--;
     document.all(top.id).parentNode.parentNode.style.display=""; //显示它
     //如果这个被显示的目录处于展开状态,那么它的所有一级子目录应该加到栈中 
     if(top.type==this.FOLDER_TYPE &&
      document.all(top.id).firstChild.firstChild.lastChild.firstChild.src.indexOf(this.UNFOLD_IMAGE)>=0)
     for(i=top.children.length-1;i>=0;i--) stack[stack.length]=top.children[i];
    }             
   }
   /* 如果该文件夹已经展开 */
   else
   {
    //首先折叠它
    e.src=this.FOLDER_IMAGE;
    //然后隐藏它的所有子目录,这里用到一个栈,来求出所有子目录
    var stack=new Array(); //初始化目录栈
    //先将所有一级子目录加到栈中
    for(i=item.children.length-1;i>=0;i--)
     stack[stack.length]=item.children[i];
    var top;
    while(stack.length>0)
    {
     /*弹出一个目录*/
     top=stack[stack.length-1];stack.length--;
     document.all(top.id).parentNode.parentNode.style.display="none"; //隐藏它
     //再将这个被隐藏的目录的所有一级子目录加到栈中 
     for(i=top.children.length-1;i>=0;i--) stack[stack.length]=top.children[i];
    }             
   }
  }
  /*如果以前有被单击的目录,那么以前被单击的元素要恢复到未单击的状态*/
  if(this.elementClicked.length!=0)
   document.all(this.elementClicked).firstChild.firstChild.lastChild.lastChild.style.background=this.itemBackground;
  this.elementClicked=itemID; 
  document.all(this.elementClicked).firstChild.firstChild.lastChild.lastChild.style.background=this.selectedItemBackground;
 }
}

function Tree_findItem(id)
{
 /*从根目录开始找*/
 var root=this.root;
 var item=root;
 if(item.id==id) return item;
 
 var i;
 for(i=0;i<root.children.length;i++)
 {
  item=root.children[i];
  /*正好找到*/
  if(item.id==id) return item;
  /*
   如果这个目录的层次大于或者等于要找的目录的层次,那么就不必再找了。
   由于目录的层次与目录的id中的下划线个数相同,所以可以用这种方法
  */
  if(this.findUnderlines(item.id)>this.findUnderlines(id)) return null;
  /*如果找到它的一个直系祖先,那么就应该马上从它的直系祖先开始找下去*/
  if(id.indexOf(item.id)==0 && id.charAt(item.id.length)=='_')
  {
   i=-1;root=item;
  }
 }
 return null;
}

9 版本信息(自从1.05

Versions of TreeMenu
By liaomingxue Version Number Time Description 1.05 2004.7.20 Eliminated some unnecessary attributes of Class Tree. Now I use xml to present version information and programer reference. The files are within dir aboutTree. The action property of the slefItem of Tree has changed to open a new window to show versions.xml which dwells in dir aboutTree. But Classes' UML presentations are unavailabe at present. Maybe the next version. 1.06 2004.7.20 Eliminated the attribute parent from Class TreeItem. UML diagram of class TreeItem, available. 1.07 2004.7.21 UML diagram beautified. File versions.xsl modified. 1.08 2004.7.21 Class TreeItem changed. No change on TreeItem's functionality.UML diagram of class TreeItem changed. 1.09 2004.7.21 simplified the Class Tree. The former vesions let an "IMG" html element be within an "A" element, now "A" removed. This results in an about 1/8 decrease in length of html property of Tree. 1.10 2004.7.21 Now there is no id property for Tree 1.11 2004.7.22 UML representation of Class Tree, available 1.12 2004.7.23 Redundant information removed to suit the release in CSDN. Some code polished.

10 测试程序演示图


附件:
    关闭窗口
    打印文档
    账号登录
    保持登录 忘记密码?
    账号与武进教师培训平台同步