How to create an accordion in jQuery

Well… I should start by saying that I actually hate Javascript and jQuery (sorry, guys…). If you really have to ask me why, the reason I hate it is because of the time I had the first encounter with it. It’s rather a psychological reason. The first time I took a look at Javascript was in its infancy. I was amazed at what it can do… But then I opened the same html file with another browser… it was a disaster. Then… another browser… another disaster. That is why I hate Javascript. Now someone may say that this is no longer a problem. Most browsers (if not all) are not respecting the Javascript engine directives (or whatever you call them…). But, as I said, it is a psychological hate.

Now, about “Why use jQuery, which is bloated as hell, when you can use plain javascript?”, I only have a reply for that: “Why use an excavator when you can use a shovel?”. Enough about the subject. Let’s go and create the accordion.

First we will start by creating the html file, let’s name it accordion.html:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <div class="accordion">
      <h2 class="tab-title">This is the title for the first tab</h2>
      <div class="tab-content">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum sit amet sapien nec facilisis. Nulla sed tellus lobortis, vehicula ligula elementum, condimentum urna. Curabitur laoreet malesuada risus, ut luctus leo. Suspendisse vestibulum, enim vitae eleifend iaculis, nisi mauris varius magna, eu laoreet leo est quis nulla. Quisque sit amet auctor velit. Morbi tempus sollicitudin nisi ut suscipit. Cras ultricies dolor elit, quis aliquam magna hendrerit a. Etiam rhoncus, nunc vitae pharetra egestas, diam velit fermentum nulla, in gravida diam urna et lacus. Sed faucibus sem dolor, sed varius quam laoreet at.</div>
      <h2 class="tab-title">This is the title for the second tab</h2>
      <div class="tab-content">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum sit amet sapien nec facilisis. Nulla sed tellus lobortis, vehicula ligula elementum, condimentum urna. Curabitur laoreet malesuada risus, ut luctus leo. Suspendisse vestibulum, enim vitae eleifend iaculis, nisi mauris varius magna, eu laoreet leo est quis nulla. Quisque sit amet auctor velit. Morbi tempus sollicitudin nisi ut suscipit. Cras ultricies dolor elit, quis aliquam magna hendrerit a. Etiam rhoncus, nunc vitae pharetra egestas, diam velit fermentum nulla, in gravida diam urna et lacus. Sed faucibus sem dolor, sed varius quam laoreet at.</div>
      <h2 class="tab-title">This is the title for the third tab</h2>
      <div class="tab-content">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum sit amet sapien nec facilisis. Nulla sed tellus lobortis, vehicula ligula elementum, condimentum urna. Curabitur laoreet malesuada risus, ut luctus leo. Suspendisse vestibulum, enim vitae eleifend iaculis, nisi mauris varius magna, eu laoreet leo est quis nulla. Quisque sit amet auctor velit. Morbi tempus sollicitudin nisi ut suscipit. Cras ultricies dolor elit, quis aliquam magna hendrerit a. Etiam rhoncus, nunc vitae pharetra egestas, diam velit fermentum nulla, in gravida diam urna et lacus. Sed faucibus sem dolor, sed varius quam laoreet at.</div>      
    </div>
  </body>
</html>

Cool. It looks like shit, but is good enough. Of course, you can use whatever tags you feel you need. I’ve used h2’s for titles and simple divs for the content. Now how about inserting the jquery? We will take it from a CDN just to speed things up. So we just paste this just before closing the body:

<script src="https://code.jquery.com/jquery-2.1.4.min.js" type="javascript"></script>To verify that all is ok, we will call jQuery and see if something happens when we click on the <em><strong>".tab-title"</strong></em> element. So, just after calling the jQuery script we write:<script>// <![CDATA[
$(function(){ $('.tab-title').click(function(){ console.log('hello'); }); });
// ]]></script>

Now, if we open Developer tool inside Chrome or Firefox and look into “Console” tab we will see that every click on the titles will result in an “Hello”. Nice. Now let’s modify it in order for it to toggle show/hide the content of the “.tab-content” div:

<script>// <![CDATA[
  $(function(){
	$('.tab-title').click(function(){
	  //console.log('hello');
	  $(this).toggleClass('selected').next('.tab-content').toggle('fast');
	});        
  });
// ]]></script>

What did we do in here? We asked jQuery to:

  1. toggleClass() – we told it to toggle class ‘selected’ on the current element which is the title of the tab;
  2.  ‘next(‘.tab-content’).toggle(‘fast’) – we told it to show/hide (toggle) the next element from the current element that has “tab-content” as class;

But if we add “selected” class to the heading title now, the content is hidden. So why not make the contents hidden by default? We do this by adding come CSS styling. So let’s put these lines just before closing the head tag:

<style>
.tab-content {display:none;}
</style>

Now, when someone clicks for the first time on a title, that title gets the class “selected” and the content is shown.

But, looking at the animation it seems a bit… clunky. So why not do the animation in CSS?
We can do this by using CSS transitions and using classes for the two states: the shown state and the hidden state:

$(this).toggleClass('selected').next('.tab-content').toggleClass('hidden visible');

As you will see, when clicking on the title the classes are switched from hidden to visible and back.

Now we need to change the css:

<style>
  .hidden, .visible {-webkit-transition: opacity 0.5s ease;-moz-transition: opacity 0.5s ease;-ms-transition: opacity 0.5s ease;-o-transition: opacity 0.5s ease;transition: opacity 0.5s ease;}
  .hidden {max-height: 0; overflow:hidden; opacity: 0; }
  .visible {max-height: auto; overflow:visible; opacity: 1;}
</style>

Unfortunately we cannot animate the height yet in CSS, so the titles simply “jump down” when pushed by the content. So, you have two possibilities, either the toggle() method or what I’ve shown you here.

Now… What if we need to hide the other tabs when another tab title is clicked? For this we need to return to our toggles and first ask to hide the currently open contents, and the “selected” classes before opening the current content of the title:

<script>// <![CDATA[
$(function(){ $('.tab-title').click(function(){ //console.log('hello'); $(this).parent().children('.tab-content').removeClass('visible').addClass('hidden'); $(this).parent().children('.tab-title').removeClass('selected'); $(this).toggleClass('selected').next('.tab-content').toggleClass('hidden visible'); }); });
// ]]></script>

Looks nice… We seem to be almost done… But… What if we want to open a certain accordion tab when we access a specific url hashtag

For example, if we access the “accordion.html#tab2” we would want the second tab to be opened. So how do we do this? First of all we need to do some changes to the titles of the tabs. We will do this by adding a “data-title” attribute to each tab title. Let’s see how the accordion div looks now:

<div class="accordion">
<h2 class="tab-title" data-title="tab1">This is the title for the first tab</h2>
<div class="tab-content hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum sit amet sapien nec facilisis. Nulla sed tellus lobortis, vehicula ligula elementum, condimentum urna. Curabitur laoreet malesuada risus, ut luctus leo. Suspendisse vestibulum, enim vitae eleifend iaculis, nisi mauris varius magna, eu laoreet leo est quis nulla. Quisque sit amet auctor velit. Morbi tempus sollicitudin nisi ut suscipit. Cras ultricies dolor elit, quis aliquam magna hendrerit a. Etiam rhoncus, nunc vitae pharetra egestas, diam velit fermentum nulla, in gravida diam urna et lacus. Sed faucibus sem dolor, sed varius quam laoreet at.</div>
<h2 class="tab-title" data-title="tab2">This is the title for the second tab</h2>
<div class="tab-content hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum sit amet sapien nec facilisis. Nulla sed tellus lobortis, vehicula ligula elementum, condimentum urna. Curabitur laoreet malesuada risus, ut luctus leo. Suspendisse vestibulum, enim vitae eleifend iaculis, nisi mauris varius magna, eu laoreet leo est quis nulla. Quisque sit amet auctor velit. Morbi tempus sollicitudin nisi ut suscipit. Cras ultricies dolor elit, quis aliquam magna hendrerit a. Etiam rhoncus, nunc vitae pharetra egestas, diam velit fermentum nulla, in gravida diam urna et lacus. Sed faucibus sem dolor, sed varius quam laoreet at.</div>
<h2 class="tab-title" data-title="tab3">This is the title for the third tab</h2>
<div class="tab-content hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum sit amet sapien nec facilisis. Nulla sed tellus lobortis, vehicula ligula elementum, condimentum urna. Curabitur laoreet malesuada risus, ut luctus leo. Suspendisse vestibulum, enim vitae eleifend iaculis, nisi mauris varius magna, eu laoreet leo est quis nulla. Quisque sit amet auctor velit. Morbi tempus sollicitudin nisi ut suscipit. Cras ultricies dolor elit, quis aliquam magna hendrerit a. Etiam rhoncus, nunc vitae pharetra egestas, diam velit fermentum nulla, in gravida diam urna et lacus. Sed faucibus sem dolor, sed varius quam laoreet at.</div>
</div>

Now we need to create a function in order to open the specific tabs of the required hashtags:

$(window).on('load hashchange', function(){
var hash = window.location.hash;
if(hash.length&gt;0)
{
var title = hash.replace('#','');
$('.tab-title').removeClass('selected');
$('.tab-content').removeClass('visible').addClass('hidden');
$('.tab-title[data-title="' + title + '"]').addClass('selected').next('.tab-content').toggleClass('hidden visible');
}
});

As you can see we use the function either on page load or on hash change.

Cool… now after a bit of styling, let us see the full html:

<!DOCTYPE html>
<html>
  <head>
    <style>
      h2, div {margin:0; padding: 0; font-family:Arial,sans-serif;}
      .tab-title {font-weight:normal; font-size: 16px; background-color:#eee; padding: 10px; margin-bottom: 2px;}
      .tab-content {padding: 10px 0;}
      .hidden, .visible {-webkit-transition: opacity 0.5s ease;-moz-transition: opacity 0.5s ease;-ms-transition: opacity 0.5s ease;-o-transition: opacity 0.5s ease;transition: opacity 0.5s ease;}
      .hidden {max-height: 0; overflow:hidden; opacity: 0;padding:0;margin:0; }
      .visible {max-height: auto; overflow:visible; opacity: 1;}
    </style>
  </head>
  <body>
    <div class="accordion">
      <h2 class="tab-title" data-title="tab1">This is the title for the first tab</h2>
      <div class="tab-content hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum sit amet sapien nec facilisis. Nulla sed tellus lobortis, vehicula ligula elementum, condimentum urna. Curabitur laoreet malesuada risus, ut luctus leo. Suspendisse vestibulum, enim vitae eleifend iaculis, nisi mauris varius magna, eu laoreet leo est quis nulla. Quisque sit amet auctor velit. Morbi tempus sollicitudin nisi ut suscipit. Cras ultricies dolor elit, quis aliquam magna hendrerit a. Etiam rhoncus, nunc vitae pharetra egestas, diam velit fermentum nulla, in gravida diam urna et lacus. Sed faucibus sem dolor, sed varius quam laoreet at.</div>
      <h2 class="tab-title" data-title="tab2">This is the title for the second tab</h2>
      <div class="tab-content hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum sit amet sapien nec facilisis. Nulla sed tellus lobortis, vehicula ligula elementum, condimentum urna. Curabitur laoreet malesuada risus, ut luctus leo. Suspendisse vestibulum, enim vitae eleifend iaculis, nisi mauris varius magna, eu laoreet leo est quis nulla. Quisque sit amet auctor velit. Morbi tempus sollicitudin nisi ut suscipit. Cras ultricies dolor elit, quis aliquam magna hendrerit a. Etiam rhoncus, nunc vitae pharetra egestas, diam velit fermentum nulla, in gravida diam urna et lacus. Sed faucibus sem dolor, sed varius quam laoreet at.</div>
      <h2 class="tab-title" data-title="tab3">This is the title for the third tab</h2>
      <div class="tab-content hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum sit amet sapien nec facilisis. Nulla sed tellus lobortis, vehicula ligula elementum, condimentum urna. Curabitur laoreet malesuada risus, ut luctus leo. Suspendisse vestibulum, enim vitae eleifend iaculis, nisi mauris varius magna, eu laoreet leo est quis nulla. Quisque sit amet auctor velit. Morbi tempus sollicitudin nisi ut suscipit. Cras ultricies dolor elit, quis aliquam magna hendrerit a. Etiam rhoncus, nunc vitae pharetra egestas, diam velit fermentum nulla, in gravida diam urna et lacus. Sed faucibus sem dolor, sed varius quam laoreet at.</div>      
    </div>
    <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script>
      $(function(){
        $('.tab-title').click(function(){
          //console.log('hello');
          $(this).parent().children('.tab-content').removeClass('visible').addClass('hidden');
          $(this).parent().children('.tab-title').removeClass('selected');
          $(this).toggleClass('selected').next('.tab-content').toggleClass('hidden visible');
        });
        
        $(window).on('load hashchange', function(){
          var hash = window.location.hash;
          if(hash.length>0)
          {
            var title = hash.replace('#','');
            $('.tab-title').removeClass('selected');
            $('.tab-content').removeClass('visible').addClass('hidden');
            $('.tab-title[data-title="' + title + '"]').addClass('selected').next('.tab-content').toggleClass('hidden visible');
          }
        });
      });
    </script>
  </body>
</html>

Of course, this is not a perfect accordion. As I said, I hate anything that has to do with Javascript. If you have any proposals on how to improve this, please do feel free to put it in the comments below.

Leave a Reply

Your email address will not be published. Required fields are marked *

No spam? * Time limit is exhausted. Please reload CAPTCHA.