打包下載
如大量信息查看,當網頁多窗口框架等都會用到,現在網上基於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