Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 290|回复: 0

[默认分类] JavaScript 多级联动浮动(下拉)菜单 (第二版)

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-5-20 13:55:50 | 显示全部楼层 |阅读模式
    上一个版本(第一版请看这里)基本实现了多级联动和浮动菜单的功能,但效果不是太好,使用麻烦还有些bug,实用性不高。
    这次除了修改已发现的问题外,还对程序做了大幅调整和改进,使程序实用性更高,功能更强大。  

    效果预览



    //容器集合
    var FixedMenu = function(containers, options) {
    this._timerContainer = null;//容器定时器
    this._timerMenu = null;//菜单定时器
    this._frag = document.createDocumentFragment();//碎片对象,保存菜单元素
    this._menus = {};//菜单对象

    this._containers = [];//容器集合

    this._setOptions(options);
    var opt = this.options;

    this._custommenu = opt.menu;

    this.css = opt.css;
    this.hover = opt.hover;
    this.active = opt.active;
    this.tag = opt.tag;
    this.HTML = opt.html;
    this.relContainer = opt.relContainer;
    this.relative = opt.relative;
    this.attribute = opt.attribute;
    this.property = opt.property;

    this.onBeforeShow = opt.onBeforeShow;

    this.delay = parseInt(opt.delay) || 0;

    //修正自定义容器
    $$A.forEach($$A.isArray(containers) ? containers : [containers], function(o, i){
      //自定义容器 id:定位元素 menu:插入菜单元素
      var pos, menu;
      if ( o.id ) {
       pos = o.id; menu = o.menu ? o.menu : pos;
      } else {
       pos = menu = o;
      };
      pos = $$(pos); menu = $$(menu);
      //容器对象 pos:定位元素 menu:插入菜单元素
      pos && menu && this._iniContainer( i, { "pos": pos, "menu": menu } );
    }, this);

    //初始化程序
    this._iniMenu();
    };
    FixedMenu.prototype = {
      //设置默认属性
      _setOptions: function(options) {
    this.options = {//默认值
      menu:   [],//自定义菜单集合
      delay:   200,//延迟值(微秒)
      tag:   "div",//默认生成标签
      css:   undefined,//默认样式
      hover:   undefined,//触发菜单样式
      active:   undefined,//显示下级菜单时显示样式
      html:   "",//菜单内容
      relContainer: false,//是否相对容器定位(否则相对菜单)
      relative:  { align: "clientleft", vAlign: "bottom" },//定位对象
      attribute:  {},//自定义attribute属性
      property:  {},//自定义property属性
      onBeforeShow: function(){}//菜单显示时执行
    };
    $$.extend( this.options, options || {} );
      },
      //程序初始化
      _iniMenu: function() {
    this.hide();//隐藏菜单
    this._buildMenu();//生成菜单对象
    this._forEachContainer(this._resetContainer);//重置容器属性
    this._insertMenu(0, 0);//显示菜单
      },
      //根据自定义菜单对象生成程序菜单对象
      _buildMenu: function() {
    //清除旧菜单dom(包括自定义的)
    this._forEachMenu(function(o){
      var elem = o._elem;
      if ( elem ) {
       //防止dom内存泄漏
       $$E.removeEvent( elem, "mouseover", o._event );
       elem.parentNode.removeChild(elem);
       o._elem = o.elem = null;
      };
    });
    //设置菜单默认值
    var options = {
      id:    0,//id
      rank:   0,//排序
      elem:   "",//自定义元素
      tag:   this.tag,
      css:   this.css,
      hover:   this.hover,
      active:   this.active,
      html:   this.html,
      relContainer: !!this.relContainer,
      fixed:   this.relative,
      attribute:  this.attribute,
      property:  this.property
    };
    //先定义"0"顶级菜单
    this._menus = { "0": { "_children": [] } };
    //整理自定义菜单并插入到程序菜单对象
    $$A.forEach(this._custommenu, function(o) {
      //生成菜单对象(由于包含对象,要用深度扩展)
      var menu = $$.deepextend( $$.deepextend( {}, options ), o || {} );
      //去掉相同id菜单,同时排除了id为"0"的菜单
      if ( !!this._menus[ menu.id ] ) { return; };
      //重置属性
      menu._children = []; menu._index = -1;
      this._menus[menu.id] = menu;
    }, this);
    //建立树形结构
    this._forEachMenu(function( o, id, menus ) {
      if ( "0" === id ) { return; };//顶级没有父级菜单
      var parent = this._menus[o.parent];
      //父级菜单不存在或者父级是自己的话,当成一级菜单
      if ( !parent || parent === o ) { parent = menus[o.parent = "0"]; };
      //插入到父级菜单对象的_children中
      parent._children.push(o);
    });
    //整理菜单对象
    this._forEachMenu(function(o) {
      //如果有自定义元素的话先放到碎片文档中
      !!o.elem && ( o.elem = $$(o.elem) ) && this._frag.appendChild(o.elem);
      //修正样式,优先使用自定义元素的class
      if ( !!o.elem && o.elem.className ) {
       o.css = o.elem.className;
      } else if ( o.css === undefined ) { o.css = ""; };
      if ( o.hover === undefined ) { o.hover = o.css; };
      if ( o.active === undefined ) { o.active = o.hover; };
      //对菜单对象的_children集合排序(先按rank再按id排序)
      o._children.sort(function( x, y ) { return x.rank - y.rank || x.id - y.id; });
    });
      },
      //插入菜单
      _insertMenu: function(index, parent) {
    var container = this._containers[index];
    //如果是同一个父级菜单不用重复插入
    if ( container._parent === parent ) { return; };
    container._parent = parent;
    //把原有容器内菜单移到碎片对象中
    $$A.forEach( container._menus, function(o) { o._elem && this._frag.appendChild(o._elem); }, this );
    //重置子菜单对象集合
    container._menus = [];
    //把从父级菜单元素的子菜单对象集合获取的元素插入到容器
    $$A.forEach(this._menus[parent]._children, function( menu, i ){
      this._checkMenu( menu, index );//检查菜单
      container._menus.push(menu);//加入到容器的子菜单集合,方便调用
      container.menu.appendChild(menu._elem);//菜单元素插入到容器
    }, this);
      },
      //检查菜单
      _checkMenu: function(menu, index) {
    //索引保存到菜单对象属性中,方便调用
    menu._index = index;
    //如果菜单对象没有元素
    if ( !menu._elem ) {
      var elem = menu.elem;
      //如果没有自定义元素的话创建一个
      if ( !elem ) { elem = document.createElement(menu.tag); elem.innerHTML = menu.html; };
      //设置property
      $$.extend( elem, menu.property );
      //设置attribute
      var attribute = menu.attribute;
      for (var att in attribute) { elem.setAttribute( att, attribute[att] ); };
      //设置样式
      elem.className = menu.css;
      //设置事件
      menu._event = $$F.bindAsEventListener( this._hoverMenu, this, menu );//用于清除事件
      $$E.addEvent( elem, "mouseover", menu._event );
      //保存到菜单对象
      menu._elem = elem;
    };
      },
      //触发菜单
      _hoverMenu: function(e, menu) {
    var elem = menu._elem;
    //如果是内部元素触发直接返回
    if ( $$D.contains( elem, e.relatedTarget ) || elem === e.relatedTarget ) { return; };
    clearTimeout(this._timerMenu);
    //可能在多个容器间移动,所以全部容器都重新设置样式
    this._forEachContainer(function(o, i){
      if ( o.pos.visibility === "hidden" ) { return; };
      this._resetCss(o);
      //设置当前菜单为active样式
      var menu = o._active;
      if ( menu ) { menu._elem.className = menu.active; };
    });
    //设置当前菜单为触发样式
    if ( this._containers[menu._index]._active !== menu ) { elem.className = menu.hover; };
    //触发显示菜单
    this._timerMenu = setTimeout( $$F.bind( this._showMenu, this, menu ), this.delay );
      },
      //显示菜单
      _showMenu: function(menu) {
    var index = menu._index, container = this._containers[index], child = !!menu._children.length;
    //隐藏不需要的容器
    this._forEachContainer( function(o, i) { i > index && this._hideContainer(o); } );
    //重置当前容器_active
    container._active = null;
    //如果有子级菜单
    if ( child ) {
      //设置当前容器_active
      container._active = menu;
      //显示下一级容器
      index++;//设置索引
      this._checkContainer(index);//检查容器
      this._insertMenu(index, menu.id);//插入菜单
      this._showContainer(menu);//显示容器
    };
    //重置当前容器的css
    this._resetCss(container);
    //设置当前菜单样式
    menu._elem.className = child ? menu.active : menu.hover;
      },
      //初始化容器(索引, 容器元素)
      _iniContainer: function(index, container) {
    var oContainer = container.pos;
    //重置属性
    this._resetContainer(container);
    //添加事件
    $$E.addEvent(oContainer, "mouseover", $$F.bind(function(){ clearTimeout(this._timerContainer); }, this));
    $$E.addEvent(oContainer, "mouseout", $$F.bindAsEventListener(function(e){
      //先判断是否移出到所有容器之外
      var elem = e.relatedTarget,
       isOut = $$A.every(this._containers, function(o){ return  o.pos == elem || !($$D.contains(o.pos, elem)); });
      if ( isOut ) {
       //清除定时器并隐藏
       clearTimeout(this._timerContainer); clearTimeout(this._timerMenu);
       this._timerContainer = setTimeout( $$F.bind( this.hide, this ), this.delay );
      };
    }, this));
    //除了第一个容器外设置浮动样式
    if ( index ) {
      $$D.setStyle(container.pos, {
       position: "absolute", display: "block", margin: 0,
       zIndex: this._containers[index - 1].pos.style.zIndex + 1//要后面的覆盖前面的
      });
    };
    //ie6处理select
    if ( $$B.ie6 ) {
    var iframe = document.createElement("");
      document.body.insertBefore(iframe, document.body.childNodes[0]);
      container._iframe = iframe;
    };
    //记录索引,方便调用
    container._index = index;
    //插入到容器集合
    this._containers[index] = container;
      },
      //检查容器
      _checkContainer: function(index) {
    if ( index > 0 && !this._containers[index] ) {
      //如果容器不存在,根据前一个容器复制成新容器,第一个容器必须自定义
      var pre = this._containers[index - 1].pos
      //用了新的添加事件方式,没有ie的cloneNode的bug
       ,container = pre.parentNode.insertBefore( pre.cloneNode(false), pre );
      //清除id防止冲突
      container.id = "";
      //初始化容器
      this._iniContainer( index, { "pos": container, "menu": container } );
    };
      },
      //显示容器
      _showContainer: function(menu) {
    var index = menu._index
      ,container = this._containers[index + 1].pos
      ,elem = menu.relContainer ? this._containers[index].pos : menu._elem
      ,pos = RelativePosition( elem, container, menu.fixed );
    //执行显示前事件
    this.onBeforeShow(container, menu);
    //定位并显示容器
    $$D.setStyle(container, {
      left: pos.Left + "px", top: pos.Top + "px", visibility: "visible"
    });
    //ie6处理select
    if ( $$B.ie6 ) {
      $$D.setStyle(this._containers[index + 1]._iframe, {
       width: container.offsetWidth + "px",
       height: container.offsetHeight + "px",
       left: pos.Left + "px", top: pos.Top + "px",
       display: ""
      });
    };
      },
      //隐藏容器
      _hideContainer: function(container) {
    //设置隐藏
    $$D.setStyle( container.pos, { left: "-9999px", top: "-9999px", visibility: "hidden" } );
    //重置上一个菜单的触发菜单对象
    this._containers[container._index - 1]._active = null;
    //ie6处理select
    if ( $$B.ie6 ) { container._iframe.style.display = "none"; };
      },
      //重置容器对象属性
      _resetContainer: function(container) {
    container._active = null;//重置触发菜单
    container._menus = [];//重置子菜单对象集合
    container._parent = -1;//重置父级菜单id
      },
      //隐藏菜单
      hide: function() {
    this._forEachContainer(function(o, i){
      if ( i === 0 ) {
       //如果是第一个重设样式和_active
       this._resetCss(o);
      } else {//隐藏容器
       this._hideContainer(o);
      };
    });
      },
      //重设容器菜单样式
      _resetCss: function(container) {
    $$A.forEach( container._menus, function(o, i){ o._elem.className = o.css; }, this );
      },
      //历遍菜单对象集合
      _forEachMenu: function(callback) {
    for ( var id in this._menus ) { callback.call( this, this._menus[id], id, this._menus ); };
      },
      //历遍容器对象集合
      _forEachContainer: function(callback) {
    $$A.forEach( this._containers, callback, this );
      },
      //添加自定义菜单
      add: function(menu) {
    this._custommenu = this._custommenu.concat(menu);
    this._iniMenu();
      },
      //修改自定义菜单
      edit: function(menu) {
    $$A.forEach( $$A.isArray( menu ) ? menu : [ menu ], function(o){
      //如果对应id的菜单存在
      if ( o.id && this._menus[o.id] ) {
       //从自定义菜单中找出对应菜单,并修改
       $$A.every( this._custommenu, function(m, i){
        if ( m.id === o.id ) {
         this._custommenu = $$.deepextend( m, o ); return false;
        };
        return true;
        //用every可以跳出循环
       }, this );
      };
    }, this );
    this._iniMenu();
      },
      //删除自定义菜单
      del: function() {
    var ids = Array.prototype.slice.call(arguments);
    this._custommenu = $$A.filter( this._custommenu, function(o){
      return $$A.indexOf(ids, o.id) === -1;
    });
    this._iniMenu();
      }
    };


    .container1 {height:30px;}
    .container1 div {float:left;}
    .container1 div, .container1_2 div {width:100px;background:#FAFCFD;border:1px solid #5c9cc0;padding:10px;}
    div.on1 {font-weight:bold;background:#EEF3F7;}
    div.on1_2 {font-weight:bold;background:#fffff7;border:1px solid #ffcc00;}
    菜单使用演示:








    -------------test-------------  


















    位置:
    第四个 第三个 第二个 第一个  
    1秒 0.5秒 0.2秒 不延时  

    var menu = [
    { id: 1, parent: 0, html: "自定义样式", hover: "on1_2", rank: 1 },
    { id: 2, parent: 0, html: "下拉菜单", active: "on1_2", rank: 2 },
    { id: 3, parent: 0, html: "任意定位", fixed: { align: "left" }, rank: 3 },

    { id: 11, parent: 1, html: "点击关闭", hover: "on1_2", property: { onclick: function(){ fm.hide(); } }},

    { id: 21, parent: 2, html: "相对容器", active: "on1_2", relContainer: true },
    { id: 22, parent: 2, html: "相对菜单", active: "on1_2", fixed: { align: "right", vAlign: "clienttop" } },
    { id: 23, parent: 21, html: "三级菜单" },
    { id: 24, parent: 21, html: "三级菜单" },
    { id: 25, parent: 22, html: "三级菜单" },
    { id: 26, parent: 22, html: "三级菜单" },

    { id: 31, parent: 3, html: "无法到达的", fixed: { align: "left" } },
    { id: 32, parent: 31, html: "目光", fixed: { align: "right" } },
    { id: 33, parent: 32, html: "无法到达的", fixed: { align: "right", vAlign: "top" } },
    { id: 34, parent: 33, html: "到达", fixed: { percentTop: 100 } },
    { id: 35, parent: 34, html: "到达", fixed: { align: "left", vAlign: "clienttop", percentLeft: -100 } },
    { id: 36, parent: 35, html: "梦想", fixed: { vAlign: "top" } },
    { id: 37, parent: 36, html: "目光", fixed: { vAlign: "top", percentTop: -100 } },
    { id: 38, parent: 37, html: "脚步", fixed: { align: "right", vAlign: "clienttop", percentLeft: 100 } },
    { id: 39, parent: 38, html: "地方", fixed: { percentTop: 100 } },
    { id: 40, parent: 39, html: "地方", fixed: { align: "left" } },
    { id: 41, parent: 40, html: "可以", fixed: { vAlign: "top", percentTop: -100 } },
    { id: 42, parent: 41, html: "可以" }
    ];
    var fm = new FixedMenu(["idContainer1", "idContainer1_2"], { hover: "on1", menu: menu });
    //编辑测试
    $$("idEdit").onclick = function(){
    if(this.value == "添加菜单 +"){
      fm.add([
       { id: 100, parent: 0, html: "新加菜单+", rank: $$("idRank").value | 0 },
       { id: 101, parent: 100, html: "新加菜单++" },
       { id: 102, parent: 100, html: "新加菜单+++" }
      ]);
      this.value = "删除菜单 -"
    }else{
      fm.del( 100, 101, 102 );
      this.value = "添加菜单 +"
    }
    }
    //延时测试
    $$("idDelay").onchange = function(){ fm.Delay = this.value | 0; }




    仿京东商城商品分类菜单:





    .container2, .container2 dd, .container2_2 dl, .container2_2 dd {margin:0;}
    .container2 {font-size:14px;width:190px;border:1px solid #cf2020;background:#fffff5;padding:5px 8px; line-height:30px; color:#333;}
    .container2 dt {font-weight:bold;color:#cf2020;}
    .container2 dd {background:url(http://images.cnblogs.com/cnblogs_com/cloudgamer/143727/n4.jpg) 180px 10px no-repeat;_zoom:1;}
    .container2_2 {background-color:#bebec3; display:none;}
    .container2_2 dl {font-size:14px;width:200px;border:1px solid #969696;background:#fff; position:relative; left:-3px; top:-3px; }
    .container2_2 dd div {padding:5px 20px; background:url(http://images.cnblogs.com/cnblogs_com/cloudgamer/143727/n4.jpg) 6px 7px no-repeat;_zoom:1;}
    .container2_2 dt, .shadow {padding:0 5px; position:absolute;background:#fff; border:1px solid #969696; border-right:0;width:169px;left:-180px; top:-1px;height:24px;line-height:24px;}
    .shadow {background-color:#bebec3;border-color:#bebec3; top:0;}
    .container2_2 a{display:block;_zoom:1;}
    .container2_2 a:link, .container2_2 a:visited, .container2_2 a:active {color:#333;text-decoration: none;}
    .container2_2 a:hover {color:#ff6026;text-decoration: underline;}



      图片动画
      

      图片效果
      

      动画效果


      系统其他
      

      系统效果
      

      其他效果



      
      
       
      
       
       
        图片滑动切换效果
       
       
        图片变换效果(ie only)
       
       
        图片滑动展示效果
       
       
        图片切割效果
       
       
        Tween算法及缓动效果
       
       
        渐变效果
       
       
        无刷新多文件上传系统
       
       
        图片切割系统
       
       
        拖放效果
       
       
        拖拉缩放效果
       
       
        滑动条效果
       
      



    var menu2 = [
    { id: 1, parent: 0, elem: "idMenu2_1" },

    { id: 2, parent: 0, elem: "idMenu2_2" },
    { id: 3, parent: 0, elem: "idMenu2_3" },

    { id: 11, parent: 2, elem: "idMenu2_11" },
    { id: 12, parent: 2, elem: "idMenu2_12" },
    { id: 13, parent: 2, elem: "idMenu2_13" },
    { id: 14, parent: 2, elem: "idMenu2_14" },

    { id: 21, parent: 3, elem: "idMenu2_21" },
    { id: 22, parent: 3, elem: "idMenu2_22" },

    { id: 51, parent: 0, elem: "idMenu2_51" },

    { id: 52, parent: 0, elem: "idMenu2_52" },
    { id: 53, parent: 0, elem: "idMenu2_53" },

    { id: 61, parent: 52, elem: "idMenu2_61" },
    { id: 62, parent: 52, elem: "idMenu2_62" },

    { id: 71, parent: 53, elem: "idMenu2_71" },
    { id: 72, parent: 53, elem: "idMenu2_72" },
    { id: 73, parent: 53, elem: "idMenu2_73" }
    ];
    var container2 = [ "idContainer2", { id: "idContainer2_2", menu: "idMenu2" } ];
    new FixedMenu(container2, { menu: menu2,
    relative: { align: "clientleft", vAlign: "clienttop", customTop: 5, customLeft: 176 },
    onBeforeShow: function(container, menu){ $$("idTitle").innerHTML = menu._elem.innerHTML; }
    });


    仿window xp右键菜单:



    .container3 {font-size:12px;border:1px solid #9d9da1;padding:3px;line-height:18px; background:#FFF; cursor:default;-moz-user-select:none;_overflow:hidden;}
    .container3 div {padding:0 20px;}
    .menu3_1 {color:#aca899;_zoom:1;}
    .menu3_2 {background:url(http://images.cnblogs.com/cnblogs_com/cloudgamer/169629/o_menu.gif) 133px 0 no-repeat;}
    .menu3_2_on {background-position:133px -18px;}
    .menu3_3 {background:url(http://images.cnblogs.com/cnblogs_com/cloudgamer/169629/o_menu.gif) left -36px no-repeat;}
    .menu3_3_on {background-position:left -54px;}
    .menu3_4 {background:url(http://images.cnblogs.com/cnblogs_com/cloudgamer/169629/o_menu.gif) left -72px no-repeat;}
    .menu3_4_on {background-position:left -90px;}
    .line3 {border-bottom:1px solid #aca899; _font-size:0; margin:4px 0;}
    .on3 {background-color:#316ac5;color:#FFF;}
    .area3 { width:500px; height:200px;border:1px solid #aca899;}
    .pos3 {position:absolute; display:none;width:150px;}








    var menu3 = [
    { id: 1, parent: 0, html: "查看(V)" },
    { id: 2, parent: 0 },
    { id: 3, parent: 0, html: "排列图标(I)" },
    { id: 4, parent: 0, html: "刷新(E)" },
    { id: 5, parent: 0 },
    { id: 6, parent: 0, html: "自定义文件夹(F)..." },
    { id: 7, parent: 0 },
    { id: 8, parent: 0, html: "粘贴(P)" },

    { id: 9, parent: 0, html: "粘贴快捷方式(S)" },
    { id: 10, parent: 0 },
    { id: 11, parent: 0, html: "新建(P)" },
    { id: 12, parent: 0 },
    { id: 13, parent: 0, html: "属性(S)" },

    { id: 21, parent: 1, html: "缩略图(H)" },
    { id: 22, parent: 1, html: "平铺(S)", css: "menu3_3", hover: "menu3_3 menu3_3_on on3" },
    { id: 23, parent: 1, html: "图标(N)" },
    { id: 24, parent: 1, html: "列表(L)" },
    { id: 25, parent: 1, html: "详细信息(D)" },

    { id: 31, parent: 3, html: "名称(N)", css: "menu3_3", hover: "menu3_3 menu3_3_on on3" },
    { id: 32, parent: 3, html: "类型(S)" },
    { id: 33, parent: 3, html: "大小(T)" },
    { id: 34, parent: 3, html: "修改时间(M)" },
    { id: 35, parent: 3 },
    { id: 36, parent: 3, html: "按组排列(G)", css: "menu3_4", hover: "menu3_4 menu3_4_on on3" },
    { id: 37, parent: 3, html: "自动排列(A)" },
    { id: 38, parent: 3, html: "对齐到网格(L)" },

    { id: 41, parent: 11, html: "AlertBox 弹出层效果", href: "http://www.cnblogs.com/cloudgamer/arcHive/2010/10/11/AlertBox.html" },
    { id: 42, parent: 11, html: "图片3D展示空间", href: "http://www.cnblogs.com/cloudgamer/archive/2010/09/20/Image3D.html" },
    { id: 43, parent: 11, html: "图片变换效果", href: "http://www.cnblogs.com/cloudgamer/archive/2010/08/16/ImageTrans.html" },
    { id: 44, parent: 11, html: "图片滑动展示效果", href: "http://www.cnblogs.com/cloudgamer/archive/2010/07/29/SlideView.html" }
    ];
    $$A.forEach(menu3, function(menu){
    var id = menu.id | 0;
    switch (id) {
      case 1 :
      case 3 :
      case 11 ://有下级菜单
       menu.css = "menu3_2"; menu.hover = "menu3_2 menu3_2_on on3"; break;
      case 2 :
      case 5 :
      case 7 :
      case 10 :
      case 12 :
      case 35 ://分割线
       menu.css = menu.hover = "line3"; break;
      case 8 :
      case 9 ://不能选择
       menu.css = menu.hover = "menu3_1"; break;
      case 4 ://刷新
       menu.property = { onmouseup: function(){ location.reload(); } }; break;
      case 21 :
      case 22 :
      case 23 :
      case 24 :
      case 25 ://"查看"子菜单
       menu.property = { onmouseup: function(){ Select([21,22,23,24,25], id, "menu3_3", "menu3_3 menu3_3_on on3"); } }; break;
      case 31 :
      case 32 :
      case 33 :
      case 34 ://"排列图标"子菜单1
       menu.property = { onmouseup: function(){ Select([31,32,33,34], id, "menu3_3", "menu3_3 menu3_3_on on3"); } }; break;
      case 36 :
      case 37 :
      case 38 ://"排列图标"子菜单2
       menu.property = { onclick: function(){ Select([36,37,38], id, "menu3_4", "menu3_4 menu3_4_on on3"); } }; break;
      case 41 :
      case 42 :
      case 43 :
      case 44 :
       menu.property = { onclick: function(){ location.href = menu.href; } }; break;
    }
    });

    var fm3 = new FixedMenu(["idContainer3", "idContainer3_2"], {
    menu: menu3, delay: 0, hover: "on3",
    relative: { align: "right", vAlign: "clienttop", customTop: -4, customLeft: -2, adaptive: true, reset: true }
    });
    var area = $$("idArea"), container3 = $$("idContainer3"), container3_2 = $$("idContainer3_2");
    function Hide(){
    fm3.hide(); container3.style.display = "none";
    }
    function Select(group, id, css, hover){
    Hide();
    var menu = [];
    $$A.forEach(group, function(i){
      i !== id && menu.push({ "id": i, "css": "", "hover": "on3" });
    });
    menu.push({ "id": id, "css": css, "hover": hover });
    fm3.edit(menu);
    }
    $$E.addEvent(area, "contextmenu", function(e){
    with(container3.style){
      left = e.pageX + "px"; top = e.pageY + "px"; display = "block";
    }
    e.preventDefault();
    });
    container3.oncontextmenu = container3_2.oncontextmenu = function(e){ $$E.fixEvent(e).preventDefault(); }
    container3.onselectstart = container3_2.onselectstart = function(){ return false; }//ie chrome safari
    $$E.addEvent(container3, "mouseup", function(e){ e.stopPropagation(); });
    $$E.addEvent(document, "mouseup", Hide);
    $$E.addEvent(window, "blur", Hide);


    仿淘宝拼音索引菜单:





    .container4 li, .container4_2 li{ list-style:none;}
    .container4 ul, .container4_2{margin:0;}
    .container4 {width:350px;padding:7px 10px;font:12px Verdana;border:1px solid #ccc;background:#fffeed; line-height:15px;height:15px; _overflow:hidden;}
    .container4 li {float:left;padding:0 10px; border-right:1px solid #ccc; }
    .container4 div {float:left;color:#000;padding-right:10px;}
    li.menu4 {position:relative;margin-left:-1px; top:-1px; z-index:9999;border:1px solid #85ccff; border-bottom:0; padding-bottom:8px; color:#ff6026; background:#dbf3ff;}
    .container4_2 {width:350px;padding:10px;border:1px solid #85ccff;background:#dbf3ff;line-height:25px;font-size:14px; font-weight:bold;display:none;}
    .container4_2 a{ display:block;_zoom:1;}
    .container4_2 a:link, .container4_2 a:visited, .container4_2 a:active {color:#565553;text-decoration: none;}
    .container4_2 a:hover {color:#ff5500;text-decoration: underline;}
    .container4 a:link, .container4 a:visited, .container4 a:hover, .container4 a:active {color:#565553;text-decoration: none;}
    .menu4 a:link, .menu4 a:visited, .menu4 a:active {color:#ff6026;}
    .menu4 a:hover{color:#ff6026;text-decoration:underline;}



      Tag索引
      

      Table  
      Fixed  
      Color  
      Date  
      Select  
      


    Table行定位效果
    Table排序
    浮动定位提示效果
    仿LightBox内容显示效果
    颜色梯度和渐变效果
    blog式日历控件
    日期联动选择器
    多级联动select  
















    var menu4 = [
    { id: 1, parent: 0, elem: "idMenu4_1", active: "menu4" },
    { id: 2, parent: 0, elem: "idMenu4_2", active: "menu4" },
    { id: 3, parent: 0, elem: "idMenu4_3", active: "menu4" },
    { id: 4, parent: 0, elem: "idMenu4_4", active: "menu4" },
    { id: 5, parent: 0, elem: "idMenu4_5", active: "menu4" },

    { id: 11, parent: 1, elem: "idMenu4_11" },
    { id: 12, parent: 1, elem: "idMenu4_12" },

    { id: 21, parent: 2, elem: "idMenu4_21" },
    { id: 22, parent: 2, elem: "idMenu4_22" },

    { id: 31, parent: 3, elem: "idMenu4_31" },

    { id: 41, parent: 4, elem: "idMenu4_41" },
    { id: 42, parent: 4, elem: "idMenu4_42" },

    { id: 51, parent: 5, elem: "idMenu4_51" }
    ];
    new FixedMenu([ { id: "idContainer4", menu: "idMenu4" }, "idContainer4_2" ], {
    menu: menu4, relContainer: true,
    relative: { align: "clientleft", vAlign: "bottom", customTop: -1 }
    });

    程序原理
    程序最关键的地方是多级联动,先大概说明一下:
    首先第一级的菜单元素整理好后,从他们开始,当某个菜单元素触发显示下级菜单时,
    准备好下一级的容器元素,并把下一级的菜单元素放进去,再定位并显示容器元素。
    里面的菜单元素又可以触发显示下级菜单,然后按上面的步骤执行下去。
    这样一级一级的递推下去,形成多级的联动菜单。

    程序说明
    【容器对象】
    在多级联动中,每一级都需要一个容器元素来存放菜单元素。
    程序中每个容器元素都对应一个容器对象,用来记录该容器的相关信息。
    容器对象的集合记录在程序的_containers属性中。
    容器参数containers是程序实例化时的必要参数,它的结构如下:


    [
         容器元素(id),
         { id: 容器元素(id), menu: 插入菜单元素(id) },
         
    ]


    首先如果containers不是数组的话,程序会自动转成单元素数组。
    如果菜单插入的元素就是容器元素本身,可以直接用容器元素(id)作为数组元素。
    否则应该使用一个对象结构,它包括一个id属性表示是容器元素(id)和一个menu属性表示菜单插入的元素(id)。
    containers会在程序初始化时这样处理:



    Code


      $$A.forEach($$A.isArray(containers) ? containers : [containers], function(o, i){
         var pos, menu;
         if ( o.id ) {
             pos = o.id; menu = o.menu ? o.menu : pos;
         } else {
             pos = menu = o;
         };
         pos = $$(pos); menu = $$(menu);
         pos && menu && this._iniContainer( i, { "pos": pos, "menu": menu } );
    }, this);


    主要是生成一个容器对象,其中pos属性是容器元素,menu属性是插入菜单的元素。
    然后传递索引和容器对象给_iniContainer函数,对容器对象做初始化。
    在_iniContainer中,首先用_resetContainer重置容器对象可能在程序中设置过的属性。
    再给容器元素添加事件:



    Code


      $$E.addEvent(oContainer, "mouseover", $$F.bind(function(){ clearTimeout(this._timerContainer); }, this));
    $$E.addEvent(oContainer, "mouseout", $$F.bindAsEventListener(function(e){
         //先判断是否移出到所有容器之外
         var elem = e.relatedTarget,
             isOut = $$A.every(this._containers, function(o){ return  o.pos == elem || !($$D.contains(o.pos, elem)); });
         if ( isOut ) {
             //清除定时器并隐藏
             clearTimeout(this._timerContainer); clearTimeout(this._timerMenu);
             this._timerContainer = setTimeout( $$F.bind( this.hide, this ), this.delay );
         };
    }, this));


    在mouseout时,先判断是否容器内部或容器之间触发,不是的话再用定时器执行hide隐藏函数。
    在hide里面,主要是隐藏容器:


    this
    ._forEachContainer(
    function
    (o, i){
         
    if
      ( i
    ===
      
    0
      ) {
             
    this
    ._resetCss(o);
         }
    else
      {
             
    this
    ._hideContainer(o);
         };
    });


    由于第一级容器一般是不自动隐藏的,只需要用_resetCss来重置样式。
    其他容器会用_hideContainer函数来处理隐藏:


    $$D.setStyle( container.pos, { left:
    "
    -9999px
    "
    , top:
    "
    -9999px
    "
    , visibility:
    "
    hidden
    "
      } );

    this
    ._containers[container._index
    -
      
    1
    ]._active
    =
      
    null
    ;


    其中_active属性是保存该容器触发下级菜单的菜单对象,在隐藏容器同时重置上一级容器的_active。
    在mouseover时清除容器定时器,其实就是取消hide执行。
    之后是设置样式:


    if
      ( index ) {
         $$D.setStyle(container.pos, {
             position:
    "
    absolute
    "
    , display:
    "
    block
    "
    , margin:
    0
    ,
             zIndex:
    this
    ._containers[index
    -
      
    1
    ].pos.style.zIndex
    +
      
    1

         });
    };


    除了第一级容器外,都设置浮动需要的样式。
    最后用_index属性记录索引,方便调用,并把容器对象插入到容器集合中:  


    container._index
    =
      index;

    this
    ._containers[index]
    =
      container;


    这个索引很重要,它决定了容器是用在第几级菜单。

    【菜单对象】
    容器元素插入了菜单元素才算一个菜单。
    程序中每个菜单元素都对应一个菜单对象,用来记录该菜单的相关信息。
    程序初始化前,应该先创建好自定义菜单集合,它的结构是这样的:  


    [
         { id:
    1
    , parent:
    0
    , html: 元素内容 },
         { id:
    2
    , parent:
    1
    , html: 元素内容 },
         
    ]


    其中id是菜单的唯一标识,parent是父级菜单的id。
    除了这两个关键属性外,还可以包括以下属性:
    rank:排序属性
    elem:自定义元素
    tag:生成标签
    css:默认样式
    hover:触发菜单样式
    active: 显示下级菜单时显示样式
    html:菜单内容
    relContainer:是否相对容器定位(否则相对菜单)
    relative:定位对象
    attribute:自定义Attribute属性
    property:自定义Property属性
    其中relContainer和relative是用于下级容器定位的。
    自定义菜单集合会保存在_custommenu属性中。
    在程序初始化时会执行_buildMenu程序,根据这个_custommenu生成程序需要的_menus菜单对象集合。
    _buildMenu是比较关键的程序,菜单的层级结构就是在这里确定,它由以下几步组成:
    第一步,清除旧菜单对象集合的dom元素。
    这一步后面“内存泄漏”会详细说明。
    第二步,生成菜单对象集合。
    为了能更有效率地获取指定id的菜单对象,_menus是以id作为字典关键字的对象。
    首先创建带根菜单(id为“0”)对象的_menus:  


    this
    ._menus
    =
      {
    "
    0
    "
    : {
    "
    _children
    "
    : [] } };


    然后整理_custommenu并插入到_menus中:



    Code


      $$A.forEach(this._custommenu, function(o) {
         var menu = $$.deepextend( $$.deepextend( {}, options ), o || {} );
         if ( !!this._menus[ menu.id ] ) { return; };
         menu._children = []; menu._index = -1;
         this._menus[menu.id] = menu;
    }, this);


    其中菜单对象中包含对象属性,要用deepextend深度扩展来复制属性。
    为确保id是唯一标识,会排除相同id的菜单,间接排除了id为“0”的菜单。
    在重置_children(子菜单集合)和_index(联级级数)之后,就可以插入到_menus中了。


    第三步,建立树形结构。
    菜单之间的关系是一个树形结构,程序通过id和parent来建立这个关系的(写过数据库分级结构的话应该很熟悉)。
    而第一版是把子类直接菜单写在菜单元素的menu属性中,形成类似多维数组的结构。
    比较这两个方法,第一版的优势在于定义菜单时就直接确立了关系,而新版还必须根据id和parent来判断增加代码复杂度。
    新版的优势是使用维护方便,灵活,级数越多就越体现出来,而第一版刚好相反。
    能不能结合这两个方法的优势呢?
    这里采用了一个折中的方法,在写自定义菜单对象时用的是新版的方法,然后程序初始化时把它转换成类多维数组结构。
    转换过程是这样的:
    首先根据parent找到父菜单对象:


    var
      parent
    =
      
    this
    ._menus[o.parent];


    如果找不到父菜单对象或父菜单对象就是菜单对象本身的,当成一级菜单处理:


    if
      (
    !
    parent
    ||
      parent
    ===
      o ) { parent
    =
      menus[o.parent
    =
      
    "
    0
    "
    ]; };


    最后把当前菜单对象放到父菜单对象的_children集合中:  


    parent._children.push(o);


    这就把_menus变成了类多维数组结构,而且这个结构不会发生死循环。
    第四步,整理菜单对象集合。
    这步主要是整理_menus里面的菜单对象。
    首先,把自定义菜单元素放到碎片文档中:


    !!
    o.elem
    &&
      ( o.elem
    =
      $$(o.elem) )
    &&
      
    this
    ._frag.appendChild(o.elem);


    菜单元素是需要显示时才会处理的,这样可以防止在容器上出现未处理的菜单元素。
    然后是修正样式(详细看样式设置部分)。
    最后,对菜单对象的_children集合进行排序:


    o._children.sort(
    function
    ( x, y ) {
    return
      x.rank
    -
      y.rank
    ||
      x.id
    -
      y.id; });


    先按rank再按id排序,跟菜单对象定义的顺序是无关的。
    执行完BuildMenu程序之后,_menus菜单对象集合就建立好了。
    麻烦的是在每次修改_custommenu之后,都必须执行一次_buildMenu程序。

    【多级联动】
    容器对象和菜单对象都准备好了,下面就是如何利用它们来做程序的核心——多级联动效果了。
    多级联动包括以下步骤:
    第一步,准备一级容器。
    一级容器一般是显示状态的(也可以自己定义它的显示隐藏,像仿右键菜单那样)。
    第二步,向容器插入菜单。
    通过_insertMenu程序,可以向指定容器插入指定菜单,其中第一个参数是索引,第二个参数是父菜单id。
    在_insertMenu程序里面,先判断是否同一个父级菜单,是的话就返回不用重复操作了:  


    var
      container
    =
      
    this
    ._containers[index];

    if
      ( container._parent
    ===
      parent ) {
    return
    ; };
    container._parent
    =
      parent;


    接着把原有容器内菜单移到碎片对象中:


    $$A.forEach( container._menus,
    function
    (o) { o._elem
    &&
      
    this
    ._frag.appendChild(o._elem); },
    this
      );


    在第一版,菜单每次使用都会重新创建,新版改进后会把旧菜单元素保存到碎片对象中,要使用时再拿出来。
    然后根据parent获取父菜单对象,并把父菜单的_children子菜单集合的插入到容器中:


    $$A.forEach(
    this
    ._menus[parent]._children,
    function
    ( menu, i ){
         
    this
    ._checkMenu( menu, index );
         container._menus.push(menu);
         container.menu.appendChild(menu._elem);
    },
    this
    );


    这样整个菜单就准备好了。


    第三步,添加触发下级菜单事件。
    上面在把菜单插入到容器之前,会先用_checkMenu程序检查菜单对象。
    _checkMenu程序主要是检测和处理菜单元素。
    首先判断没有自定义元素,没有的话就创建一个:


    var
      elem
    =
      menu.elem;

    if
      (
    !
    elem ) { elem
    =
      document.createElement(menu.tag); elem.innerHTML
    =
      menu.html; };


    第一版并不能自定义元素,但考虑到seo、渐进增强等,在新版加入了这个功能。
    但每次BuildMenu之后会把所有菜单元素包括自定义元素都清除,这个必须留意。
    然后分别设置property、attribute和className属性:


    $$.extend( elem, menu.property );

    var
      attribute
    =
      menu.attribute;

    for
      (
    var
      att
    in
      attribute) { elem.setAttribute( att, attribute[att] ); };
    elem.className
    =
      menu.css;


    ps:关于property和attribute的区别请看这里的attribute/property部分
    然后是关键的一步,添加HoverMenu触发事件程序:


    menu._event
    =
      $$F.bindAsEventListener(
    this
    ._hoverMenu,
    this
    , menu );
    $$E.addEvent( elem,
    "
    mouseover
    "
    , menu._event );


    处理后的元素会保存在菜单对象的_elem属性中。


    第四步,触发显示下级菜单事件。
    当触发了显示下级菜单事件,就会执行_hoverMenu程序。
    在_hoverMenu程序里面,主要是做一些样式设置,详细参考后面的样式设置部分。
    然后是用定时器准备执行_showMenu显示菜单程序。
    第五步,整理菜单容器。
    在_showMenu程序中,首先是隐藏不需要的容器:


    this
    ._forEachContainer(
    function
    (o, i) { i
    >
      index
    &&
      
    this
    ._hideContainer(o); } );


    然后判断当前菜单是否有子菜单,当有子菜单时,先用_checkContainer程序检查下级菜单容器。
    _checkContainer程序主要是检查容器是否存在,不存在的话就自动添加一个:


    var
      pre
    =
      
    this
    ._containers[index
    -
      
    1
    ].Pos
         ,container
    =
      pre.parentNode.insertBefore( pre.cloneNode(
    false
    ), pre );
    container.id
    =
      
    ""
    ;


    其实就是用cloneNode复制前一个容器,注意要重置id防止冲突。
    虽然程序能自动创建菜单,但也要求至少自定义一个容器。
    第六步,显示菜单容器。
    在显示之前,先按第二步向容器插入菜单,最后就是执行_showContainer程序来定位和显示容器了。
    当下一个容器内的菜单触发显示下级菜单事件时,会显示下下级的菜单容器。
    程序就是这样一级一级递推下去,形成多级联级效果。

    【样式设置】
    样式设置也是一个重要的部分,不是说要弄出多炫的界面,而是如何使程序能最大限度地灵活地实现那些界面。
    菜单对象有三个样式相关的属性,分别是:
    css:默认样式
    hover:鼠标进入菜单时使用样式
    active:显示下级菜单时使用样式
    在_buildMenu程序中,会对这些样式属性进行整理:



    Code


      if ( !!o.elem && o.elem.className ) {
         o.css = o.elem.className;
    } else if ( o.css === undefined ) { o.css = ""; };
    if ( o.hover === undefined ) { o.hover = o.css; };
    if ( o.active === undefined ) { o.active = o.hover; };


    可以看到,程序会优先使用自定义元素的class,避免被程序设置的默认样式覆盖。
    空字符串也可能被用来清空样式,所以要用undefined来判断是否自定义了样式。
    程序中主要在两个地方设置样式:在鼠标移到菜单元素上时(_hoverMenu)和显示下级菜单时(_showMenu)。
    在_hoverMenu程序中,先对每个显示的容器设置一次样式:


    this
    ._forEachContainer(
    function
    (o, i){
         
    if
      ( o.pos.visibility
    ===
      
    "
    hidden
    "
      ) {
    return
    ; };
         
    this
    ._resetCss(o);
         
    var
      menu
    =
      o._active;
         
    if
      ( menu ) { menu._elem.className
    =
      menu.active; };
    });


    由于鼠标可能是在多个容器间移动,所以所有显示的容器都需要设置。
    用_resetCss重置容器样式后再设置有下级菜单的菜单的样式为active。
    为了方便获取,容器对象用一个_active属性来保存当前容器触发了下级菜单的菜单对象。
    然后是设置鼠标所在菜单的样式:


    if
      (
    this
    ._containers[menu._index]._active
    !==
      menu ) { elem.className
    =
      menu.hover; };


    为了优先设置active样式,在当前菜单不是容器的_active时才设置hover样式。
    在_showMenu程序中,首先把显示下级菜单的菜单对象保存到容器的_active属性。
    再用_resetCss重置当前容器样式,这个在同级菜单中移动时会有用。
    然后再根据当前菜单是否有下级菜单来设置样式为active或hover。

    【内存泄漏】
    上面“菜单对象”中说到清除旧菜单对象的dom元素,这个主要是为了防止内存泄漏。
    关于内存泄漏也有很多文章,这里推荐看看Douglas Crockford的“JScript Memory Leaks”和winter的“浏览器中的内存泄露”。
    下面说说我解决本程序内存泄漏的经过:
    首先,通过调用程序的Add和Delete数千次来测试是否有内存泄漏。
    怎么看出来呢?可以找些相关的工具来检测,或者直接看任务管理器的页面文件(pf)使用记录。
    结果发现,虽然每个元素都用removeChild移出了dom,但随着循环的次数增多,pf还是稳步上升。
    于是按照Memory Leaks中说的“we must null out all of its event handlers to break the cycles”去掉事件:


    removeEvent( elem,
    "
    mouseover
    "
    , o._event );


    效果是有了,但不太理想,然后再逐一排除,发现原来是_elem属性还关联着元素,结果经过一些操作后,又把元素append到dom上,还重新创建了一个元素。
    于是在移除元素后,立即重置_elem和elem属性:


    o._elem
    =
      o.elem
    =
      
    null
    ;


    内存泄漏就没有了,其实这里也不算是内存泄露了,而是程序设计有问题了。
    所以清除dom元素时必须注意:
    1,按照Douglas Crockford的建议,移除所有dom元素相关的事件函数;
    2,删除/重置所有关联dom元素的js对象/属性。

    【cloneNode的bug】
    在上面多级联动中说到,会用cloneNode复制容器,但cloneNode在ie中有一个bug:
    在ie用attachEvent给dom元素绑定事件,在cloneNode之后会把事件也复制过去。
    而用addEventListener添加的事件就不会,可以在ie和ff测试下面的代码:  



    Code


      <!DOCTYPE html>
    <html>
    <body>
    <div id="t">div</div>
    <script>
    var o = document.getElementById("t");
    if(o.attachEvent){
      o.attachEvent("onclick", function(){alert(2)});
    }else{
      o.addEventListener("click", function(){alert(2)}, false);
    }
    document.body.appendChild(o.cloneNode(true));
    </script>
    </body>
    </html>


    在ie和ff点击第一个div都会触发alert,关键是第二个div,在ff不会触发,而ie就会。
    当然这个是不是bug还不清楚,或许attachEvent本来就是这样设计的也说不定。
    但第一版就是由于这个bug,而没有用cloneNode。
    在找解决方法之前,再扩展这个问题,看看直接添加onclick事件会不会有同样的bug。
    首先测试在元素里面添加onclick:  


    <!
    DOCTYPE html
    >


    <
    html
    >


    <
    body
    >


    <
    div id
    =
    "
    t
    "
      onclick
    =
    "
    alert(1)
    "
    >
    div
    <
    /
    div>


    <
    script
    >


    var
      o
    =
      document.getElementById(
    "
    t
    "
    );
    document.body.appendChild(o.cloneNode(
    true
    ));

    <
    /
    script>


    <
    /
    body>


    <
    /
    html>


    结果在ie和ff都会复制事件。
    再测试在js添加onclick:  



    Code


      <!DOCTYPE html>
    <html>
    <body>
    <div id="t">div</div>
    <script>
    var o = document.getElementById("t");
    o.onclick = function(){alert(1)}
    document.body.appendChild(o.cloneNode(true));
    </script>
    </body>
    </html>


    结果在ie和ff都不会复制事件,看来只有attachEvent会引起这个bug。
    下面是解决方法:
    用John Resig在《精通javaScript》推荐的Dean Edwards写的addEvent和removeEvent方法来添加/移除事件。
    它的好处就不用说了,而且它能在ie解决上面说到的cloneNode的bug。
    因为它的实现原理是在ie用onclick来绑定事件,而上面的测试也证明用onclick绑定的事件是不会被cloneNode复制的。
    ps:我对原版的方法做了些修改,方便调用。

    【浮动定位】
    容器的浮动定位用的是浮动定位提示效果中的定位方法。
    在该文章中已经详细说明了如何获取指定的浮动定位坐标,这里做一些补充。
    一般来说用getBoundingClientRect配合scrollLeft/scrollTop就能获得对象相对文档的位置坐标。
    测试下面代码:



    Code


      <!DOCTYPE html>
    <html>
    <body style="padding:1000px 0;">
    <div id="t1" style="border:1px solid; width:100px; height:100px;"></div>
    <div id="t2"></div>
    <script>
    var $$ = function (id) {
         return "string" == typeof id ? document.getElementById(id) : id;
    };
    var b = 0;
    window.onscroll=function(){
      var t = $$("t1").getBoundingClientRect().top + document.documentElement.scrollTop;
      if( t != b ){ b = t; $$("t2").innerHTML += t + "<br>"; }
    }
    </script>
    </body>
    </html>


    在除ie8外的浏览器,t会保持在一个固定值,但在ie8却会在1008和1009之间变换(用鼠标一格一格滚会比较明显)。
    虽然多数时候还是标准的1008,但原来的效果可能就会被这1px的差距破坏(例如仿京东和仿淘宝的菜单)。
    ps:chrome和safari要把documentElement换成body。
    为了解决这个问题,只好在ie8的时候用回传统的offset来取值了(详细参考代码)。
    至于造成这个问题的原因还没弄清楚,各位有什么相关资料的记得告诉我哦。

    使用技巧
    在仿京东商城商品分类菜单中,实现了一个阴影效果。
    原理是这样的:
    底部是一个灰色背景层(阴影),里面放内容层,然后设置内容层相对定位(position:relative),并做适当的偏移(left:-3px;top:-3px;)。
    由于相对定位会保留占位空间,这样就能巧妙地做出了一个可自适应大小的背景层(阴影)。
    ps:博客园首页也做了类似的效果,但貌似错位有些严重哦。
    仿右键菜单效果并不支持opera,因为opera并没有类似oncontextmenu这样的事件,要实现的话会很麻烦。
    ps:如果想兼容opera的话,可以看看这篇文章“Opera下自定义右键菜单的研究”。
    注意,在oncontextmenu事件中要用阻止默认事件(preventDefault)来取消默认菜单的显示。
    这个效果还做了一个不能选择的处理,就是拖动它的内容时不会被选择。
    在ff中把样式-moz-user-select设为none就可以了,而ie、chrome和safari通过在onselectstart返回false来实现相同的效果。
    ps:css3有user-select样式,但貌似还没有浏览器支持。
    当然,还有很多不完善的地方,这里只是做个参考例子,就不深究了。
    仿淘宝拼音索引菜单主要体现了active的用法和相对容器定位,难的地方还是在样式(具体参考代码)。
    ps:这几个例子都只是二级菜单,其实可以自己用更简单的方法实现。

    使用说明
    实例化时,第一个必要参数是自定义容器对象:  


    new
      FixedMenu(
    "
    idContainer
    "
    );


    第二个可选参数用来设置系统的默认属性,包括
    属性:   默认值//说明
    menu:   [],//自定义菜单集合
    delay:   200,//延迟值(微秒)
    tag:   "div",//默认生成标签
    css:   undefined,//默认样式
    hover:   undefined,//触发菜单样式
    active:   undefined,//显示下级菜单时显示样式
    html:   "",//菜单内容
    relContainer: false,//是否相对容器定位(否则相对菜单)
    relative:  { align: "clientleft", vAlign: "bottom" },//定位对象
    attribute:  {},//自定义attribute属性
    property:  {},//自定义property属性
    onBeforeShow: function(){}//菜单显示时执行
    其中包括菜单对象的默认属性。
    还提供了以下方法:
    hide:隐藏菜单,隐藏所有菜单容器;
    add:添加菜单,参数是菜单对象或菜单对象集合;
    edit:修改菜单,找出对应id的菜单对象修改属性设置;
    del:删除菜单,参数是要删除菜单对象的id。
    其中后面三个编辑方法都会执行Ini初始化程序,效率较低,一般来说尽量不要使用。


      
    完整实例下载
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-6 17:59 , Processed in 0.427251 second(s), 48 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表