使用Popup窗口创建无限级Web页菜单(5)-阿里云开发者社区

开发者社区> 唐玄奘> 正文

使用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.giffunction 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.gifCreateMenu
   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/,如需转载请自行联系原博主。 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
分享录制的正则表达式入门、高阶以及使用 .NET 实现网络爬虫视频教程
我发布的「正则表达式入门以及高阶教程」,欢迎学习。 课程简介 正则表达式是软件开发必须掌握的一门语言,掌握后才能很好地理解到它的威力; 课程采用概念和实验操作 4/6 分隔,帮助大家理解概念后再使用大量的实例加深对概念的理解; 实例操作是对概念最好的理解,也是学习新语言最有效的办法; 在课程中也穿插着大量软件开发的技巧和大家分享; 应该是把晦涩的正则表达式讲解的最生动的课程; 掌握了正则表达式后,您一定会觉得这是一门最值得掌握的语言。
886 0
PolarDB-X 1.0-API参考-1.0(2017版本)-OpenAPI使用-签名机制
关于 Access Key ID 与 Access Key Secret PolarDB-X 服务会对每个 API 的调用请求进行身份验证,所以无论使用 HTTP 还是 HTTPS 协议提交请求,都需要在请求中包含由 Access Key ID 与 Access Key Secret 所产生的签名(Signature)信息。 Access Key ID 和 Access Key Secret 由阿里云官方颁发给访问者(可以通过阿里云官方网站申请和管理),其中 Access Key ID 用于标识访问者的身份。 Access Key Secret 是用于加密签名字符串和服务器端验证签名字符串
11 0
PolarDB-X 1.0-API参考-1.0(2017版本)-OpenAPI使用-请求结构
OpenAPI 服务地址 PolarDB-X API 的服务接入地址为:drds.aliyuncs.com。
9 0
PolarDB-X 1.0-API参考-1.0(2017版本)-OpenAPI使用-公共参数
公共参数包括两个部分:公共请求参数与公共返回参数。 公共请求参数:每个接口都需要使用的请求参数。 公共返回参数:任何的接口调用,无论成功与否,系统都会返回的参数。
6 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
3965 0
python网络爬虫(14)使用Scrapy搭建爬虫框架
python网络爬虫(14)使用Scrapy搭建爬虫框架阅读目录 目的意义说明创建scrapy工程一些介绍说明创建爬虫模块-下载强化爬虫模块-解析强化爬虫模块-包装数据强化爬虫模块-翻页强化爬虫模块-存储强化爬虫模块-图像下载保存启动爬虫修正目的意义爬虫框架也许能简化工作量,提高效率等。
1040 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
5715 0
+关注
641
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载