netnik.com

Bare-bones Accordion

Start here

Similar to tabs, an accordian is useful for displaying panels of information, one panel at a time. So, here we go with a bare-bones accordion. It's called an accordion, since it expands and contracts like the musical instrument. By bare-bones, I mean as simple as possible.

In my first article, I discussed tabs and how to make them degrade gracefully for users who have javascript off. I've learned a little since then and so this page takes a slightly different approach — the flip-side of graceful degradation, which is called "progressive enhancement." So we start off with a page that works without javascript, and then using a little javascript and jquery we make a little nicer interface for those who have javascript turned on. Click here to see how this page looks with javascript removed. To see how it would look without CSS, in Firefox's menubar choose View > Page Style > No Style. (In either case, the content is still usable and available to search engines.)

Part 2

As usual, we start off with some nice, clean, and simple HTML, which you can see under the HTML panel header. In the HTML head, we call our CSS and two javascript files — first the jQuery file, which must come first, and then our hand-written file, pageload.js, which I will discuss later.

<link rel="stylesheet" media="screen" type="text/css" href="style.css" />
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="pageload.js"></script>
		

In the body of the document, there is a div that I gave the class of, appropriately enough, "accordion." This is followed by a series of h3-div sets - one set for each panel of the accordion. In each set the h3 is the header and the following div is the panel.

<div class="accordion">
<h3><a href="#">Start here</a></h3>
<div>
<p>
Text of article goes here.
</p>
</div>
... </div>
		

In the CSS file, I have styled the accordion and its h3 elements with the following:

div.accordion {
	float: left;
	width: 600px;
	padding: 0;
	border: 3px solid #789;
}

div.accordion h3 {
	font-size: 1em;
	font-weight: bold;
	padding: 6px;
	margin: 0;
	background-color: #456;
	border-bottom: 1px solid #abc;
}

div.accordion h3 a {
	color: #fff;
	text-decoration: none;
}

Simple enough! Under the accordion, I could have added "display: block;" but that's not necessary since a div is a block element by default. The "float: left;" is not necessary in this case either, but will allow me to add more elements to the page directly to the right of the accordion, which would be desirable if I was using the accordian, as it commonly is used, for navigation. (A navigational accordion would have a smaller width and you could put a <ul> inside the div instead of the <p>s.)

Since I have removed the the underlining from the <a> tag inside the h3, I put a slight color change on hover to give visitor a visual cue that the element is clickable:

div.accordion h3:hover {
	background-color: #789;
}
	

In part 3, I will discuss the javascript that makes our accordion "play."

Part 3

Here is the javascript that handles opening and closing of the panels in our accordion.

//pageload.js

// do stuff when DOM is ready
$(document).ready(function() {

	// hide all the panels of the accordian
	$('div.accordion> div').hide();   
 
	window.scroll(0,0);
	// animated page loading stuff
	// first we hide all the page elements
	// using jquery
	$("h1").hide();
	$("h2").hide();
	$(".accordion").hide();
	
	// then we show them
	$("h1").slideDown(3000);
	var wait = setTimeout("$('h2').fadeIn(3000);",2000);
	var wait = setTimeout("$('.accordion').fadeIn(3000);",3000);
	
	
	// the function that runs when accordion is clicked
	$('div.accordion> h3').click(function() {
		var $nextDiv = $(this).next();
		var $visibleSiblings = $nextDiv.siblings('div:visible');
		if ($visibleSiblings.length ) {
		$visibleSiblings.slideUp('fast', function() {
		$nextDiv.slideToggle('fast');
		});
		} else {
		$nextDiv.slideToggle('fast');
		}
		window.scroll(0,0);
	});

});
	

The code is heavily commented, but I would like to mention a few points. I said that this was to be a bare-bones discussion of the accordion, but I wanted to throw in a few extras, since they are easy enough to do.

There are basically two things happening in this script, the page loading visual effects and the accordion clicking functionality.

The page loading visual effects are run once the DOM has loaded since they are inside the usual jQuery opening function "$(document).ready(function() {".

The next line, "$('div.accordion> div').hide();", is actually part of the accordion functionality. It is hiding all the divs inside the accordion, so all the panels are closed and only the headings are showing.

An important point about the visual effects, is that the elements are hidden and then shown using jQuery. The same effect could be gotten by hiding the elements with CSS (e.g. h1 { display: none; }), but that would result in a blank page for users without javascript. By doing both the hiding and the showing with jQuery, we provide an enhanced animated page load for those with javascript and a normal CSS displayed page for those with out it.

The accordion click handling function, "$('div.accordion> h3').click(function() {", is not run when the page is loaded, but the function is stored so the browser knows what to do when an accordion header is clicked.

This function loads the identity of the div that comes after the h3 that is clicked (this.next) into the variable $nextDiv. Then it checks if any of the divs inside the accordion are visible and store its identity in the $visibleSiblings variable. If so (i.e. if the length of $visibleSiblings variable is > 0), it hides it. Then it toggles the visiblity of the $nextDiv by using the jQuery action slideToggle. The use of the toggle elegantly allows a click on an open panel header to close the panel, as well as allowing the click on a closed panel header to open the panel.

Lastly, the "window.scroll(0,0);" brings us to the top of the page. This prevents the user from being left with an apparently blank page in the instance where a long panel is closed by clicking on a shorter panel below it. The same "window.scroll(0,0);" is run when the page is loaded to prevent similar weirdness if a user reloads the page when a long panel is open.

That's all, folks.

 

HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bare-bones Accordion</title>
<meta name="Author" content="Bob Hay" />

<link rel="stylesheet" media="screen" type="text/css" href="style.css" />
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="pageload.js"></script>

</head>
<body>

<div id="page">

<h2>Bare-bones Accordion</h2>

<div class="accordion">
<h3><a href="#">Start here</a></h3>
<div>
<p>
Text of article goes here.
</p>
</div>

<h3><a href="#">Part 2</a></h3>
<div>
<p>
Text of part 2 goes here.
</p>
</div>

<h3><a href="#">Part 3</a></h3>
<div>
<p>
Text of part 3 goes here.
</p>
</div>

<h3><a href="#">HTML</a></h3>
<div>
<p>
The HTML code will go here.
</p>
</div>

<h3><a href="#">CSS</a></h3>
<div>
<p>
The CSS mark-up will go here.
</p>
</div>

<h3><a href="#">JavaScript</a></h3>
<div>
<p>
The javascript will go here.
</p>
</div>

</div>

<div id="forcescroll"></div>

</div>

</body>
</html>

		

CSS

/*  netnik header  */
h1 {
	height: 34px;
	width: 100%;
	margin: 0;
	background-color: #abc;
	border-bottom: 1px solid #789;
}

h1 img { border: 0px none; }

/*  bare-bones accordion article style sheet */

body {
	color: #000;
	font-family: "Lucida Sans",verdana, helvetica, arial, sans-serif;
	font-size: 16px;
	margin: 0;
	padding: 0;
	background-color: #9c9c9c;
	background-image: url(http://netnik.com/images/bg1.jpg);
	background-repeat: repeat-x;
}

div#page {
	margin: 20px 40px;
}

div#forcescroll {   /* this forces a vertical scroll bar to prevent jumpiness */
	float: left;
	width: 2px;
	height: 2000px;
}

div.accordion {
	float: left;
	width: 600px;
	padding: 0;
	border: 3px solid #789;
}

div.accordion h3 {
	font-size: 1em;
	font-weight: bold;
	padding: 6px;
	margin: 0;
	background-color: #456;
	border-bottom: 1px solid #abc;
}

div.accordion h3:hover {
	background-color: #789;
}

div.accordion h3 a {
	color: #fff;
	text-decoration: none;
}

div.accordion p {
	background-color: #eee;
	font-size: .8em;
	line-height: 1.4em;
	padding: 5px 20px;
	margin: 0;
}

div.accordion p a, a.visited { color: #000; }

pre {
	background-color: #eee;
	margin: 0;
	font-family: "courier new", courier, monospace;
	font-size: .7em;
	color: #900;
	padding: 5px 20px;
}

	

JavaScript

//pageload.js

// do stuff when DOM is ready
$(document).ready(function() {

	// hide all the panels of the accordian
	$('div.accordion> div').hide();   
 
 	window.scroll(0,0);
	// animated page loading stuff
	// first we hide all the page elements
	// using jquery
	$("h1").hide();
	$("h2").hide();
	$(".accordion").hide();
	
	// then we show them
	$("h1").slideDown(3000);
	var wait = setTimeout("$('h2').fadeIn(3000);",2000);
	var wait = setTimeout("$('.accordion').fadeIn(3000);",3000);
	
	// the function that runs when accordion is clicked
	$('div.accordion> h3').click(function() {
		var $nextDiv = $(this).next();
		var $visibleSiblings = $nextDiv.siblings('div:visible');
		if ($visibleSiblings.length ) {
		$visibleSiblings.slideUp('fast', function() {
		$nextDiv.slideToggle('fast');
		});
		} else {
		$nextDiv.slideToggle('fast');
		}
		window.scroll(0,0);
	});

});