Andrew David Joseph Hall

jQuery - Simple Accordion

To build the Accordion: first develop the Statement of Need - see side-bar - then link to the jQuery Library, then include your accordion jQuery Javascript include file... To add an accordion to the page add HTML code like that in the following example...

Example HTML in Example Accordion

   <h3 class="accordion">Statement of Need Example</h3>
   <div>
     <p>You have an <h3> element followed by a <div> element and 
         you would like the <div> to be hidden 
         until the user clicks on the <h3> 
         element. etc...</p>
   </div>

Initialization

jQuery

You need to link to a jQuery library, for example, from Google:

   
   <script 
      src="https://ajax.googleapis.com
       /ajax/libs/jquery/1.7.0/jquery.min.js"> 
         </script>

and include your own JavaScript>

    
   <script src="accordion.js" type="text/javascript">
      </script>

Your JavaScript include will invoke jQuery and your code, when the page is loaded, if you wrap your code as shown.

   
   jQuery(function() {

        Your code will go here...

	});

Add an 'open' image to the <h3> element.

   jQuery(function() {

   jQuery("h3.accordion,").append("<img  
       class='accordion-toggle' 
          src='../images/system/plus.png' 
             alt='Open'/>");   

	  });
  • jQuery("h3.accordion")
  • selects <h3> tags of class (' . ') "accordion"

  • .append("<img class= ... />")
  • inserts an image as the last child element of each <h3>

The browser's view of the HTML will now be...

   <h3 class="accordion">Statement of Need Example
      <img class='accordion-toggle' 
          src='../images/system/plus.png' 
              alt='Open'/></h3>
   <div>
   <p>You have an <h3> element followed by a <div> element and 
      you would like the <div> to be hidden 
      until the user clicks on the <h2> element. etc...</p>
   </div>

Add a 'close' image to the <div> element.

   jQuery(function() {

   jQuery("h3.accordion,").append("<img  
       class='accordion-toggle' 
          src='../images/system/plus.png' 
             alt='Open'/>");
 
   jQuery("h3.accordion").next("div").append("<img 
     class='accordion' 
       src='../images/system/close.png' 
         alt='Close Division' />");

	  });
  • jQuery("h3.accordion")
  • selects <h3> tags of class (' . ') "accordion"

  • .next("div")
  • finds the first <div> element following each <h3> tag of class "accordion"

  • .append("<img class= ... />")
  • inserts an image element in each <div> as the last child element of the <div>

The browser's view of the HTML will now be...

   <h3 class="accordion">Statement of Need Example
         <img class='accordion-toggle' 
            src='../images/system/plus.png' 
              alt='Open'/></h3>
   <div>
     <p>You have an <h3> element followed by a <div> element and 
         you would like the <div> to be hidden 
         until the user clicks on the <h2> element. etc...</p>
         <img class='accordion' 
            src='../images/system/close.png' 
              alt='Close Division' />
   </div>
    

Hide the accordion <div> elements.

   jQuery(function() {

   jQuery("h3.accordion,").append("<img ... 
      class='accordion-toggle' 
        src='../images/system/plus.png' 
          alt='Open'/>");
 
   jQuery("h3.accordion").next("div").append("<img 
      class='accordion' 
        src='../images/system/close.png' 
           alt='Close Division' />");

   jQuery("h3.accordion").next("div").hide();

	});
  • jQuery("h3.accordion")
  • selects <h3> tags of class (' . ') "accordion"

  • .next("div")
  • finds the first <div> element following each <h3> tag of class "accordion"

  • .hide()
  • sets the <div> element's height to zero, hiding it from view

Add a class to the accordion <div> elements.

   jQuery(function() {

   jQuery("h3.accordion,").append("<img ... 
      class='accordion-toggle' 
        src='../images/system/plus.png' 
          alt='Open'/>");
 
   jQuery("h3.accordion").next("div").append("<img 
     class='accordion' 
       src='../images/system/close.png' 
         alt='Close Division' />");

   jQuery("h3.accordion").next("div").hide();

   jQuery("h3.accordion").next("div").addClass("accordion");

   });
  • jQuery("h3.accordion")
  • selects <h3> tags of class (' . ') "accordion"

  • .next("div")
  • finds the first <div> element following each <h3> tag of class "accordion"

  • .addClass("accordion")
  • Adds the class "accordion" to the <div> elements following <h2> elements of class "accordion".

<h3> Event Handlers or Listeners

Each <h3> element needs code that handles clicking on the element and reveals or conceals the material in the accordion <div> element.

A jQuery on-click Event Handler

.click() adds an "onclick" event to elements

   jQuery("h3.accordion").click(function()
   {
      // functionality associated with clicking on <h3>
   });

Identify Which <h3> Image is Currently Shown

When the user clicks on the <h3> element we want the code to reveal/conceal the <div> and to change the image child of the <h3> element. So we need to test for the status of the <div> or discover which image is currently the child of <h3>. We could query the display attribute of the <div> element but I test the image associated with the <h3> tag instead...

   jQuery("h3.accordion").click(function()
   {
      var h3image = jQuery(this).children("img").first();
      if (h3image.attr("src") 
            === "../images/system/plus.png") 
	  {
            // change the image from plus to minus
      } else {
            // change the image from minus to plus
      }
   });

Within the Event Handler "this" is the element for which the Event Handler is registered. jQuery(this) wraps the element in a jQuery Object and makes the .children() method available to it. .children("img") selects all <img> elements that are first level children of the element. .first() selects the first of these children. .attr("src") selects the value of the attribute "src". and this is tested against the string "../images/system/plus.png" to which the attribute was initialized.

  • var h3image = jQuery(this).children("img").first();
  • Sets up a variable referencing the image associated with the <h3> clicked on.

Change the Image Shown

The attributes "src" and "alt" can now be set as appropriate using attribute name attribute value pairs.

    jQuery("h3.accordion").click(function()
    {
       var h3image = jQuery(this).children("img").first();
       if (h3image.attr("src") === "../images/system/plus.png") 
       {
	          h3image.attr("src","../images/system/minus.png");
		      h3image.attr("alt","Close");
       } else {
           h3image.attr("src","../images/system/plus.png");
		      h3image.attr("alt","Open");	
	      }
    });

Storing the Vertical Scroller Position

When the user conceals the <div> using the close image of class "accordion", that we added to it in the Initialization phase, the action of scolling down to reveal the full extent of the division can leave the page further down in its scroller than seems natural. To be able to return the scroller to its position when reveal was selected when the division is concealed we need to store the position of the window vertical scroller. The .data() method allows storage of data associated with an element. The method .scrollTop() allows the position of the vertical scroller to be queried or if a value is entered for the position to be set.

Using these methods our next line of code:

   jQuery(this).next("div").children("img.accordion")
       .first().data("scrollpos",jQuery("body").scrollTop());

Uses this, the <h3> element finds the <div> following it, selects the child <img> of class "accordion" and stores data to it in the pairing "scrollpos", jQuery("body").scrollTop().

Reveal or Conceal the <div>

Our last line of code for the Event Handler on <h3> actually reveals or conceals the <div>. .slideToggle interrogates the display attribute of the element that it is called by and either conceals or reveals the <div>.

   jQuery(this).next("div").slideToggle(200);

The full <h3> on-click event handler registration code.

   jQuery("h3.accordion").click(function()
   {
       var h3image = jQuery(this).children("img").first();
       if (h3image.attr("src") === "../images/system/plus.png") 
       {
              h3image.attr("src","../images/system/minus.png");
              h3image.attr("alt","Close");
       } else {
              h3image.attr("src","../images/system/plus.png");
              h3image.attr("alt","Open");	
	      }
   });
   jQuery(this).next("div").children("img.accordion")
        .first().data("scrollpos",jQuery("body").scrollTop());
	  jQuery(this).next("div").slideToggle(200);
   });

<div> Close Image Event Handlers or Listeners

We added an image to each <div> to allow the <div> to be concealed by clicking on the image. We need code that handles clicking on the <img>and conceals the material in the accordion <div> element.

Change the <h3> Image

An event handler on the <img> understands "this" as being the image element.

   jQuery("img.accordion").click( function()
   {
      var pai = jQuery(this).parent().prev();
      pai.children("img").first()
          .attr("src","../images/system/plus.png");
      pai.children("img").first()
          .attr("alt","Open");	
   });
  • jQuery(this).parent()
  • Selects the image's parent which is the <div> to be concealed.

  • var pai = jQuery(this).parent().prev();
  • Creates a reference to the <h3> of the <div> to be concealed.
    <h3> is the previous sibling element to the <div> to be concealed.

  • pai.children("img").first()
  • selects the open / close image child of <h3>

Since we are closing the <div> we can assume that the <h3> image can be set to plus.png and need not test for its value.

Conceal the <div>

   jQuery("img.accordion").click( function()
   {
      var pai = jQuery(this).parent().prev();
      pai.children("img").first()
          .attr("src","../images/system/plus.png");
      pai.children("img").first()
          .attr("alt","Open");

      pai.next("div").slideToggle(200);	

   });
  • jQuery(this).parent();
  • Selects the image's parent which is the <div> to be concealed.

  • pai.next("div") is equivalent to jQuery(this).parent()
  • Both refer to the <div> to be concealed.

  • pai.children("img").slideToggle(200)
  • Reduces the height of a revealed <div> over 200 milliseconds to nil, concealing the content.

Re-positioning the Window

When we created the close image in <div> we stored the position of the vertical slider for the browser window.

Returning the browser Window to the position it was in when the <div> was revealed after it is closed feels "natural". To do so we use:

   jQuery("body").scrollTop(jQuery(this).data("scrollpos");
  • jQuery("body")
  • Selects the <body> and wraps it in a jQuery object.

  • jQuery("body).scrollTop(value);
  • Sets the vertical scroller position to "value"

  • jQuery(this).data("scrollpos");
  • Gets the value that we stored as data element "scrollpos" when we created the close image in <div>. So using this value resets the vertical scoller to where it was when the used revealed the division.

The full <div> image on-click event handler registration code.

   jQuery("img.accordion").click( function()
   {
      var pai = jQuery(this).parent().prev();
      pai.children("img").first()
          .attr("src","../images/system/plus.png");
      pai.children("img").first()
          .attr("alt","Open");	
      pai.next("div").slideToggle(200);
      jQuery("body").scrollTop(jQuery(this).data("scrollpos"));
   });

Element Styles

We use Cascading Style Sheets styles to control the appearance of the <div>, the <img> and the open/close image.

We assigned these to classes "accordion-content", "accordion" and "accordion-toggle" respectively, so we include CSS for div.accordion-content, img.accordion and img.accordion-toggle to control their appearance.

   div.accordion-content {
        background:#FFFDF2;
        padding-left:10px;
        padding-right:10px;	
        margin-left:10px;
        border:inset;
   }
   
   img.accordion {
        position:relative;
        left:98.5%;
   }

   img.accordion-toggle {
        width:18px;
        height:18px;
   }

The img.accordion style places the image to the right hand side of the containing division. It appears at the bottom of the division because it was "appended to the division and so became the last element in the division.

The img.accordion style allows the height and width of the open/close image to be controlled easily.