Building Vertical-Tabbed Content Sections with jQuery

by Jake Rocheleau

on September 25, 2013

in Tutorials

Ajax-style loading boxes are useful when displaying large sets of related data. Think about something like an FAQ page, knowledgebase, or support system. Any type of navigation is often suitable as long as the user can determine how to navigate between content areas.

In this tutorial I want to demonstrate how we can build a custom vertical content section using jQuery. All of the internal content is held inside div containers which can be navigated with an icon-based menu. This content isn’t loaded externally via Ajax, but is instead hidden & displayed using content sections already on the page. Check out my live sample demo to get an idea of what we are building.

jquery vertical content tabs sections tutorial preview screenshot

Getting Started

There are only two required files we need for this dynamic content section to work. The first is jQuery which you can download in a compressed local format. The other is Font Awesome which is a free open source CSS3 icon webfont. This means we can include certain fonts using @import and render icon graphics as font elements in the page.

<!doctype html>
<html lang="en-US">
<head>
  <meta charset="utf-8">
  <meta http-equiv="Content-Type" content="text/html">
  <title>jQuery Vertical Tabbed Content Sections</title>
  <meta name="author" content="Jake Rocheleau">
  <link rel="shortcut icon" href="http://designm.ag/favicon.ico">
  <link rel="icon" href="http://designm.ag/favicon.ico">
  <link rel="stylesheet" type="text/css" media="all" href="css/styles.css">
  <link rel="stylesheet" type="text/css" media="all" href="css/font-awesome.min.css">
  <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
</head>

This is what my document header looks like. I have included these two required items along with my core styles.css stylesheet. Also be sure that when you include the Font Awesome CSS file that you also copy over the entire /font/ directory. This includes all the important file types such as TTF and OTF, which are necessary to recognize any of the icon characters.

Creating Inner Body Content

The two main sections of the page are held within a single inner wrapping div. Obviously the navigation is much smaller than the content, so I have the navbar floating left without a connecting background. #sidemenu is actually an unordered list of items which display as block elements inside a fixed-width container.

<ul id="sidemenu">
  <li>
    <a href="#home-content" class="open"><i class="icon-home icon-large"></i><strong>Home</strong></a>
  </li>

  <li>
    <a href="#about-content"><i class="icon-info-sign icon-large"></i><strong>About</strong></a>
  </li>
  
  <li>
    <a href="#ideas-content"><i class="icon-lightbulb icon-large"></i><strong>Ideas</strong></a>
  </li>
  
  <li>
    <a href="#contact-content"><i class="icon-envelope icon-large"></i><strong>Contact</strong></a>
  </li>
</ul>

Now the other page element uses the ID #content which links to a number of internal div elements. These are labeled according to the navigation anchor links with the various IDs of home, about, ideas, and contact.

<div id="content">
    <div id="home-content" class="contentblock">
      <h1>The Main Page!</h1>
      <p>I swear this has some really great stuff. Content is courtesy of <a href="http://bluthipsum.com/">Bluth Ipsum</a>.</p>
      
      <p>Smack of ham. What is she doing at a beauty pageant? Is she running the lights or something? She's always got to wedge herself in the middle of us so that she can control everything. Yeah. Mom's awesome.</p>
      
      <p>Probably out there without a flipper, swimming around in a circle, freaking out his whole family.</p>
      
      <p>Fun and failure both start out the same way.</p>
    </div><!-- @end #home-content -->
    
    
    <div id="about-content" class="contentblock hidden">
      ...
    </div><!-- @end #about-content -->
    
    <div id="ideas-content" class="contentblock hidden">
      ...
    </div><!-- @end #ideas-content -->
    
    <div id="contact-content" class="contentblock hidden">
      ...
    </div><!-- @end #contact-content -->
</div><!-- @end #content -->

Notice that each of the inner div elements uses .contentblock as an additional class for styling. Yet #home-content is the only one without the extra .hidden class because this homepage content should immediately display once everything finishes loading. We can use jQuery to un-hide the other page areas and re-hide the homepage content section when switching in the navigation.

CSS Design Styles

With the Font Awesome CSS already included we can use this font family anytime. I’ve also linked to another font family named Cantora One which is hosted by Google Web Fonts. Other than this my stylesheet uses a few basic resets along with a core container for each inner div section.

@import url('http://fonts.googleapis.com/css?family=Cantora+One');

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
  outline: none;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  -ms-text-size-adjust: 100%;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
html { overflow-y: scroll; }
body { 
  font-size: 62.5%; 
  line-height: 1;
  padding: 45px 20px;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  background: #fff url('../images/bg.png'); /* http://subtlepatterns.com/connected/ */
}

br { display: block; line-height: 1.6em; } 
img { border: 0; max-width: 100%; }

h1 {
  font-family: 'Cantora One', Arial, sans-serif;
  font-size: 3.0em;
  line-height: 1.6em;
  margin-bottom: 10px;
  color: #787878;
}

p {
  font-size: 1.4em;
  line-height: 1.25em;
  color: #575757;
  font-weight: normal;
  margin-bottom: 10px;
}

a { color: #8dadd8; }
a:hover { color: #7299cf; }

/* main page */
#w {
  display: block;
  width: 900px;
  margin: 0 auto;
  background: none;
}


#content {
  display: block;
  background: #fff;
  padding: 0px 15px;
  margin-left: 80px;
  -webkit-box-shadow: 1px 2px 1px rgba(0,0,0,0.4);
  -moz-box-shadow: 1px 2px 1px rgba(0,0,0,0.4);
  box-shadow: 1px 2px 1px rgba(0,0,0,0.4);
  min-height: 300px;
}


.contentblock {
  display: block;
  padding: 15px 20px;
}

.contentblock.hidden {
  display: none;
}

You will notice that I’ve only included a white background on the main #content div which keeps the navigation off to the side as a separate element. There are so many different ways you could design this interface, but I like seeing the background on the active link turn white and appear to blend into the page itself. Also I have .contentblock.hidden set to no display which we can manipulate directly in jQuery.

/* side navigation */
#sidemenu {
  margin: 0;
  padding: 0;
  width: 80px;
  float: left;
  background: #e8e8e8;
  border-right: 1px solid #c9c9c9;
}


#sidemenu li { display: block; text-align: center; border-top: 1px solid #fff; font-size: 1.1em; }

#sidemenu li a { 
  display: block;
  padding: 6px 2px;
  color: #555;
  text-decoration: none;
  border-bottom: 1px solid #d1d1d1;

}
#sidemenu li a:hover {
  background: #f2f2f2;
}

#sidemenu li a strong { 
  display: block;
  margin-top: 5px;
}

#sidemenu li a.open {
  width: 101%;
  background: #fff;
}

The outer navigation container is fixed to a width of 80px and floated off to the left side. We can use margins and padding on the other content box to keep these two from overlapping. The #sidemenu element uses a border on the right to encapsulate all the links.

Any anchor link with the additional .open class will be set to 101% width. This means it will be wider than the border and appear to be naturally connected into the page content. Everything else about the nav CSS is related to styling the icons and the text which are held in two separate HTML elements.

jQuery Animations

Finally we get to the bottom of the index.html file which includes a small block of jQuery. The main event handler is triggered whenever a user clicks on the navigation link items. First we want to disable the click event using e.preventDefault(). Since the content is loading dynamically, we don’t want to append the page ID onto the URL unless JavaScript were disabled.

$(function(){
  $('#sidemenu a').on('click', function(e){
    e.preventDefault();

    if($(this).hasClass('open')) {
      // do nothing because the link is already open
    } else {
      var oldcontent = $('#sidemenu a.open').attr('href');
      var newcontent = $(this).attr('href');
      
      $(oldcontent).fadeOut('fast', function(){
        $(newcontent).fadeIn().removeClass('hidden');
        $(oldcontent).addClass('hidden');
      });
      
     
      $('#sidemenu a').removeClass('open');
      $(this).addClass('open');
    }
  });
});

Jumping into the actual logic, we first check if the currently clicked item has a class of .open. If so then the link is already visible so we do nothing. Otherwise we create two new variables named oldcontent and newcontent. Using jQuery .fadeOut() on the old content we can then use a callback function to add the .hidden class while simultaneously showing the new content with .fadeIn().

The final two lines of code adjust the menu item once all the inner content has finished animating. We remove all .open classes from the menu links and then apply it onto the newly selected link item. This all happens within 1-2 seconds of time so it appears very sequential. We could always use a jQuery delay if there was some need to halt the animations. But this generic interface is quite easy to setup and shouldn’t require a whole lot of convoluted scripting.

Final Thoughts

I hope this tutorial may be useful to some web designers or developers who need a similar widget. The codes are easy to transfer into your own script or even into a plugin if needed. Of course, plugin functionality might be more convoluted than we need – but this type of dynamic webpage effect is perfect for section-based content with divisions in the material. Feel free to download a copy of my source codes and see if you can implement this design in your own future projects.

Powered by Shutterstock

About Jake Rocheleau

Jake is a digital researcher and writer on many popular design magazines. He frequently writes on topics including web design, user experience, mobile apps, and project management. You can find him all throughout Google and tweeting @jakerocheleau. Connect with Jake on google+