4
\$\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');
                            }
                            });
    }
\$\endgroup\$

migrated from stackoverflow.com Feb 21 '11 at 21:08

This question came from our site for professional and enthusiast programmers.

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)

\$\endgroup\$
  • \$\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 Feb 21 '11 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 Feb 21 '11 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 Feb 22 '11 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 Feb 22 '11 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 Feb 22 '11 at 10:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.