订阅 漓筝轩 的RSS 

使用window.createPopup创建无限级跨帧下拉菜单

我的BLOG搬家到自己的站点
站点链接
RSS
  先帖样子
    
使用层或者其他技术所实现的JS菜单不能解决的问题就是这些菜单不能跨帧,也就是说在Frame之间的时候无可奈何,所幸的是IE5+提供了createPopup这个函数,可以提供窗口的创建,使用createPopup需要注意以下几个问题:
1、函数没有任何参数
2、CreatePopup函数返回的值是新窗口的句柄,这个窗口和普通的窗口一样,所有该有的东西都有。
3、新窗口的parent属性可以对父级窗口(调用createPopup函数的窗口进行访问)
4、一个窗口只能创建一个popup窗口,新调用createPopup将会吧以前的窗口关闭。
5、新窗口的内容初始的时候是空的,没有任何内容,使用document.write和document.body.innerHTML设置值
6、窗口显示的时候调用popwindow.show函数,调用契约为 show(left,top, width, height, document.body);最后一个参数指明位置属性相对的对象
7、窗口隐藏直接调用hide函数。
8、在父级窗口中点击鼠标将会自动将popwindow隐藏。
9、销毁父亲窗口不一定销毁其创建的popwindow,前提是保存窗口句柄的对象是否被销毁。
10、使用alt-tab转换窗口的时候,有时这些新窗口会悬浮在桌面顶层,不会随IE窗口转到后台而隐藏(IE6、IE7都是)
11、新窗口的对象不能使用父窗口的CSS风格,需要手工复写
12、新窗口中的链接(Anchors)需要注意点击之后链接显示的窗口是当前的窗口,一般无效。
13、新窗口中的JavaScript出现错误的时候并不会在当前的IE状态栏中提示!
14、新窗口的CSS风格不支持expression,晕!
15、显示窗口的时候(调用show函数)必须指定窗口的位置和大小,尤其是大小,新窗口可不能自动进行缩放!

实现跨帧菜单首先确定显示方式,每一级的菜单都是显示在一个Popwindow中,如前所述,在一个窗口中只能有一个popwindow,如图显示二级菜单就无法显示了,如何解决这个问题呢?
刚才我们提到,popwindow对象本身就是一个完整的窗口对象,要解决这个问题的最直接的办法就是,下级菜单的生成有父亲菜单所在的窗口(不管是主窗口还是popwindow)调用createPopup生成,这样,各级菜单都可以拥有自己的popwindow,而且可以自动的在同级菜单中进行切换,具体的脚本如下

var ele=control==null?event.srcElement:$(control);
/*...*/
var popw=ele.document.parentWindow.window.createPopup();
/*...*/

层次结构可以构造菜单的基本样式,刚才提到,popwindow不支持CSS风格,也就是说需要手工将CSS风格写到popwindow中,写CSS风格可以使用document.write方式或者直接构造styleSheet对象然后插入rule的方式(注意,直接使用document.body.innerHTML写的style标签好像没有生效),我采用前者,主要原因是,我在主窗口中配置菜单的显示风格,而后将这些显示css风格的文本信息直接保存下来,然后对每个新窗口进行写操作,这样就可以保持每级菜单的CSS风格完全一致。
读取主窗口CSS的代码如下

FrameMenuConfig.CssText="";
    for(var n=0;FrameMenuConfig.CssPrefix!=null && FrameMenuConfig.CssPrefix.length>0 && n<document.styleSheets.length;n++)
    {
      var sts=document.styleSheets[n];
      for(var x=0;x<sts.rules.length;x++)
      {
        var rr=sts.rules[x];
        if(rr.selectorText.indexOf(FrameMenuConfig.CssPrefix)>=0)
        {
          FrameMenuConfig.CssText+=rr.selectorText+"{"+rr.style.cssText+"}";
        }
      }
    }

这里使用的是匹配FrameMenuConfig.CssPrefix的CSS风格才写入到新的窗口中。

然后涉及到菜单的数据结构的定义,这个定义比较简单,就不扯了,用膝盖也能想出来。我这边处理的时候为了防止函数被重复定义,使用了简单的类静态函数的方式进行定义。

到现在为止,我们可以画出一层一层的菜单,在每层的菜单项上挂接onmouseover处理函数就可以自动弹出下级菜单,一切看起来已经完成了。

呵呵,好像还有点东西,菜单的链接有问题,如何解决在新窗口中的Anchor链接指向的页面在我们指定的框架中显示?首先还是要强调,每个popupwindow都是一个window对象,使用parent可以取得上级的对象,我的处理方法是在主窗口中定义了一个goto(url,target)的函数,这个函数负责在主窗口中将URL正确的进行跳转,带出来的问题是,怎么让popwindw正确的调用这个函数,第一层菜单使用parent.goto,第二层菜单使用parent.parent.goto,第三层使用.....Embarassed

最后一个就是解决alt-tab的问题,这个问题说起来也简单,当alt-tab处理的时候隐藏IE窗口会触发document.onfocusout事件,在这个事件中对所有的popwindow 进行关闭即可,实际测试的时候,却发现如果不对IE窗口的内容进行点击操作(鼠标划过不算),不会使得document取得焦点,也就无法触发onfocusout事件,简单的做法是生成菜单之后调用一下document.focus()函数,这个函数可能会将焦点移动,所以不是很好,但是找不到解决的办法了 

没啥藏着掖着的,源代码下载test.rar (7.97 kb)

以下是测试代码,包含鼠标悬停、自动创建和右键菜单,详细请参考压缩包中的东西


<style type="text/css">
  #fm_MainContainer
  {
    width:100%;
    height:20px;
    border:solid menu 1px;
    background-color:ghostwhite;
    padding:3px;
    font-size:10pt;
    color:menu;
  }
  #fm_MainContainer a
  {
    padding-left:15px;
    padding-right:15px;
    border-left:solid 2px #104E8B;
    border-right:solid 1px #104E8B;
    text-decoration=none;
    color:blue;
    font-size:10pt;
    font-weight:bold;
    background-color:;
  }
  #fm_MainContainer a:hover
  {
    text-decoration=underline;
    color:red;
    background-color:yellow;
  }
  #fm_Container
  {
    background-color:#E8E8E8;
    height:19px;    
    cursor:hand;
    width:150;
    padding-right:3px;
    border-bottom:solid 1px menu;
    border-left:solid 5px #B0C4DE;    
  }
  #fm_Container a
  {
    padding-left:15px;
    padding-right:15px;
    font-size:10pt;
    text-decoration=none;
    color:blue;
    font-weight:normal;    
  }
  #fm_Container a:hover
  {
    text-decoration=underline;
    color:red;
    background: url(goto.png) no-repeat;
  }
</style>
<script type="text/javascript" src="../framemenu.js"></script>
<script type="text/javascript">
  //系统生成的菜单如果包含有下级菜单在菜单项的左边显示的图片
  FrameMenuConfig.FolderImage="leftbtn.png";
  //系统菜单生成的结构为<div ><a>...</a></div>,此处设置div的显示风格,a的显示风格请附带在div中设置
  FrameMenuConfig.CssPrefix="#fm_Container";
  //insert函数直接插入一格记录,格式为 父亲代码、节点代码、节点显示文本、节点URL、节点的目标框架,目标框架支持_self和_blank.
  FrameMenu.insert(null,"a","滚动规划");
  //add2函数插入一个菜单,返回这个菜单的父亲节点的实例,格式为 节点显示文本、节点URL、节点的目标框架,目标框架支持_self和_blank.节点的ID自动生成
  FrameMenu.insert(null,"b","立项管理").add2("立项1").add2("立项2");
  //add函数插入一个菜单,返回这个新的菜单的实例,格式为 节点显示文本、节点URL、节点的目标框架,目标框架支持_self和_blank.节点的ID自动生成
  FrameMenu.insert(null,"c","工程实施").add("工程实施1").add("工程实施1_1");
  FrameMenu.insert(null,"d","验收管理").add2("测试用例").add2("测试用例");
  FrameMenu.insert(null,null,"系统菜单").add2("用户管理").add2("测试用例").add2("测试用例").add("角色权限").add2("角色授权").add2("用户授权").add2("资源授权");
  FrameMenu.insert(null,null,"帮助系统").add2("关于本系统").add2("退出系统");
  FrameMenu.insert("a","a1","本地文件","localfile.htm","body");
  FrameMenu.insert("a","a12","新浪网(弹出窗口)","http://wwww.sina.com","_blank");
  FrameMenu.insert("a","a13","多级菜单");
  FrameMenu.insert("a13","a13_1","新浪网(本窗口)","http://www.sina.com","_self");
  FrameMenu.insert("a13","a13_2","新浪网(本窗口)","http://www.sina.com");
  FrameMenu.insert("a13","a13_3","新浪网(Body)","http://www.sina.com","body");
</script>
<body topmargin="0" leftmargin="0" oncontextmenu="FrameMenuConfig.showMenu();return false;">
  这个是TopFrame,Name=TopFrame
  <div id="fm_MainContainer">系统菜单是安排在这里滴,嘻嘻</div>
  <br/><button onmouseover="FrameMenuConfig.showMenu()">鼠标悬停方式显示完整菜单</button>
<script defer>
  FrameMenuConfig.createFrameMenu("fm_MainContainer",false);
</script>
</body>

 

posted @ 2007-11-14 09:41  Jeason  阅读(4379)  评论(6编辑  收藏  举报
订阅 漓筝轩 的RSS