使用XML/HTC/DHTML模拟标准Windows菜单

翻译|其它|编辑:郝浩|2004-02-19 20:15:00.000|阅读 1383 次

概述:

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>

随着internet的发展,XML作为一种跨平台的通用结构化数据描述语言越来越得到人们的重视,并已经得到了广泛应用,如 MicroMedia公司出品的Dreamweaver、Flash以及游戏抢滩登录等软件都利用了XML文件作为数据存储方式,而且Microsoft.NET也是架构在XML上 面的。目前出现的取代HTML语言的下一代网页制作语言XHTML(可扩展超文本标记语言),就是建立在XML基础上,因此掌握XML技术是未来网页 制作者必备技能。

本文通过一个模拟标准Windows菜单程序来介绍有关XML技术应用。

一、 程序原理
考虑到层叠菜单复杂性,为使程序简化,只模拟到二级子菜单。下面我们来看看标准windows菜单过程:鼠标移入菜单时突出显示,移出时 回复原状,点击则菜单凹陷显示并打开子菜单,鼠标点击子菜单则完成相应功能。当子菜单打开后移动鼠标,则移入菜单凹陷显示并打开相应 子菜单,移出菜单则回复原状并隐藏相应子菜单,若鼠标并不是移动到另外菜单而是在页面其它地方,则移出菜单仍凹陷显示,相应子菜单也 不隐藏,只有当在非菜单处点击时才恢复原状并隐藏子菜单。标准Windows菜单一个重要特点就是子菜单打开后,在鼠标移出菜单时并不隐藏或 者在文档中非菜单处任意单击时才隐藏。目前很多网页菜单程序都做不到这点,一般都是移入菜单时打开子菜单,移出菜单时隐藏子菜单。采 用IE5.0中新提供的鼠标捕获技术就可以完全解决这个问题。所谓鼠标捕获,就是与鼠标相关的所有事件都由设定鼠标捕获的对象进行处理,而 无论这些事件是否由该对象触发。

由于xml文档非常适合描述结构化数据,故我们使用xml文档存储菜单及一级子菜单的有关信息。

为便于在其它页面使用模拟标准Windows菜单,我们并不使用代码功能重用性能比较差的外部脚本包含文件方法,而是采用IE 5.0新引入的DHTML行为(Behavior)技术将菜单功能代码封装在一个HTML 行为组件(HTC)内。 HTML行为组件封装了页面上特定的功能或动作,当把某个行为组件附加到页面上的标准HTML元素(对象)时,将增强该元素(对象)的默认功能, 使得该元素(对象)含有行为的属性、方法或事件,这使代码重用变得十分容易,并简化了页面的HTML代码,提高了页面的可管理性。

因篇幅所限,有关xml文档及HTML行为组件的更多细节请参阅有关资料,本文不予详述。

二、程序实现

1.主页面(index.htm)
我们首先在主页面中定义充当菜单容器的Div元素,并使用CSS中的behavior属性附加行为组件(Menu.htc),这样就可以使用元素扩充的属 性xmlsrc来指定存储菜单信息的数据文件(Menu.xml)以及使用onMenuClick来捕获组件触发的新事件并调用相应事件处理函数。

此外为简化程序,在主页面中定义了突出(Up_Menu)、凹陷(Dn_Menu)、原状(Menu)三种菜单外观样式,在实际应用时应在组件中定义菜单显 示外观,这样更符合组件开发原则。

2.菜单组件(Menu.htc)
模拟标准Windows菜单的功能主要由菜单组件提供。菜单由以下Html元素组成:充当菜单容器的Div元素(在主页面定义)、用于一级菜单项 的span元素(初始化菜单时创建)、包含二级菜单项的Div元素(单击一级菜单时建立)、二级菜单项的Div元素(单击一级菜单时建立)。在该组件 中定义了用于指定菜单数据文件的xmlsrc属性、点击二级菜单时触发的onMenuClick事件。

当主页面解析完毕后触发ondocumentready事件,组件捕获这个事件并调用事件处理MenuInit()函数初始化一级菜单。当触发鼠标事件时, 如onmouseover、onmouseout、onmouseclick等,组件将分别捕获这些事件并调用相应的事件处理函数MenuOver()、MenuOut()、MenuClick()。 在这些处理函数中首先通过Event.srcElement属性确定触发事件的对象,然后依据对象type类型(如parentMenu、subMenu或其它)分别做相应处 理,如更改菜单显示外观、打开或隐藏子菜单、触发新事件等。

在一级菜单触发有关鼠标事件后,打开二级菜单并通过setCapture()方法对一级菜单对象设置鼠标捕获。此后所有在该菜单或者文档任意位 置触发的onmouseover、onmouseout、onmouseclick鼠标事件都由该菜单对象负责处理,但event.srcElement属性仍是触发鼠标事件的对象而不 是设置鼠标捕获的对象。当在二级子菜单或文档中非菜单处任意单击时,隐藏二级菜单并通过releaseCapture()方法释放鼠标捕获。

单击二级菜单时,其事件处理函数onmouseclick通过createEventObject方法创建一个event(事件)对象,再通过fire方法触发新事件,并利 用event对象的有关属性传递信息(如event.menuId返回所选定的菜单ID),然后由容器捕获这个事件,并调用相应事件处理函数HanderSubMenu ()。在主页面中定义函数HanderSubMenu ()能够使菜单组件更具通用性。

3.菜单数据文件(Menu.xml)
Item节点存储一级菜单信息,SubItem节点存储二级菜单信息,每个节点都含有两个属性:value(菜单ID)、name(菜单名称)。

该程序必须在IE5.0及以上版本浏览器运行,运行结果如图。怎么样?象不象标准Windows菜单。

附:模拟标准Windows菜单源代码(在Win98+IE5.0下通过)
附:模拟标准Windows菜单源代码(在Win98+IE5.0下通过)

index.htm文件:


<Html>
<Head>
<Title>菜单组件示例</Title>
<style>
<!--
.Menu{ 
border-top:2 solid #CCCCCC;border-bottom:1 solid #CCCCCC;
border-left:2 solid #CCCCCC;border-right:1 solid #CCCCCC; }
.Up_Menu { border-top:2 outset;border-left:2 outset;
border-right:1 outset #FFFFFF;border-bottom:1 outset #FFFFFF; 

background-color:#CCCCCC; }
.Dn_Menu{ border-top:2 inset ;border-left:2 inset ;
border-right:1 inset ;border-bottom:1 inset ;background-color:#CCCCCC; }
-->
</style>
<script>
function HanderSubMenu(){//选择子菜单后相应处理
  switch(event.menuId){
    case event.menuId:
      alert(event.menuId);
      break;
  }
}
</script>
</Head>
<Body style="background-color:#CCCCCC;">
<TABLE style="font-size:12px;"> 
  <TBODY><TR height="10" >
    <TD Align="center" height=25 style="border-top:2px groove;border-bottom:2px groove;">
      <div id="Menu" style="behavior:url(Menu.htc)" xmlsrc="Menu.xml" 
onMenuClick="HanderSubMenu()"></div> 
    </TD>                   
  </TR>
  </TBODY>
</TABLE>
</Body>
</Html>

Menu.htc文件:


<PUBLIC:COMPONENT>
<PUBLIC:PROPERTY Name="xmlsrc" PUT="putxmlsrc"/>
<PUBLIC:EVENT Name="onMenuClick" ID="menuClick" />
<PUBLIC:ATTACH EVENT="ondocumentready" For="element" ONEVENT="MenuInit()" />
<PUBLIC:ATTACH EVENT="onmouseover"  for="element" ONEVENT="MenuOver()" />
<PUBLIC:ATTACH EVENT="onmouseout" for="element" ONEVENT="MenuOut()" />
<PUBLIC:ATTACH EVENT="onclick"  for="element" ONEVENT="MenuClick()" />
<Script language="JavaScript">
var xmldoc=null;// xml文档对象变量
var activeMenu=null;//打开子菜单的父菜单对象变量
var menuContainer=null;//包含子菜单项容器对象变量
function putxmlsrc(str){//载入菜单数据文件
  xmldoc= new ActiveXObject("Microsoft.XMLDOM");//创建xml文档对象
  xmldoc.async=false;
  xmldoc.load(str);//载入菜单数据
}
function MenuInit(){//初始化菜单
  var parentMenuItems = xmldoc.selectNodes("//Itemlist/Item"); //读取一级菜单数据    
  var xmlElement = parentMenuItems.nextNode(); 
  var newElement;     
  while (xmlElement != null){     
    newElement = document.createElement("span"); //创建菜单元素
    newElement.type = "parentMenu";    
    with (newElement){
      innerHTML = xmlElement.getAttribute("name"); 
      id=xmlElement.getAttribute("value"); 
      className="Menu";      
    }     
    with (newElement.style){     
      position="relative";
      width= 60;
      cursor="default";
    }     
    element.appendChild(newElement); //element指附加行为的元素     
    xmlElement = parentMenuItems.nextNode();       
  }
}
function MenuOver(){
  var EventSource=event.srcElement;//捕获触发事件的对象
  switch(activeMenu){//判断子菜单是否存在
    case null://不存在子菜单
      if(EventSource.type=="parentMenu")EventSource.className="Up_Menu";
      break;
    default:
      switch(EventSource.type){//判断触发事件对象类型
        case  "parentMenu"://移入到一级菜单
          if(activeMenu!=EventSource){//判断是否移入到新一级菜单
            removeSubMenu();//删除原有子菜单
            buildSubMenu(EventSource.id);//建立新子菜单
            EventSource.setCapture();//对新菜单设置鼠标捕获
            activeMenu.className="Menu";
            activeMenu=EventSource; //更新打开子菜单的父菜单对象变量
            activeMenu.className="Dn_Menu";
            }
          break;
        case "subMenu"://移入到子菜单
          EventSource.style.color="#FF0000";
          EventSource.style.textDecoration="underline";
          break;
      }
      break;
  }
}
function MenuOut(){
  EventSource=event.srcElement         
  switch(activeMenu){
    case null:
      if(EventSource.type=="parentMenu")EventSource.className="Menu";
      break;
    default:
      if(EventSource.type=="subMenu"){
        EventSource.style.color="#000000";
        EventSource.style.textDecoration="none";
      }
      break;
  }
}
function MenuClick(){
  EventSource=event.srcElement
  switch(EventSource.type){
    case "parentMenu":
      if(activeMenu!=EventSource){
        removeSubMenu();
        buildSubMenu(EventSource.id);
        EventSource.setCapture();
        EventSource.className="Dn_Menu";
        activeMenu=EventSource;
      }
      break;
    case "subMenu":
      removeSubMenu();
      activeMenu.className="Menu";
      activeMenu.releaseCapture();//释放鼠标捕获设置
      activeMenu=null;
      var eventObject = createEventObject();//创建事件对象
      eventObject.menuId = EventSource.id;//通过事件对象属性传递所选子菜单ID
      menuClick.fire(eventObject);//将事件触发到容器页面
      break;  
    default:
      removeSubMenu();
      if(activeMenu!=null){
        activeMenu.releaseCapture();
        activeMenu.className="Menu";
        activeMenu=null;
       } 
      break;
  } 
}
function buildSubMenu(EventSourceid){//建立子菜单
  menuContainer = document.createElement("div");
  with (menuContainer.style){ 
    position="absolute";
    left=0;
    top=15;
    color="#000000";
    cursor="default";
  }
  eval(EventSourceid).appendChild(menuContainer);
  var subMenuItems = xmldoc.selectNodes("//Item[@value='"+EventSourceid+"']/SubMenu");
  var xmlElement = subMenuItems.nextNode();  
  var newElement;
  while (xmlElement != null){      
    newElement = document.createElement("div");      
    newElement.type = "subMenu";
    with (newElement){      
      innerHTML ="□ "+xmlElement.getAttribute("name");      
      id=xmlElement.getAttribute("value");
    }      
    with (newElement.style){
      width=75;
      height=18;
    }      
    menuContainer.appendChild(newElement);
    menuContainer.className="Up_Menu";       
    xmlElement = subMenuItems.nextNode();        
  }
}
function removeSubMenu(){//删除子菜单
  if(menuContainer!=null)menuContainer.removeNode(true);
}
</Script>
</Component>

Menu.xml文件:


<?xml version="1.0" encoding="gb2312" ?>//xml处理指令指明为中文编码,若不指明则不支持中文
<Itemlist>
  <Item value="file" name="文件" > 
    <SubMenu value="New" name="新建"></SubMenu>
    <SubMenu value="Open" name="打开"></SubMenu>
    <SubMenu value="Save" name="保存"></SubMenu>
    <SubMenu value="Print" name="打印"></SubMenu>
    <SubMenu value="Exit" name="退出"></SubMenu>
  </Item> 
  <Item value="Edit" name="编辑" >
    <SubMenu value="Cut" name="剪切"></SubMenu>
    <SubMenu value="Copy" name="复制"></SubMenu>
    <SubMenu value="Paste" name="粘贴"></SubMenu>
    <SubMenu value="Del" name="删除"></SubMenu>
  </Item>
  <Item value="Help" name="帮助">
    <SubMenu value="Topic" name="主题"></SubMenu>
    <SubMenu value="About" name="关于"></SubMenu>
  </Item>
</Itemlist>


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com


为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP