CSS-Plus
CSS-Plus
Play with web technologies

Create Accordions with CSS3, HTML5 and jQuery

August 22, 2011
Attention: This article is old and the content is outdated. I've left the article here for historical purposes.
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.
Demo