Stylin’ with CSS

The Blog and Books of Charles Wyke-Smith

Click States with jQuery - A Tabbed Panel - Part 2

by Charles Wyke-Smith

In Click States with jQuery - Part 1, you saw how a toggle interaction (once for on, once for off) could be created by using jQuery to add and remove a class alternately each time an HTML element is clicked. When the class is added to the HTML element, a CSS rule whose selector uses that class is then invoked to produce a visual change onscreen. The example in Part 1 was intended to simply illustrate the concept of class switching—in this second article, let's look at a more practical (and of course, more complex) example that uses class switching to manage a tabbed panel. When a tab on the panel is clicked, the addition of a class to the HTML markup causes the related content to animate into position. Let's start with the HTML


<div id="tab_panel">
  <nav>
  <ul>
    <li><a class="tab show" href="#visual_stylin">Visual Stylin'</a></li>
    <li><a class="tab" href="#scriptin">Scriptin'</a></li>
    <li><a class="tab" href="#stylin">Stylin'</a></li>
  </ul>
  </nav>
 <section> 
    <article id="tab1 show">Visual Stylin..etc.</article>
    <article id="tab2">Scriptin' with Ajax...etc.</article>
    <article id="tab3">Stylin' with CSS...etc.</article>
 </section>
</div>

And here's the CSS


div#menu * {margin:0; padding:0;}
  div#menu { 
  width:600px; 
  font-family:Helvetica, Arial, sans-serif;
  margin:0 0 10px 0;
          }
#menu ul {
  display:inline-block; 
  overflow:hidden; 
  margin:0 0 -5px 0; /* positions the tabs over the box */
  }
#menu li {float:left; list-style-type:none;}
#menu a {
  display:block; 
  position:relative; 
  z-index:3; /* moves tabs on top of main area */
  padding:3px 10px; /* space around tab type */
  margin:0 4px 0 0; /* space between tabs */
  border:1px solid #069; 
  border-top-left-radius:5px; border-top-right-radius:5px;
  background:-webkit-linear-gradient(#eee, #fff);
  background:-moz-linear-gradient(#f4f4f4, #fff); 
  /* add other VSPs as needed */
  background:linear-gradient(#f4f4f4, #fff); 
  color:#069; text-decoration:none;
  }
#menu a:hover {
  background:#aaa; /* for browsers that can't do gradients */
  background:-webkit-linear-gradient(#eee, #fff);
  background:-moz-linear-gradient(#eee, #fff);
  color:#888; 
  }
#menu li.show {color:#069;}
#menu a.show {
  background:#fff; 
  color:#333;  
  border-bottom:1px solid #fff;
  }
div#menu section {
  position:relative; 
  overflow:hidden; 
  height:250px; 
  border:1px solid #069; 
  border-radius:0 5px 5px 5px; 
  box-shadow:0 10px 10px 0px #aaa;
  background:#fff; 
  }
#menu section div {
  left:100%;
  overflow-y:scroll; /* makes content vertically scrollable */
  position:absolute;/* positioning context for sliding panels */
  height:210px;
  width:550px; 
  top:0px;  
  margin:0 10px; /* prevents covering of rounded corners */
  padding:20px; 
  -webkit-transition:1s left ease-out; 
  -moz-transition:1s left ease-out;
  transition:1s left ease-out;
  background:#fff;
  }
#menu section div.show {
  left:0%; 
  z-index:10;
  }

Figure 5 shows the tabbed panel component.

Figure 5 A tabbed panel interface component.

You can see in the markup that I have added the show class on the second tab and its related content element. Adding these classes causes that choice to be displayed when the page loads; if you choose not to add them, then nothing will be displayed until the user clicks and the classes are added by the code.

If you click on a tab, two things happen—the tab is highlighted and the related content is displayed. Let's look at the jQuery that makes that happen.


$(document).ready(function(){             /* when the page is  loaded in browser... */
  $('nav a.tab').click(function() {       /* bind click event to the tab links. */
    $('#tab_panel *').removeClass('show');/* When tab clicked, remove 'show' classes */
    var theRef=$(this).attr('href');      /* get href value of  clicked element */
    $(theRef).addClass('show')            /* add 'show' class to panel with that ID */
    $(this).addClass('show')              /*  add 'show' class the clicked tab */
    return false;                         /* stop HTML executing the click */
    });
  });
});

Each of the links that form the tabs has an href such as #stylin that is in the format of an anchor link. In HTML, anchor links scroll the page to an element with the corresponing ID. I can override this behavior with jQuery, and use this same markup to associate a clicked element with an element that the click will affect. All I have to do is grab the href of the clicked element and use that as my selector name in jQuery to select the paragraph I want to display.

Each paragraph is initially positioned at 100% left so its left edge aligns with the right edge of its section element parent. That means it’s positioned outside of its parent section element, off to the right, and because the overflow property of the section element is set to hidden, the paragraph is not visible. When the class is added, the transition is triggered to set the paragraph to 0% left, so then it slides back within the the parent box where it is visible. Here is the part of the CSS that does this work when the class is added.


#menu p {
    position:absolute; 
    height:250px;
    width:550px;
    padding:20px; 
    top:0px; 
    left:100%; 
    -webkit-transition:1s left ease-out; background:#fff;
    }
#menu p.show {
    left:0%; 
    z-index:10;
    }

You can see that the absolutely-positioned paragraph has a transition associated with it that animates the left property. Note that a CSS transition is set on the initial state, not the end state.

I also use z-index, which controls the stacking order of elements, to place the tabs over the border around the section element where the content appears. This creates the illusion that the tabs and the content area are all one element, enabling me to highlight the active tab the same color as the content area to give the appearance of the tab moving to the front.

Note in the final line of the code, I use return false to ensure that the HTML does not also handle the click; I don't want the default HTML anchor link effect that would scroll the page.

Also, note there is a small horizontal margin on the sliding div. This prevents it from sliding in all the way to the left where its square (un-rounded) corners would cover the rounded corners of its section container. I don’t know why rounded corners don't clip their content, but they don’t, even when overflow is set to hidden, as in this case.

I hope this gives you some ideas for how you can create click responses using jQuery to add and remove classes. Once you get the hang of how this code works it’s easy to do things like disable a Submit button once the user clicks it to prevent multiple submissions, or dispay and hide additional information for the user. Happy coding.

Leave a comment

More posts