• 打造html右键菜单


    今天是给大家介绍一款在网页上使用的右键菜单,原作者的网址是:http://51jsr.javaeye.com/blog/305517

    这个右键菜单已经非常优秀,不过呢。却是IE Only,而且在DTD模式下
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd%22>

    连IE显示都是有问题的,所以呢只有自己动手了,另外就顺便改造成jQuery控件,顺便分析一下代码。

    首先来看一下效果吧

    image

    ↑这是控件的效果

    image ←Windows Se7en 系统的邮件菜单

    插一句吧,其实我最终的目标是提供一个ASP.NET MVC 框架前台UI Controls解决方案,因为后面的控件会用到这个右键菜单所以就讲一下。

    首先还是来分析一下HTML吧

    1:一级菜单(每一组菜单)即是一个独立的div容器

    2:每一项又是div,嵌套一个nobr(可用div代替不过要额外写个class)的标签,里面是图标和span包裹的位置内容

    image菜单项/菜单组    image 分割线

    这里一个要注意的地方就是多级菜单其实在HTMl结构是分离的,只是通过显示的位置在视觉上给人连载一起(另外就是箭头图标了)

    第二接着是CSS了(是修改过的)

    CSS非常简单,因为HTML结构本身也不复杂

    CSS中会用到的所有图片 m_arrow m_item m_splitLine menu_bg  注意有四张图片哦。。

    第三来看javascript了

    先来看个完整的吧

    那接着就一步一步来分析呗,首先既然改造成jQuery控件那么自然还是老架子

    1
    2
    3
    4
    ;(function($) {
        $.fn.contextmenu = function(option) {
        }
    })(jQuery);
    接着是默认参数了哦
    1
    2
    3
    //alias:"唯一标示"(这个标示很重要哦,可以实现多次调用只生成一个菜单哦),
    //width菜单宽度
    option = $.extend({ alias: "cmroot", 150 }, option);
    默认参数只有两个,另外几个的只是没有默认值而已
    1
    2
    3
    4
    5
    6
    /*参数说明
    option: {Number, items:Array, onShow:Function, rule:JSON}
    成员语法(三种形式)    -- para.items
    -> {text:String, icon:String, type:String, alias:String, Number, items:Array}        --    菜单组
    -> {text:String, icon:String, type:String, alias:String, action:Function }                --    菜单项
    -> {type:String}   --分割线*/

    详细描述下:

    items:Array 右键菜单的内容定义,数组的元素格式如下所示:

    {text: String, icon: String, alias: String, type: "group"|"item"|"splitLine", int, items:Array,action:Funtion}

    其中:
    text:String 菜单项的文字说明 。
    icon: String 图标的Src地址,如果没有图标,如果item不需要图标,请设置成none.gif(在images/icons/中可以找到)。
    alias:String 唯一标识菜单项。
    type:"group"|"item"|"splitLine" 分别为组,项,分割线,当选择是"splitLine"则其他设置项无需设置。
    int 当且仅当type="group"时有效,设置新组容器的宽度。

    items:Array 子元素可无限层次。
    action:Function 当菜单项被点击时被使用
    alias: String (可选参数)唯一标识,当页面上只有一种右键菜单时可以省略
    width : Number (可选参数) 右键菜单根的宽度, 默认值:150px。
    onContextMenu: Function (可选参数) 当右键菜单触发时预先调用的函数,返回参数为Boolean指示是否显示菜单
    onShow: Function (可选参数) 当菜单显示时触发,一般在该函数中应用规则
    rule : Json (可选参数) 默认规则,设置哪些项默认为禁用,格式如下所示 { name:String, disable: Boolean, items:Array}

    name:String 规则名称 disable:Boolean 规则是禁用还是启用 items:Array 需要应用规则的item alias的集合

    有点复杂哈,如果还有不明白看示例哈。

    定义一堆临时变量,还有4个模板临时变量

    1
    2
    3
    4
    5
    6
    7
       var ruleName = null, target = null,
           groups = {}, mitems = {}, actions = {}, showGroups = [], //定义内部的临时变量。用到的地方再来分析
       //一个菜单项的模板哦  ,容器和项,分割线的模板
    itemTpl = "<div class='b-m-$[type]' unselectable=on><nobr unselectable=on><img src='$[icon]' align='absmiddle'/><span unselectable=on>$[text]</span></nobr></div>";
           var gTemplet = $("<div/>").addClass("b-m-mpanel").attr("unselectable", "on").css("display", "none");
           var iTemplet = $("<div/>").addClass("b-m-item").attr("unselectable", "on");
           var sTemplet = $("<div/>").addClass("b-m-split");

    接着我们要跳过一些函数的定义,直接来看创建HTML的部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //获取菜单的跟
    var $root = $("#" + option.alias);
          var root = null;
          if ($root.length == 0) { //如果顶级不存在,这创建顶级菜单哦
              root = buildGroup.apply(gTemplet.clone()[0], [option]);
              root.applyrule = applyRule; //把一个方法注册到dom上
              root.showMenu = showMenu; //另外一个方法注册的该dom上
              addItems(option.alias, option.items); //添加菜单项
          }
          else {
              root = $root[0]; //否则就用这个了
          }
    这个代码很玄乎,好像做了些什么好像有什么都没做,其实只有来看下buildGroup方法和addItems才知道到底干了什么
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var buildGroup = function(obj) { //创建菜单容器
            groups[obj.alias] = this; //菜单项注册到临时变量中
            this.gidx = obj.alias;
            this.id = obj.alias;
            if (obj.disable) { //如果是禁用状态
                this.disable = obj.disable;
                this.className = "b-m-idisable";
            }
        //设置菜单宽度,设置事件的阻止事件冒泡,并添加到body中
            $(this).width(obj.width).click(returnfalse).mousedown(returnfalse).appendTo($("body"));
            obj = null;
            return this; //返回菜单本身
        };

    有了容器就可以往里面添加菜单项了,我在代码中加了详细的注释了,应该可以很好的理解了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    //添加菜单项
      var addItems = function(gidx, items) {
          var tmp = null;
          for (var i = 0; i < items.length; i++) {
              if (items[i].type == "splitLine") { //如果是分割线
                  //菜单分隔线
                  tmp = sTemplet.clone()[0];
              } else {
                  items[i].gidx = gidx; //把group的标识赋给item上
                  if (items[i].type == "group") {
                      //菜单组
                      buildGroup.apply(gTemplet.clone()[0], [items[i]]); //每个菜单组都是独立的div哦,所以顶级一样调用生产组的方法
                      arguments.callee(items[i].alias, items[i].items);//递归生成菜单项
                      items[i].type = "arrow"; //如果是group生成箭头
                      tmp = buildItem.apply(iTemplet.clone()[0], [items[i]]);//生成菜单项的html
                  } else {
                      //菜单项
                      items[i].type = "ibody";
                      tmp = buildItem.apply(iTemplet.clone()[0], [items[i]]);//生成菜单项的html
                      $(tmp).click(function(e) { //如果菜单项那么注册click事件
                          if (!this.disable) {
                              if ($.isFunction(actions[this.idx])) {
                                  actions[this.idx].call(this, target);
                              }
                              hideMenuPane();
                          }
                          return false;
                      });
     
                  } //Endif
               //把菜单项的右键事件屏蔽,同时注册hover的效果
                  $(tmp).bind("contextmenu", returnfalse).hover(overItem, outItem);
              } //Endif
              groups[gidx].appendChild(tmp); //把菜单项添加到group的中
              tmp = items[i] = items[i].items = null;
          } //Endfor
          gidx = items = null;
      };

    builditem方法就比较简单,就不详细描述了,接着我们还是继续往下看主流程了哦

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    var me = $(this).each(function() {
           //给元素添加右键事件了哦
                return $(this).bind('contextmenu', function(e) {
            //如果(option.onContextMenu 存在则调用并判断返回值是否显示菜单,可以利用这个在特定情况下禁用菜单
                    var bShowContext = (option.onContextMenu && $.isFunction(option.onContextMenu)) ? option.onContextMenu.call(this, e) : true;
                    if (bShowContext) {
                //触发onShow事件,这个事件中可以执行修改rule,禁用某几项菜单项哦
                        if (option.onShow && $.isFunction(option.onShow)) {
                            option.onShow.call(this, root);
                        }
                        root.showMenu(e, this);//调用显示菜单
                    }
            //阻止冒泡
                    return false;
                });
            });
            //设置显示规则,第一次执行时的规则,同时也可以onshow中动态设置rule
            if (option.rule) {
                applyRule(option.rule);
            }

    基本就OK了,另外几个方法就比较简单了,还有亮点是边缘的处理,这个前面的datepicker中也有相应的说明逻辑差不多就不在描述了,同样还是来看下demo吧。关于打包下载,大家可以把demo的网页完整的另存为即可

    http://jscs.cloudapp.net/ControlsSample/CM

    你的支持是我继续写作的动力。

    欢迎转载,但是请保留原链接:http://www.cnblogs.com/xuanye/archive/2009/10/29/1592585.html

  • 相关阅读:
    我是如何学习写一个操作系统(七):进程的同步与信号量
    我是如何学习写一个操作系统(六):进程的调度
    我是如何学习写一个操作系统(五):故事的高潮之进程和线程1
    我是如何学习写一个操作系统(四):操作系统之系统调用
    我是如何学习写一个操作系统(三):操作系统的启动之保护模式
    我是如何学习写一个操作系统(二):操作系统的启动之Bootloader
    我是如何学习写一个操作系统(一):开篇
    Android计量单位px,in,mm,pt,dp,dip,sp和获取屏幕尺寸与密度
    Azure自定义角色实现RBAC
    Linux两块4TB的数据磁盘创建8TB的Raid0
  • 原文地址:https://www.cnblogs.com/mq0036/p/8482549.html
Copyright © 2020-2023  润新知