Create Accordions with CSS3, HTML5 and jQuery

First thing is first, let me clarify what I mean by Create an Accordion with CSS3, HTML5 and jQuery. I am talking about creating 3 different accordions, one which relies heavily on CSS3, one with jQuery and one with HTML5.

If you haven’t already had to create some sort of accordion by now, I’m sure you will run into it sooner or later. It’s actually a very simple concept that requires very little script to create a fully functional accordion.

Accordions are typically used to toggle extra information. They help to organize and de-clutter information. You could make use of an accordion to display:

  • child pages in a menu
  • the answers to questions on a FAQ page
  • WordPress makes heavy use of accordions in it’s admin-area.
  • Your imagination is the limit

Whatever the use, it’s essential to have the ability to create any kind of accordion in your web-dev-brain-toolbox.

First, we’re going to create a basic accordion with jQuery, then an accordion with CSS3 and finally an accordion with HTML5. The jQuery version will have excellent browser support while the CSS3 version will have less, and finally the HTML5 version will have the least browser support.

All right, let’s get started.

HTML and basic CSS

This is the basic HTML and CSS of the accordion we’ll be working with:

HTML

The layout is pretty basic.

<div class="accordion">
	<h2>Aliquam tincidunt mauris eu risus</h2>
	<p>lorem ipsum dolor....</p>
	<h2>Lorem ipsum dolor sit amet, consectetuer adipiscing elit</h2>
	<p>lorem ipsum dolor....</h2>
	<h2>Lorem ipsum dolor sit amet, consectetuer adipiscing elit</h2>
	<div>
		<p>lorem ipsum dolor....</p>
		<p>lorem ipsum dolor....</p>
	</div>
</div>

The h2 will be the “button” for the accordion. The element that comes directly after that will be the element being toggled by clicking on the previous h2. Any element, meaning it could be an image, a paragraph, a div containing whatever you want. I feel that’s the most efficient way of creating this type of accordion.

CSS

.accordion{border: 1px solid #ddd; border-top: none; margin: 10px 0; width: 470px;}
#accordion > a{display: block; text-decoration: none;}
#accordion > h2, .accordion > a{background-color: #fff; background-image: url(../img/gradient.jpg);
background-image: -moz-linear-gradient(bottom, #f1f1f1, #fff);
background-image: -ms-linear-gradient(bottom, #f1f1f1, #fff);
background-image: -o-linear-gradient(bottom, #f1f1f1, #fff);
background-image: -webkit-linear-gradient(bottom, #f1f1f1, #fff);
background-image: linear-gradient(bottom, #f1f1f1, #fff); border-top: 1px solid #ddd;
color: #222; font: 14px/30px 'Verdana', sans-serif; height: 30px; margin: 0; padding: 0; text-indent: 10px;}
p{color: #555; font: 12px/18px 'Verdana', sans-serif; padding: 20px 10px;}

The CSS is just doing some basic styling. I’ve thrown in some CSS3 gradients with a fallback image because that’s how I roll.

The jQuery

It’s really amazing at how simple jQuery can make our lives. This is all that is required for the accordion:

$('h2.accordion').click(function(){
    $(this).next().slideToggle();
}).next().hide();

Now if that’s not simple, I don’t know what is.

That’s it. You now have a fully functional jQuery accordion!

The CSS3 version

The CSS version has some slightly different HTML and 2 new CSS lines.

The HTML becomes

<div class="accordion">
	<a href="#accordion-1">Aliquam tincidunt mauris eu risus</a>
	<p id="accordion-1">lorem ipsum dolor....</p>
	<a href="#accordion-2">Lorem ipsum dolor sit amet, consectetuer adipiscing elit</h2>
	<p id="accordion-2">lorem ipsum dolor....</h2>
	<a href="#accordion-3">Aliquam tincidunt mauris eu risus</a>
	<div id="accordion-3">
		<p>lorem ipsum dolor....</p>
		<p>lorem ipsum dolor....</p>
	</div>
</div>

There are 3 basic changes:

  • The <h2>s have been turned in to anchor links
  • The hidden contents have been given an id
  • The anchor links have been given an href containing the id of the following element with an appended hash

The CSS

The CSS is the same as before, but with the added lines:

a[href^="#accordion"] + *{display: none;}
#accordion :target{display: block;}

Very basically, the CSS is doing this:

Target all anchor links that have an href that begins with ‘#accordion’ and target whatever element comes directly after that.

As for the #accordion :target line, that’s saying whatever is target should just display block. Unfortunately we can’t use the transition property over here since we would have to define a set height. Transition doesn’t animate display or height: auto so the height can’t be dynamic — or maybe I just haven’t figured that out yet.

There is a con to using the CSS3 method: The anchor links cause the page to jump to the :target which is pretty annoying. There are two ways around this that I’ve come up with.

  • return false with javascript
  • Make sure the page height is too short to have a scrollbar – This will work, but I’m not serious about this one

The Javascript – Remove page jump

$('#accordion a').click(function(){
	return false;
});

Return false just tells the HTML not react with it’s normal behaviour.

The HTML5 method

I think this is the most semantic way of doing this and it has a pretty decent fallback (Same fallback as the jQuery version). If the sliding functionality isn’t 100% essential, this is a pretty cool way of doing it. Currently only webkit (Chrome and Safari) support this.

The HTML

<div id="accordion-html5" class="accordion">
	<details>
	  <summary><h2>Vestibulum auctor dapibus neque</h2></summary>
	  <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum.sis luctus, metus</p>
	</details>
	<details>
	  <summary><h2>Vestibulum auctor dapibus neque</h2></summary>
	  <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum.sis luctus, metus</p>
	</details>
</div>

The CSS

By default the browser adds a little arrow next to <summary>. I didn’t want this so I’ve turned it off:

details summary::-webkit-details-marker{display: none;}

Conclusion

The jQuery method is definitely the most cross-browser-friendly by far, but I think the HTML5 way may because used more and more as the browsers progress.
Regardless of the CSS3 methods’ cross-browser issues, it doesn’t really work well enough for us to use in production without the added bit of javascript so I think it’s best to use one of the other two methods.

Any questions or comments?

  1. prax says:

    I have a question, I am a bit of a newbie when it come to web dev and I was unsure of where to place the jQuery code. I am using this for a word press themes FAQ’s Section. Any help would be much appreciated.

    Thanks.

    1. Jamy Golden says:

      Hey,

      It’s good practice to put most of your javascript in the footer area so that a delay in the javascript load doesn’t hold up the the whole web page. So drop it in just before the </body> tag. Also make sure to put the jQuery code after the link to the jQuery library itself.

  2. Mohammad Shahnawaz says:

    Nice atricle, i enjoy…

  3. camdenroberts says:

    I think there’s an error:

    $('h3.accordion').click(function(){
        $(this).next().slideToggle();
    }).next().hide();

    h3.accordion should be h2.accordion

    I fixed the code to:

    $('h2.accordion').click(function(){
        $(this).next().slideToggle();
    }).next().hide();

    but it still doesn/t work

    1. Jamy Golden says:

      Could you show me the html you’re using too? Also, is jQuery definitely loading and working on the page?

  4. Ricky says:

    what the different of acordions and query?

    1. Jamy Golden says:

      Are you asking what the difference between them is? Basically it’s just different ways of doing the same thing. The jQuery version is the most cross-browser compatible.

  5. Ron says:

    Hi Jamy – Nicely done but I had to make some changes to your jQuery Accordion to get it to work.
    Give the html id=”accordion” and
    the javascript a

    $(document).ready(function(){
    	$('#accordion').find('h2').click(function(){
    		 $(this).next().slideToggle("slow");
    	}).next().hide();
    });

    Question? How do I get the accordion to close previously opened element before opening a new element?

    1. Jamy Golden says:

      Hey, and thanks :p
      Try:

      $(document).ready(function(){
      	$('#accordion').find('h2').click(function(){
      		$(this).siblings('h2').next().slideUp(); // closes siblings
      		$(this).next().slideToggle("slow");
      	}).next().hide();
      });
  6. Richard says:

    I tried this with after the and it doesnt seem to work…. any ideas? im using chrome

    1. Richard says:

      that was meant to have a div tag in the sentence

    2. Jamy Golden says:

      Hmm, create a JSfiddle and I’ll see if I can help.

  7. Hey, man! I am trying to implement the jQuery version in a site I am working on. I would love if you could troubleshoot with me. I do have jQuery (1.7.2) loading on my site.

    I copied your HTML, CSS, and jQuery exactly, put them in their appropriate places, verified that they were showing up when the site refreshed, and it didn’t work for me. I then tried implementing the code you wrote on July 23 in response to Ron. I changed the HTML div from a class to an ID, updated the CSS from classes to IDs, and pasted your jQuery exactly as-is. However, neither implementation worked. Do you have any thoughts? I love how minimal the code you’ve written is and would love to get this to work.

    Thanks for the post!

    1. Jamy Golden says:

      Could you provide a link for me to check out the problem?

    2. Hey man! I ended up using a plugin, so the code’s not there anymore. Thanks anyway!

  8. Hugh Guiney says:

    Just wanted to point out that at the moment, headings inside of summary elements are invalid. I tried bringing this up on the WHATWG mailing list to see if this could be changed, but no dice.

    1. Jamy Golden says:

      Thanks for that, I’ll update the example.

  9. Mike says:

    Hi there, nice bit of code, thanks for sharing.

    Just wondering how could you set it to open the first element on page load?

    And also, how would you make it so it gives the open h3 a class of ‘open’ ? (for styling)

    Much appreciated, Mike

    1. Mike says:

      BTW, this is the version im using:

      $(document).ready(function(){
          $(‘.accordion’).find(‘h3′).click(function(){
              $(this).siblings(‘h3′).next().slideUp(); // closes siblings
              $(this).next().slideToggle(“slow”);
          }).next().hide();
      });
    2. Jamy Golden says:

      Hey, yeah that should be pretty simple – Based on your js:

      $(document).ready(function(){
          $(‘.accordion’).find(‘h3′).click(function(){
              $(this).siblings(‘h3′).next().slideUp(); // closes siblings
              $(this).toggleClass('open').next().slideToggle(“slow”);
          }).next().hide();
      });

      Let me know if you have any other questions.

    3. Mike says:

      Hmm, tried it but it didn’t open the first element. Also, it needs to add the open class to the element on page load and remove the ‘open’ class when another h3 is clicked. Cheers for taking a look tho.

      I stuck a function in at the end, but really it needs to add the open class to the element on page load.

      $(document).ready(function(){
          $('.accordion').find('h3').click(function(){
              $(this).siblings('h3').next().slideUp(); // closes siblings
              $(this).toggleClass('open').next().slideToggle('slow');
          }).next().hide();
          $(".accordion ul:first").show();
      });
    4. Jamy Golden says:

      Try:

      $(document).ready(function(){
          $('.accordion').find('h3').click(function(){
              $(this).siblings('h3').removeClass('open').next().slideUp(); // closes siblings
              $(this).toggleClass('open').next().slideToggle('slow');
          }).next().hide();
          $(".accordion ul:first").show();
      });

      I would have to see your html or an example page to be able to give better suggestions.

  10. Cris says:

    I’ve been asked to make the previously-opened items a different color. Is this possible with jquery?

    (I can imagine doing this with a CSS/Html5 combo using an a:visited state for heads because they are href, but I much prefer jquery for a variety of reasons – not the least of which is I’m working in Drupal with a view that’s wrapped several layers around the content)

    1. Jamy Golden says:

      Try this:

      $('h2.accordion').click(function(){
          $(this).addClass('is-visited');
      });

      A visited item should get that class which you should be able to style easily.

  11. Marco says:

    hi i’m using this:

    $(document).ready(function(){
    	$('#accordion').find('h2').click(function(){
    		$(this).siblings('h2').next().slideUp(); // closes siblings
    		$(this).next().slideToggle("slow");
    	}).next().hide();
    });

    wonder how to get only the first one opened on load.
    THANKS!!!

    1. Jamy Golden says:

      Hey, try this:

      $(document).ready(function(){
      	$('#accordion').find('h2').click(function(){
      		$(this).siblings('h2').next().slideUp(); // closes siblings
      		$(this).next().slideToggle("slow");
      	}).next().hide();
      
      	$('#accordion').find('h2:first-child').next().show();
      });
  12. Marco says:

    How can I have the first tab opened on load? Thankssss

    1. Jamy Golden says:

      Assuming you’re using the jQuery version, add this to your script:

      $('h2.accordion:first-child').next().show();
  13. Anwar says:

    Hi Jamy,
    Very Nice tutorial about accordion.
    Am not able to add anything below the accordion, for example the footer information.
    Please suggest me asap

    Regards,
    Anwar

    1. Jamy Golden says:

      I’m not sure why you would be having this problem. Could you perhaps link me to an example page with this problem?

  14. edward says:

    Do you know how I’d get this to work in a Shopify store for say an FAQ’s section?

    1. Jamy Golden says:

      I’ve never used shopify, but if you’re able to edit the html or php pages then this shouldn’t be a problem. I could give you more useful information if I knew more about the problems you’re facing.

  15. kevin says:

    Hey Jamy. This is great, and I really appreciate how you actually answer peoples questions :) Most people don’t do that, so, Thank you.

    I’m using the jQuery option and trying to learn more about how this works. I’m wondering why it only opens the following tag? How would you make it open an entire , for example, that contains several headline and paragraph tags?

    like this:

    Version 1
    October 15, 2013

    lorem ipsum dolor….
    October 16, 2013

    lorem ipsum dolor….

    Version 2
    October 17, 2013

    lorem ipsum dolor….
    October 18, 2013

    lorem ipsum dolor….

    seems like that’s possible, but not sure how it works.
    Thanks, kevin

    1. Jamy Golden says:

      Hey Kevin, I’m not sure what you meant by Version 1 and Version 2. Code you added may have been stripped out. Can you perhaps create a pen or a jsfiddle of the HTML and explain again what you’re trying to show or hide or move with the js.

    2. Kevin says:

      Woah, yeah, sorry about that. The code didn’t show up here. Here’s a PEN: http://codepen.io/CREVIN/pen/Heafq

      and the original question (which didn’t show up quite right either) still remains – How would you make it open an entire DIV?

      Thanks. I really appreciate your insight. – Kevin

  16. Amit paste says:

    Hello Jamy. Thank you for the code. I use your code in asp.net mvc4 website. Your code is is working properly on chrome browser bur it is not working on firfox browser. Please if possible give me solution.

    i use this code on this url
    http://thanehomes.in/TermsAndConditions

    Thanks

    1. Jamy Golden says:

      Hi Amit. The reason that is happening is because Firefox doesn’t currently support <detai;> and <summary>. You should try adding a polyfill to add support for browsers that don’t yet support it. Have a look at this polyfill. Good luck!

  17. Kay says:

    This works for a finite number of elements, in jquery case, 3, what if we add ‘n’ more?

    1. Kay says:

      I’ve seen the demo for jQuery. How to auto collapse others when one is viewed?

    2. Jamy Golden says:

      One doesn’t have control over the HTML5 version, the CSS auto-collapses by default and to get the jQuery version to auto-collapse, the JS would be changed to this:

      $('h2.accordion').click(function(){
          // This slides collapses the other open items
          $(this).siblings('h2').slideUp();
      
          // This opens the item clicked
          $(this).next().slideToggle();
      }).next().hide();
    3. Jamy Golden says:

      What do you mean it works for 3 elements? The demo has an example of 6 unless I’m misunderstanding something. The jQuery version should work for ‘n’ amount of elements provided you have the <h2> and the <p>.

  18. Vicky says:

    working jsfiddle:- http://jsfiddle.net/6KDLj/

  19. Sarah says:

    Hi Jamy,
    thank you for this code, I have it working nicely but my site is not live yet. I wanted to know if there was a way to highlight the accordion headers when you hover to indicate that they are in fact something to click on and open. something similar to the way the hand appears in the CSS version but without using the CSS version.

    1. Jamy Golden says:

      Hi :) Add the following to your CSS:

      .accordion h2 { cursor: pointer; }

      .

  20. Vin says:

    I am trying to build a nested Accordion.with the help of your code (JQuery one- accordion-js) .I have Goals, which will have a list of Objectives and each objectives can have many strategies. How do I go about doing this. This is my first jquery web app.
    Appreciate all your help!

    Thanks

  21. Bevin Smith says:

    Great Solution!

    Is it possible to keep all selected panes open after a postback request from a button? If so can you please post the modified code…I have been struggling with this…

    Thanks,

Leave a Reply to Ron Cancel reply