1.1 概述
高效地利用Web页面有限的空间并不容易,特别是要在页面中安排大量的链接时尤为困难。如何才能组织好各种链接以便为其它重要内容留出空间?是一次性地展示所有链接还是分成多个页面把它们深深地隐藏起来?显然,这两种方法都不理想。利用DHTML,我们可以在为用户提供快速方便的访问链接的同时,为其它内容保留足够的页面空间。
本文介绍一个菜单系统的实现。这个菜单与Windows的“开始”菜单非常相似,用户只需点击一次鼠标即可访问所有链接。菜单的内容由XML文档定义,客户端行为由DHTML实现。XML文档的解析在服务器端完成,因此对用户浏览器是否支持XML并没有要求。
本文接下来的内容具体介绍其实现,包括XML文档的结构以及如何分析这个XML文档、配合客户端JavaScript一起构造出最终的菜单。
1.2 静态和动态
页面内容动态生成是指在用户请求时才动态地(自动地)生成内容,静态内容则是将自动生成的内容保存为文件,用户请求的时候发送给他们的是这些静态的文件。有读者曾经来信指出这个问题,我们在《再论Web内容的发布——用XML与ASP分离内容与设计》中转发了读者来信并谈了自己的看法。
依赖于菜单的实际大小和菜单内容变化的频繁程度,有些时候可能不想让菜单动态生成。这时候可以将菜单代码做成静态的:运行本文介绍的脚本,将所生成的菜单HTML代码保存为包含文件再直接引用就可以了。如果可以采用这个方案的话,它将节省大量宝贵的服务器资源。
本文以动态菜单为例具体介绍实现原理。不过为了方便读者,在下载包中我们提供了两个版本的服务器脚本,分别用于动态生成菜单和生成静态的菜单HTML代码。使用静态版本时,先打开xmlCreateMenu_Entry.asp页面,在这里输入如下两个信息:第一是所使用的XML文件,第二是包含文件的路径,这个包含文件将保存HTML格式的菜单代码。输入上述信息后即可自动运行服务器脚本生成菜单代码。以后如果要改变菜单内容,只要修改XML文件,然后再重复上述过程就可以了。
使用静态菜单时,参考本文default.asp中的动态菜单使用方法,只需将default.asp中的< !--#include file="XMLCreateMenu_Dynamic.asp"-- >改成包含静态菜单文件即可。
1.3 数据结构
XML无疑是当前最为热门的话题之一,人们普遍相信未来的Web就在于XML。不过本文使用XML来定义菜单内容并非是为了赶时髦,在这里使用XML确实有其特别的好处。
首先,既然使用了菜单系统,那么它就不大可能只用在一、二个页面上,往往是整个网站的所有页面都要用到同一菜单。这就要求能够快速地构造菜单,尽量地减少访问菜单定义数据所消耗的时间。第二,菜单是一个层次结构,定义菜单内容的数据最好也具有层次结构。这不仅可以方便地体现出各个菜单项之间的从属关系(从而有利于修改),而且在本文后面我们还可以看到,用递归函数来分析层次结构的数据是相当方便的。
XML可以很好地满足这两个要求,不仅数据可以快速地访问(无需额外的网络连接,无需登录数据库等),而且XML能够非常自然地描述出数据的层次结构。此外,前面我们已经提到,XML文档的解析在服务器端完成,因此并不要求用户浏览器支持XML。
编辑XML文档既可以使用普通的文本编辑器,也有许多专用的XML编辑器可供选择,有关这方面的内容以及XML基础概念的说明,请参见《XML简明教程》,也请参见XML Tools。
创建菜单系统的第一步是在XML文档中定义菜单内容。在考虑了几种可能的方法之后,我们决定采用最简单的一种方法,使得不太熟悉XML的读者也能理解和更改这个XML文件。每一个菜单项至少包含两个数据,即菜单项的文本和链接;如果菜单项拥有多个子菜单,可以很方便地加入子节点。为保持整个系统的简洁性,菜单最多不能超过三层。下面是本文XML文件的一个片断。
< menuItem >
< hyperLink/ >
< name >娱乐休闲< /name >
< menuItem >
< hyperLink >Link1.asp< /hyperLink >
< name >游戏< /name >
< /menuItem >
< menuItem >
< hyperLink >link4.asp< /hyperLink >
< name >汽车/摩托车< /name >
< /menuItem >
< /menuItem >
每一个菜单项由一个< menuItem >标记定义,它至少有两个子节点:子节点< hyperLink >定义的是该菜单项的链接,而< name >包含该菜单项文本。
可以看到,前面代码中的第一个< hyperLink >没有给出链接,这是因为这个菜单项用来弹出子菜单,它并不指向任何具体的Web页面。子节点的定义方法和父节点定义方法相同,使用的都是< menuItem >标记。这种分层结构可以一直嵌套下去,但我们已经提到,嵌套层次不宜过多,菜单系统的唯一目的应该是方便用户访问,而不是使得访问更加困难。 2.1 遍历XML树
在XML文档中定义了菜单内容后,接下来的任务是遍历和分析这些层次结构的数据、生成菜单的HTML代码。这主要通过一个递归函数walkTree实现。这部分代码的效率非常重要,不能为了生成菜单而让用户等待太长的时间。
在调用递归函数walkTree之前,我们首先要找出XML文档的根。这个根可以利用以下代码得到(完整的代码请从本文后面下载):
var XMLRoot = XMLDoc.documentElement;
在得到文档的根后,接下来程序创建一个数组来描述菜单项之间的层次关系。如下面的代码所示,这个数组以JavaScript的eval函数动态命名。变量startMenu决定完整菜单名字的基础部分,从本文后面我们还可以看到,客户端代码也要利用该菜单名字来激活菜单。
var image = "< img align=right vspace=2 height=10
width=10 border=0 src='images/tri.gif' >";
var currentNode = "";
var newNode = "";
var currentMenu = "";
var startMenu = "menu1";
var level = 1;
var arrayHolderArray = new Array();
var arrayNamesArray = new Array();
var tempString = "";
//数组的名字为DIV名字加"_Array"
eval("var " + startMenu + "_Array = new Array()")
for (var i=0;i< XMLRoot.childNodes.length;i++) {
currentNode = XMLRoot.childNodes.item(i);
if (currentNode.hasChildNodes() == true && currentNode.childNodes.length >1) {
currentMenu = startMenu + "_" + (i+1);
thisMenu = startMenu;
if (currentNode.childNodes.length >2) {
sMenuItem = escape("< SPAN id="" + thisMenu + "_span" + (i+1) +
" false;
}
stateChange()函数有三个参数:菜单名字,当前菜单项的< SPAN >元素,以及层次。如果用户选择了一个不同的< SPAN >元素(菜单项),stateChange()改变该菜单项的背景和文本颜色,同时改变以前选中菜单项的状态。此外,stateChange()还负责调用其他函数隐藏菜单以及在菜单位置不合适时,修改菜单的位置。
菜单所用的样式在menuStyle.css中,修改该文件可调整菜单的外观。
请从这里下载本文代码,8 K。
高效地利用Web页面有限的空间并不容易,特别是要在页面中安排大量的链接时尤为困难。如何才能组织好各种链接以便为其它重要内容留出空间?是一次性地展示所有链接还是分成多个页面把它们深深地隐藏起来?显然,这两种方法都不理想。利用DHTML,我们可以在为用户提供快速方便的访问链接的同时,为其它内容保留足够的页面空间。
本文介绍一个菜单系统的实现。这个菜单与Windows的“开始”菜单非常相似,用户只需点击一次鼠标即可访问所有链接。菜单的内容由XML文档定义,客户端行为由DHTML实现。XML文档的解析在服务器端完成,因此对用户浏览器是否支持XML并没有要求。
本文接下来的内容具体介绍其实现,包括XML文档的结构以及如何分析这个XML文档、配合客户端JavaScript一起构造出最终的菜单。
1.2 静态和动态
页面内容动态生成是指在用户请求时才动态地(自动地)生成内容,静态内容则是将自动生成的内容保存为文件,用户请求的时候发送给他们的是这些静态的文件。有读者曾经来信指出这个问题,我们在《再论Web内容的发布——用XML与ASP分离内容与设计》中转发了读者来信并谈了自己的看法。
依赖于菜单的实际大小和菜单内容变化的频繁程度,有些时候可能不想让菜单动态生成。这时候可以将菜单代码做成静态的:运行本文介绍的脚本,将所生成的菜单HTML代码保存为包含文件再直接引用就可以了。如果可以采用这个方案的话,它将节省大量宝贵的服务器资源。
本文以动态菜单为例具体介绍实现原理。不过为了方便读者,在下载包中我们提供了两个版本的服务器脚本,分别用于动态生成菜单和生成静态的菜单HTML代码。使用静态版本时,先打开xmlCreateMenu_Entry.asp页面,在这里输入如下两个信息:第一是所使用的XML文件,第二是包含文件的路径,这个包含文件将保存HTML格式的菜单代码。输入上述信息后即可自动运行服务器脚本生成菜单代码。以后如果要改变菜单内容,只要修改XML文件,然后再重复上述过程就可以了。
使用静态菜单时,参考本文default.asp中的动态菜单使用方法,只需将default.asp中的< !--#include file="XMLCreateMenu_Dynamic.asp"-- >改成包含静态菜单文件即可。
1.3 数据结构
XML无疑是当前最为热门的话题之一,人们普遍相信未来的Web就在于XML。不过本文使用XML来定义菜单内容并非是为了赶时髦,在这里使用XML确实有其特别的好处。
首先,既然使用了菜单系统,那么它就不大可能只用在一、二个页面上,往往是整个网站的所有页面都要用到同一菜单。这就要求能够快速地构造菜单,尽量地减少访问菜单定义数据所消耗的时间。第二,菜单是一个层次结构,定义菜单内容的数据最好也具有层次结构。这不仅可以方便地体现出各个菜单项之间的从属关系(从而有利于修改),而且在本文后面我们还可以看到,用递归函数来分析层次结构的数据是相当方便的。
XML可以很好地满足这两个要求,不仅数据可以快速地访问(无需额外的网络连接,无需登录数据库等),而且XML能够非常自然地描述出数据的层次结构。此外,前面我们已经提到,XML文档的解析在服务器端完成,因此并不要求用户浏览器支持XML。
编辑XML文档既可以使用普通的文本编辑器,也有许多专用的XML编辑器可供选择,有关这方面的内容以及XML基础概念的说明,请参见《XML简明教程》,也请参见XML Tools。
创建菜单系统的第一步是在XML文档中定义菜单内容。在考虑了几种可能的方法之后,我们决定采用最简单的一种方法,使得不太熟悉XML的读者也能理解和更改这个XML文件。每一个菜单项至少包含两个数据,即菜单项的文本和链接;如果菜单项拥有多个子菜单,可以很方便地加入子节点。为保持整个系统的简洁性,菜单最多不能超过三层。下面是本文XML文件的一个片断。
< menuItem >
< hyperLink/ >
< name >娱乐休闲< /name >
< menuItem >
< hyperLink >Link1.asp< /hyperLink >
< name >游戏< /name >
< /menuItem >
< menuItem >
< hyperLink >link4.asp< /hyperLink >
< name >汽车/摩托车< /name >
< /menuItem >
< /menuItem >
每一个菜单项由一个< menuItem >标记定义,它至少有两个子节点:子节点< hyperLink >定义的是该菜单项的链接,而< name >包含该菜单项文本。
可以看到,前面代码中的第一个< hyperLink >没有给出链接,这是因为这个菜单项用来弹出子菜单,它并不指向任何具体的Web页面。子节点的定义方法和父节点定义方法相同,使用的都是< menuItem >标记。这种分层结构可以一直嵌套下去,但我们已经提到,嵌套层次不宜过多,菜单系统的唯一目的应该是方便用户访问,而不是使得访问更加困难。 2.1 遍历XML树
在XML文档中定义了菜单内容后,接下来的任务是遍历和分析这些层次结构的数据、生成菜单的HTML代码。这主要通过一个递归函数walkTree实现。这部分代码的效率非常重要,不能为了生成菜单而让用户等待太长的时间。
在调用递归函数walkTree之前,我们首先要找出XML文档的根。这个根可以利用以下代码得到(完整的代码请从本文后面下载):
var XMLRoot = XMLDoc.documentElement;
在得到文档的根后,接下来程序创建一个数组来描述菜单项之间的层次关系。如下面的代码所示,这个数组以JavaScript的eval函数动态命名。变量startMenu决定完整菜单名字的基础部分,从本文后面我们还可以看到,客户端代码也要利用该菜单名字来激活菜单。
var image = "< img align=right vspace=2 height=10
width=10 border=0 src='images/tri.gif' >";
var currentNode = "";
var newNode = "";
var currentMenu = "";
var startMenu = "menu1";
var level = 1;
var arrayHolderArray = new Array();
var arrayNamesArray = new Array();
var tempString = "";
//数组的名字为DIV名字加"_Array"
eval("var " + startMenu + "_Array = new Array()")
for (var i=0;i< XMLRoot.childNodes.length;i++) {
currentNode = XMLRoot.childNodes.item(i);
if (currentNode.hasChildNodes() == true && currentNode.childNodes.length >1) {
currentMenu = startMenu + "_" + (i+1);
thisMenu = startMenu;
if (currentNode.childNodes.length >2) {
sMenuItem = escape("< SPAN id="" + thisMenu + "_span" + (i+1) +
" false;
}
stateChange()函数有三个参数:菜单名字,当前菜单项的< SPAN >元素,以及层次。如果用户选择了一个不同的< SPAN >元素(菜单项),stateChange()改变该菜单项的背景和文本颜色,同时改变以前选中菜单项的状态。此外,stateChange()还负责调用其他函数隐藏菜单以及在菜单位置不合适时,修改菜单的位置。
菜单所用的样式在menuStyle.css中,修改该文件可调整菜单的外观。
请从这里下载本文代码,8 K。
