• 基于jQuery打造TabPanel


    停了许久,终于有时间继续下一篇,这次我们要实现的控件时Tab控件,在实际的应用中也比较多,如大量信息查看,当网页多窗口框架等都会用到,现在网上基于jquery Tab控件,其实也蛮多了,我以前用过的idtabs,就比较简单实用,也是比较灵活,但是对于复杂情况就要编码多些,太简单了些。还有就是jquery UI的里面的tab控件(没用过,我对jquery ui不太感冒),另外就是近期有点小火的easyui 中的tab控件,最早在javaeye上面看到的,界面还算漂亮,因为之前没开源,所以一直没跟进(好像最近开源了,前几天下载了看看,编码风格有点像prototype,看不出jquery的影子,不知道为什么叫jquery easyui呵呵,因为没太深入去学习,也不好做其他评价)。说了那么多,我们还是回到主题,因为种种原因不得不想着自己开发一个吧。于是就有了这篇,先来看看效果吧。

    下图是单网页多窗口框架的效果图

    image

    下载上图demo 

    下图是文末提供调用示例的效果截图。

    image

    大家可以看到了还是使用ExtJs的效果。其实CSS基本上是直接copy它的。我觉得它那个就非常好看,当然实际使用的时候大家有能力完全可以自己样子

    第一 我们还是从HTML开始吧

    注:我先控件的思路始终是先确定HTML结构,其次是样式,最终才是js实现的事件方法等。

    其实看图我们就可以基本确定,tab控件主要有两个部分的html 一个是头,用于放tab选项卡的;另外一个是体,是内容的容器。那么就是两个Div容器,讲tab控件分成了header和body两部分。

    其中header部分因为包含多选项卡 所以很容易想到ul +li的配合。来看一下header中的实际html结构

    image

    通过通过其中li即是一个选项卡,第一个a是关闭按钮,第二个a才是实际内容 通过嵌套标签来实现 左右中的背景图片设置(这个做法比较多见的)。当然能够有个好的效果,还是要靠CSS支持。必须对CSS有一定的了解。

    Body的结构则更简单就是div嵌套div就就结束了。

    第二 CSS样式表

    因为CSS是copy EXTJS的我也就不多介绍了。大家可以看代码下载里面的实际代码,如果有问题可以再沟通交流

    第三:开始编写JS了

    老规矩先来一段完整的JS代码,大概有500行左右的代码,其实我换行比较勤快,实际的代码量其实还是比较少。

    ; (function ($) {
        $.fn.tabpanel =function(option){
            var dfop ={
                   items:[], //选项卡数据项 {id,text,classes,disabled,closeable,content,url,cuscall,onactive}
                   500,
                   height:400,
                   scroll100,//如果存在滚动条,点击按钮次每次滚动的距离
                   autoscroll:true //当选项卡宽度大于容器时自动添加滚动按钮
           };
           var headerheight=28;
           $.extend(dfop, option);      
           var me =$(this).addClass("x-tab-panel").width(dfop.width);
           innerwidth = dfop.width-2;
           //构建Tab的Html    
           var tcs= dfop.autoscroll?"x-tab-scrolling-top":"";      
           var header = $("<div class='x-tab-panel-header x-unselectable "+tcs+"' unselectable='on' style='"+innerwidth+"px;MozUserSelect:none;KhtmlUserSelect:none;'></div>");
           var stripwrap = $("<div class='x-tab-strip-wrap'/>");
           var scrollerright = $("<div class='x-tab-scroller-right x-unselectable' style='height: 24px; visibility: hidden; mozuserselect: none; khtmluserselect: none;' unselectable='on'/>");
           var scrollerleft = $("<div class='x-tab-scroller-left x-unselectable' style='height: 24px; visibility: hidden; mozuserselect: none; khtmluserselect: none;' unselectable='on'/>");
           var ulwrap = $("<ul class='x-tab-strip x-tab-strip-top'></ul>");
           var stripspacer = $("<div class='x-tab-strip-spacer'/>");
           var litemp =[];
           for(var i=0,l=dfop.items.length; i<l ;i++)
           {          
               var item =dfop.items[i];
               builditemlihtml(item,litemp);
           }
           litemp.push("<li class='x-tab-edge'/><div class='x-clear'></div>");
           
           ulwrap.html(litemp.join(""));
           litemp =null;
           stripwrap.append(ulwrap);
           if(dfop.autoscroll)
           {
             header.append(scrollerright).append(scrollerleft);
           }
           header.append(stripwrap).append(stripspacer);
           var bodyheight=dfop.height-headerheight;
           var bodywrap = $("<div class='x-tab-panel-bwrap'/>");
           var body = $("<div class='x-tab-panel-body x-tab-panel-body-top'/>").css({innerwidth,height:bodyheight});
           var bodytemp=[];
           for(var i=0,l=dfop.items.length; i<l ;i++){          
              var item =dfop.items[i];
              builditembodyhtml(item,bodytemp);       
           }
           body.html(bodytemp.join("")).appendTo(bodywrap);
           me.append(header).append(bodywrap);
           initevents();
           function builditemlihtml(item,parray)
           {           
               parray.push("<li id='tab_li_",item.id,"' class='",item.isactive?"x-tab-strip-active":"",item.disabled?"x-tab-strip-disabled":"",item.closeable?" x-tab-strip-closable":"",item.classes?" x-tab-with-icon":"","'>");
               parray.push("<a class='x-tab-strip-close' onclick='return false;'/>");
               parray.push("<a class='x-tab-right' onclick='return false;' href='#'>");
               parray.push("<em class='x-tab-left'><span class='x-tab-strip-inner'><span class='x-tab-strip-text ",item.classes||"","'>",item.text,"</span></span></em>");
               parray.push("</a></li>");    
           }
           function builditembodyhtml(item,parray)
           {  
              parray.push("<div class='x-panel x-panel-noborder",item.isactive?"":" x-hide-display","' id='tab_item_",item.id,"' style='",innerwidth,"px'>");
              parray.push("<div class='x-panel-bwrap'>");
              parray.push("<div class='x-panel-body x-panel-body-noheader x-panel-body-noborder'  id='tab_item_content_",item.id,"' style='position:relative;  ",innerwidth,"px; height:",bodyheight,"px; overflow: auto;'>");
              if(item.url){
                parray.push("<iframe name='tab_item_frame_",item.id,"' width='100%' height='100%'  id='tab_item_frame_",item.id,"' src='about:blank' frameBorder='0' />");
              }
              else if(item.cuscall){
                parray.push("<div class='loadingicon'/>");
              }
              else{
                parray.push(item.content);
              }
              parray.push("</div></div></div>");
           }
           function initevents()
           {
                //reset scoller
                resetscoller();
                scollerclick();
                ulwrap.find("li:not(.x-tab-edge)").each(function(e){
                  inititemevents(this);
                });
           }
           function inititemevents(liitem)
           {
                liswaphover.call(liitem);
                liclick.call(liitem);
                closeitemclick.call(liitem);
           }
           function scollerclick()
           {
                 if(dfop.autoscroll)
                {
                    scrollerleft.click(function(e){scolling("left")});
                    scrollerright.click(function(e){scolling("right")});
                }
           }
           function resetscoller()
           {
               if(dfop.autoscroll)
               {
                   var edge = ulwrap.find("li.x-tab-edge");
                   var eleft =edge.position().left;
                   var sleft = stripwrap.attr("scrollLeft");              
                   if( sleft+eleft>innerwidth )
                   {
                        
                        header.addClass("x-tab-scrolling");
                        scrollerleft.css("visibility","visible");
                        scrollerright.css("visibility","visible");
                        if(sleft>0)
                        {
                           scrollerleft.removeClass("x-tab-scroller-left-disabled");
                        }
                        else{
                          scrollerleft.addClass("x-tab-scroller-left-disabled");
                        }
                        if(eleft>innerwidth)
                        {
                           scrollerright.removeClass("x-tab-scroller-right-disabled");
                        }
                        else{
                           scrollerright.addClass("x-tab-scroller-right-disabled");
                        }
                        dfop.showscrollnow =true;
                       
                   }
                   else
                   {
                        header.removeClass("x-tab-scrolling");
                        stripwrap.animate({"scrollLeft":0},"fast");
                        scrollerleft.css("visibility","hidden");
                        scrollerright.css("visibility","hidden");
                        dfop.showscrollnow =false;
                   }
               }
           }
           //
           function scolling(type,max)
           {
                //debugger;
                if(!dfop.autoscroll || !dfop.showscrollnow)
                {
                    return;
                }
                //debugger;
                //var swidth = stripwrap.attr("scrollWidth");
                var sleft = stripwrap.attr("scrollLeft");
                var edge = ulwrap.find("li.x-tab-edge");
                var eleft = edge.position().left ;            
                if(type=="left"){
                  if(scrollerleft.hasClass("x-tab-scroller-left-disabled"))
                  {
                    return;
                  }           
                  if(sleft-dfop.scrollwidth-20>0)
                  {
                    sleft -=dfop.scrollwidth;                
                  }
                  else{
                    sleft =0;
                    scrollerleft.addClass("x-tab-scroller-left-disabled");
                  }
                  if(scrollerright.hasClass("x-tab-scroller-right-disabled"))
                   {
                      scrollerright.removeClass("x-tab-scroller-right-disabled");
                   } 
                  stripwrap.animate({"scrollLeft":sleft},"fast");
                }
                else{    
                   if(scrollerright.hasClass("x-tab-scroller-right-disabled") && !max)
                   {
                     return;
                   }              
                    //left + ;
                   if(max || (eleft>innerwidth && eleft-dfop.scrollwidth-20<=innerwidth))
                   {         
                     //debugger;
                     sleft = sleft+eleft-(innerwidth-38) ;
                     scrollerright.addClass("x-tab-scroller-right-disabled");
                     // sleft = eleft-innerwidth;
                   }
                   else
                   {                 
                      sleft +=dfop.scrollwidth;                 
                   }
                   if(sleft>0)
                   {
                      if(scrollerleft.hasClass("x-tab-scroller-left-disabled"))
                      {
                        scrollerleft.removeClass("x-tab-scroller-left-disabled");
                      }
                   }              
                  stripwrap.animate({"scrollLeft":sleft},"fast");
                }
           }
           function scollingToli(liitem)
           {
                var sleft = stripwrap.attr("scrollLeft");    
                var lleft = liitem.position().left;
                var lwidth = liitem.outerWidth(); 
                var edge = ulwrap.find("li.x-tab-edge");
                var eleft = edge.position().left ; 
                if(lleft<=0)
                {
                    sleft +=(lleft-2) ;               
                    if(sleft<0)
                    {
                        sleft=0;
                        scrollerleft.addClass("x-tab-scroller-left-disabled");
                    }   
                    if(scrollerright.hasClass("x-tab-scroller-right-disabled"))
                    {
                      scrollerright.removeClass("x-tab-scroller-right-disabled");
                    }                    
                    stripwrap.animate({"scrollLeft":sleft},"fast");
                }
                else{
                    if(lleft+lwidth>innerwidth-40)
                    {                     
                        sleft = sleft+lleft+lwidth+-innerwidth+40; // 40 =scrollerleft and scrollerrightwidth;
                        if(scrollerleft.hasClass("x-tab-scroller-left-disabled"))
                        {
                          scrollerleft.removeClass("x-tab-scroller-left-disabled");
                        } 
                        //滚到最后一个了,那么就要禁用right;
                        if(eleft-(lleft+lwidth+-innerwidth+40)<=innerwidth)
                        {
                            scrollerright.addClass("x-tab-scroller-right-disabled");
                        }
                        stripwrap.animate({"scrollLeft":sleft},"fast");
    
                    }
                }
                liitem.click();           
                
           }
           function liswaphover()
           {
              $(this).hover(function(e){
                  if(!$(this).hasClass("x-tab-strip-disabled"))
                  {
                    $(this).addClass("x-tab-strip-over");
                  }
              },function(e){ 
                  if(!$(this).hasClass("x-tab-strip-disabled"))
                  {
                    $(this).removeClass("x-tab-strip-over");
                  }
              });
           }
           function closeitemclick()
           {
             if($(this).hasClass("x-tab-strip-closable"))
             {
                $(this).find("a.x-tab-strip-close").click(function(){
                    deleteitembyliid($(this).parent().attr("id"));
                });
             }
           }
           function liclick()
           {
              $(this).click(function(e){
                 var itemid = this.id.substr(7);
                 var curr = getactiveitem();
                 if( curr !=null && itemid == curr.id)
                 {
                    return;
                 }
                 var clickitem = getitembyid(itemid);
                 if(clickitem && clickitem.disabled)
                 {
                     return ;
                 }
                 if(curr)
                 {             
                    $("#tab_li_"+curr.id).removeClass("x-tab-strip-active");
                    $("#tab_item_"+curr.id).addClass("x-hide-display");
                    curr.isactive =false;
                 }
                 if(clickitem)
                 {
                    
                    $(this).addClass("x-tab-strip-active");
                    $("#tab_item_"+clickitem.id).removeClass("x-hide-display");
                    if(clickitem.url)
                    {
                        var cururl = $("#tab_item_frame_"+clickitem.id).attr("src");
                        if(cururl =="about:blank")
                        {
                            $("#tab_item_frame_"+clickitem.id).attr("src",clickitem.url);
                        }
                    }
                    else if(clickitem.cuscall && !clickitem.cuscalled)
                    {
                       var panel = $("#tab_item_content_"+clickitem.id);
                       var ret = clickitem.cuscall(this,clickitem,panel);
                       clickitem.cuscalled =true;
                       if(ret) //如果存在返回值,且不为空
                       {
                           clickitem.content = ret;
                           panel.html(ret);
                       }
                    }
                    clickitem.isactive =true;
                    if(clickitem.onactive)
                    {
                       clickitem.onactive.call(this,clickitem);
                    }
                 }
              });
           }
           //获取当前活跃项
           function getactiveitem()
           {
                for(var i=0,j=dfop.items.length;i<j ;i++)
                {
                    if(dfop.items[i].isactive)
                    {
                        return dfop.items[i];
                        break;
                    }
                }
                return null;
           }
           //根据ID获取Item数据
           function getitembyid(id)
           {
                for(var i=0,j=dfop.items.length;i<j ;i++)
                {
                    if(dfop.items[i].id == id)
                    {
                        return dfop.items[i];
                        break;
                    }
                }
                return null;
           }
           function getIndexbyId(id)
           {
                for(var i=0,j=dfop.items.length;i<j ;i++)
                {
                    if(dfop.items[i].id == id)
                    {
                        return i;
                        break;
                    }
                }
                return -1;
           }
           //添加项
           function addtabitem(item)
           {
              var chkitem =getitembyid(item.id);
              if(!chkitem){
                var isactive =item.isactive;
                item.isactive =false;
                var lastitem = dfop.items[dfop.items.length-1];
                dfop.items.push(item);
                var lastli = $("#tab_li_"+lastitem.id);
                var lastdiv = $("#tab_item_"+lastitem.id);
                var litemp =[];
                var bodytemp = [];
                builditemlihtml(item,litemp);
                builditembodyhtml(item,bodytemp);
                var liitem = $(litemp.join(""));
                var bodyitem= $(bodytemp.join(""));
                lastli.after(liitem);
                lastdiv.after(bodyitem);
                //事件
                var li = $("#tab_li_"+item.id);
                inititemevents(li);           
                if(isactive)
                {                
                   li.click();
                }    
                resetscoller(); 
                scolling("right",true);
              }
              else{
                alert("指定的tab项已存在!");
              }
           }
           function openitemOrAdd(item,allowAdd)
           {
              var checkitem =  getitembyid(item.id);
              if(!checkitem && allowAdd )
              {
                addtabitem(item);
              }
              else{
                 var li = $("#tab_li_"+item.id);
                 scollingToli(li);
              }
           }
           //移除一个tab 项
           function deleteitembyliid(liid)
           {
              var id= liid.substr(7);
              $("#"+liid).remove();    
              $("#tab_item_"+id).remove();
              var index = getIndexbyId(id);
              if(index>=0)
              {
                var nextcur;
                if(index < dfop.items.length -1)
                {
                 nextcur = dfop.items[index+1];
                }
                else if(index>0){             
                    nextcur = dfop.items[index-1];
                }
                if(nextcur)
                {
                     $("#tab_li_"+nextcur.id).click();
                }
                dfop.items.splice(index,1);
                resetscoller();           
                scolling("right",true);
              }
           }
           function resize(width,height)
           {
            if(width ==dfop.width && height ==dfop.height)
            {
                return;
            }
             if(width){ dfop.width=width};
             if(height){ dfop.height =height;}
             innerwidth = width-2;
             bodyheight=dfop.height-headerheight;
             me.css("width",dfop.width);
             header.css("width",innerwidth);
             body.css({innerwidth,height:bodyheight});
             for(var i=0,j=dfop.items.length;i<j;i++)
             {
                var item =dfop.items[i];
                $("#tab_item_"+item.id).css({innerwidth});
                $("#tab_item_content_"+item.id).css({innerwidth,height:bodyheight});
             }
             resetscoller();
           }
           //设置选项卡项是否disabled
           function setdisabletabitem(itemId,disabled)
           {
                 var chitem= getitembyid(itemId);
                 if(!chitem || chitem.disabled ==disabled)
                 {
                    return;
                 }
                 if(disabled)
                 {
                    chitem.disabled =true;
                    $("#tab_item_"+item.id).addClass("x-tab-strip-disabled");
                 }
                 else{
                   chitem.disabled =false;
                   $("#tab_item_"+item.id).removeClass("x-tab-strip-disabled");
                 }
           }
           me[0].tab = {
            addtabitem:addtabitem,
            opentabitem:openitemOrAdd,
            resize:resize,
            setdisabletabitem:setdisabletabitem
           };
        };
        $.fn.addtabitem =function(item)
        {
            if(this[0].tab)
            {
               return this[0].tab.addtabitem(item);
            }
            return false;
        }
        $.fn.opentabitem =function(item,orAdd)
        {
            if(this[0].tab)
            {
               return this[0].tab.opentabitem(item,orAdd);
            }
            return false;
        }
        $.fn.resizetabpanel =function(w,h)
        {
            if(this[0].tab)
            {
               return this[0].tab.resize(w,h);
            }
            return false;
        }
        $.fn.setdisabletabitem =function(itemId,disabled)
        {
            if(this[0].tab)
            {
               return this[0].tab.setdisabletabitem(itemId,disabled);
            }
            return false;
        }
    })(jQuery);

    接着我们来一步一步来分析我的实现,开始还是编写jQuery控件的“模板”,关于为什么要这么写,请参考这篇的说明

    ; (function ($) {
        $.fn.tabpanel =function(option){
    };
    )(jQuery);

    接着就是编写默认参数

    var dfop ={
                   items:[], //选项卡数据项 {id,text,classes,disabled,closeable,content,url,cuscall,onactive}
                   500,
                   height:400,
                   scroll100,//如果存在滚动条,点击按钮次每次滚动的距离
                   autoscroll:true //当选项卡宽度大于容器时自动添加滚动按钮
           };

    默认参数还是比较简单,我已加上了注释,其中就是item数组的项麻烦些,不过我相信大家通过字面的意思就已经知道大半了,我还是描述一下吧:id 即标示,必须唯一、text显示的文本、classes 特定的样式,如效果中的主页,我加了个图标,就通过此属性实现、disabled 是否禁用、closeable 是否可关闭、

    content 和url 和cuscall 三个只要设置其中之一即可,content就是实际的内容html、url标示内容为网页,自动往内容中添加iframe,cuscall则是自定义,即内容显示什么有cuscall执行的结果来决定,可通过此属性来实现异步content内容。

    onactive是指当tab项被激活时触发的事件。 是一个接受item内容的函数,详见demo

    参数设置完了,通过外部传递的参数来更新默认的参数:

    $.extend(dfop, option);    

    接着就是构建html的部分,这部分比较长,我就不重复贴代码了。

    当我们把html构建完成之后,就要给html元素添加事件,包括 选项卡的点击事件,左移按钮,右移按钮的点击事件,选项卡的鼠标hover效果事件等。

     
     function initevents()
           {
                //reset scoller
                resetscoller(); //设置默认是否出现滚动掉
                scollerclick(); //滚动条的点击事件,如果存在的话
                ulwrap.find("li:not(.x-tab-edge)").each(function(e){
                  inititemevents(this); //给每个选项卡 添加事件
                });
           }
           function inititemevents(liitem)
           {
                liswaphover.call(liitem); //选项卡的鼠标hover效果
                liclick.call(liitem); //选项卡的点击事件
                closeitemclick.call(liitem); // 点击关闭按钮的事件
           }

    至于事件的实现,其实一个个来做,各个击破也就简单了。主要繁琐在控制滚动按钮的出现和禁用等的处理上,其他点击事件等都比较简单。

    最后就是公开方法,和为了公开这些方法来编写一些内部方法,这个tabpanel自然还是比较简单易用,同时扩展性。大家可以根据实际的需求做些调整,当然现在的功能应该也满足大部分的要求了。

    最后来看一下公开了哪些方法:

    1:动态 新增tab项的方法,即通过js动态新增tab项,这里其实就是对items数据的维护,然后重新调用tabitem的输出html方法,最后单独为其设置事件。简单

    2:选中或者新增。这也是通过js调用的方法,是对上一方法的扩展,即可通过js让某个tab项激活,如果该项不存在则通过参数来新增该选项卡

    3:重新设置tabpanel的大小,即通过js重新设置tabpanel的大小,这个在窗口大小变化时调用,非常实用哦。

    4:设置某项为禁用,通过js方法设置某项tabitem状态为禁用。

    附上演示的地址:

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

    最后大家可以通过 代码 包括之前控件的实例,我已经提供了一个压缩包,但是我更推荐大家实用SVN获取最新代码。因为有的时候一些小的变动我就不发文告知了。

    http://code.google.com/p/xjplugin/downloads/list

  • 相关阅读:
    第二次结对编程作业
    5 线性回归算法
    4 K均值算法--应用
    3 K均值算法
    2 机器学习相关数学基础
    1 机器学习概述
    15. 语法制导的语义翻译
    14.算符优先分析
    13.自下而上语法分析
    12.实验二 递归下降语法分析
  • 原文地址:https://www.cnblogs.com/xuanye/p/Xuanye_jQuery_TabPanel.html
Copyright © 2020-2023  润新知