I am creating a tab plugin. I want to know if there is a better way of doing this or if what I have is good. It works just fine, but there may be some shortcuts or a more optimized way of accomplishing this. I plan to replace the startTab
variable with an options set, but I am not quite there yet.
View the current updated version here
Here is what I have so far:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<style>
ul.tabNavigation {
list-style: none;
margin: 0;
padding: 0;
}
ul.tabNavigation li {
display: inline;
}
ul.tabNavigation li a {
text-decoration: none;
color:#000;
}
.tabViews {
width: 100%;
border: #8db2e3 solid 1px;
clear:both;
height:18px;
margin-top: -12px;
background: url(css/tabs/images/tabs_Lower_Header_Background.png) repeat-x;
}
.tab {
height: 25px;
font-family:'Trebuchet MS', Arial, Helvetica, sans-serif;
font-size:14px;
float:left;
padding-left: 4px;
position: relative;
top: 1px
}
.tab .right {
float:left;
background:url(css/tabs/images/tab_Selected_Right.png) no-repeat;
height:25px;
width: 8px;
}
.tab .content {
float:left;
background:url(css/tabs/images/tab_Selected_Content.png) repeat-x;
height:25px;
text-align: center;
padding-left: 5px;
padding-right: 5px;
padding-top: 4px;
}
.tab .left {
float:left;
background:url(css/tabs/images/tab_Selected_Left.png) no-repeat;
height:25px;
width: 6px;
}
.tabHover {
height: 25px;
font-family:'Trebuchet MS', Arial, Helvetica, sans-serif;
font-size:14px;
float:left;
padding-left: 4px;
position: relative;
top: 1px;
cursor: pointer;
}
.h1 {
float:left;
height:25px;
width: 14px;
}
.h2 {
float:left;
height:25px;
text-align: center;
padding-left: 7px;
padding-right: 5px;
padding-top: 4px;
}
.h3 {
float:left;
height:25px;
width: 10px;
}
.tabHover:hover .h1 {
background:url(css/tabs/images/tab_Hover_Right.png) no-repeat;
}
.tabHover:hover .h2 {
background:url(css/tabs/images/tab_Hover_Content.png) repeat-x;
}
.tabHover:hover .h3 {
background:url(css/tabs/images/tab_Hover_Left.png) no-repeat;
}
</style>
<script type="text/javascript" src="javascript/jquery/jquery.js"></script>
<script>
$.fn.tabs = function(startTab){
//Set currentIndex
var currentIndex = 0;
//Get all tab views
var tabViews = $('.tabViews > div',$(this));
//Current tab container
var tabsContainer = $(this);
//Hide all tabView containers
tabViews.hide();
//Get all tabs
var $tabs = $('ul > li', tabsContainer);
$tabs.each(function(index){
$('a',$(this)).click(function(){
changeTab(index);
});
});
//call the changeTab method for the first time and selected the starting tab.
changeTab(startTab);
function changeTab(selectedIndex){
if(selectedIndex != currentIndex){
switchClass(selectedIndex);
var previousSelectedTab = $('a',$tabs[currentIndex]);
var currentSelectedTab = $('a',$tabs[selectedIndex]);
$(previousSelectedTab.attr('href')).hide();
$(currentSelectedTab.attr('href')).show();
currentIndex = selectedIndex;
}
}
//Method to switch the calles for selected and non selected tabs.
function switchClass(selectedIndex){
$tabs.each(function(index){
//Get Child Left, Content, right divs
var classBuilder = $('div > div',$(this))
//Current Selected Tab
if(index == selectedIndex){
var tabContainer = $('a > div',$(this))
tabContainer.removeClass('tabHover');
tabContainer.addClass('tab');
$(classBuilder[0]).removeClass('h3');
$(classBuilder[0]).addClass('left');
$(classBuilder[1]).removeClass('h2');
$(classBuilder[1]).addClass('content');
$(classBuilder[2]).removeClass('h1');
$(classBuilder[2]).addClass('right');
}
//Previously selected tab
if(index == currentIndex){
var tabContainer = $('a > div',$(this))
tabContainer.removeClass('tab');
tabContainer.addClass('tabHover');
$(classBuilder[0]).removeClass('left');
$(classBuilder[0]).addClass('h3');
$(classBuilder[1]).removeClass('content');
$(classBuilder[1]).addClass('h2');
$(classBuilder[2]).removeClass('right');
$(classBuilder[2]).addClass('h1');
}
});
}
}
$(function() {
$('#rfiTabs').tabs(0);
});
</script>
</head>
<body>
<div id="rfiTabs">
<ul class="tabNavigation">
<li><a href="#rfiBasic">
<div class="tab">
<div class="left"></div>
<div class="content">Basic</div>
<div class="right"></div>
</div>
</a></li>
<li><a href="#rfiHome">
<div class="tabHover">
<div style="" class="h3"></div>
<div style="" class="h2">Home</div>
<div class="h1"></div>
</div>
</a></li>
</ul>
<div class="tabViews" style="">
<div id="rfiBasic">Test</div>
<div id="rfiHome">test this</div>
</div>
</div>
</body>
</html>
I realized I can optimize the left, content, and center part of the tabs by updating the CSS and jQuery for the hover section:
.tabHover .right {
float:left;
height:25px;
width: 14px;
}
.tabHover .content {
float:left;
height:25px;
text-align: center;
padding-left: 7px;
padding-right: 5px;
padding-top: 4px;
}
.tabHover .left {
float:left;
height:25px;
width: 10px;
}
.tabHover:hover .right {
background:url(css/tabs/images/tab_Hover_Right.png) no-repeat;
}
.tabHover:hover .content {
background:url(css/tabs/images/tab_Hover_Content.png) repeat-x;
}
.tabHover:hover .left {
background:url(css/tabs/images/tab_Hover_Left.png) no-repeat;
}
function switchClass(selectedIndex){
$tabs.each(function(index){
//Get Child Lef, Content, right divs
var classBuilder = $('div > div',$(this))
if(index == selectedIndex){
var tabContainer = $('a > div',$(this))
tabContainer.removeClass('tabHover');
tabContainer.addClass('tab');
//$(classBuilder[0]).removeClass('h3');
// $(classBuilder[0]).addClass('left');
// $(classBuilder[1]).removeClass('h2');
// $(classBuilder[1]).addClass('content');
// $(classBuilder[2]).removeClass('h1');
// $(classBuilder[2]).addClass('right');
}
if(index == currentIndex){
var tabContainer = $('a > div',$(this))
tabContainer.removeClass('tab');
tabContainer.addClass('tabHover');
//$(classBuilder[0]).removeClass('left');
// $(classBuilder[0]).addClass('h3');
// $(classBuilder[1]).removeClass('content');
// $(classBuilder[1]).addClass('h2');
// $(classBuilder[2]).removeClass('right');
// $(classBuilder[2]).addClass('h1');
}
});
}
1 Answer 1
You could remove tab navigation from html and create it dynamically in plugin so the html will look like this:
<div id="rfiTabs">
<div class="tabViews" style="">
<div id="rfiBasic">Test</div>
<div id="rfiHome">test this</div>
</div>
</div>
</body>
</html>
you don't need to call
var tabsContainer = $(this);
this is already jQuery object
inside switchClass function you call $(this) 3 times you can call it once and stor the value
var $this = $(this);
you get element from jQuery object and then wrap it again.
$(classBuilder[0]).removeClass('h3');
you could use eq jquery method
classBuilder.eq(0).removeClass('h3');
instead
if(index == selectedIndex){
}
if(index == currentIndex){
}
use
if(index == selectedIndex){
} else if(index == currentIndex){
}
you don't need to check if index == currentIndex
if index == selectedIndex
this code is repeaded twice but with different class names create a function for it (it looks almost the same — if you see something like this always create new function)
$(classBuilder[0]).removeClass('h3');
$(classBuilder[0]).addClass('left');
$(classBuilder[1]).removeClass('h2');
$(classBuilder[1]).addClass('content');
$(classBuilder[2]).removeClass('h1');
$(classBuilder[2]).addClass('right');
and you can chain jquery methods:
classBuilder.eq(0).removeClass('h3').addClass('left');
classBuilder.eq(1).removeClass('h2').addClass('content');
classBuilder.eq(2).removeClass('h1').addClass('right');
you don't need to iterate over $tabs
function switchClass(selectedIndex){
$tabs.eq(selectedIndex)
and I think that this:
var classBuilder = $tabs.eq(selectedIndex).find('div > div');
will be the same as
var classBuilder = $('div > div',$(this))
and this:
if(index == currentIndex){
var tabContainer = $('a > div',$(this))
tabContainer.removeClass('tab');
tabContainer.addClass('tabHover');
is the same as
$tabs.eq(currentIndex).find('a> div').removeClass('tab').addClass('tabHover');
and at the end of the script you should return this
so it could be chained
$('#rfiTabs').tabs(0).css('background-color', 'red');
you can also do this:
return this.each(function() { var $this = $(this); //and create your tabs here });
so if you call it
$('.myalltabs').tabs(0);
it will create tabs for every element that have .myalltabs
class
Update
if you your jquery object have multiple elements (But this is Something Completely Different)
$.fn.tabs = function(n) {
var = tabcontainer = $('<div/>').attr('class', 'mainTabClass')
.appendTo($('body'));
var nav = $('<ul>').addClass('navigation').appendTo(tabcontainer);
this.each(function() {
var $this = $(this);
var id = $this.attr('id');
$('<li>').append('<a>').appendTo(nav).children().
attr('href', '#' + id).html($this.attr('name'));
}).addClass('tabContent').detach().appendTo(tabcontainer);
nav.find('li a').click(function() {
tabcontainer.find('tabContent').removeClass('selected');
tabcontainer.find($(this).attr('href')).addClass('selected');
}).eq(n).addClass('selected');
};
in this case you need only
.selected {
display: block;
}
.mainTabClass .tabContent {
display: none;
}
....
<div id="home" name="Home Page">this is home</div>
....
<div id="about" name="About me">this is about</div>
....
name attribute in case you won't different name than id.
$('#home, #about').tabs(1);
and you will have
<div class="mainTabClass">
<ul class="navigation">
<li><a href="#home">Home Page</a></li>
<li><a href="#about">About me</a></li>
</ul>
<div class="tabContent" id="home">this is home</div>
<div class="tabContent selected" id="about">this is about</div>
</div>
I don't know why you have repeated left
, right
and content
for every tab, if you need to use those - create one instance and change it on click (in case you will have 100 tabs you will have 3 DOM elements instead of 300)
-
\$\begingroup\$ Do you think I should pass a tab array into the plugin? So instead of creating the tabs in a list format just create them inside the Jquery? \$\endgroup\$DDiVita– DDiVita2011年02月21日 22:23:58 +00:00Commented Feb 21, 2011 at 22:23
-
\$\begingroup\$ You can created it from jquery objects - $('ul li').tabs(0) this will create tabs from all li elements instead of creating multiply tabs from children inside li element, in this case you can use something like this $('div.foo, div.bar, div#baz').tabs(0) and it will create tabs from whatever are in
this
inside the plugin, so panels from your tabs can be in different palaces in the DOM. \$\endgroup\$jcubic– jcubic2011年02月21日 22:35:59 +00:00Commented Feb 21, 2011 at 22:35 -
\$\begingroup\$ I don't quite understand how my left,conent,right div structure would fit into your suggestion. Would you mind showing me an example of what you are saying? \$\endgroup\$DDiVita– DDiVita2011年02月22日 03:35:16 +00:00Commented Feb 22, 2011 at 3:35
-
\$\begingroup\$ you create tabNavigation and left,content,right divs dynamically and call
this.hide().detach().appendTo($('<div>').addClass('tabViews').appendTo(dynamicly_created_rfiTabs));
\$\endgroup\$jcubic– jcubic2011年02月22日 10:47:15 +00:00Commented Feb 22, 2011 at 10:47 -
\$\begingroup\$ or
this.hide().detach().wrapAll('<div class="tabViews"/>'). parent().appendTo(dynamicly_created_rfiTabs);
if you create a plugin you should minimize the amount of code that is needed to use it. \$\endgroup\$jcubic– jcubic2011年02月22日 10:59:16 +00:00Commented Feb 22, 2011 at 10:59
Explore related questions
See similar questions with these tags.