• Skip to main content
  • Skip to primary sidebar
  • Skip to footer

Matt Doyle | Elated Communications

Web and WordPress Development

  • About Me
  • Blog
    • Design & Multimedia
      • Photoshop
      • Paint Shop Pro
      • Video & Audio
    • Online Marketing
      • E-Commerce
      • Social Media
    • Running a Website
      • WordPress
      • Apache
      • UNIX and Linux
      • Using FTP
    • Web Development
      • HTML
      • CSS
      • JavaScript
      • PHP
      • Perl and CGI Scripting
  • Portfolio
  • Contact Me
  • Hire Me
Home / Blog / Web Development / JavaScript / The Document Object Model / A JavaScript Accordion to Show and Hide HTML

A JavaScript Accordion to Show and Hide HTML

19 January 2009 / 29 Comments

In this tutorial you learn how to create a JavaScript accordion script using the Document Object Model. What’s an accordion? Click the link below to find out!

JavaScript accordion screenshot
See the JavaScript accordion in action

As you can see, a JavaScript accordion is a useful way to squeeze lots of page content into a small space. It consists of a list of items, of which only one is expanded at any one time. When you click on another item’s heading, the first item collapses and the second item expands.

Let’s see how the accordion is put together.

Creating the HTML

The first thing to do is build the markup. It’s nice to keep the HTML as simple as possible, so we’ll just use a div element with a class of accordionItem for each accordion item. Within each accordionItem div we’ll place two elements: an h2 element for the item’s title bar, and a div element for the item’s content. Here’s the first of the three accordion items as an example:


<div class="accordionItem">
  <h2>About accordions</h2>
  <div>
    <p>JavaScript accordions let you squeeze a lot of content into a small space in a Web page.</p>
    <p>This simple accordion degrades gracefully in browsers that don't support JavaScript or CSS.</p>
  </div>
</div>

(To add more accordion items, just create additional accordionItem divs in a similar fashion.)

Creating the CSS

The CSS in the page serves two purposes:

  1. It makes the accordion look pretty and more usable.
  2. It provides a .hide CSS class for hiding accordion item bodies.

Here’s the CSS used in the example:


body { font-size: 80%; font-family: 'Lucida Grande', Verdana, Arial, Sans-Serif; }
.accordionItem h2 { margin: 0; font-size: 1.1em; padding: 0.4em; color: #fff; background-color: #944; border-bottom: 1px solid #66d; }
.accordionItem h2:hover { cursor: pointer; }
.accordionItem div { margin: 0; padding: 1em 0.4em; background-color: #eef; border-bottom: 1px solid #66d; }
.accordionItem.hide h2 { color: #000; background-color: #88f; }
.accordionItem.hide div { display: none; }

Each CSS rule does the following:

body
This just sets a nice font and font size for the page.
.accordionItem h2
Styles the item heading inside each accordion item.
.accordionItem h2:hover
Makes the mouse cursor change to a pointer when the heading is hovered over. (Doesn’t work in IE6 β€” no biggie.)
.accordionItem div
Styles the content div inside each accordion item.
.accordionItem.hide h2
Styles the item headings for all the collapsed items.
.accordionItem.hide div
Ensures that the bodies of the collapsed items are not shown.

Creating the JavaScript

Now comes the fun part! The basic strategy here is:

  • Start by assigning a toggleItem() onclick event handler to each of the h2 accordion item headings.
  • Hide all item bodies except the first, so that only the top item is visible when the page loads.
  • When an item heading is clicked, toggleItem() hides all items in the list, then shows the clicked item if previously hidden. This allows the visitor to toggle any item while keeping the other items collapsed.
Why not hide all items except the first by using markup, rather than JavaScript? Because:

  • It would require extra markup.
  • On non-JavaScript browsers the hidden items would be permanently hidden, rendering them unreadable.

First you need to create a global array to hold the accordionItem divs:


    var accordionItems = new Array();

Now you need to create three functions:

  • init() to set up the accordion
  • toggleItem() to expand/collapse an accordion item
  • getFirstChildWithTagName(), a short helper function used by init().

These functions are described below.

The init() function

The init() function is triggered by the body element’s onload event:


    function init() {

      // Grab the accordion items from the page
      var divs = document.getElementsByTagName( 'div' );
      for ( var i = 0; i < divs.length; i++ ) {
        if ( divs[i].className == 'accordionItem' ) accordionItems.push( divs[i] );
      }

      // Assign onclick events to the accordion item headings
      for ( var i = 0; i < accordionItems.length; i++ ) {
        var h2 = getFirstChildWithTagName( accordionItems[i], 'H2' );
        h2.onclick = toggleItem;
      }

      // Hide all accordion item bodies except the first
      for ( var i = 1; i < accordionItems.length; i++ ) {
        accordionItems[i].className = 'accordionItem hide';
      }
    }

This function comprises 3 parts, as follows:

  1. The first part pulls the accordion item divs into JavaScript DOM objects and stores the objects in the accordionItems array. To do this it first calls document.getElementsByTagName() to get a list of all divs in the page. Then it loops through this list, pushing any divs that have a class of 'accordionItem' onto the accordionItems array.
    <aside”>Find out about getElementsByTagName() in Retrieving page elements via the DOM. The init() function also accesses the element.className property, which is a handy shorthand way of setting or getting an element’s class attribute. </aside”>
  2. The second block of code goes through each accordion item div, assigning an onclick event handler function called toggleItem() to the h2 element inside the div. To locate the h2 element it calls a helper function, getFirstChildWithTagName() (described in a moment).
  3. The third chunk of code loops through all the accordion items except the first, setting each div‘s class to 'accordionItem hide'. Due to the CSS in the page, this has the effect of hiding each item’s content div, as well as giving each item’s h2 heading an “unselected” style.

Register the init() function as the body element’s onload event handler, like this:


  <body onload="init()">

This causes init() to run when the page loads.

The toggleItem() function

The toggleItem() event handler is called when an accordion item’s heading is clicked on. It deals with the showing and hiding of the accordion items:


    function toggleItem() {
      var itemClass = this.parentNode.className;

      // Hide all items
      for ( var i = 0; i < accordionItems.length; i++ ) {
        accordionItems[i].className = 'accordionItem hide';
      }

      // Show this item if it was previously hidden
      if ( itemClass == 'accordionItem hide' ) {
        this.parentNode.className = 'accordionItem';

      }
    }

The function:

  1. Stores the current CSS class of the accordion item whose heading was clicked in an itemClass string variable to refer to later.
  2. Loops through all the accordion items, hiding them by setting their class to 'accordionItem hide'.
  3. Shows the clicked item if its previous class was 'accordionItem hide' β€” that is, if it was previously hidden. This provides the toggling mechanism. To show the item it simply sets its CSS class to 'accordionItem' instead of 'accordionItem hide'.
Notice that, in an event handler function, this refers to the object that triggered the event (in this case the item heading that was clicked on).

The getFirstChildWithTagName() function

Nearly finished! You just need to create a small helper function, getFirstChildWithTagName(), that retrieves the first child of a specified element that matches a specified tag name. This function is used by init() to retrieve the first h2 element inside each accordionItem div element.


    function getFirstChildWithTagName( element, tagName ) {
      for ( var i = 0; i < element.childNodes.length; i++ ) {
        if ( element.childNodes[i].nodeName == tagName ) return element.childNodes[i];
      }
    }

The function is very simple. It loops through each of the child nodes of the supplied element until it finds a node with the supplied tag name. When it finds such a node, it returns it.

Find out about the childNodes and nodeName properties in Looking inside DOM page elements.

Putting it all together

That’s all there is to building a JavaScript accordion! Take a look at the demo again, and view the page source to see how the HTML, CSS and JavaScript come together to make the accordion.

Filed Under: The Document Object Model Tagged With: collapse, css, dhtml, expand, javascript accordion, onclick, show and hide html, show hide div

Reader Interactions

Comments

  1. robinski says

    2 March 2010 at 7:07 pm

    Hi, thanks for the tutorial. Is there any way to have the 2nd or 3rd accordion item open as the default, instead of the 1st?

    Reply
  2. matt says

    5 March 2010 at 3:02 pm

    Hi Robin, welcome to Elated.

    Just change the following code within the init() function:

          // Hide all accordion item bodies except the first
          for ( var i = 1; i < accordionItems.length; i++ ) {
            accordionItems[i].className = 'accordionItem hide';
          }
    

    to:

          // Hide all accordion item bodies except one
          for ( var i = 0; i < accordionItems.length; i++ ) {
            if ( i != 2 ) accordionItems[i].className = 'accordionItem hide';
          }
    

    Change the “2” to the item you want to show (0=first item, 1=second item, 2=third item and so on).

    Hope that helps!
    Matt

    Reply
  3. robinski says

    5 March 2010 at 9:56 pm

    That works great. Thank you!

    Reply
  4. matt says

    9 March 2010 at 2:57 am

    You’re welcome. πŸ™‚ Feel free to ask if you need any more help.

    Cheers,
    Matt

    Reply
  5. dsulek says

    28 September 2010 at 1:40 pm

    Hi Matt,

    Thank you for all of the above information. I’m wondering if there is a way to keep the accordion opened if/when you add a link to another page(without opening the new page in another window) and you navigate back to the accordion page.

    Thanks,
    Debbie

    Reply
  6. matt says

    29 September 2010 at 5:59 pm

    @dsulek: You mean using the browsers’ Back button? Tricky – you’d need to store the currently-selected item’s hash value in the URL so that it’s recorded in the browser history. Then when the Back button is pressed, the browser will remember the history and load the URL with the hash. Your JavaScript would then need to read this hash value and restore the selected div.

    Here’s something similar that I wrote for my JavaScript Tabs tutorial (which works in a similar way to the accordion):

    http://www.elated.com/forums/topic/4717/#post18005

    You’ll also need to modify toggleItem() to set the hash in the address bar accordingly, eg:

    document.location.hash = (the hash value);
    
    Reply
  7. sansarin says

    8 October 2010 at 1:50 pm

    Very grateful to have this code and instructions, Matt. Thank you.

    Please would you show me how and where to add a millisecond timer to the code to give a slower reveal?

    Thanks again.

    Reply
  8. matt says

    10 October 2010 at 8:13 pm

    Hi sansarin, welcome to Elated!

    Easiest way is to hack a bit of jQuery in there. Use jQuery’s slideUp() and slideDown() methods to show/hide the items. Full example:

    <!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>
        <!-- This page is copyright Elated Communications Ltd. (www.elated.com) -->
    
        <title>JavaScript accordion example</title>
    
        <style type="text/css">
          body { font-size: 80%; font-family: 'Lucida Grande', Verdana, Arial, Sans-Serif; }
          .accordionItem h2 { margin: 0; font-size: 1.1em; padding: 0.4em; color: #fff; background-color: #944; border-bottom: 1px solid #66d; }
          .accordionItem h2:hover { cursor: pointer; }
          .accordionItem div { margin: 0; padding: 1em 0.4em; background-color: #eef; border-bottom: 1px solid #66d; }
          .accordionItem.hide h2 { color: #000; background-color: #88f; }
        </style>
    
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    
        <script type="text/javascript">
        //<![CDATA[
    
        var accordionItems = new Array();
    
        function init() {
    
          // Grab the accordion items from the page
          var divs = document.getElementsByTagName( 'div' );
          for ( var i = 0; i < divs.length; i++ ) {
            if ( divs[i].className == 'accordionItem' ) accordionItems.push( divs[i] );
          }
    
          // Assign onclick events to the accordion item headings
          for ( var i = 0; i < accordionItems.length; i++ ) {
            var h2 = getFirstChildWithTagName( accordionItems[i], 'H2' );
            h2.onclick = toggleItem;
          }
    
          // Hide all accordion item bodies except the first
          for ( var i = 1; i < accordionItems.length; i++ ) {
            accordionItems[i].className = 'accordionItem hide';
            $(accordionItems[i]).find('div').slideUp();
          }
        }
    
        function toggleItem() {
          var itemClass = this.parentNode.className;
    
          // Hide all items
          for ( var i = 0; i < accordionItems.length; i++ ) {
            accordionItems[i].className = 'accordionItem hide';
            $(accordionItems[i]).find('div').slideUp();
          }
    
          // Show this item if it was previously hidden
          if ( itemClass == 'accordionItem hide' ) {
            this.parentNode.className = 'accordionItem';
            $(this).parent().find('div').slideDown();
          }
        }
    
        function getFirstChildWithTagName( element, tagName ) {
          for ( var i = 0; i < element.childNodes.length; i++ ) {
            if ( element.childNodes[i].nodeName == tagName ) return element.childNodes[i];
          }
        }
    
         //]]>
        </script>
      </head>
      <body onload="init()">
        <h1>JavaScript accordion example</h1>
    
        <div class="accordionItem">
          <h2>About accordions</h2>
    
          <div>
            <p>JavaScript accordions let you squeeze a lot of content into a small space in a Web page.</p>
            <p>This simple accordion degrades gracefully in browsers that don't support JavaScript or CSS.</p>
          </div>
        </div>
    
        <div class="accordionItem">
          <h2>Accordion items</h2>
    
          <div>
            <p>A JavaScript accordion is made up of a number of expandable/collapsible items. Only one item is ever shown at a time.</p>
            <p>You can include any content you want inside an accordion item. Here's a bullet list:</p>
            <ul>
              <li>List item #1</li>
              <li>List item #2</li>
              <li>List item #3</li>
    
            </ul>
          </div>
        </div>
    
        <div class="accordionItem">
          <h2>How to use a JavaScript accordion</h2>
          <div>
            <p>Click an accordion item's heading to expand it. To collapse the item, click it again, or click another item heading.</p>
    
          </div>
        </div>
    
        <p><a href="/articles/javascript-accordion/">Return to the JavaScript Accordion article</a></p>
    
      </body>
    </html>
    

    More on jQuery animations at:

    http://www.elated.com/articles/super-easy-animated-effects-with-jquery/

    Cheers,
    Matt

    Reply
  9. myexpression says

    11 October 2010 at 4:41 pm

    Firstly, thank you for posting this. I was just dusting off an old Javascript collapse code, and this looks way more elegant than the code I was using.

    I want to use an image/button to show and hide the content, so I would just replace the “H2” with “img” in this bit of code I’m assuming correct?

      // Assign onclick events to the accordion item category image
      for ( var i = 0; i < accordionItems.length; i++ ) {
    	var img = getFirstChildWithTagName( accordionItems[i], 'img' );
    	img.onclick = toggleItem;
      }
    

    And then if I want all accordianItem content to hide itself by default, I would just remove this portion of the javascript right?

      // Hide all accordion item bodies except the first
      for ( var i = 1; i < accordionItems.length; i++ ) {
    	accordionItems[i].className = 'accordionItem hide';
      }
    

    I’m sure I’ll figure this out through trial and error, but confirmation is always nice =)

    Thanks again

    [Edited by myexpression on 11-Oct-10 16:46]

    Reply
  10. matt says

    12 October 2010 at 3:46 am

    @myexpression:

    “I want to use an image/button to show and hide the content, so I would just replace the “H2” with “img” in this bit of code I’m assuming correct?”

    Probably “IMG”, but yes. But why not just style the H2 using a CSS background image? Semantically better, and you won’t have to change the JavaScript.

    “And then if I want all accordianItem content to hide itself by default, I would just remove this portion of the javascript right?”

    If you mean you want to hide the first item too on page load, then change the 1 to a 0:

          // Hide all accordion item bodies
          for ( var i = 0; i < accordionItems.length; i++ ) {
            accordionItems[i].className = 'accordionItem hide';
          }
    

    Cheers,
    Matt

    Reply
  11. JUNTAI says

    14 October 2010 at 8:46 am

    Hi, this is a great work and is really easy to understand, but i want a little help… i want to know if is any way to mantain multiple tabs opened, thanks man.

    Reply
  12. matt says

    14 October 2010 at 9:15 pm

    @JUNTAI: Not quite sure how that would work…?

    Reply
  13. JUNTAI says

    15 October 2010 at 12:37 pm

    Thank you, I did manage to find a solution to mantain multiple accordion items opened, it’s really easy :D…

    just replace this code on the function toggleItem():

    function toggleItem() {
                          var itemClass = this.parentNode.className;
                          // Hide all items
                          for ( var i = 0; i < accordionItems.length; i++ ) {
                            if ( accordionItems[i].className == 'accordionItem hide' ) accordionItems[i].className = 'accordionItem hide';
                            //if ( accordionItems[i].className == 'accordionItem' ) accordionItems[i].className = 'accordionItem hide';
                          }
                          // Show this item if it was previously hidden
                          //if ( itemClass == 'accordionItem hide' || itemClass == 'accordionItem') {
                            this.parentNode.className = 'accordionItem';
                            if ( itemClass == 'accordionItem') {
                                this.parentNode.className = 'accordionItem hide';
                            }
                          //}
                        }
    

    Thanks again man, this script is awesome…

    Reply
  14. matt says

    17 October 2010 at 9:06 pm

    @JUNTAI: Ah, I see what you mean now. Nice hack! πŸ™‚

    Reply
  15. adidasdude says

    16 November 2010 at 4:11 pm

    Instead of leaving it with inline CSS and inline JS, I copied and pasted the HTML, JS and CSS in to separate files. Now the example doesn’t expand/collapse at all.

    I removed the

    <BODY onload=init()>
    

    from the HTML page and added

    window.onload = init;
    

    to the JS file instead.

    Do you know why the example doesn’t work when I separate the HTML, JS and CSS?

    Reply
  16. matt says

    16 November 2010 at 10:38 pm

    @adidasdude: Hard to tell without seeing the actual page. Do you get any errors in the error console? What browser are you using?

    You could try putting the call to init() in the markup after the accordion markup perhaps:

    <script>
    init();
    </script>
    
    Reply
  17. adidasdude says

    16 November 2010 at 11:15 pm

    Everything shows up fine and my cursor changes to a click icon when I hover over the accordion. However, I can’t click on anything.

    Due to restrictions, I have to use IE7. I installed a web developer plugin for IE7 but it’s not as good as firebug or safari’s web developer stuff. Do you know how to check the error console on IE7?

    I will try your code first thing tomorrow and I’ll try to paste what the error console says.

    Thanks again.

    Reply
  18. matt says

    18 November 2010 at 3:53 am

    Tricky with IE7- you could try this: http://bytes.com/topic/javascript/answers/677272-finding-javascript-errors-ie-7-a

    On IE8, just hit F12…

    Reply
  19. cklein says

    25 August 2011 at 1:00 pm

    Hello,

    Is there a way to get this script to work without using getElementsByTagName but using getElementByID?

    Thanks!

    Reply
  20. matt says

    29 August 2011 at 6:53 am

    @cklein: Why would you want to?

    Reply
  21. MarkH says

    17 December 2011 at 3:26 pm

    I am writing a website with an FAQ page that displays with all the answers hidden using much of your code approach above (works fine). What I’d like to be able to do is to sometimes link to the FAQ page from other pages within the website and instead display the answer to one specific question when I come from that particular link.

    For example, the 5th FAQ may be “What is an XXX” and another page may have “and when you use an XXX” I want to be able to have a link from XXX to the FAQ page and automatically show the answer to question 5 (“what is an XXX”).

    I hope that makes sense.

    Reply
  22. adidasdude says

    17 December 2011 at 5:07 pm

    @ MarkH

    Are you referring to dynamically loading in that particular div’s contents through AJAX?

    Can you please send a screenshot?

    Reply
  23. MarkH says

    17 December 2011 at 7:32 pm

    No, nothing that fancy. I simply mean going to the FAQ page but instead of by default, hiding all the answers (eg, if you selected FAQ page from the mainmenu only a list of questions show), un-hiding/showing the answer to the relevant question that the link refers to. In my example above, this would be the answer to question 5.

    Reply
  24. matt says

    19 December 2011 at 11:25 pm

    @MarkH: I posted some code for my JavaScript Tabs script that I think will do what you need:

    http://www.elated.com/forums/topic/4717/#post18005

    The accordion code is fairly similar to the tabs code, so it should be simple to integrate.

    Reply
  25. abtecas says

    29 August 2013 at 11:40 am

    Hi. Thanks for the post. I want ot ask how I would apply this when I don’t have access to the <body> tag? I am working inside of a CMS where I’m only accessing the content area.

    Thanks for the reply

    Reply
  26. chrishirst says

    29 August 2013 at 2:51 pm

    Then call the init() method after the content and before the closing body tag.

    Reply
  27. judyofthewoods says

    9 November 2013 at 5:40 am

    Thank you for showing us how to create a nice lean accordion with great flexibility.

    But here is my big problem, and it is not with the technical side of the code, but legal usage.

    A while ago I searched high and low for a lean accordion to use in an html app. I also looked at licensing, and narrowed it down to just a couple of accordions, this being the preferred. I’m not sure if I got the licencing into a muddle (I just verified that the other one is free to use under CC), but I believed this one to be MIT licensed, as that is what I added to the attribution. Now I can’t find any reference to it on this site, only the general Terms of Use which make “content” copyright, permission required.

    Can you please clarify what the usage terms are of the accordion, as well as the tabs and similar?

    And will I go to jail now 😯 ?

    Many appologies if I made a mistake, and I will rectify if I have made one.

    Reply
  28. JL says

    7 April 2020 at 3:25 pm

    Worked great, thanks!

    Reply
  29. Keerthana says

    20 October 2021 at 7:11 am

    Can you please give me an example code for displaying array of objects using accordion inside foor loop. The data should be dynamic not static.

    Reply

Leave a Reply Cancel reply

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

To include a block of code in your comment, surround it with <pre> ... </pre> tags. You can include smaller code snippets inside some normal text by surrounding them with <code> ... </code> tags.

Allowed tags in comments: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre> .

Primary Sidebar

Hire Matt!

Matt Doyle headshot

Need a little help with your website? I have over 20 years of web development experience under my belt. Let’s chat!

Matt Doyle - Codeable Expert Certificate

Hire Me Today

Call Me: +61 2 8006 0622

Stay in Touch!

Subscribe to get a quick email whenever I add new articles, free goodies, or special offers. I won’t spam you.

Subscribe

Recent Posts

  • Make a Rotatable 3D Product Boxshot with Three.js
  • Speed Up Your WordPress Website: 11 Simple Steps to a Faster Site
  • Reboot!
  • Wordfence Tutorial: How to Keep Your WordPress Site Safe from Hackers
  • How to Make Awesome-Looking Images for Your Website

Footer

Contact Matt

  • Email Me
  • Call Me: +61 2 8006 0622

Follow Matt

  • E-mail
  • Facebook
  • GitHub
  • LinkedIn
  • Twitter

Copyright © 1996-2023 Elated Communications. All rights reserved.
Affiliate Disclaimer | Privacy Policy | Terms of Use | Service T&C | Credits