使用Popup窗口创建无限级Web页菜单(5)

简介:
 代码框架在( 4)里面已经全部列出来了,现在工作就是按流程把他们完成。本来实现一个prototype的Menu菜单类只需要最多300行代码,可是后来做了一些操作习惯支持和UI显示上的优化后,代码猛增到了1000多行。不过final版本看起来确实比土不拉叽的prototype强很多哦 emteeth.gif

    为了比较直观的说明代码的作用,我就从菜单的显示开始说。要显示一个菜单显必须构建一个菜单的实例,实例构建的完整代码如下:
None.gif < div  oncontextmenu ="return ShowContextMenu(this);"  style ="width: 200; height: 200; border: solid 1px blue;" >
None.gif     < table  width ="100%"  height ="100%"  border ="0" >
None.gif          < tr >
None.gif              < td  valign ="middle"  align ="center" >
None.gif                  right click me
None.gif              </ td >
None.gif          </ tr >
None.gif     </ table >
None.gif </ div >
None.gif < script  language ="javascript" >
None.gif function ShowContextMenu(elmt)
None.gif{
None.gif     if ( !elmt.contextMenu )
None.gif    {
None.gif         elmt.contextMenu = CreateContextMenu();
None.gif    }
None.gif     var win = window; 
None.gif    elmt.contextMenu.Show(win);
None.gif     return  false
None.gif}
ContractedBlock.gif CreateMenu
   contextmenu2.gif   

生成的Context Menu效果


    这是完全手工添加菜单条目生成的一个Context Menu,最后会完成一个自动解析菜单数据来生成菜单的方法。

    我们现在来看这个Menu.prototype.Show(win)方法,这是菜单的 第一级显示时的方法,它是由用户来调用的,因为菜单需要定位于用户给定位置(ContextMenu的用户给定位置就是鼠标点击的位置)。在菜单显示出第一级后,后续的子菜单的显示,都是在Menu类内部来处理的,子菜单位置是相对于parent menu,后续逻辑就都封装在Menu类内部了。Show()方法代码如下:
None.gifMenu.prototype.Show =  function(win)
None.gif{
None.gif     if ( !win )
None.gif    {
None.gif         return;
None.gif    }
None.gif     var menuObj =  this;
None.gif    menuObj.m_Opener = win;
None.gif    menuObj.__resumeItem();
None.gif     var win = menuObj.m_Opener;
None.gif     var popup, popwin, popdoc;
None.gif     //  判断菜单的容器popup是否建立
None.gif
     if ( !menuObj.m_Popup )
None.gif    {
None.gif        popup = win.createPopup();
None.gif        popup.document.body.bgColor = 'windowtext';
None.gif        popup.document.body.style.backgroundColor = 'window';
None.gif        menuObj.m_Popup = popup;
None.gif    }
None.gif     else
None.gif    {
None.gif        popup = menuObj.m_Popup;
None.gif        menuObj.__resumeAll();
None.gif    }
None.gif    popdoc = popup.document;
None.gif    popwin = popdoc.parentWindow;
None.gif     //  判断是否需要重绘菜单的内容
None.gif
     if ( menuObj.m_Invalidate || !menuObj.m_Drawn )
None.gif    {
None.gif        popdoc.body.innerHTML = menuObj.Render().outerHTML;
None.gif         //  popdoc.body.appendChild(menuObj.Render());
None.gif
        menuObj.m_Invalidate =  false;
None.gif        menuObj.m_Drawn =  true;
None.gif    }
None.gif     //  获取菜单的主table(菜单是使用table来实现的)
None.gif
     var menuHtml = popup.document.getElementById('menu');
None.gif     //  这个show只是为了测量菜单的bounds而调用的
None.gif
    popup.show(0, 0, 1, 1);
None.gif     var w = popdoc.body.scrollWidth; 
      //  判断菜单条目的Text的显示宽度是否在许可范围内,
      // 如果超出许可范围则ellipsis处理并返回新的MenuItem的width

None.gif    w =  this.__isEllipsis( this, menuHtml);
None.gif     var h = popdoc.body.scrollHeight;
None.gif     var x = win.event.clientX + win.screenLeft;
None.gif     var y = win.event.clientY + win.screenTop;
None.gif    popup.show(x, y, w, h); 
      // 菜单的显示特效,使用filter实现的
None.gif     this.FadeinEffect(Menu.Attributes.ShowMenuEffect);
None.gif    menuObj.m_Bounds = 
None.gif    {
None.gif        top: x, left: y,
None.gif        width: menuHtml.offsetWidth,
None.gif        height: menuHtml.offsetHeight
None.gif    }; 
       //  把菜单操作的事件attach到菜单上,鼠标和键盘操作等
None.gif    menuObj.AttachEvents(menuHtml);
None.gif};

    上面注解应该都比较清楚了,只是这个popup.show(0, 0, 1, 1);比较有意思哈,当我们向popup里添加好了菜单的HTML元素后,我们发现在popup没有显示过之前,是根本取不到构成Menu UI的那个Table element的bounds信息的。这里show上一下后,就是为了让IE算出其bounds信息,然后再使用实际的bounds信息show菜单。这算一个小hack吧,也是这个Menu中比较有效率的地方,因为除了这个show(0, 0, 1,1)就在没有计算菜单bounds的地方了,当然也用不着了。然而为什么又没有把bounds计算也做成lazy load象popup的生成那样呢?是因为用户可能在菜单显示后修改IE的字体大小(比如按住Ctrl再滚动鼠标滚轮),这样保证了再次显示菜单时能修正菜单的实际bounds。而后面把menu的bounds存了起来是为了在显示子菜单时,方便判断其默认向右展开空间是否足够,如果不够宽则从parent menu左侧展开。 


本文转自博客园鸟食轩的博客,原文链接:http://www.cnblogs.com/birdshome/,如需转载请自行联系原博主。 

目录
相关文章
|
存储 IDE 前端开发
SpringBoot2.x系列教程02--新纪元之SpringBoot创建Web项目的常用方式
前言 在上一章节中,壹哥 给大家介绍了SpringBoot的优缺点,并重点介绍了其”约定大于配置“的思想,你现在还能记得吗? 而且上文中,壹哥 说我们创建SpringBoot项目有3种方式,我们已经学习了第一种创建项目的方式了,接下来还有另外两种创建项目的方式,这两种方式该怎么创建项目呢?今天 壹哥 就把剩余的两种方式也一股脑都抖搂给大家吧。 一. 以官网模板方式创建Web项目(了解) 首先 壹哥 给大家介绍第2种创建Web项目的方式,对于这种方式大家仅做了解即可,其原理与第一种以Spring Initializr创建项目的方式一样。 1. 在spring.io官网下载模板构建项目 首先我们
322 0
|
存储 前端开发 JavaScript
【一步步一起学DApp开发】(四)web3.js 基本使用 | 连接geth | 创建web客户端
【一步步一起学DApp开发】(四)web3.js 基本使用 | 连接geth | 创建web客户端
601 0
|
JSON JavaScript 中间件
【node.js从入门到精通】使用express创建web服务器,路由,进行中间件的创建链接路由及其他中间件
【node.js从入门到精通】使用express创建web服务器,路由,进行中间件的创建链接路由及其他中间件
197 1
【node.js从入门到精通】使用express创建web服务器,路由,进行中间件的创建链接路由及其他中间件
|
NoSQL MongoDB 数据库
MongoDB v4.4.6安装、创建服务及Web客户端访问MongoDB详解
MongoDB v4.4.6安装、创建服务及Web客户端访问MongoDB详解
277 0
MongoDB v4.4.6安装、创建服务及Web客户端访问MongoDB详解
|
数据采集 Go
Go Web编程实战(9)----创建客户端
Go Web编程实战(9)----创建客户端
140 1
|
Java Go
Go Web编程实战(8)----创建HTTP与HTTPS服务器端
Go Web编程实战(8)----创建HTTP与HTTPS服务器端
188 0
Go Web编程实战(8)----创建HTTP与HTTPS服务器端
|
JavaScript
【Node.JS 】创建基本的web服务器
【Node.JS 】创建基本的web服务器
158 0
【Node.JS 】创建基本的web服务器
|
Java Maven
IDEA2022版本创建maven web项目(两种方式)
创建maven web项目有两种方式,一种是使用骨架方式,一种是不使用骨架的方式
693 1
IDEA2022版本创建maven web项目(两种方式)
|
Web App开发 XML 开发框架
WebAPI学习(一)——创建Web API程序
WebAPI学习(一)——创建Web API程序
248 0
|
JavaScript 前端开发
Node.js 创建并封装静态 WEB 服务器
Node.js 创建并封装静态 WEB 服务器
122 0
Node.js 创建并封装静态 WEB 服务器

热门文章

最新文章