6
\$\begingroup\$

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');
 }
 });
 }
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Feb 21, 2011 at 20:42
\$\endgroup\$
0

1 Answer 1

6
\$\begingroup\$

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)

answered Feb 21, 2011 at 21:47
\$\endgroup\$
9
  • \$\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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented Feb 22, 2011 at 10:59

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.