您的位置:首页 > Web前端 > JQuery

用jQuery打造TabPanel效果代码

2013-05-27 16:35 453 查看

用jQuery打造TabPanel效果代码

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

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



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



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

第一 我们还是从HTML开始吧

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

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

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



通过通过其中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}

width:500,

height:400,

scrollwidth:100,//如果存在滚动条,点击按钮次每次滚动的距离

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='width:"+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({width: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='width:",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; width:",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({width:innerwidth,height:bodyheight});

for(var i=0,j=dfop.items.length;i<j;i++)

{

var item =dfop.items[i];

$("#tab_item_"+item.id).css({width:innerwidth});

$("#tab_item_content_"+item.id).css({width: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}

width:500,

height:400,

scrollwidth:100,//如果存在滚动条,点击按钮次每次滚动的距离

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状态为禁用。

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

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

http://xiazai.jb51.net/201005/yuanma/xjPlugin_addtabpanel.rar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: