For example, when viewing a large amount of information, it will be used when using multi-window frameworks on web pages. Nowadays, there are quite a lot of jquery-based Tab controls on the Internet. The idtabs I have used before is relatively simple and practical, and it is also more flexible. But for complex situations, more coding is needed, which is too simple. There is also the tab control in jquery UI (never used it, I am not very interested in jquery ui), and the tab control in easyui, which has been a bit popular recently, was first seen on javaeye , the interface is quite beautiful, because it has not been open sourced before, so I have not followed up (it seems that it has been open sourced recently. I downloaded it a few days ago and took a look. The coding style is a bit like prototype, and I can’t see the shadow of jquery. I don’t know why it is called jquery easyui haha) , because I didn’t study it in depth, and it’s not easy to make other evaluations). Having said that, let’s go back to the topic. For various reasons, we have to think about developing one ourselves. So here is this article, let’s take a look at the effect first.
The picture below is the rendering of a single web page multi-window frame
The picture below is a screenshot of the calling example provided at the end of the article.
You can see the effect of using ExtJs. In fact, CSS is basically a direct copy of it. I think it looks very good. Of course, when it comes to actual use, everyone can look it their own way
First, let’s start with HTML
Note: My idea of control first is always to determine the HTML structure first, then the style, and finally the event methods implemented by js.
In fact, looking at the picture, we can basically confirm that the tab control mainly has two parts of HTML. One is the header, which is used to place the tab tab; the other is the body, which is the container for the content. Then there are two Div containers. The tab control is divided into two parts: header and body.
The header part contains multiple tabs, so it is easy to think of the cooperation of ul li. Let’s take a look at the actual html structure in the header
By passing li as a tab, the first a is the close button, and the second a is the actual content, the background image settings in the left and right are achieved through nested tags (this approach is more common). Of course, to achieve good results, we still need CSS support. Must have some knowledge of CSS.
The structure of the Body is simpler, just a div nested within a div.
Second CSS Stylesheet
Because CSS is copied from EXTJS, I won’t introduce it in detail. You can see the actual code in the code download. If you have any questions, you can communicate
Third: Start writing JS
The old rule is to start with a complete JS code, which is about 500 lines of code. In fact, I am more diligent in changing lines, and the actual amount of code is actually relatively small.
; (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 = $("");
var stripwrap = $("
");
var scrollerright = $("
");
var scrollerleft = $("
");
var ulwrap = $("
");
var stripspacer = $("
");
var litemp =[];
for(var i=0,l=dfop.items.length; i
{
var item =dfop.items[i];
builditemlihtml(item,litemp);
}
litemp.push("");
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 = $("");
var body = $("").css({width:innerwidth,height:bodyheight});
var bodytemp=[];
for(var i=0,l=dfop.items.length; ivar 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("");
parray.push("");
parray.push("");
parray.push("",item.text,"");
parray.push("");
}
function builditembodyhtml(item,parray)
{
parray.push("");
parray.push("
");
parray.push("
");
if(item.url){
parray.push("
");
}
else if(item.cuscall){
parray.push("
");
}
else{
parray.push(item.content);
}
parray.push("
");
}
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{
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{
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{
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{
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);
Then let’s analyze my implementation step by step. Let’s start by writing jQuery The "template" of the control. Regarding why it is written like this, please refer to the instructions in this article
; (function ($) {
$.fn.tabpanel =function(option){
};
)(jQuery);
Continue Just write the default parameters
var dfop ={
items:[] , //Tab data item {id, text, classes, disabled, closeable, content, url, cuscall, onactive}
width:500,
height:400,
scrollwidth:100,//if There is a scroll bar, and the distance of scrolling each time the button is clicked
autoscroll:true //Automatically add a scroll button when the tab width is larger than the container
};
The default parameters are still relatively simple , I have added comments, among which the items of the item array are more troublesome, but I believe you already know most of them through the literal meaning. Let me describe it: id is the label, which must be unique, text displays the text, and classes For a specific style, such as the home page in the effect, I added an icon and implemented it through this attribute, whether disabled is disabled, whether closeable can be closed,
content, url and cuscall. Just set one of them. Yes, content is the actual content html, and url marks the content as a web page, and iframe is automatically added to the content. Cuscall is customized, that is, the content displayed is determined by the result of cuscall execution. This attribute can be used to implement asynchronous content.
onactive refers to the event triggered when the tab item is activated. It is a function that accepts item content. See demo for details.
After the parameters are set, update the default parameters through externally passed parameters:
$.extend(dfop, option);
The next step is to build the html. This part is quite long, so I won’t post the code again.
After we complete the html construction, we need to add events to the html elements, including click events of the tab, click events of the left move button, right move button, mouse hover effect events of the tab, etc.
function initevents()
{
//reset scoller
resetscoller(); //Set whether scrolling will appear by default
scollerclick(); //Click event of the scroll bar, if it exists
ulwrap.find(" li:not(.x-tab-edge)").each(function(e){
initemevents(this); //Add events to each tab
});
}
function inititemevents(liitem)
{
liswaphover.call(liitem); //The mouse hover effect of the tab
liclick.call(liitem); //The click event of the tab
closeitemclick. call(liitem); // Click the close button event
}
As for the implementation of the event, it is actually done one by one, and it is simple to defeat each one. The main tediousness is in controlling the appearance and disabling of scroll buttons. Other click events are relatively simple.
The last is the public methods, and to write some internal methods in order to expose these methods, this tabpanel is naturally relatively simple and easy to use, and at the same time scalable. You can make adjustments according to actual needs. Of course, the current functions should meet most requirements.
Finally, let’s take a look at the disclosed methods:
1: The method of dynamically adding tab items, that is, dynamically adding tab items through js. This is actually the maintenance of items data, and then Recall tabitem's output html method, and finally set an event for it separately. Simple
2: Select or add. This is also a method called through js. It is an extension of the previous method. You can activate a certain tab item through js. If the item does not exist, add the tab through parameters
3: Re- Set the size of the tabpanel, that is, reset the size of the tabpanel through js. This is called when the window size changes, which is very practical.
4: Set an item to be disabled, and set the tabitem status of a certain item to be disabled through the js method.
Finally, you can use the code to include examples of previous controls. I have provided a compressed package, but I recommend you to use SVN to get the latest code. Because sometimes I won’t post any notifications about small changes.
http://code.google.com/p/xjplugin/downloads/list http://xiazai.jb51.net/201005/ yuanma/xjPlugin_addtabpanel.rar