- 浏览: 37871 次
- 性别:
- 来自: 广州
最新评论
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MzTree.aspx.cs" Inherits="MzTree" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>无标题页</title>
<script language="JavaScript" src="scripts/jsframework.js"></script>
</head>
<body leftmargin="0" topmargin="2">
<form id="form1" runat="server">
<script language="javascript" type="text/javascript">
<%=LoadTree() %>
</script>
<%--
<SCRIPT LANGUAGE="JavaScript">
window.tree = new MzTreeView("tree");
//tree.setIconPath("menutree/"); //可用相对路径
<%=str%> //这里的str是在后台定义的一个string,他包含了从数据库取出来的所有节点信息,只能够写这里
//tree.setURL("Catalog.asp");
//tree.setTarget("MzMain");
//document.write(tree.toString()); //亦可用 obj.innerHTML = tree.toString();
//tree.focus(111); //让id是111的节点获得焦点,同时也展开了这个节点上的父节点!
//-->
</SCRIPT> --%>
</form>
</body>
</html>
public string LoadTree()
{
string sql = "SELECT Parent,ID, Name FROM [T_Tree] ";
SqlDataAdapter da = new SqlDataAdapter(sql, "DATABASE=aa;SERVER=localhost;UID=sa;PWD=sa;");
DataTable dt = new DataTable();
da.Fill(dt);
string nodes = "";
nodes = "var data = {};";
foreach(DataRow dr in dt.Rows)
{
nodes += "data[\'" + dr["Parent"].ToString() + "_" + dr["ID"].ToString() + "\'] = \'text:" + dr["Name"].ToString() + ";\';\r\n";
}
nodes += "Using(\"System.Web.UI.WebControls.MzTreeView\");";
nodes += "var a = new MzTreeView();";
nodes += "a.dataSource = data;";
nodes += "a.setJsDataPath(\"csdn/community/treedata/\");";
nodes += "a.setXmlDataPath(\"csdn/community/treedata/\");";
//a.rootId="1";
nodes += "a.autoSort=false;";
nodes += "a.useCheckbox=true;";
nodes += "a.canOperate = true;";
nodes += "document.write(a.render());";
nodes += "a.expandLevel(1);";
return nodes;
}
【摘要】
梅花雪树控件1.0,2.0的使用。
【全文】
梅花雪树1.0控件 有以下属性和方法。
属性
MzTreeView 类的一些属性: 属性名 类型 属性的具体说明
MzTreeView.nodes 集合 服务器端给树指定数据源时数据存放的对象,具体存放格式如:
MzTreeViewHandle.nodes["parentId_nodeId"] = "text: nodeText; icon: nodeIcon; url: nodeURL; ...";
MzTreeView.url 地址字符串 可读写,树缺省的URL,默认值是 #
MzTreeView.target 目标框架名 可读写,树缺省的链接target,默认值是 _self
MzTreeView.name 字符 只读,树的实例名,同树实例化时作为参数传入(大小写敏感):
var Tree = new MzTreeView("Tree");
MzTreeView.currentNode 树节点 只读,树当前得到焦点的节点对象
MzTreeView.icons 集合 树所使用的所有图标存放
MzTreeView.iconsExpand 集合 树里展开状态的图标存放
MzTreeView.colors 集合 树里使用到的几个颜色存放
MzTreeView 在客户端的节点所拥有的属性: 属性名 属性的具体说明
node.id 数字文本,节点的ID
node.parentId 数字文本,节点对应的父节点ID
node.text 文本,节点的显示文本
node.hint 文本,节点的注释说明
node.icon 文本,节点对应的图标
node.path 文本,节点在树里的绝对路径:0_1_10_34
node.url 文本,该节点的 URL
node.target 文本,该节点链接的目标框架名
node.data 文本,该节点所挂载的数据
node.method 文本,该节点的点击对应处理语句
node.parentNode 对象,节点的父节点对象
node.childNodes 数组,包含节点下所有子节点的数组
node.sourceIndex 文本,服务器给予的数据里对象的 parentId_nodeId 的组合字符串
node.hasChild 布尔值,指该节点是否有子节点
node.isLoad 布尔值,本节点的子节点数据是否已经在客户端初始化
node.isExpand 布尔值,节点的展开状态
方法
MzTreeView 类的一些方法: 方法名 方法的具体说明
MzTreeView.toString() 类的默认初始运行
MzTreeView.buildNode(id) 将该节点的所有下级子节点转换成 HTML 并在网页上体现出来
MzTreeView.nodeToHTML(node, AtEnd) 将 node 转换成 HTML
MzTreeView.load(id) 从数据源里加载当前节点下的所有子节点
MzTreeView.nodeInit(sourceIndex, parentId) 节点的信息初始,从数据源到客户端完整节点的转化
MzTreeView.focus(id) 聚集到某个节点上
MzTreeView.expand(id[, sureExpand]) 展开节点(包含下级子节点数据的加载初始化)
MzTreeView.setIconPath(path) 给节点图片设置正确的路径
MzTreeView.nodeClick(id) 节点链接点击时同时被触发的点击事件处理方法
MzTreeView.upperNode() 跳转到当前聚集节点的父级节点
MzTreeView.lowerNode() 跳转到当前聚集节点的子级节点
MzTreeView.pervNode() 跳转到当前聚集节点的上一节点
MzTreeView.nextNode() 跳转到当前聚集节点的下一节点
MzTreeView.expandAll() 展开所有的树点,在总节点量大于500时这步操作将会比较耗时
下面是几个例子。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Tree._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>目录树</title>
<script type="text/javascript" language="javascript" src="MzTreeView10.js"></script>
<link href="css/Global.css" type="text/css" rel="stylesheet"/>
<style type="text/css">
A.MzTreeview
{
font-size: 9pt;
padding-left: 3px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<script type="text/javascript" language="javascript">
<!--
window.tree = new MzTreeView("tree");
tree.icons["property"] = "property.gif";
tree.icons["css"] = "collection.gif";
tree.icons["book"] = "book.gif";
tree.iconsExpand["book"] = "bookopen.gif";
tree.setIconPath("/images/");
<% LoadTree(); %>
tree.focus(4945);
tree.setURL("#");
tree.setTarget("");
document.write(tree.toString());
-->
</script>
</form>
</body>
</html>
后台页面:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.Sql;
using System.Data.SqlClient;
using TreeDemo.DBUtility;
namespace Tree
{
public partial class _Default : System.Web.UI.Page
{
public static string str;
protected void Page_Load(object sender, EventArgs e)
{
}
public void LoadTree()
{
string sql = "Select SiteID,isnull(PID,0) as PID,SiteCaption From AreaSite order by SiteID";
SqlDataReader dr = TreeDemo.DBUtility.SqlHelper.ExecuteReader(SqlHelper.ConnectionString,CommandType.Text, sql, null);
string node = "";
while(dr.Read())
{
node = "\r\n tree.nodes[" + "\"" + dr["PID"].ToString() + "_" + dr["SiteID"].ToString() + "\"" + "] = ";
node = node + "\"" + "text:" + dr["SiteCaption"].ToString() + ";" + "\"";
Response.Write(node);
}
if (!dr.IsClosed)
dr.Close();
}
}
}
//下面是用AJAX来实现的,这里是一次性加载所有数据,没有实现异步加载。
var tree = new MzTreeView("tree");//创建一个树的实例
function TheTree()
{
tree.icons["proj"] = "project.gif";
tree.icons["book"] = "book.gif";
tree.iconsExpand["book"] = "bookopen.gif"; //设置图片
tree.setIconPath("image/");
tree.nodes["0_1"] = "text:项目列表";//虚拟根目录
var DataSet = FinetWebCPM.TreeTest.GetProject().value;
var rows = DataSet.Tables[0].Rows;
for(i = 0 ;i < rows.length ; i++)
{
tree.nodes["1_"+rows[i].ProjID] = "text:"+rows[i].SCName;//nodes数组的下表的格式是[父节点id_子节点id]
var DataSetPPage = FinetWebCPM.TreeTest.GetPage(rows[i].ProjID).value;
if(DataSetPPage != null)
{
var rowspage = DataSetPPage.Tables[0].Rows;
if(rowspage.length > 0)
{
for(n = 0 ;n < rowspage.length ; n++)
{
tree.nodes[rows[i].ProjID+"_"+rowspage[n].PageID] = "text:"+rowspage[n].PageTitle+";url:"+rowspage[n].PageUrl;
}
}
else
{
tree.nodes[rows[i].ProjID+"_None"] = "text:无此项目页面!";
}
}
else
{
tree.nodes[rows[i].ProjID+"_None"] = "text:无此项目页面!";
}
}
document.getElementById("treeviewarea").innerHTML = tree.toString();
}
下面是梅花雪树空间2.0的例子。
2.0的功能有了很大的改进,加入了动态加载,可以使用xml,js,一个数组等来作为数据源,在节点中还加入了checkbox。
下面看看用xml,和js来作为数据源的。
myxml.xml文档:
<?xml version="1.0" encoding="utf-8"?>
<nodes>
<node id="100" url="/page/100.htm" text="100页面" />
<node id="101" url='/page/101.htm' text="101页面">
<node text="child node" />
</node>
<node id="102" url='/page/102.htm' text="102页面" />
<node id="103" url='/page/103.htm' text="103页面" />
</nodes>
help.js:
var data={};
data['3_301'] = 'text:ASP ; data:roomid=301';
data['3_35409'] = 'text:JSP ; data:roomid=5409';
data['3_303'] = 'text:PHP ; data:roomid=303';
js部分:
<SCRIPT LANGUAGE="JavaScript">
var data={};
data["-1_1"] = "text: 起点;";//nodes的下标和1.0的格式是一样的。
data['1_9001'] = 'text: 首页;';
data['1_9002'] = 'text: 介绍; XMLData: data/myxml.xml ';
data['1_9009'] = 'text: 帮助; url:page/help?typenum=1&roomid=1 ; JSData: data/help.js ';//可以设置url的弹出方式target:_blank;,其默认是_self。
var xmlstr='<nodes>'+
'<node text="中国" />'+
'<node text="江西" />'+
'<node text="广东">'+
'<node text="梅州" />'+
'<node text="深圳" />'+
'</node>'+
'<node text="河北" />'+
'</nodes>';
Using("System.Web.UI.WebControls.MzTreeView");
var tree = new MzTreeView();
tree.dataSource = data
tree.loadXmlDataString(xmlstr, 1);//xml字符串作为数据源,loadXmlDataString方法的第一个参数是xml字符串,第二个参数父节点ID
tree.setJsDataPath("data/");//设置数据源的位置
tree.setXmlDataPath("data/");
tree.autoSort=false;
tree.useCheckbox=true;//是否使用checkbox
tree.canOperate=true;
document.write(a.render());//输出树
tree.expandLevel(1);//展开1节点
</SCRIPT>
梅花雪树的url默认是#,如果你的树很高,你单击下面节点的时候会跳到页面的顶层,你可以将url写成url:javascript:return;这样来屏蔽掉默认的url,不过你设置成 这样还将target属性是指成_blank的话,将会弹出一个新的窗口。
也许你要修改这些默认设置的话,你可以修改\scripts\system\web\ui\webcontrols\mztreeview.js里面的代码。如果你会英语及JS的话,应该是很简单的。
梅花雪树控件只有双击,和单击事件,下面来为树控件加各右键菜单事件,打开\scripts\system\web\ui\webcontrols\mztreeview.js,文件,找到render事件,这方法改成
MzTreeView.prototype.render = function()
{
function loadImg(C){for(var i in C){if("string"==typeof C[i]){
var a=new Image(); a.src=me.iconPath + C[i]; C[i]=a;}}} var me=this;
loadImg(MzTreeView.icons.expand);loadImg(MzTreeView.icons.collapse);
loadImg(MzTreeView.icons.line); me.firstNode=null;
loadCssFile(this.iconPath +"mztreeview.css", "MzTreeView_CSS");
this.initialize(); var str="no data", i, root=this.rootNode;
if (root.hasChild){var a = [], c = root.childNodes; me.firstNode=c[0];
for(i=0;i<c.length;i++)a[i]=c[i].render(i==c.length-1);str=a.join("");}
setTimeout(function(){me.afterRender();}, 10);
return "<div class='mztreeview' id='MTV_root_"+ this.index +"' "+
"onclick='Instance(\""+ this.index +"\").clickHandle(event)' "+
"ondblclick='Instance(\""+ this.index +"\").dblClickHandle(event)' "+
"oncontextmenu='Instance(\""+ this.index +"\").contextMenuHandle(event)' "+//这里是我们添加的右键事件
">"+ str +"</div>";
};
然后我们还得多写个方法。
//private: contextMenuHandle
MzTreeView.prototype.contextMenuHandle = function(e)
{
e = window.event || e; e = e.srcElement || e.target;
if((e.tagName=="A" || e.tagName=="IMG")&& e.id)
{
e=this.nodes[e.id.substr(e.id.lastIndexOf("_") +1)];
//e是一个节点对象,如我这里是判断这个节点是否有子节点,然后执行相应的方法 e.hasChild ? Tool(e,"project"):Tool(e,"page");
e.focus();
this.currentNode=e;this.dispatchEvent(new System.Event("oncontextmenu"));
}
};
MzTreeView 1.0 from--->>> http://www.meizz.com/
开发文档: http://www.meizz.com/Web/Article.asp?id=436
控件下载: http://www.meizz.com/Web/Download/MzTreeView10.rar
应用示例: http://www.meizz.com/Web/Demo/MzTreeView10.htm
说明
MzTreeView 1.0 是数据一次性加载,客户端节点异步展示的WEB脚本树。MzTreeView 1.0 的理论节点数设计上限为十万节点,在节点数三万的情况下页面打开时间小于 3 秒。无限层次无限节点的数的层级组成方式:id parentId。即每个节点除本身的节点id之外还有它的父层节点id,通过这种方式就可以组合成无限层级的树了。
在 MzTreeView 里都有一个虚的根节点,其ID为0,用户可见的根节点其父节点ID皆为0
MzTreeView 1.0在数据库库表里的字段名称:
字段名 字段的具体说明
id 节点ID(不可为0,可以是数字或字符)
parentId 本节点的父节点ID(若本节点已为根节点,此处填0)
text 节点的显示文本(一般不允许为空,不过有一种情况例外,即根节点,若根节点文本为空,这个根节点将不会在页面里显示)
hint 节点的说明注解
icon 节点的图标,MzTreeView 1.0允许每个节点拥有不同的图标(对应的名字在类里的icons和iconsExpand定义)
data 节点挂的数据,格式是 param=value¶m=value&... url里?后的那串字符串格式,
url 每个节点允许拥有不同的链接,它为空或者为#时,树里这个节点的链接,点击将无反应
target 每个节点的链接允许在不同的target里打开,为空时取类里的默认值
method 点击该链接时所想触发的脚本语句
特注:每个字段值中不可有冒号: 不可以换行 引号酌情考虑应不与节点字符串的引号相冲突
设计模式
为了达到能够在浏览器中快速打开多节点树的页面,我做了很多的优化与创新,下面我将详细解说几项最重要的部分:
•数据一次性加载 首先我要说的就是数据的一次性加载。在目前的 B/S 架构开发中对于多节点多层次的树,特别是树节点量超过两千的情况下,几乎都是采取数据异步加载来达到目的,即用户需要展开某个节点时,再从服务器端加载下级子节点的数据,数据异步加载最为经典的例子就是 MSDN 网站左边的目录树了。异步加载的优点在于可以扩充到无限级无限节点树,树的数据来源可以多样化(数据库或XML文件)等,但是它的缺点也是非常多的:设计模式比数据一次性加载要复杂得多,要考虑到 Browser/Server 之间的应答,要判断子节点是否含有孙节点,后台数据源的层级关系模型等。对网络传输的信赖性太大,每个节点的展开都需要连一次 Server,只要在取某节点数据时网络出现问题,就会导致该节点及其以下的子节点加载失败。而采取数据一次加载的模式只要一次加载成功,服务器就可以不用管它了,服务器压力减轻,脚本设计则完全独立,对整棵树节点的检索可以在客户端完成,节点展开响应速度快等等优势,因此在节点数不多的情况下数据一次性加载更有优势,那么这个节点数不多不多到底多少节点量为平衡点呢?就 ASP.net 里带的那个 TreeView 来说,在一两千节点以下一次性加载比较具有优势,而 MzTreeView 1.0 在节点量三万至五万以非常具有优势。
•节点信息的压缩传输 在浏览器里显示的树结构其实都是一个个 HTML 元素组合起来的,在 WEB 页面里的树都是根据树节点的信息组合成一串的 HTML 元素列来显示,这一步从节点信息到 HTML 的转化可以在两个地方生成:一个是在服务器端,一个是在客户端。在服务器端生成的优点在于不须考虑客户端的浏览器的兼容性,节点信息的可控性非常强,但是它的缺点也是非常大的:加重服务器的负担,增加网络传输量。在服务器端直接生成树节点的 HTML 给服务器带来的压力是显而易见的,也是非常巨大的,估计只要有几十个并发连接就能让服务器暂时停止响应了。这种直接在服务器生成树的做法在实际运用环境中也有人使用,不过本人非常不赞成这种做法。当然也有人直接将树生成为一个静态文件放在服务器端,这种做法对于树节点相对固定不变的情况还是非常有利的,只需要生成一次,以后就不需要再生成了,服务器的压力也非常小,但它的弊病在于可变化性太小,比如说不同的权限能看到的树节点的不同这种情况,用这种生成静态树放在服务器端的做就没有办法解决,且不管是服务器端动态计算还是直接生成静态树文件放在服务器端都有一个避免不了的问题就是网络传输量巨大。可以计算一下,一个树点所需要的HTML字符量大约300到600字节之间。即含有一千节点的树的网页大小就有300K到600K,给网络造成的压力是非常巨大的,所以MzTreeView 1.0采用了节点信息的压缩传输,大至一千节点的总传输量在30K左右,这可以差了一个数量级呀。本树将每个节点所必要的信息组合成一个字符串序列,传递到客户端,然后在客户端再用客户端脚本对这些信息进行分析,再生成一个个的节点HTML,这也符合了WEB的分散计算的原理,当然服务器端可以有选择性输出部分节点,这样又做到节点的灵活多变性。
•传输的节点信息的可扩展性 服务器端将节点的必要信息组合成一个字符串序列传递到客户端,然后客户端再用脚本对这个字符串序列进行分析,生成树的节点,那么这个字符串序列对整个树的生成的效率就有重要的影响了。我也参照过很多组串传输的例子,在一般的做法当中大多采用函数的参数方式传递。比如说定义一个函数 funName(p1, p2, p3, ...),然后服务器组串的时候就按位置给定数据。这种组串的弊病是非常大的,首先就是位置绝对错不得,只要有一个位置数据出错,这个节点的信息就乱了,对于那些在函数里已经定义的但节点里没有的信息也得用空字符串补上,诸如:(p1, "", p3, p4, "", "", ""),且万一这种组串的对应分析函数发生了变化,那么这种串就算是废了,得重新定义服务器端的字符串位置序列了,可以说这种组串的方式可扩展性极差。
节点信息组串传输的另一种常用模式就是XML。XML以它的无限可扩展性已惭有代替HTML称霸WEB的味道了。XML最大的优点就在于它的无限可扩展性,可以用任意的TagName名,可以有任意的Attribute名,节点与子节点已经有层级的关系,用XML来做WEB树的数据源其实是最理想的,MSDN的资源目录树就是采用XML作为传递的字符串,它唯一的不足之处就是不是所有浏览器都能很好地支持它,特别是在一些低版本的浏览器中,所以我只好忍痛割爱没有启用XML作为中间的传媒。那么是不是有可能结合XML的扩展性对第一种组串的方式进行改进呢?当我愁眉不展的时候,HTML里的STYLE样式表写法跳入我眼,样式的写法是"param1: value1; param2: value3; ...",哈哈,这不是现成地给我指明了路吗?这种写法拥有XML的可扩展性,位置顺序的随意性,又是传统的长字符串,正合我意也!服务器给定这种数据源字符串,我不光可以在TreeView里用它,还可以直接做Outlook Bar,下拉式层级菜单,右键层级菜单的数据源,豁然开朗也!我写了一个函数专门解析这种文本:getAttribute()。
•客户端节点数据的存储方式及快速检索 现在数据源准备好了,数据传输也已做到最大优化了,下面就是客户端的脚本解析了,而这一步也正是树生成的效率瓶颈所在。由于我没有采用XML做为数据源,所以我这里就不讨论XML+XSL和XMLDOM的模式,而只考虑HTML+DOM模式了。在HTML+DOM模式下客户端存储的方式有很多种,我就曾经看到过一种直接将字符串输出在多行文本框<textarea>里的,但归究起来最常用的方式就是用一个数组来存放节点信息,不过就算是用数组也是种类多多,比如说数组里套数组,节点通过记录父节点在数组里的索引号表示层级的等等,说到数组存放树节点模式,我推荐阿信写的那棵树。在服务器端组织好脚本及节点字符串,客户端浏览器加载这个页面的时候顺序执行其里面的脚本,将一个个节点做最初步的解析,然后再一个个地ADD到数组中去。这种流程看起来似乎没有什么问题,在很多的传统C/S结构编程中也就这种模式,但是随着节点数的增多,这种流程在B/S里的缺点就出现了:脚本的执行效率不如强语言高!我做过测试,在5000节点的数据量时这一步操作将耗时1秒钟,也许你认为这1秒钟不算长,哪里有5000这么多节点的树?但对于一个程序员来说,严谨是第一位的,若存在优化的可能性,情况出现的可能性,我们就得将它考虑到。那么有没有可能再进一步优化呢?当然有,否则我就用不着这么大费口舌在这里讲了。既然在脚本里执行 for 循环插入数组效率不高那我不用循环,且考虑到在数组里这么多节点中搜索我想要的某几个节点,还得用循环,所以我干脆就不再用数组来存放树节点了,那用什么呢?说到这里我得插几句题外话,客户端脚本也可以写一些类,虽说写出来的类没有强语言那么好,继承性不好等,但脚本终究还是可以写出一些简单的类的。类可以定义属性,也可以定义方法,并且访问属性的时候可以通过属性名下标直接访问到属性值,关于脚本里如何写类,大家可以参考我在JavaScript和VBScript里对这方面的详细说明。呵呵,说了这么多的题外话,不过相信大家也猜到了,新的节点存储方式就是类的属性自定义扩展方式。这种存储方式随着页面的打开,页面里的脚本被执行,类的属性值不需要做任何添加的动作就直接写到内存当中,比数组模式少去了ADD操作,这一步从字符串到内存的时间就是页面打开的时间,我测试了一下,5000节点的树用这种方式打开,只需0.1秒呀,与数组模式速度整整差了一个数量级呀!且这种模式还有一种好处,在几千个节点里要找到目标节点,只需要知道节点对应的属性名,一步就可以直接找到。
客户端树节点存储模式说清楚了,那节点的快速检索也就清楚了。已知节点名即在类里的属性名的话,一步就找到了该节点,不过为了配合下一节的异步展示,我的检索就没有这么简单了。试想在几千或者几万个节点里要快速地找出符合条件的几十个节点,这一步的耗时可想而知了,首先就得排除for循环法,for循环的效率在数组模式里大家就看到了,这一步操作特别在总节点数多的情况下就能让使用者等疯掉,显然得另想办法。于是我想到了正则表达式里的字符串模式匹配match(),我将所有的节点名(即类的属性名)join()成一个大字符串,然后再用正则表达式匹配,一步就找出了想要的节点了。经测试,在三万节点里找30个节点对象耗时小于0.1秒!
•异步展示 再来讲一下节点字符串被解析之后转化成HTML元素的这一步操作。上面已经有过一个计算,表达1000节点的树的HTML字符量就有300K-600K之多,且这一步只能一个节点一个节点慢慢地生成,没有什么取巧的办法,想快点也只能是减小单个节点的HTML元素量罢,不过最快也得1-3秒每千节点呀,这也是没有法子的事,谁叫DOM的效率不高呢!总得想个什么法子吧,否则象5000节点量的树让使用者等上个半分钟一分钟的,谁也受不了呀!因此我想出异步展示这招:页面加载时并不立即生成所有节点的HTML元素,而是用户展开多少节点就生成多少节点,节点的生成发生在用户展开这个节点的时候。使用者在页面打开的时候并不会立即把所有的节点都一次全部展开,而是一级级地往下展开的,就象你查看windows注册表一样,想看哪级才会去展开哪一级,这样我就只需要在你展开那级的时候把这一级的节点转化成HTML即可,每次转化的节点只有几十分甚至只有几个而已,消除了使用者的等待时间。经过上面这几个环节,终于一棵实用性,效率,扩展性俱佳的WEB TreeView出世了。
•采用文字竖线 每个浏览器随着使用客户的不同,而总会产生不同的设置,其中有一项就是客户端设置每次访问都检查网页新版本,即客户端不缓存。这个设置对一般的应用来说问题不会很大,但是对于使用图片作为竖线的树来说随着节点总数的增多,图片的使用量也就跟着巨量增加,可能会使用几千甚至几万个小图片,每张图片是都很小,但量一大的话,将会严重影响树的快速展示,因此针对这种情况就得换一种模式来展现了,那就是文字竖线,用文字加样式就可以解决这个问题。我加了一个变量:MzTreeView.wordLine(布尔型,默认值为false),当网速过慢或者没有使用本地缓存时这个属性会自动设置成 true 而以文字代替图片完成竖线(注:Opera 浏览器不支持文字竖线模式),当然你也可以一开始就强行设置值为 true,这样树就会始终用文字竖线了。
属性
MzTreeView 类的一些属性:
属性名 类型 属性的具体说明
MzTreeView.nodes 集合 服务器端给树指定数据源时数据存放的对象,具体存放格式如:
MzTreeViewHandle.nodes["parentId_nodeId"] = "text: nodeText; icon: nodeIcon; url: nodeURL; ...";
MzTreeView.url 地址字符串 可读写,树缺省的URL,默认值是 #
MzTreeView.target 目标框架名 可读写,树缺省的链接target,默认值是 _self
MzTreeView.name 字符 只读,树的实例名,同树实例化时作为参数传入(大小写敏感):
var Tree = new MzTreeView("Tree");
MzTreeView.currentNode 树节点 只读,树当前得到焦点的节点对象
MzTreeView.icons 集合 树所使用的所有图标存放
MzTreeView.iconsExpand 集合 树里展开状态的图标存放
MzTreeView.colors 集合 树里使用到的几个颜色存放
MzTreeView 在客户端的节点所拥有的属性:
属性名 属性的具体说明
node.id 数字文本,节点的ID
node.parentId 数字文本,节点对应的父节点ID
node.text 文本,节点的显示文本
node.hint 文本,节点的注释说明
node.icon 文本,节点对应的图标
node.path 文本,节点在树里的绝对路径:0_1_10_34
node.url 文本,该节点的 URL
node.target 文本,该节点链接的目标框架名
node.data 文本,该节点所挂载的数据
node.method 文本,该节点的点击对应处理语句
node.parentNode 对象,节点的父节点对象
node.childNodes 数组,包含节点下所有子节点的数组
node.sourceIndex 文本,服务器给予的数据里对象的 parentId_nodeId 的组合字符串
node.hasChild 布尔值,指该节点是否有子节点
node.isLoad 布尔值,本节点的子节点数据是否已经在客户端初始化
node.isExpand 布尔值,节点的展开状态
方法
MzTreeView 类的一些方法:
方法名 方法的具体说明
MzTreeView.toString() 类的默认初始运行
MzTreeView.buildNode(id) 将该节点的所有下级子节点转换成 HTML 并在网页上体现出来
MzTreeView.nodeToHTML(node, AtEnd) 将 node 转换成 HTML
MzTreeView.load(id) 从数据源里加载当前节点下的所有子节点
MzTreeView.nodeInit(sourceIndex, parentId) 节点的信息初始,从数据源到客户端完整节点的转化
MzTreeView.focus(id) 聚集到某个节点上
MzTreeView.expand(id[, sureExpand]) 展开节点(包含下级子节点数据的加载初始化)
MzTreeView.setIconPath(path) 给节点图片设置正确的路径
MzTreeView.nodeClick(id) 节点链接点击时同时被触发的点击事件处理方法
MzTreeView.upperNode() 跳转到当前聚集节点的父级节点
MzTreeView.lowerNode() 跳转到当前聚集节点的子级节点
MzTreeView.pervNode() 跳转到当前聚集节点的上一节点
MzTreeView.nextNode() 跳转到当前聚集节点的下一节点
MzTreeView.expandAll() 展开所有的树点,在总节点量大于500时这步操作将会比较耗时
示例
<script language="JavaScript"
src="http://www.meizz.com/Web/Plugs/MzTreeView10.js"></script>
<base href="http://www.meizz.com/Web/">
<style>
A.MzTreeview
{
font-size: 9pt;
padding-left: 3px;
}
</style>
<script language="JavaScript">
var tree = new MzTreeView("tree");
tree.icons["property"] = "property.gif";
tree.icons["css"] = "collection.gif";
tree.icons["book"] = "book.gif";
tree.iconsExpand["book"] = "bookopen.gif"; //展开时对应的图片
tree.setIconPath("http://www.meizz.com/Icons/TreeView/"); //可用相对路径
tree.nodes["0_1"] = "text:WEB 编程";
tree.nodes["1_100"] = "text:代码示例; data:id=100";
tree.nodes["1_200"] = "text:梅花雪脚本控件集; data:id=200";
tree.nodes["1_310"] = "text:CSS; icon:css; data:id=310";
tree.nodes["1_320"] = "text:DHTML; data:id=320";
tree.nodes["1_300"] = "text:HTML; data:id=300";
tree.nodes["1_400"] = "text:JavaScript; icon:book; data:id=400";
tree.nodes["320_322"] = "text:属性; icon: property; data:id=322";
tree.nodes["320_323"] = "text:方法; data:id=323";
tree.nodes["320_324"] = "text:事件; icon:event; data:id=324";
tree.nodes["320_325"] = "text:集合; data:id=325";
tree.nodes["400_407"] = "text:对象; data:id=407";
tree.nodes["400_406"] = "text:方法; data:id=406";
tree.nodes["400_408"] = "text:运算符; data:id=408";
tree.nodes["400_409"] = "text:属性; data:id=409";
tree.nodes["407_1140"] = "text:Date; url:Article.asp; data:id=140";
tree.nodes["406_1127"] = "text:toString; url:Article.asp; data:id=127";
tree.nodes["408_1239"] = "text:||; url:Article.asp; data:id=239";
tree.nodes["409_1163"] = "text:E; url:Article.asp; data:id=163";
tree.setURL("Catalog.asp");
tree.setTarget("MzMain");
document.write(tree.toString()); //亦可用 obj.innerHTML = tree.toString();
</script>
//MzTreeView1.0网页树类, 在实例化的时候请把实例名作参数传递进来
function MzTreeView(Tname)
{
if(typeof(Tname) != "string" || Tname == "")
throw(new Error(-1, '创建类实例的时候请把类实例的引用变量名传递进来!'));
//【property】
this.url = "#";
this.target = "_self";
this.name = Tname;
this.wordLine = false;
this.currentNode = null;
this.useArrow = true;
this.nodes = {};
this.node = {};
this.names = "";
this._d = "x0f";
this.index = 0;
this.divider = "_";
this.node["0"] =
{
"id": "0",
"path": "0",
"isLoad": false,
"childNodes": [],
"childAppend": "",
"sourceIndex": "0"
};
this.colors =
{
"highLight" : "#0A246A",
"highLightText" : "#FFFFFF",
"mouseOverBgColor" : "#D4D0C8"
};
this.icons = {
L0 : 'L0.gif', //┏
L1 : 'L1.gif', //┣
L2 : 'L2.gif', //┗
L3 : 'L3.gif', //━
L4 : 'L4.gif', //┃
PM0 : 'P0.gif', //+┏
PM1 : 'P1.gif', //+┣
PM2 : 'P2.gif', //+┗
PM3 : 'P3.gif', //+━
empty : 'L5.gif', //空白图
root : 'root.gif', //缺省的根节点图标
folder : 'folder.gif', //缺省的文件夹图标
file : 'file.gif', //缺省的文件图标
exit : 'exit.gif'
};
this.iconsExpand = { //存放节点图片在展开时的对应图片
PM0 : 'M0.gif', //-┏
PM1 : 'M1.gif', //-┣
PM2 : 'M2.gif', //-┗
PM3 : 'M3.gif', //-━
folder : 'folderopen.gif',
exit : 'exit.gif'
};
//扩展 document.getElementById(id) 多浏览器兼容性
//id 要查找的对象 id
this.getElementById = function(id)
{
if (typeof(id) != "string" || id == "") return null;
if (document.getElementById) return document.getElementById(id);
if (document.all) return document.all(id);
try {return eval(id);} catch(e){ return null;}
}
//MzTreeView 初始化入口函数
this.toString = function()
{
this.browserCheck();
this.dataFormat();
this.setStyle();
this.load("0");
var rootCN = this.node["0"].childNodes;
var str = "<A id='"+ this.name +"_RootLink' href='#' style='DISPLAY: none'></A>";
if(rootCN.length>0)
{
this.node["0"].hasChild = true;
for(var i=0; i<rootCN.length; i++)
str += this.nodeToHTML(rootCN[i], i==rootCN.length-1);
setTimeout(this.name +".expand('"+ rootCN[0].id +"', true); "+
this.name +".focusClientNode('"+ rootCN[0].id +"'); "+ this.name +".atRootIsEmpty()",10);
}
if (this.useArrow) //使用方向键控制跳转到上级下级父级子级节点
{
if (document.attachEvent)
document.attachEvent("onkeydown", this.onkeydown);
else if (document.addEventListener)
document.addEventListener('keydown', this.onkeydown, false);
}
return "<DIV class='MzTreeView' "+
"onclick='"+ this.name +".clickHandle(event)' "+
"ondblclick='"+ this.name +".dblClickHandle(event)' "+
">"+ str +"</DIV>";
};
this.onkeydown= function(e)
{
e = window.event || e; var key = e.keyCode || e.which;
switch(key)
{
case 37 : eval(Tname).upperNode(); break; //Arrow left, shrink child node
case 38 : eval(Tname).pervNode(); break; //Arrow up
case 39 : eval(Tname).lowerNode(); break; //Arrow right, expand child node
case 40 : eval(Tname).nextNode(); break; //Arrow down
}
};
}
//浏览器类型及版本检测
MzTreeView.prototype.browserCheck = function()
{
var ua = window.navigator.userAgent.toLowerCase(), bname;
if(/msie/i.test(ua))
{
this.navigator = /opera/i.test(ua) ? "opera" : "";
if(!this.navigator) this.navigator = "msie";
}
else if(/gecko/i.test(ua))
{
var vendor = window.navigator.vendor.toLowerCase();
if(vendor == "firefox") this.navigator = "firefox";
else if(vendor == "netscape") this.navigator = "netscape";
else if(vendor == "") this.navigator = "mozilla";
}
else this.navigator = "msie";
if(window.opera) this.wordLine = false;
};
//给 TreeView 树加上样式设置
MzTreeView.prototype.setStyle = function()
{
/*
width: 16px;
height: 16px;
width: 20px;
height: 20px;
*/
var style = "<style>"+
"DIV.MzTreeView DIV IMG{border: 0px solid #FFFFFF;}"+
"DIV.MzTreeView DIV SPAN IMG{border: 0px solid #FFFFFF;}";
if(this.wordLine)
{
style +="
DIV.MzTreeView DIV
{
height: 20px;"+
(this.navigator=="firefox" ? "line-height: 20px;" : "" ) +
(this.navigator=="netscape" ? "" : "overflow: hidden;" ) +"
}
DIV.MzTreeView DIV SPAN
{
vertical-align: middle; font-size: 21px; height: 20px; color: #D4D0C8; cursor: default;
}
DIV.MzTreeView DIV SPAN.pm
{
width: "+ (this.navigator=="msie"||this.navigator=="opera" ? "11" : "9") +"px;
height: "+ (this.navigator=="netscape"?"9":(this.navigator=="firefox"?"10":"11")) +"px;
font-size: 7pt;
overflow: hidden;
margin-left: -16px;
margin-right: 5px;
color: #000080;
vertical-align: middle;
border: 1px solid #D4D0C8;
cursor: "+ (this.navigator=="msie" ? "hand" : "pointer") +";
padding: 0 2px 0 2px;
text-align: center;
background-color: #F0F0F0;
}";
}
style += "</style>";
document.write(style);
};
//当根节点为空的时候做的处理
MzTreeView.prototype.atRootIsEmpty = function()
{
var RCN = this.node["0"].childNodes;
for(var i=0; i<RCN.length; i++)
{
if(!RCN[i].isLoad) this.expand(RCN[i].id);
if (RCN[i].text=="")
{
var node = RCN[i].childNodes[0], HCN = node.hasChild;
if(this.wordLine)
{
var span = this.getElementById(this.name +"_tree_"+ node.id);
span = span.childNodes[0].childNodes[0].childNodes[0];
span.innerHTML = RCN[i].childNodes.length>1 ? "┌" : "─";
}
else
{
node.iconExpand = RCN[i].childNodes.length>1 ? HCN ? "PM0" : "L0" : HCN ? "PM3" : "L3"
this.getElementById(this.name +"_expand_"+ node.id).src = this.icons[node.iconExpand].src;
}
}
}
};
//初始化数据源里的数据以便后面的快速检索
MzTreeView.prototype.dataFormat = function()
{
var a = new Array();
for (var id in this.nodes) a[a.length] = id;
this.names = a.join(this._d + this._d);
this.totalNode = a.length; a = null;
};
//在数据源检索所需的数据节点
//id 客户端节点对应的id
MzTreeView.prototype.load = function(id)
{
var node = this.node[id], d = this.divider, _d = this._d;
var sid = node.sourceIndex.substr(node.sourceIndex.indexOf(d) + d.length);
var reg = new RegExp("(^|"+_d+")"+ sid +d+"[^"+_d+d +"]+("+_d+"|$)", "g");
var cns = this.names.match(reg), tcn = this.node[id].childNodes; if (cns){
reg = new RegExp(_d, "g"); for (var i=0; i<cns.length; i++)
tcn[tcn.length] = this.nodeInit(cns[i].replace(reg, ""), id); }
node.isLoad = true;
};
//初始化节点信息, 根据 this.nodes 数据源生成节点的详细信息
//sourceIndex 数据源中的父子节点组合的字符串 0_1
//parentId 当前树节点在客户端的父节点的 id
MzTreeView.prototype.nodeInit = function(sourceIndex, parentId)
{
this.index++;
var source= this.nodes[sourceIndex], d = this.divider;
var text = this.getAttribute(source, "text");
var hint = this.getAttribute(source, "hint");
var sid = sourceIndex.substr(sourceIndex.indexOf(d) + d.length);
this.node[this.index] =
{
"id" : this.index,
"text" : text,
"hint" : hint ? hint : text,
"icon" : this.getAttribute(source, "icon"),
"path" : this.node[parentId].path + d + this.index,
"isLoad": false,
"isExpand": false,
"parentId": parentId,
"parentNode": this.node[parentId],
"sourceIndex" : sourceIndex,
"childAppend" : ""
};
this.nodes[sourceIndex] = "index:"+ this.index +";"+ source;
this.node[this.index].hasChild = this.names.indexOf(this._d + sid + d)>-1;
if(this.node[this.index].hasChild) this.node[this.index].childNodes = [];
return this.node[this.index];
};
//从XML格式字符串里提取信息
//source 数据源里的节点信息字符串(以后可以扩展对XML的支持)
//name 要提取的属性名
MzTreeView.prototype.getAttribute = function(source, name)
{
var reg = new RegExp("(^|;|s)"+ name +"s*:s*([^;]*)(s|;|$)", "i");
if (reg.test(source)) return RegExp.$2.replace(/[x0f]/g, ";"); return "";
};
//根据节点的详细信息生成HTML
//node 树在客户端的节点对象
//AtEnd 布尔值 当前要转换的这个节点是否为父节点的子节点集中的最后一项
MzTreeView.prototype.nodeToHTML = function(node, AtEnd)
{
var source = this.nodes[node.sourceIndex];
var target = this.getAttribute(source, "target");
var data = this.getAttribute(source, "data");
var url = this.getAttribute(source, "url");
if(!url) url = this.url;
if(data) url += (url.indexOf("?")==-1?"?":"&") + data;
if(!target) target = this.target;
var id = node.id;
var HCN = node.hasChild, isRoot = node.parentId=="0";
if(isRoot && node.icon=="") node.icon = "root";
if(node.icon=="" || typeof(this.icons[node.icon])=="undefined")
node.icon = HCN ? "folder" : "file";
node.iconExpand = AtEnd ? "└" : "├";
var HTML = "<DIV noWrap='True'><NOBR>";
if(!isRoot)
{
node.childAppend = node.parentNode.childAppend + (AtEnd ? " " : "│");
if(this.wordLine)
{
HTML += "<SPAN>"+ node.parentNode.childAppend + (AtEnd ? "└" : "├") +"</SPAN>";
if(HCN) HTML += "<SPAN class='pm' id='"+ this.name +"_expand_"+ id +"'>+</SPAN>";
}
else
{
node.iconExpand = HCN ? AtEnd ? "PM2" : "PM1" : AtEnd ? "L2" : "L1";
HTML += "<SPAN>"+ this.word2image(node.parentNode.childAppend) +"<IMG "+
"align='absmiddle' id='"+ this.name +"_expand_"+ id +"' "+
"src='"+ this.icons[node.iconExpand].src +"' style='cursor: "+ (!node.hasChild ? "":
(this.navigator=="msie"||this.navigator=="opera"? "hand" : "pointer")) +"'></SPAN>";
}
}
HTML += "<IMG "+
"align='absMiddle' "+
"id='"+ this.name +"_icon_"+ id +"' "+
"src='"+ this.icons[node.icon].src +"'><A "+
"class='MzTreeview' hideFocus "+
"id='"+ this.name +"_link_"+ id +"' "+
"href='"+ url +"' "+
"target='"+ target +"' "+
"title='"+ node.hint +"' "+
"onfocus=""+ this.name +".focusLink('"+ id +"')" "+
"onclick="return "+ this.name +".nodeClick('"+ id +"')">"+ node.text +
"</A></NOBR></DIV>";
if(isRoot && node.text=="") HTML = "";
HTML = "rn<SPAN id='"+ this.name +"_tree_"+ id +"'>"+ HTML
HTML +="<SPAN style='DISPLAY: none'></SPAN></SPAN>";
return HTML;
};
//在使用图片的时候对 node.childAppend 的转换
MzTreeView.prototype.word2image = function(word)
{
var str = "";
for(var i=0; i<word.length; i++)
{
var img = "";
switch (word.charAt(i))
{
case "│" : img = "L4"; break;
case "└" : img = "L2"; break;
case " " : img = "empty"; break;
case "├" : img = "L1"; break;
case "─" : img = "L3"; break;
case "┌" : img = "L0"; break;
}
if(img!="")
str += "<IMG align='absMiddle' src='"+ this.icons[img].src +"' height='20'>";
}
return str;
}
//将某个节点下的所有子节点转化成详细的<HTML>元素表达
//id 树的客户端节点 id
MzTreeView.prototype.buildNode = function(id)
{
if(this.node[id].hasChild)
{
var tcn = this.node[id].childNodes, str = "";
for (var i=0; i<tcn.length; i++)
str += this.nodeToHTML(tcn[i], i==tcn.length-1);
var temp = this.getElementById(this.name +"_tree_"+ id).childNodes;
temp[temp.length-1].innerHTML = str;
}
};
//聚集到客户端生成的某个节点上
//id 客户端树节点的id
MzTreeView.prototype.focusClientNode = function(id)
{
if(!this.currentNode) this.currentNode=this.node["0"];
var a = this.getElementById(this.name +"_link_"+ id); if(a){ a.focus();
var link = this.getElementById(this.name +"_link_"+ this.currentNode.id);
if(link)with(link.style){color=""; backgroundColor="";}
with(a.style){color = this.colors.highLightText;
backgroundColor = this.colors.highLight;}
this.currentNode= this.node[id];}
};
//焦点聚集到树里的节点链接时的处理
//id 客户端节点 id
MzTreeView.prototype.focusLink= function(id)
{
if(this.currentNode && this.currentNode.id==id) return;
this.focusClientNode(id);
};
//点击展开树节点的对应方法
MzTreeView.prototype.expand = function(id, sureExpand)
{
var node = this.node[id];
if (sureExpand && node.isExpand) return;
if (!node.hasChild) return;
var area = this.getElementById(this.name +"_tree_"+ id);
if (area) area = area.childNodes[area.childNodes.length-1];
if (area)
{
var icon = this.icons[node.icon];
var iconE = this.iconsExpand[node.icon];
var Bool = node.isExpand = sureExpand || area.style.display == "none";
var img = this.getElementById(this.name +"_icon_"+ id);
if (img) img.src = !Bool ? icon.src :typeof(iconE)=="undefined" ? icon.src : iconE.src;
var exp = this.icons[node.iconExpand];
var expE = this.iconsExpand[node.iconExpand];
var expand= this.getElementById(this.name +"_expand_"+ id);
if (expand)
{
if(this.wordLine) expand.innerHTML = !Bool ? "+" : "-";
else expand.src = !Bool ? exp.src : typeof(expE) =="undefined" ? exp.src : expE.src;
}
if(!Bool && this.currentNode.path.indexOf(node.path)==0 && this.currentNode.id!=id)
{
try{this.getElementById(this.name +"_link_"+ id).click();}
catch(e){this.focusClientNode(id);}
}
area.style.display = !Bool ? "none" : "block";//(this.navigator=="netscape" ? "block" : "");
if(!node.isLoad)
{
this.load(id);
if(node.id=="0") return;
//当子节点过多时, 给用户一个正在加载的提示语句
if(node.hasChild && node.childNodes.length>200)
{
setTimeout(this.name +".buildNode('"+ id +"')", 1);
var temp = this.getElementById(this.name +"_tree_"+ id).childNodes;
temp[temp.length-1].innerHTML = "<DIV noWrap><NOBR><SPAN>"+ (this.wordLine ?
node.childAppend +"└" : this.word2image(node.childAppend +"└")) +"</SPAN>"+
"<IMG border='0' height='16' align='absmiddle' src='"+this.icons["file"].src+"'>"+
"<A style='background-Color: "+ this.colors.highLight +"; color: "+
this.colors.highLightText +"; font-size: 9pt'>请稍候...</A></NOBR></DIV>";
}
else this.buildNode(id);
}
}
};
//节点链接单击事件处理方法
//id 客户端树节点的 id
MzTreeView.prototype.nodeClick = function(id)
{
var source = this.nodes[this.node[id].sourceIndex];
eval(this.getAttribute(source, "method"));
return !(!this.getAttribute(source, "url") && this.url=="#");
};
//为配合系统初始聚集某节点而写的函数, 得到某节点在数据源里的路径
//sourceId 数据源里的节点 id
MzTreeView.prototype.getPath= function(sourceId)
{
Array.prototype.indexOf = function(item)
{
for(var i=0; i<this.length; i++)
{
if(this[i]==item) return i;
}
return -1;
};
var _d = this._d, d = this.divider;
var A = new Array(), id=sourceId; A[0] = id;
while(id!="0" && id!="")
{
var str = "(^|"+_d+")([^"+_d+d+"]+"+d+ id +")("+_d+"|$)";
if (new RegExp(str).test(this.names))
{
id = RegExp.$2.substring(0, RegExp.$2.indexOf(d));
if(A.indexOf(id)>-1) break;
A[A.length] = id;
}
else break;
}
return A.reverse();
};
//在源代码里指定 MzTreeView 初始聚集到某个节点
//sourceId 节点在数据源里的 id
MzTreeView.prototype.focus = function(sourceId, defer)
{
if (!defer)
{
setTimeout(this.name +".focus('"+ sourceId +"', true)", 100);
return;
}
var path = this.getPath(sourceId);
if(path[0]!="0")
{
alert("节点 "+ sourceId +" 没有正确的挂靠有效树节点上!rn"+
"节点 id 序列 = "+ path.join(this.divider));
return;
}
var root = this.node["0"], len = path.length;
for(var i=1; i<len; i++)
{
if(root.hasChild)
{
var sourceIndex= path[i-1] + this.divider + path[i];
for (var k=0; k<root.childNodes.length; k++)
{
if (root.childNodes[k].sourceIndex == sourceIndex)
{
root = root.childNodes[k];
if(i<len - 1) this.expand(root.id, true);
else this.focusClientNode(root.id);
break;
}
}
}
}
};
//树的单击事件处理函数
MzTreeView.prototype.clickHandle = function(e)
{
e = window.event || e; e = e.srcElement || e.target;
//alert(e.tagName)
switch(e.tagName)
{
case "IMG" :
if(e.id)
{
if(e.id.indexOf(this.name +"_icon_")==0)
this.focusClientNode(e.id.substr(e.id.lastIndexOf("_") + 1));
else if (e.id.indexOf(this.name +"_expand_")==0)
this.expand(e.id.substr(e.id.lastIndexOf("_") + 1));
}
break;
case "A" :
if(e.id) this.focusClientNode(e.id.substr(e.id.lastIndexOf("_") + 1));
break;
case "SPAN" :
if(e.className=="pm")
this.expand(e.id.substr(e.id.lastIndexOf("_") + 1));
break;
default :
if(this.navigator=="netscape") e = e.parentNode;
if(e.tagName=="SPAN" && e.className=="pm")
this.expand(e.id.substr(e.id.lastIndexOf("_") + 1));
break;
}
};
//MzTreeView 双击事件的处理函数
MzTreeView.prototype.dblClickHandle = function(e)
{
e = window.event || e; e = e.srcElement || e.target;
if((e.tagName=="A" || e.tagName=="IMG")&& e.id)
{
var id = e.id.substr(e.id.lastIndexOf("_") + 1);
if(this.node[id].hasChild) this.expand(id);
}
};
//回到树当前节点的父层节点
MzTreeView.prototype.upperNode = function()
{
if(!this.currentNode) return;
if(this.currentNode.id=="0" || this.currentNode.parentId=="0") return;
if (this.currentNode.hasChild && this.currentNode.isExpand)
this.expand(this.currentNode.id, false);
else this.focusClientNode(this.currentNode.parentId);
};
//展开当前节点并
MzTreeView.prototype.lowerNode = function()
{
if (!this.currentNode) this.currentNode = this.node["0"];
if (this.currentNode.hasChild)
{
if (this.currentNode.isExpand)
this.focusClientNode(this.currentNode.childNodes[0].id);
else this.expand(this.currentNode.id, true);
}
}
//聚集到树当前节点的上一节点
MzTreeView.prototype.pervNode = function()
{
if(!this.currentNode) return; var e = this.currentNode;
if(e.id=="0") return; var a = this.node[e.parentId].childNodes;
for(var i=0; i<a.length; i++){if(a[i].id==e.id){if(i>0){e=a[i-1];
while(e.hasChild){this.expand(e.id, true);
e = e.childNodes[e.childNodes.length - 1];}
this.focusClientNode(e.id); return;} else {
this.focusClientNode(e.parentId); return;}}}
};
//聚集到树当前节点的下一节点
MzTreeView.prototype.nextNode = function()
{
var e = this.currentNode; if(!e) e = this.node["0"];
if (e.hasChild){this.expand(e.id, true);
this.focusClientNode(e.childNodes[0].id); return;}
while(typeof(e.parentId)!="undefined"){
var a = this.node[e.parentId].childNodes;
for(var i=0; i<a.length; i++){ if(a[i].id==e.id){
if(i<a.length-1){this.focusClientNode(a[i+1].id); return;}
else e = this.node[e.parentId];}}}
};
//展开树的所有节点
MzTreeView.prototype.expandAll = function()
{
if(this.totalNode>500) if(
confirm("您是否要停止展开全部节点?rnrn节点过多!展开很耗时")) return;
if(this.node["0"].childNodes.length==0) return;
var e = this.node["0"].childNodes[0];
var isdo = t = false;
while(e.id != "0")
{
var p = this.node[e.parentId].childNodes, pn = p.length;
if(p[pn-1].id==e.id && (isdo || !e.hasChild)){e=this.node[e.parentId]; isdo = true;}
else
{
if(e.hasChild && !isdo)
{
this.expand(e.id, true), t = false;
for(var i=0; i<e.childNodes.length; i++)
{
if(e.childNodes[i].hasChild){e = e.childNodes[i]; t = true; break;}
}
if(!t) isdo = true;
}
else
{
isdo = false;
for(var i=0; i<pn; i++)
{
if(p[i].id==e.id) {e = p[i+1]; break;}
}
}
}
}
};
//本树将要用动的图片的字义及预载函数
//path 图片存放的路径名
MzTreeView.prototype.setIconPath = function(path)
{
var k = 0, d = new Date().getTime();
for(var i in this.icons)
{
var tmp = this.icons[i];
this.icons[i] = new Image();
this.icons[i].src = path + tmp;
if(k==9 && (new Date().getTime()-d)>20)
this.wordLine = true; k++;
}
for(var i in this.iconsExpand)
{
var tmp = this.iconsExpand[i];
this.iconsExpand[i]=new Image();
this.iconsExpand[i].src = path + tmp;
}
};
//设置树的默认链接
//url 默认链接 若不设置, 其初始值为 #
MzTreeView.prototype.setURL = function(url){this.url = url;};
//设置树的默认的目标框架名 target
//target 目标框架名 若不设置, 其初始值为 _self
MzTreeView.prototype.setTarget = function(target){this.target = target;};
// -->
一个简单的示例:
<script language="JavaScript"
src="http://www.meizz.com/Web/Plugs/MzTreeView10.js"></script>
<base href="http://www.meizz.com/Web/">
<style>
A.MzTreeview
{
font-size: 9pt;
padding-left: 3px;
}
</style>
<script language="JavaScript">
var tree = new MzTreeView("tree");
tree.icons["property"] = "property.gif";
tree.icons["css"] = "collection.gif";
tree.icons["book"] = "book.gif";
tree.iconsExpand["book"] = "bookopen.gif"; //展开时对应的图片
tree.setIconPath("http://www.meizz.com/Icons/TreeView/"); //可用相对路径
tree.nodes["0_1"] = "text:WEB 编程";
tree.nodes["1_100"] = "text:代码示例; data:id=100";
tree.nodes["1_200"] = "text:梅花雪脚本控件集; data:id=200";
tree.nodes["1_310"] = "text:CSS; icon:css; data:id=310";
tree.nodes["1_320"] = "text:DHTML; data:id=320";
tree.nodes["1_300"] = "text:HTML; data:id=300";
tree.nodes["1_400"] = "text:JavaScript; icon:book; data:id=400";
tree.nodes["320_322"] = "text:属性; icon: property; data:id=322";
tree.nodes["320_323"] = "text:方法; data:id=323";
tree.nodes["320_324"] = "text:事件; icon:event; data:id=324";
tree.nodes["320_325"] = "text:集合; data:id=325";
tree.nodes["400_407"] = "text:对象; data:id=407";
tree.nodes["400_406"] = "text:方法; data:id=406";
tree.nodes["400_408"] = "text:运算符; data:id=408";
tree.nodes["400_409"] = "text:属性; data:id=409";
tree.nodes["407_1140"] = "text:Date; url:Article.asp; data:id=140";
tree.nodes["406_1127"] = "text:toString; url:Article.asp; data:id=127";
tree.nodes["408_1239"] = "text:||; url:Article.asp; data:id=239";
tree.nodes["409_1163"] = "text:E; url:Article.asp; data:id=163";
tree.setURL("Catalog.asp");
tree.setTarget("MzMain");
document.write(tree.toString()); //亦可用 obj.innerHTML = tree.toString();
</script>
原文出处:http://www.cnblogs.com/wayne-ivan/archive/2008/06/20/1227059.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>无标题页</title>
<script language="JavaScript" src="scripts/jsframework.js"></script>
</head>
<body leftmargin="0" topmargin="2">
<form id="form1" runat="server">
<script language="javascript" type="text/javascript">
<%=LoadTree() %>
</script>
<%--
<SCRIPT LANGUAGE="JavaScript">
window.tree = new MzTreeView("tree");
//tree.setIconPath("menutree/"); //可用相对路径
<%=str%> //这里的str是在后台定义的一个string,他包含了从数据库取出来的所有节点信息,只能够写这里
//tree.setURL("Catalog.asp");
//tree.setTarget("MzMain");
//document.write(tree.toString()); //亦可用 obj.innerHTML = tree.toString();
//tree.focus(111); //让id是111的节点获得焦点,同时也展开了这个节点上的父节点!
//-->
</SCRIPT> --%>
</form>
</body>
</html>
public string LoadTree()
{
string sql = "SELECT Parent,ID, Name FROM [T_Tree] ";
SqlDataAdapter da = new SqlDataAdapter(sql, "DATABASE=aa;SERVER=localhost;UID=sa;PWD=sa;");
DataTable dt = new DataTable();
da.Fill(dt);
string nodes = "";
nodes = "var data = {};";
foreach(DataRow dr in dt.Rows)
{
nodes += "data[\'" + dr["Parent"].ToString() + "_" + dr["ID"].ToString() + "\'] = \'text:" + dr["Name"].ToString() + ";\';\r\n";
}
nodes += "Using(\"System.Web.UI.WebControls.MzTreeView\");";
nodes += "var a = new MzTreeView();";
nodes += "a.dataSource = data;";
nodes += "a.setJsDataPath(\"csdn/community/treedata/\");";
nodes += "a.setXmlDataPath(\"csdn/community/treedata/\");";
//a.rootId="1";
nodes += "a.autoSort=false;";
nodes += "a.useCheckbox=true;";
nodes += "a.canOperate = true;";
nodes += "document.write(a.render());";
nodes += "a.expandLevel(1);";
return nodes;
}
【摘要】
梅花雪树控件1.0,2.0的使用。
【全文】
梅花雪树1.0控件 有以下属性和方法。
属性
MzTreeView 类的一些属性: 属性名 类型 属性的具体说明
MzTreeView.nodes 集合 服务器端给树指定数据源时数据存放的对象,具体存放格式如:
MzTreeViewHandle.nodes["parentId_nodeId"] = "text: nodeText; icon: nodeIcon; url: nodeURL; ...";
MzTreeView.url 地址字符串 可读写,树缺省的URL,默认值是 #
MzTreeView.target 目标框架名 可读写,树缺省的链接target,默认值是 _self
MzTreeView.name 字符 只读,树的实例名,同树实例化时作为参数传入(大小写敏感):
var Tree = new MzTreeView("Tree");
MzTreeView.currentNode 树节点 只读,树当前得到焦点的节点对象
MzTreeView.icons 集合 树所使用的所有图标存放
MzTreeView.iconsExpand 集合 树里展开状态的图标存放
MzTreeView.colors 集合 树里使用到的几个颜色存放
MzTreeView 在客户端的节点所拥有的属性: 属性名 属性的具体说明
node.id 数字文本,节点的ID
node.parentId 数字文本,节点对应的父节点ID
node.text 文本,节点的显示文本
node.hint 文本,节点的注释说明
node.icon 文本,节点对应的图标
node.path 文本,节点在树里的绝对路径:0_1_10_34
node.url 文本,该节点的 URL
node.target 文本,该节点链接的目标框架名
node.data 文本,该节点所挂载的数据
node.method 文本,该节点的点击对应处理语句
node.parentNode 对象,节点的父节点对象
node.childNodes 数组,包含节点下所有子节点的数组
node.sourceIndex 文本,服务器给予的数据里对象的 parentId_nodeId 的组合字符串
node.hasChild 布尔值,指该节点是否有子节点
node.isLoad 布尔值,本节点的子节点数据是否已经在客户端初始化
node.isExpand 布尔值,节点的展开状态
方法
MzTreeView 类的一些方法: 方法名 方法的具体说明
MzTreeView.toString() 类的默认初始运行
MzTreeView.buildNode(id) 将该节点的所有下级子节点转换成 HTML 并在网页上体现出来
MzTreeView.nodeToHTML(node, AtEnd) 将 node 转换成 HTML
MzTreeView.load(id) 从数据源里加载当前节点下的所有子节点
MzTreeView.nodeInit(sourceIndex, parentId) 节点的信息初始,从数据源到客户端完整节点的转化
MzTreeView.focus(id) 聚集到某个节点上
MzTreeView.expand(id[, sureExpand]) 展开节点(包含下级子节点数据的加载初始化)
MzTreeView.setIconPath(path) 给节点图片设置正确的路径
MzTreeView.nodeClick(id) 节点链接点击时同时被触发的点击事件处理方法
MzTreeView.upperNode() 跳转到当前聚集节点的父级节点
MzTreeView.lowerNode() 跳转到当前聚集节点的子级节点
MzTreeView.pervNode() 跳转到当前聚集节点的上一节点
MzTreeView.nextNode() 跳转到当前聚集节点的下一节点
MzTreeView.expandAll() 展开所有的树点,在总节点量大于500时这步操作将会比较耗时
下面是几个例子。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Tree._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>目录树</title>
<script type="text/javascript" language="javascript" src="MzTreeView10.js"></script>
<link href="css/Global.css" type="text/css" rel="stylesheet"/>
<style type="text/css">
A.MzTreeview
{
font-size: 9pt;
padding-left: 3px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<script type="text/javascript" language="javascript">
<!--
window.tree = new MzTreeView("tree");
tree.icons["property"] = "property.gif";
tree.icons["css"] = "collection.gif";
tree.icons["book"] = "book.gif";
tree.iconsExpand["book"] = "bookopen.gif";
tree.setIconPath("/images/");
<% LoadTree(); %>
tree.focus(4945);
tree.setURL("#");
tree.setTarget("");
document.write(tree.toString());
-->
</script>
</form>
</body>
</html>
后台页面:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.Sql;
using System.Data.SqlClient;
using TreeDemo.DBUtility;
namespace Tree
{
public partial class _Default : System.Web.UI.Page
{
public static string str;
protected void Page_Load(object sender, EventArgs e)
{
}
public void LoadTree()
{
string sql = "Select SiteID,isnull(PID,0) as PID,SiteCaption From AreaSite order by SiteID";
SqlDataReader dr = TreeDemo.DBUtility.SqlHelper.ExecuteReader(SqlHelper.ConnectionString,CommandType.Text, sql, null);
string node = "";
while(dr.Read())
{
node = "\r\n tree.nodes[" + "\"" + dr["PID"].ToString() + "_" + dr["SiteID"].ToString() + "\"" + "] = ";
node = node + "\"" + "text:" + dr["SiteCaption"].ToString() + ";" + "\"";
Response.Write(node);
}
if (!dr.IsClosed)
dr.Close();
}
}
}
//下面是用AJAX来实现的,这里是一次性加载所有数据,没有实现异步加载。
var tree = new MzTreeView("tree");//创建一个树的实例
function TheTree()
{
tree.icons["proj"] = "project.gif";
tree.icons["book"] = "book.gif";
tree.iconsExpand["book"] = "bookopen.gif"; //设置图片
tree.setIconPath("image/");
tree.nodes["0_1"] = "text:项目列表";//虚拟根目录
var DataSet = FinetWebCPM.TreeTest.GetProject().value;
var rows = DataSet.Tables[0].Rows;
for(i = 0 ;i < rows.length ; i++)
{
tree.nodes["1_"+rows[i].ProjID] = "text:"+rows[i].SCName;//nodes数组的下表的格式是[父节点id_子节点id]
var DataSetPPage = FinetWebCPM.TreeTest.GetPage(rows[i].ProjID).value;
if(DataSetPPage != null)
{
var rowspage = DataSetPPage.Tables[0].Rows;
if(rowspage.length > 0)
{
for(n = 0 ;n < rowspage.length ; n++)
{
tree.nodes[rows[i].ProjID+"_"+rowspage[n].PageID] = "text:"+rowspage[n].PageTitle+";url:"+rowspage[n].PageUrl;
}
}
else
{
tree.nodes[rows[i].ProjID+"_None"] = "text:无此项目页面!";
}
}
else
{
tree.nodes[rows[i].ProjID+"_None"] = "text:无此项目页面!";
}
}
document.getElementById("treeviewarea").innerHTML = tree.toString();
}
下面是梅花雪树空间2.0的例子。
2.0的功能有了很大的改进,加入了动态加载,可以使用xml,js,一个数组等来作为数据源,在节点中还加入了checkbox。
下面看看用xml,和js来作为数据源的。
myxml.xml文档:
<?xml version="1.0" encoding="utf-8"?>
<nodes>
<node id="100" url="/page/100.htm" text="100页面" />
<node id="101" url='/page/101.htm' text="101页面">
<node text="child node" />
</node>
<node id="102" url='/page/102.htm' text="102页面" />
<node id="103" url='/page/103.htm' text="103页面" />
</nodes>
help.js:
var data={};
data['3_301'] = 'text:ASP ; data:roomid=301';
data['3_35409'] = 'text:JSP ; data:roomid=5409';
data['3_303'] = 'text:PHP ; data:roomid=303';
js部分:
<SCRIPT LANGUAGE="JavaScript">
var data={};
data["-1_1"] = "text: 起点;";//nodes的下标和1.0的格式是一样的。
data['1_9001'] = 'text: 首页;';
data['1_9002'] = 'text: 介绍; XMLData: data/myxml.xml ';
data['1_9009'] = 'text: 帮助; url:page/help?typenum=1&roomid=1 ; JSData: data/help.js ';//可以设置url的弹出方式target:_blank;,其默认是_self。
var xmlstr='<nodes>'+
'<node text="中国" />'+
'<node text="江西" />'+
'<node text="广东">'+
'<node text="梅州" />'+
'<node text="深圳" />'+
'</node>'+
'<node text="河北" />'+
'</nodes>';
Using("System.Web.UI.WebControls.MzTreeView");
var tree = new MzTreeView();
tree.dataSource = data
tree.loadXmlDataString(xmlstr, 1);//xml字符串作为数据源,loadXmlDataString方法的第一个参数是xml字符串,第二个参数父节点ID
tree.setJsDataPath("data/");//设置数据源的位置
tree.setXmlDataPath("data/");
tree.autoSort=false;
tree.useCheckbox=true;//是否使用checkbox
tree.canOperate=true;
document.write(a.render());//输出树
tree.expandLevel(1);//展开1节点
</SCRIPT>
梅花雪树的url默认是#,如果你的树很高,你单击下面节点的时候会跳到页面的顶层,你可以将url写成url:javascript:return;这样来屏蔽掉默认的url,不过你设置成 这样还将target属性是指成_blank的话,将会弹出一个新的窗口。
也许你要修改这些默认设置的话,你可以修改\scripts\system\web\ui\webcontrols\mztreeview.js里面的代码。如果你会英语及JS的话,应该是很简单的。
梅花雪树控件只有双击,和单击事件,下面来为树控件加各右键菜单事件,打开\scripts\system\web\ui\webcontrols\mztreeview.js,文件,找到render事件,这方法改成
MzTreeView.prototype.render = function()
{
function loadImg(C){for(var i in C){if("string"==typeof C[i]){
var a=new Image(); a.src=me.iconPath + C[i]; C[i]=a;}}} var me=this;
loadImg(MzTreeView.icons.expand);loadImg(MzTreeView.icons.collapse);
loadImg(MzTreeView.icons.line); me.firstNode=null;
loadCssFile(this.iconPath +"mztreeview.css", "MzTreeView_CSS");
this.initialize(); var str="no data", i, root=this.rootNode;
if (root.hasChild){var a = [], c = root.childNodes; me.firstNode=c[0];
for(i=0;i<c.length;i++)a[i]=c[i].render(i==c.length-1);str=a.join("");}
setTimeout(function(){me.afterRender();}, 10);
return "<div class='mztreeview' id='MTV_root_"+ this.index +"' "+
"onclick='Instance(\""+ this.index +"\").clickHandle(event)' "+
"ondblclick='Instance(\""+ this.index +"\").dblClickHandle(event)' "+
"oncontextmenu='Instance(\""+ this.index +"\").contextMenuHandle(event)' "+//这里是我们添加的右键事件
">"+ str +"</div>";
};
然后我们还得多写个方法。
//private: contextMenuHandle
MzTreeView.prototype.contextMenuHandle = function(e)
{
e = window.event || e; e = e.srcElement || e.target;
if((e.tagName=="A" || e.tagName=="IMG")&& e.id)
{
e=this.nodes[e.id.substr(e.id.lastIndexOf("_") +1)];
//e是一个节点对象,如我这里是判断这个节点是否有子节点,然后执行相应的方法 e.hasChild ? Tool(e,"project"):Tool(e,"page");
e.focus();
this.currentNode=e;this.dispatchEvent(new System.Event("oncontextmenu"));
}
};
MzTreeView 1.0 from--->>> http://www.meizz.com/
开发文档: http://www.meizz.com/Web/Article.asp?id=436
控件下载: http://www.meizz.com/Web/Download/MzTreeView10.rar
应用示例: http://www.meizz.com/Web/Demo/MzTreeView10.htm
说明
MzTreeView 1.0 是数据一次性加载,客户端节点异步展示的WEB脚本树。MzTreeView 1.0 的理论节点数设计上限为十万节点,在节点数三万的情况下页面打开时间小于 3 秒。无限层次无限节点的数的层级组成方式:id parentId。即每个节点除本身的节点id之外还有它的父层节点id,通过这种方式就可以组合成无限层级的树了。
在 MzTreeView 里都有一个虚的根节点,其ID为0,用户可见的根节点其父节点ID皆为0
MzTreeView 1.0在数据库库表里的字段名称:
字段名 字段的具体说明
id 节点ID(不可为0,可以是数字或字符)
parentId 本节点的父节点ID(若本节点已为根节点,此处填0)
text 节点的显示文本(一般不允许为空,不过有一种情况例外,即根节点,若根节点文本为空,这个根节点将不会在页面里显示)
hint 节点的说明注解
icon 节点的图标,MzTreeView 1.0允许每个节点拥有不同的图标(对应的名字在类里的icons和iconsExpand定义)
data 节点挂的数据,格式是 param=value¶m=value&... url里?后的那串字符串格式,
url 每个节点允许拥有不同的链接,它为空或者为#时,树里这个节点的链接,点击将无反应
target 每个节点的链接允许在不同的target里打开,为空时取类里的默认值
method 点击该链接时所想触发的脚本语句
特注:每个字段值中不可有冒号: 不可以换行 引号酌情考虑应不与节点字符串的引号相冲突
设计模式
为了达到能够在浏览器中快速打开多节点树的页面,我做了很多的优化与创新,下面我将详细解说几项最重要的部分:
•数据一次性加载 首先我要说的就是数据的一次性加载。在目前的 B/S 架构开发中对于多节点多层次的树,特别是树节点量超过两千的情况下,几乎都是采取数据异步加载来达到目的,即用户需要展开某个节点时,再从服务器端加载下级子节点的数据,数据异步加载最为经典的例子就是 MSDN 网站左边的目录树了。异步加载的优点在于可以扩充到无限级无限节点树,树的数据来源可以多样化(数据库或XML文件)等,但是它的缺点也是非常多的:设计模式比数据一次性加载要复杂得多,要考虑到 Browser/Server 之间的应答,要判断子节点是否含有孙节点,后台数据源的层级关系模型等。对网络传输的信赖性太大,每个节点的展开都需要连一次 Server,只要在取某节点数据时网络出现问题,就会导致该节点及其以下的子节点加载失败。而采取数据一次加载的模式只要一次加载成功,服务器就可以不用管它了,服务器压力减轻,脚本设计则完全独立,对整棵树节点的检索可以在客户端完成,节点展开响应速度快等等优势,因此在节点数不多的情况下数据一次性加载更有优势,那么这个节点数不多不多到底多少节点量为平衡点呢?就 ASP.net 里带的那个 TreeView 来说,在一两千节点以下一次性加载比较具有优势,而 MzTreeView 1.0 在节点量三万至五万以非常具有优势。
•节点信息的压缩传输 在浏览器里显示的树结构其实都是一个个 HTML 元素组合起来的,在 WEB 页面里的树都是根据树节点的信息组合成一串的 HTML 元素列来显示,这一步从节点信息到 HTML 的转化可以在两个地方生成:一个是在服务器端,一个是在客户端。在服务器端生成的优点在于不须考虑客户端的浏览器的兼容性,节点信息的可控性非常强,但是它的缺点也是非常大的:加重服务器的负担,增加网络传输量。在服务器端直接生成树节点的 HTML 给服务器带来的压力是显而易见的,也是非常巨大的,估计只要有几十个并发连接就能让服务器暂时停止响应了。这种直接在服务器生成树的做法在实际运用环境中也有人使用,不过本人非常不赞成这种做法。当然也有人直接将树生成为一个静态文件放在服务器端,这种做法对于树节点相对固定不变的情况还是非常有利的,只需要生成一次,以后就不需要再生成了,服务器的压力也非常小,但它的弊病在于可变化性太小,比如说不同的权限能看到的树节点的不同这种情况,用这种生成静态树放在服务器端的做就没有办法解决,且不管是服务器端动态计算还是直接生成静态树文件放在服务器端都有一个避免不了的问题就是网络传输量巨大。可以计算一下,一个树点所需要的HTML字符量大约300到600字节之间。即含有一千节点的树的网页大小就有300K到600K,给网络造成的压力是非常巨大的,所以MzTreeView 1.0采用了节点信息的压缩传输,大至一千节点的总传输量在30K左右,这可以差了一个数量级呀。本树将每个节点所必要的信息组合成一个字符串序列,传递到客户端,然后在客户端再用客户端脚本对这些信息进行分析,再生成一个个的节点HTML,这也符合了WEB的分散计算的原理,当然服务器端可以有选择性输出部分节点,这样又做到节点的灵活多变性。
•传输的节点信息的可扩展性 服务器端将节点的必要信息组合成一个字符串序列传递到客户端,然后客户端再用脚本对这个字符串序列进行分析,生成树的节点,那么这个字符串序列对整个树的生成的效率就有重要的影响了。我也参照过很多组串传输的例子,在一般的做法当中大多采用函数的参数方式传递。比如说定义一个函数 funName(p1, p2, p3, ...),然后服务器组串的时候就按位置给定数据。这种组串的弊病是非常大的,首先就是位置绝对错不得,只要有一个位置数据出错,这个节点的信息就乱了,对于那些在函数里已经定义的但节点里没有的信息也得用空字符串补上,诸如:(p1, "", p3, p4, "", "", ""),且万一这种组串的对应分析函数发生了变化,那么这种串就算是废了,得重新定义服务器端的字符串位置序列了,可以说这种组串的方式可扩展性极差。
节点信息组串传输的另一种常用模式就是XML。XML以它的无限可扩展性已惭有代替HTML称霸WEB的味道了。XML最大的优点就在于它的无限可扩展性,可以用任意的TagName名,可以有任意的Attribute名,节点与子节点已经有层级的关系,用XML来做WEB树的数据源其实是最理想的,MSDN的资源目录树就是采用XML作为传递的字符串,它唯一的不足之处就是不是所有浏览器都能很好地支持它,特别是在一些低版本的浏览器中,所以我只好忍痛割爱没有启用XML作为中间的传媒。那么是不是有可能结合XML的扩展性对第一种组串的方式进行改进呢?当我愁眉不展的时候,HTML里的STYLE样式表写法跳入我眼,样式的写法是"param1: value1; param2: value3; ...",哈哈,这不是现成地给我指明了路吗?这种写法拥有XML的可扩展性,位置顺序的随意性,又是传统的长字符串,正合我意也!服务器给定这种数据源字符串,我不光可以在TreeView里用它,还可以直接做Outlook Bar,下拉式层级菜单,右键层级菜单的数据源,豁然开朗也!我写了一个函数专门解析这种文本:getAttribute()。
•客户端节点数据的存储方式及快速检索 现在数据源准备好了,数据传输也已做到最大优化了,下面就是客户端的脚本解析了,而这一步也正是树生成的效率瓶颈所在。由于我没有采用XML做为数据源,所以我这里就不讨论XML+XSL和XMLDOM的模式,而只考虑HTML+DOM模式了。在HTML+DOM模式下客户端存储的方式有很多种,我就曾经看到过一种直接将字符串输出在多行文本框<textarea>里的,但归究起来最常用的方式就是用一个数组来存放节点信息,不过就算是用数组也是种类多多,比如说数组里套数组,节点通过记录父节点在数组里的索引号表示层级的等等,说到数组存放树节点模式,我推荐阿信写的那棵树。在服务器端组织好脚本及节点字符串,客户端浏览器加载这个页面的时候顺序执行其里面的脚本,将一个个节点做最初步的解析,然后再一个个地ADD到数组中去。这种流程看起来似乎没有什么问题,在很多的传统C/S结构编程中也就这种模式,但是随着节点数的增多,这种流程在B/S里的缺点就出现了:脚本的执行效率不如强语言高!我做过测试,在5000节点的数据量时这一步操作将耗时1秒钟,也许你认为这1秒钟不算长,哪里有5000这么多节点的树?但对于一个程序员来说,严谨是第一位的,若存在优化的可能性,情况出现的可能性,我们就得将它考虑到。那么有没有可能再进一步优化呢?当然有,否则我就用不着这么大费口舌在这里讲了。既然在脚本里执行 for 循环插入数组效率不高那我不用循环,且考虑到在数组里这么多节点中搜索我想要的某几个节点,还得用循环,所以我干脆就不再用数组来存放树节点了,那用什么呢?说到这里我得插几句题外话,客户端脚本也可以写一些类,虽说写出来的类没有强语言那么好,继承性不好等,但脚本终究还是可以写出一些简单的类的。类可以定义属性,也可以定义方法,并且访问属性的时候可以通过属性名下标直接访问到属性值,关于脚本里如何写类,大家可以参考我在JavaScript和VBScript里对这方面的详细说明。呵呵,说了这么多的题外话,不过相信大家也猜到了,新的节点存储方式就是类的属性自定义扩展方式。这种存储方式随着页面的打开,页面里的脚本被执行,类的属性值不需要做任何添加的动作就直接写到内存当中,比数组模式少去了ADD操作,这一步从字符串到内存的时间就是页面打开的时间,我测试了一下,5000节点的树用这种方式打开,只需0.1秒呀,与数组模式速度整整差了一个数量级呀!且这种模式还有一种好处,在几千个节点里要找到目标节点,只需要知道节点对应的属性名,一步就可以直接找到。
客户端树节点存储模式说清楚了,那节点的快速检索也就清楚了。已知节点名即在类里的属性名的话,一步就找到了该节点,不过为了配合下一节的异步展示,我的检索就没有这么简单了。试想在几千或者几万个节点里要快速地找出符合条件的几十个节点,这一步的耗时可想而知了,首先就得排除for循环法,for循环的效率在数组模式里大家就看到了,这一步操作特别在总节点数多的情况下就能让使用者等疯掉,显然得另想办法。于是我想到了正则表达式里的字符串模式匹配match(),我将所有的节点名(即类的属性名)join()成一个大字符串,然后再用正则表达式匹配,一步就找出了想要的节点了。经测试,在三万节点里找30个节点对象耗时小于0.1秒!
•异步展示 再来讲一下节点字符串被解析之后转化成HTML元素的这一步操作。上面已经有过一个计算,表达1000节点的树的HTML字符量就有300K-600K之多,且这一步只能一个节点一个节点慢慢地生成,没有什么取巧的办法,想快点也只能是减小单个节点的HTML元素量罢,不过最快也得1-3秒每千节点呀,这也是没有法子的事,谁叫DOM的效率不高呢!总得想个什么法子吧,否则象5000节点量的树让使用者等上个半分钟一分钟的,谁也受不了呀!因此我想出异步展示这招:页面加载时并不立即生成所有节点的HTML元素,而是用户展开多少节点就生成多少节点,节点的生成发生在用户展开这个节点的时候。使用者在页面打开的时候并不会立即把所有的节点都一次全部展开,而是一级级地往下展开的,就象你查看windows注册表一样,想看哪级才会去展开哪一级,这样我就只需要在你展开那级的时候把这一级的节点转化成HTML即可,每次转化的节点只有几十分甚至只有几个而已,消除了使用者的等待时间。经过上面这几个环节,终于一棵实用性,效率,扩展性俱佳的WEB TreeView出世了。
•采用文字竖线 每个浏览器随着使用客户的不同,而总会产生不同的设置,其中有一项就是客户端设置每次访问都检查网页新版本,即客户端不缓存。这个设置对一般的应用来说问题不会很大,但是对于使用图片作为竖线的树来说随着节点总数的增多,图片的使用量也就跟着巨量增加,可能会使用几千甚至几万个小图片,每张图片是都很小,但量一大的话,将会严重影响树的快速展示,因此针对这种情况就得换一种模式来展现了,那就是文字竖线,用文字加样式就可以解决这个问题。我加了一个变量:MzTreeView.wordLine(布尔型,默认值为false),当网速过慢或者没有使用本地缓存时这个属性会自动设置成 true 而以文字代替图片完成竖线(注:Opera 浏览器不支持文字竖线模式),当然你也可以一开始就强行设置值为 true,这样树就会始终用文字竖线了。
属性
MzTreeView 类的一些属性:
属性名 类型 属性的具体说明
MzTreeView.nodes 集合 服务器端给树指定数据源时数据存放的对象,具体存放格式如:
MzTreeViewHandle.nodes["parentId_nodeId"] = "text: nodeText; icon: nodeIcon; url: nodeURL; ...";
MzTreeView.url 地址字符串 可读写,树缺省的URL,默认值是 #
MzTreeView.target 目标框架名 可读写,树缺省的链接target,默认值是 _self
MzTreeView.name 字符 只读,树的实例名,同树实例化时作为参数传入(大小写敏感):
var Tree = new MzTreeView("Tree");
MzTreeView.currentNode 树节点 只读,树当前得到焦点的节点对象
MzTreeView.icons 集合 树所使用的所有图标存放
MzTreeView.iconsExpand 集合 树里展开状态的图标存放
MzTreeView.colors 集合 树里使用到的几个颜色存放
MzTreeView 在客户端的节点所拥有的属性:
属性名 属性的具体说明
node.id 数字文本,节点的ID
node.parentId 数字文本,节点对应的父节点ID
node.text 文本,节点的显示文本
node.hint 文本,节点的注释说明
node.icon 文本,节点对应的图标
node.path 文本,节点在树里的绝对路径:0_1_10_34
node.url 文本,该节点的 URL
node.target 文本,该节点链接的目标框架名
node.data 文本,该节点所挂载的数据
node.method 文本,该节点的点击对应处理语句
node.parentNode 对象,节点的父节点对象
node.childNodes 数组,包含节点下所有子节点的数组
node.sourceIndex 文本,服务器给予的数据里对象的 parentId_nodeId 的组合字符串
node.hasChild 布尔值,指该节点是否有子节点
node.isLoad 布尔值,本节点的子节点数据是否已经在客户端初始化
node.isExpand 布尔值,节点的展开状态
方法
MzTreeView 类的一些方法:
方法名 方法的具体说明
MzTreeView.toString() 类的默认初始运行
MzTreeView.buildNode(id) 将该节点的所有下级子节点转换成 HTML 并在网页上体现出来
MzTreeView.nodeToHTML(node, AtEnd) 将 node 转换成 HTML
MzTreeView.load(id) 从数据源里加载当前节点下的所有子节点
MzTreeView.nodeInit(sourceIndex, parentId) 节点的信息初始,从数据源到客户端完整节点的转化
MzTreeView.focus(id) 聚集到某个节点上
MzTreeView.expand(id[, sureExpand]) 展开节点(包含下级子节点数据的加载初始化)
MzTreeView.setIconPath(path) 给节点图片设置正确的路径
MzTreeView.nodeClick(id) 节点链接点击时同时被触发的点击事件处理方法
MzTreeView.upperNode() 跳转到当前聚集节点的父级节点
MzTreeView.lowerNode() 跳转到当前聚集节点的子级节点
MzTreeView.pervNode() 跳转到当前聚集节点的上一节点
MzTreeView.nextNode() 跳转到当前聚集节点的下一节点
MzTreeView.expandAll() 展开所有的树点,在总节点量大于500时这步操作将会比较耗时
示例
<script language="JavaScript"
src="http://www.meizz.com/Web/Plugs/MzTreeView10.js"></script>
<base href="http://www.meizz.com/Web/">
<style>
A.MzTreeview
{
font-size: 9pt;
padding-left: 3px;
}
</style>
<script language="JavaScript">
var tree = new MzTreeView("tree");
tree.icons["property"] = "property.gif";
tree.icons["css"] = "collection.gif";
tree.icons["book"] = "book.gif";
tree.iconsExpand["book"] = "bookopen.gif"; //展开时对应的图片
tree.setIconPath("http://www.meizz.com/Icons/TreeView/"); //可用相对路径
tree.nodes["0_1"] = "text:WEB 编程";
tree.nodes["1_100"] = "text:代码示例; data:id=100";
tree.nodes["1_200"] = "text:梅花雪脚本控件集; data:id=200";
tree.nodes["1_310"] = "text:CSS; icon:css; data:id=310";
tree.nodes["1_320"] = "text:DHTML; data:id=320";
tree.nodes["1_300"] = "text:HTML; data:id=300";
tree.nodes["1_400"] = "text:JavaScript; icon:book; data:id=400";
tree.nodes["320_322"] = "text:属性; icon: property; data:id=322";
tree.nodes["320_323"] = "text:方法; data:id=323";
tree.nodes["320_324"] = "text:事件; icon:event; data:id=324";
tree.nodes["320_325"] = "text:集合; data:id=325";
tree.nodes["400_407"] = "text:对象; data:id=407";
tree.nodes["400_406"] = "text:方法; data:id=406";
tree.nodes["400_408"] = "text:运算符; data:id=408";
tree.nodes["400_409"] = "text:属性; data:id=409";
tree.nodes["407_1140"] = "text:Date; url:Article.asp; data:id=140";
tree.nodes["406_1127"] = "text:toString; url:Article.asp; data:id=127";
tree.nodes["408_1239"] = "text:||; url:Article.asp; data:id=239";
tree.nodes["409_1163"] = "text:E; url:Article.asp; data:id=163";
tree.setURL("Catalog.asp");
tree.setTarget("MzMain");
document.write(tree.toString()); //亦可用 obj.innerHTML = tree.toString();
</script>
//MzTreeView1.0网页树类, 在实例化的时候请把实例名作参数传递进来
function MzTreeView(Tname)
{
if(typeof(Tname) != "string" || Tname == "")
throw(new Error(-1, '创建类实例的时候请把类实例的引用变量名传递进来!'));
//【property】
this.url = "#";
this.target = "_self";
this.name = Tname;
this.wordLine = false;
this.currentNode = null;
this.useArrow = true;
this.nodes = {};
this.node = {};
this.names = "";
this._d = "x0f";
this.index = 0;
this.divider = "_";
this.node["0"] =
{
"id": "0",
"path": "0",
"isLoad": false,
"childNodes": [],
"childAppend": "",
"sourceIndex": "0"
};
this.colors =
{
"highLight" : "#0A246A",
"highLightText" : "#FFFFFF",
"mouseOverBgColor" : "#D4D0C8"
};
this.icons = {
L0 : 'L0.gif', //┏
L1 : 'L1.gif', //┣
L2 : 'L2.gif', //┗
L3 : 'L3.gif', //━
L4 : 'L4.gif', //┃
PM0 : 'P0.gif', //+┏
PM1 : 'P1.gif', //+┣
PM2 : 'P2.gif', //+┗
PM3 : 'P3.gif', //+━
empty : 'L5.gif', //空白图
root : 'root.gif', //缺省的根节点图标
folder : 'folder.gif', //缺省的文件夹图标
file : 'file.gif', //缺省的文件图标
exit : 'exit.gif'
};
this.iconsExpand = { //存放节点图片在展开时的对应图片
PM0 : 'M0.gif', //-┏
PM1 : 'M1.gif', //-┣
PM2 : 'M2.gif', //-┗
PM3 : 'M3.gif', //-━
folder : 'folderopen.gif',
exit : 'exit.gif'
};
//扩展 document.getElementById(id) 多浏览器兼容性
//id 要查找的对象 id
this.getElementById = function(id)
{
if (typeof(id) != "string" || id == "") return null;
if (document.getElementById) return document.getElementById(id);
if (document.all) return document.all(id);
try {return eval(id);} catch(e){ return null;}
}
//MzTreeView 初始化入口函数
this.toString = function()
{
this.browserCheck();
this.dataFormat();
this.setStyle();
this.load("0");
var rootCN = this.node["0"].childNodes;
var str = "<A id='"+ this.name +"_RootLink' href='#' style='DISPLAY: none'></A>";
if(rootCN.length>0)
{
this.node["0"].hasChild = true;
for(var i=0; i<rootCN.length; i++)
str += this.nodeToHTML(rootCN[i], i==rootCN.length-1);
setTimeout(this.name +".expand('"+ rootCN[0].id +"', true); "+
this.name +".focusClientNode('"+ rootCN[0].id +"'); "+ this.name +".atRootIsEmpty()",10);
}
if (this.useArrow) //使用方向键控制跳转到上级下级父级子级节点
{
if (document.attachEvent)
document.attachEvent("onkeydown", this.onkeydown);
else if (document.addEventListener)
document.addEventListener('keydown', this.onkeydown, false);
}
return "<DIV class='MzTreeView' "+
"onclick='"+ this.name +".clickHandle(event)' "+
"ondblclick='"+ this.name +".dblClickHandle(event)' "+
">"+ str +"</DIV>";
};
this.onkeydown= function(e)
{
e = window.event || e; var key = e.keyCode || e.which;
switch(key)
{
case 37 : eval(Tname).upperNode(); break; //Arrow left, shrink child node
case 38 : eval(Tname).pervNode(); break; //Arrow up
case 39 : eval(Tname).lowerNode(); break; //Arrow right, expand child node
case 40 : eval(Tname).nextNode(); break; //Arrow down
}
};
}
//浏览器类型及版本检测
MzTreeView.prototype.browserCheck = function()
{
var ua = window.navigator.userAgent.toLowerCase(), bname;
if(/msie/i.test(ua))
{
this.navigator = /opera/i.test(ua) ? "opera" : "";
if(!this.navigator) this.navigator = "msie";
}
else if(/gecko/i.test(ua))
{
var vendor = window.navigator.vendor.toLowerCase();
if(vendor == "firefox") this.navigator = "firefox";
else if(vendor == "netscape") this.navigator = "netscape";
else if(vendor == "") this.navigator = "mozilla";
}
else this.navigator = "msie";
if(window.opera) this.wordLine = false;
};
//给 TreeView 树加上样式设置
MzTreeView.prototype.setStyle = function()
{
/*
width: 16px;
height: 16px;
width: 20px;
height: 20px;
*/
var style = "<style>"+
"DIV.MzTreeView DIV IMG{border: 0px solid #FFFFFF;}"+
"DIV.MzTreeView DIV SPAN IMG{border: 0px solid #FFFFFF;}";
if(this.wordLine)
{
style +="
DIV.MzTreeView DIV
{
height: 20px;"+
(this.navigator=="firefox" ? "line-height: 20px;" : "" ) +
(this.navigator=="netscape" ? "" : "overflow: hidden;" ) +"
}
DIV.MzTreeView DIV SPAN
{
vertical-align: middle; font-size: 21px; height: 20px; color: #D4D0C8; cursor: default;
}
DIV.MzTreeView DIV SPAN.pm
{
width: "+ (this.navigator=="msie"||this.navigator=="opera" ? "11" : "9") +"px;
height: "+ (this.navigator=="netscape"?"9":(this.navigator=="firefox"?"10":"11")) +"px;
font-size: 7pt;
overflow: hidden;
margin-left: -16px;
margin-right: 5px;
color: #000080;
vertical-align: middle;
border: 1px solid #D4D0C8;
cursor: "+ (this.navigator=="msie" ? "hand" : "pointer") +";
padding: 0 2px 0 2px;
text-align: center;
background-color: #F0F0F0;
}";
}
style += "</style>";
document.write(style);
};
//当根节点为空的时候做的处理
MzTreeView.prototype.atRootIsEmpty = function()
{
var RCN = this.node["0"].childNodes;
for(var i=0; i<RCN.length; i++)
{
if(!RCN[i].isLoad) this.expand(RCN[i].id);
if (RCN[i].text=="")
{
var node = RCN[i].childNodes[0], HCN = node.hasChild;
if(this.wordLine)
{
var span = this.getElementById(this.name +"_tree_"+ node.id);
span = span.childNodes[0].childNodes[0].childNodes[0];
span.innerHTML = RCN[i].childNodes.length>1 ? "┌" : "─";
}
else
{
node.iconExpand = RCN[i].childNodes.length>1 ? HCN ? "PM0" : "L0" : HCN ? "PM3" : "L3"
this.getElementById(this.name +"_expand_"+ node.id).src = this.icons[node.iconExpand].src;
}
}
}
};
//初始化数据源里的数据以便后面的快速检索
MzTreeView.prototype.dataFormat = function()
{
var a = new Array();
for (var id in this.nodes) a[a.length] = id;
this.names = a.join(this._d + this._d);
this.totalNode = a.length; a = null;
};
//在数据源检索所需的数据节点
//id 客户端节点对应的id
MzTreeView.prototype.load = function(id)
{
var node = this.node[id], d = this.divider, _d = this._d;
var sid = node.sourceIndex.substr(node.sourceIndex.indexOf(d) + d.length);
var reg = new RegExp("(^|"+_d+")"+ sid +d+"[^"+_d+d +"]+("+_d+"|$)", "g");
var cns = this.names.match(reg), tcn = this.node[id].childNodes; if (cns){
reg = new RegExp(_d, "g"); for (var i=0; i<cns.length; i++)
tcn[tcn.length] = this.nodeInit(cns[i].replace(reg, ""), id); }
node.isLoad = true;
};
//初始化节点信息, 根据 this.nodes 数据源生成节点的详细信息
//sourceIndex 数据源中的父子节点组合的字符串 0_1
//parentId 当前树节点在客户端的父节点的 id
MzTreeView.prototype.nodeInit = function(sourceIndex, parentId)
{
this.index++;
var source= this.nodes[sourceIndex], d = this.divider;
var text = this.getAttribute(source, "text");
var hint = this.getAttribute(source, "hint");
var sid = sourceIndex.substr(sourceIndex.indexOf(d) + d.length);
this.node[this.index] =
{
"id" : this.index,
"text" : text,
"hint" : hint ? hint : text,
"icon" : this.getAttribute(source, "icon"),
"path" : this.node[parentId].path + d + this.index,
"isLoad": false,
"isExpand": false,
"parentId": parentId,
"parentNode": this.node[parentId],
"sourceIndex" : sourceIndex,
"childAppend" : ""
};
this.nodes[sourceIndex] = "index:"+ this.index +";"+ source;
this.node[this.index].hasChild = this.names.indexOf(this._d + sid + d)>-1;
if(this.node[this.index].hasChild) this.node[this.index].childNodes = [];
return this.node[this.index];
};
//从XML格式字符串里提取信息
//source 数据源里的节点信息字符串(以后可以扩展对XML的支持)
//name 要提取的属性名
MzTreeView.prototype.getAttribute = function(source, name)
{
var reg = new RegExp("(^|;|s)"+ name +"s*:s*([^;]*)(s|;|$)", "i");
if (reg.test(source)) return RegExp.$2.replace(/[x0f]/g, ";"); return "";
};
//根据节点的详细信息生成HTML
//node 树在客户端的节点对象
//AtEnd 布尔值 当前要转换的这个节点是否为父节点的子节点集中的最后一项
MzTreeView.prototype.nodeToHTML = function(node, AtEnd)
{
var source = this.nodes[node.sourceIndex];
var target = this.getAttribute(source, "target");
var data = this.getAttribute(source, "data");
var url = this.getAttribute(source, "url");
if(!url) url = this.url;
if(data) url += (url.indexOf("?")==-1?"?":"&") + data;
if(!target) target = this.target;
var id = node.id;
var HCN = node.hasChild, isRoot = node.parentId=="0";
if(isRoot && node.icon=="") node.icon = "root";
if(node.icon=="" || typeof(this.icons[node.icon])=="undefined")
node.icon = HCN ? "folder" : "file";
node.iconExpand = AtEnd ? "└" : "├";
var HTML = "<DIV noWrap='True'><NOBR>";
if(!isRoot)
{
node.childAppend = node.parentNode.childAppend + (AtEnd ? " " : "│");
if(this.wordLine)
{
HTML += "<SPAN>"+ node.parentNode.childAppend + (AtEnd ? "└" : "├") +"</SPAN>";
if(HCN) HTML += "<SPAN class='pm' id='"+ this.name +"_expand_"+ id +"'>+</SPAN>";
}
else
{
node.iconExpand = HCN ? AtEnd ? "PM2" : "PM1" : AtEnd ? "L2" : "L1";
HTML += "<SPAN>"+ this.word2image(node.parentNode.childAppend) +"<IMG "+
"align='absmiddle' id='"+ this.name +"_expand_"+ id +"' "+
"src='"+ this.icons[node.iconExpand].src +"' style='cursor: "+ (!node.hasChild ? "":
(this.navigator=="msie"||this.navigator=="opera"? "hand" : "pointer")) +"'></SPAN>";
}
}
HTML += "<IMG "+
"align='absMiddle' "+
"id='"+ this.name +"_icon_"+ id +"' "+
"src='"+ this.icons[node.icon].src +"'><A "+
"class='MzTreeview' hideFocus "+
"id='"+ this.name +"_link_"+ id +"' "+
"href='"+ url +"' "+
"target='"+ target +"' "+
"title='"+ node.hint +"' "+
"onfocus=""+ this.name +".focusLink('"+ id +"')" "+
"onclick="return "+ this.name +".nodeClick('"+ id +"')">"+ node.text +
"</A></NOBR></DIV>";
if(isRoot && node.text=="") HTML = "";
HTML = "rn<SPAN id='"+ this.name +"_tree_"+ id +"'>"+ HTML
HTML +="<SPAN style='DISPLAY: none'></SPAN></SPAN>";
return HTML;
};
//在使用图片的时候对 node.childAppend 的转换
MzTreeView.prototype.word2image = function(word)
{
var str = "";
for(var i=0; i<word.length; i++)
{
var img = "";
switch (word.charAt(i))
{
case "│" : img = "L4"; break;
case "└" : img = "L2"; break;
case " " : img = "empty"; break;
case "├" : img = "L1"; break;
case "─" : img = "L3"; break;
case "┌" : img = "L0"; break;
}
if(img!="")
str += "<IMG align='absMiddle' src='"+ this.icons[img].src +"' height='20'>";
}
return str;
}
//将某个节点下的所有子节点转化成详细的<HTML>元素表达
//id 树的客户端节点 id
MzTreeView.prototype.buildNode = function(id)
{
if(this.node[id].hasChild)
{
var tcn = this.node[id].childNodes, str = "";
for (var i=0; i<tcn.length; i++)
str += this.nodeToHTML(tcn[i], i==tcn.length-1);
var temp = this.getElementById(this.name +"_tree_"+ id).childNodes;
temp[temp.length-1].innerHTML = str;
}
};
//聚集到客户端生成的某个节点上
//id 客户端树节点的id
MzTreeView.prototype.focusClientNode = function(id)
{
if(!this.currentNode) this.currentNode=this.node["0"];
var a = this.getElementById(this.name +"_link_"+ id); if(a){ a.focus();
var link = this.getElementById(this.name +"_link_"+ this.currentNode.id);
if(link)with(link.style){color=""; backgroundColor="";}
with(a.style){color = this.colors.highLightText;
backgroundColor = this.colors.highLight;}
this.currentNode= this.node[id];}
};
//焦点聚集到树里的节点链接时的处理
//id 客户端节点 id
MzTreeView.prototype.focusLink= function(id)
{
if(this.currentNode && this.currentNode.id==id) return;
this.focusClientNode(id);
};
//点击展开树节点的对应方法
MzTreeView.prototype.expand = function(id, sureExpand)
{
var node = this.node[id];
if (sureExpand && node.isExpand) return;
if (!node.hasChild) return;
var area = this.getElementById(this.name +"_tree_"+ id);
if (area) area = area.childNodes[area.childNodes.length-1];
if (area)
{
var icon = this.icons[node.icon];
var iconE = this.iconsExpand[node.icon];
var Bool = node.isExpand = sureExpand || area.style.display == "none";
var img = this.getElementById(this.name +"_icon_"+ id);
if (img) img.src = !Bool ? icon.src :typeof(iconE)=="undefined" ? icon.src : iconE.src;
var exp = this.icons[node.iconExpand];
var expE = this.iconsExpand[node.iconExpand];
var expand= this.getElementById(this.name +"_expand_"+ id);
if (expand)
{
if(this.wordLine) expand.innerHTML = !Bool ? "+" : "-";
else expand.src = !Bool ? exp.src : typeof(expE) =="undefined" ? exp.src : expE.src;
}
if(!Bool && this.currentNode.path.indexOf(node.path)==0 && this.currentNode.id!=id)
{
try{this.getElementById(this.name +"_link_"+ id).click();}
catch(e){this.focusClientNode(id);}
}
area.style.display = !Bool ? "none" : "block";//(this.navigator=="netscape" ? "block" : "");
if(!node.isLoad)
{
this.load(id);
if(node.id=="0") return;
//当子节点过多时, 给用户一个正在加载的提示语句
if(node.hasChild && node.childNodes.length>200)
{
setTimeout(this.name +".buildNode('"+ id +"')", 1);
var temp = this.getElementById(this.name +"_tree_"+ id).childNodes;
temp[temp.length-1].innerHTML = "<DIV noWrap><NOBR><SPAN>"+ (this.wordLine ?
node.childAppend +"└" : this.word2image(node.childAppend +"└")) +"</SPAN>"+
"<IMG border='0' height='16' align='absmiddle' src='"+this.icons["file"].src+"'>"+
"<A style='background-Color: "+ this.colors.highLight +"; color: "+
this.colors.highLightText +"; font-size: 9pt'>请稍候...</A></NOBR></DIV>";
}
else this.buildNode(id);
}
}
};
//节点链接单击事件处理方法
//id 客户端树节点的 id
MzTreeView.prototype.nodeClick = function(id)
{
var source = this.nodes[this.node[id].sourceIndex];
eval(this.getAttribute(source, "method"));
return !(!this.getAttribute(source, "url") && this.url=="#");
};
//为配合系统初始聚集某节点而写的函数, 得到某节点在数据源里的路径
//sourceId 数据源里的节点 id
MzTreeView.prototype.getPath= function(sourceId)
{
Array.prototype.indexOf = function(item)
{
for(var i=0; i<this.length; i++)
{
if(this[i]==item) return i;
}
return -1;
};
var _d = this._d, d = this.divider;
var A = new Array(), id=sourceId; A[0] = id;
while(id!="0" && id!="")
{
var str = "(^|"+_d+")([^"+_d+d+"]+"+d+ id +")("+_d+"|$)";
if (new RegExp(str).test(this.names))
{
id = RegExp.$2.substring(0, RegExp.$2.indexOf(d));
if(A.indexOf(id)>-1) break;
A[A.length] = id;
}
else break;
}
return A.reverse();
};
//在源代码里指定 MzTreeView 初始聚集到某个节点
//sourceId 节点在数据源里的 id
MzTreeView.prototype.focus = function(sourceId, defer)
{
if (!defer)
{
setTimeout(this.name +".focus('"+ sourceId +"', true)", 100);
return;
}
var path = this.getPath(sourceId);
if(path[0]!="0")
{
alert("节点 "+ sourceId +" 没有正确的挂靠有效树节点上!rn"+
"节点 id 序列 = "+ path.join(this.divider));
return;
}
var root = this.node["0"], len = path.length;
for(var i=1; i<len; i++)
{
if(root.hasChild)
{
var sourceIndex= path[i-1] + this.divider + path[i];
for (var k=0; k<root.childNodes.length; k++)
{
if (root.childNodes[k].sourceIndex == sourceIndex)
{
root = root.childNodes[k];
if(i<len - 1) this.expand(root.id, true);
else this.focusClientNode(root.id);
break;
}
}
}
}
};
//树的单击事件处理函数
MzTreeView.prototype.clickHandle = function(e)
{
e = window.event || e; e = e.srcElement || e.target;
//alert(e.tagName)
switch(e.tagName)
{
case "IMG" :
if(e.id)
{
if(e.id.indexOf(this.name +"_icon_")==0)
this.focusClientNode(e.id.substr(e.id.lastIndexOf("_") + 1));
else if (e.id.indexOf(this.name +"_expand_")==0)
this.expand(e.id.substr(e.id.lastIndexOf("_") + 1));
}
break;
case "A" :
if(e.id) this.focusClientNode(e.id.substr(e.id.lastIndexOf("_") + 1));
break;
case "SPAN" :
if(e.className=="pm")
this.expand(e.id.substr(e.id.lastIndexOf("_") + 1));
break;
default :
if(this.navigator=="netscape") e = e.parentNode;
if(e.tagName=="SPAN" && e.className=="pm")
this.expand(e.id.substr(e.id.lastIndexOf("_") + 1));
break;
}
};
//MzTreeView 双击事件的处理函数
MzTreeView.prototype.dblClickHandle = function(e)
{
e = window.event || e; e = e.srcElement || e.target;
if((e.tagName=="A" || e.tagName=="IMG")&& e.id)
{
var id = e.id.substr(e.id.lastIndexOf("_") + 1);
if(this.node[id].hasChild) this.expand(id);
}
};
//回到树当前节点的父层节点
MzTreeView.prototype.upperNode = function()
{
if(!this.currentNode) return;
if(this.currentNode.id=="0" || this.currentNode.parentId=="0") return;
if (this.currentNode.hasChild && this.currentNode.isExpand)
this.expand(this.currentNode.id, false);
else this.focusClientNode(this.currentNode.parentId);
};
//展开当前节点并
MzTreeView.prototype.lowerNode = function()
{
if (!this.currentNode) this.currentNode = this.node["0"];
if (this.currentNode.hasChild)
{
if (this.currentNode.isExpand)
this.focusClientNode(this.currentNode.childNodes[0].id);
else this.expand(this.currentNode.id, true);
}
}
//聚集到树当前节点的上一节点
MzTreeView.prototype.pervNode = function()
{
if(!this.currentNode) return; var e = this.currentNode;
if(e.id=="0") return; var a = this.node[e.parentId].childNodes;
for(var i=0; i<a.length; i++){if(a[i].id==e.id){if(i>0){e=a[i-1];
while(e.hasChild){this.expand(e.id, true);
e = e.childNodes[e.childNodes.length - 1];}
this.focusClientNode(e.id); return;} else {
this.focusClientNode(e.parentId); return;}}}
};
//聚集到树当前节点的下一节点
MzTreeView.prototype.nextNode = function()
{
var e = this.currentNode; if(!e) e = this.node["0"];
if (e.hasChild){this.expand(e.id, true);
this.focusClientNode(e.childNodes[0].id); return;}
while(typeof(e.parentId)!="undefined"){
var a = this.node[e.parentId].childNodes;
for(var i=0; i<a.length; i++){ if(a[i].id==e.id){
if(i<a.length-1){this.focusClientNode(a[i+1].id); return;}
else e = this.node[e.parentId];}}}
};
//展开树的所有节点
MzTreeView.prototype.expandAll = function()
{
if(this.totalNode>500) if(
confirm("您是否要停止展开全部节点?rnrn节点过多!展开很耗时")) return;
if(this.node["0"].childNodes.length==0) return;
var e = this.node["0"].childNodes[0];
var isdo = t = false;
while(e.id != "0")
{
var p = this.node[e.parentId].childNodes, pn = p.length;
if(p[pn-1].id==e.id && (isdo || !e.hasChild)){e=this.node[e.parentId]; isdo = true;}
else
{
if(e.hasChild && !isdo)
{
this.expand(e.id, true), t = false;
for(var i=0; i<e.childNodes.length; i++)
{
if(e.childNodes[i].hasChild){e = e.childNodes[i]; t = true; break;}
}
if(!t) isdo = true;
}
else
{
isdo = false;
for(var i=0; i<pn; i++)
{
if(p[i].id==e.id) {e = p[i+1]; break;}
}
}
}
}
};
//本树将要用动的图片的字义及预载函数
//path 图片存放的路径名
MzTreeView.prototype.setIconPath = function(path)
{
var k = 0, d = new Date().getTime();
for(var i in this.icons)
{
var tmp = this.icons[i];
this.icons[i] = new Image();
this.icons[i].src = path + tmp;
if(k==9 && (new Date().getTime()-d)>20)
this.wordLine = true; k++;
}
for(var i in this.iconsExpand)
{
var tmp = this.iconsExpand[i];
this.iconsExpand[i]=new Image();
this.iconsExpand[i].src = path + tmp;
}
};
//设置树的默认链接
//url 默认链接 若不设置, 其初始值为 #
MzTreeView.prototype.setURL = function(url){this.url = url;};
//设置树的默认的目标框架名 target
//target 目标框架名 若不设置, 其初始值为 _self
MzTreeView.prototype.setTarget = function(target){this.target = target;};
// -->
一个简单的示例:
<script language="JavaScript"
src="http://www.meizz.com/Web/Plugs/MzTreeView10.js"></script>
<base href="http://www.meizz.com/Web/">
<style>
A.MzTreeview
{
font-size: 9pt;
padding-left: 3px;
}
</style>
<script language="JavaScript">
var tree = new MzTreeView("tree");
tree.icons["property"] = "property.gif";
tree.icons["css"] = "collection.gif";
tree.icons["book"] = "book.gif";
tree.iconsExpand["book"] = "bookopen.gif"; //展开时对应的图片
tree.setIconPath("http://www.meizz.com/Icons/TreeView/"); //可用相对路径
tree.nodes["0_1"] = "text:WEB 编程";
tree.nodes["1_100"] = "text:代码示例; data:id=100";
tree.nodes["1_200"] = "text:梅花雪脚本控件集; data:id=200";
tree.nodes["1_310"] = "text:CSS; icon:css; data:id=310";
tree.nodes["1_320"] = "text:DHTML; data:id=320";
tree.nodes["1_300"] = "text:HTML; data:id=300";
tree.nodes["1_400"] = "text:JavaScript; icon:book; data:id=400";
tree.nodes["320_322"] = "text:属性; icon: property; data:id=322";
tree.nodes["320_323"] = "text:方法; data:id=323";
tree.nodes["320_324"] = "text:事件; icon:event; data:id=324";
tree.nodes["320_325"] = "text:集合; data:id=325";
tree.nodes["400_407"] = "text:对象; data:id=407";
tree.nodes["400_406"] = "text:方法; data:id=406";
tree.nodes["400_408"] = "text:运算符; data:id=408";
tree.nodes["400_409"] = "text:属性; data:id=409";
tree.nodes["407_1140"] = "text:Date; url:Article.asp; data:id=140";
tree.nodes["406_1127"] = "text:toString; url:Article.asp; data:id=127";
tree.nodes["408_1239"] = "text:||; url:Article.asp; data:id=239";
tree.nodes["409_1163"] = "text:E; url:Article.asp; data:id=163";
tree.setURL("Catalog.asp");
tree.setTarget("MzMain");
document.write(tree.toString()); //亦可用 obj.innerHTML = tree.toString();
</script>
原文出处:http://www.cnblogs.com/wayne-ivan/archive/2008/06/20/1227059.html
发表评论
-
18个Java开源CMS系统一览
2011-11-09 15:49 8151.InfoGlue infoglue是一个 ... -
WEB免费打印控件推荐(转载)
2011-09-14 12:36 1038在WEB系统中,打印的确是个烦人的问题。 要么自己开发打印控件 ... -
spring +jpa(转载)
2011-05-16 10:21 994spring整合jpa dev.firnow.com 时 ... -
关于jbpm4的一些学习心得
2011-04-28 11:14 998前段时间学习jbpm4,也用来做了三个项目。一开始因为没 ... -
读取excel数据转换成为json数据,和把json数据生成到excel文件中
2011-04-28 11:01 4841这个程序是自己做项目的时候,有时需要把一些原始的excel数据 ...
相关推荐
梅花雪控件 1.0 & 2.0 MzTreeView 1.0 是数据一次性加载,客户端节点异步展示的WEB脚本树。MzTreeView 1.0 的理论节点数设计上限为十万节点,在节点数三万的情况下页面打开时间小于 3 秒。无限层次无限节点的数的...
梅花雪树形菜单2.0是一款专为软件开发者设计的用户界面组件,它以其独特的视觉效果和高效的操作体验,使得应用程序的菜单系统更加生动且易于使用。"2.0"代表这是该组件的一个升级版本,通常意味着在功能、性能或用户...
梅花雪树形控件1.0和2.0MzTreeView /*---------------------------------------------------------------------------*\ | Subject: JavaScript Framework | Author: meizz | Created: 2005-02-27 | Version: 2006-...
梅花雪树控件(js版)2.0是一款在前端开发中广泛使用的树形控件,因其高效、灵活和强大的特性而备受青睐。这个控件的设计理念是为了解决网页中展示复杂层级关系数据的问题,尤其适用于需要动态加载和无限级别的场景...
"梅花雪树形菜单2.0带复选框,动态加载,级联选中(CSDN树)"是一个特定的实现,它包含了一些关键特性,下面将详细介绍这些特性。 1. **复选框**:在每个菜单项旁边添加了复选框,用户可以多选或单选菜单项,这在需要...
在压缩包中的“MzTreeView10(梅花雪树控件)”文件可能包含控件的开发库、示例代码、文档资料等,供开发者学习和使用。通过深入研究这些文件,开发者可以更好地理解和应用梅花雪树控件,实现更高效、更美观的数据呈现...
"梅花雪树2.0修改版"是一款针对前端开发的高性能树形组件,尤其适合处理大量数据节点的加载。在原版Mztree2.0的基础上,开发者进行了优化和改进,提高了组件的性能和用户体验。这个修改版的主要特点是能够流畅地一次...
【梅花雪树2.0】是一款专为程序员设计的图形界面控件库,尤其在CSDN平台上有着广泛的应用和讨论。它主要提供了一种树形视图(Tree View)的实现,使得开发者能够轻松地在应用程序中展示层级结构的数据。在Windows...
"梅花雪树形菜单1.0"是一款基于JavaScript编写的动态树形菜单系统,由MzTreeView1.0实现。这个版本的菜单设计简洁而优雅,旨在为网站提供直观、高效的导航体验。开发者通过这款菜单可以轻松构建层级分明、交互性强的...
"梅花雪树形控件CSS+Javascript"是一个专门用于构建树形结构的前端组件,它结合了CSS(层叠样式表)和Javascript的技术,旨在提供美观且功能丰富的图形化界面。在网页设计中,树形控件常用于展示层级关系的数据,如...
为了进一步学习和使用"梅花雪树",可以参考提供的压缩包文件"MzTreeView10",其中可能包含了示例代码、文档、库文件等资源,帮助开发者快速理解和集成这个组件到自己的项目中。通过深入研究这些材料,可以更好地掌握...
结合两者,"超经典的梅花雪树+右键菜单"可能是一个被广泛使用的组件,特别适用于管理具有层次关系的数据,比如文件系统、数据库结构、组织架构或项目任务等。这种设计在权限管理中也很常见,因为它允许根据用户的...
很抱歉,根据您提供的信息,"梅花雪树大全! 经典作品 修改版"似乎是一个与IT技术直接关联性不强的主题,它可能指的是某种艺术、文学或摄影作品的集合,而不是传统的IT知识或软件资源。标签和压缩包子文件的文件名称...
【标题】"梅花雪树(做好的工程)"所指的是一个已经完成开发并可供使用的项目。这个项目的名称可能源于中国传统文化中的元素,"梅花雪树"常常象征坚韧与纯洁,暗示了项目可能具有高雅的设计风格或者寓意着某种技术上...
梅花雪MzTreeView1.0 梅花雪MzTreeView1.0 梅花雪MzTreeView1.0 梅花雪MzTreeView1.0 梅花雪MzTreeView1.0
"改进版梅花雪树"是一种在IT领域中用于图形用户界面(GUI)设计的特定树形控件。这种控件通常被用在软件或应用程序中,以显示层次结构的数据,如文件系统、组织架构或者菜单选项。在描述中提到的“梅花雪树”可能是...
【标题】"梅花雪树js文件下载"涉及的是一个JavaScript库或者项目,名为"梅花雪树"。在JavaScript编程领域,库和项目通常用于提供特定功能或简化开发流程。"梅花雪树"可能是专为某种特定用途设计的,比如数据可视化、...
MzTreeView 梅花雪树形菜单通用版,MzTreeView 1.0 是数据一次性加载,客户端节点异步展示的WEB脚本树。MzTreeView 1.0 的理论节点数设计上限为十万节点,在节点数三万的情况下页面打开时间小于 3 秒。无限层次无限...
本篇文章将聚焦于三种特定的Java树控件:DTree、RTree以及梅花雪树,并探讨它们的特性、使用场景和实现方式。 首先,DTree(Dynamic Tree)是一种动态的树形控件,它允许程序在运行时动态地添加、删除和修改节点。...