Skip to main content

Simple Accordion using Java Script and CSS

Well you can find many online, but it's difficult to find one with out any dependent API. Most of the available accordions use other APIs for animation and other stuff. For those who just want accordion with out any other stuff, this one is the perfect one. It's very simple and you don't have to be a geek to understand it. Basic knowledge of Java script and CSS will do to understand how this works. In this article I'll take you through the steps of developing an accordion. Couple of minutes you are ready to write your own. Well then let's start developing one.

Layout of the HTML block looks something like the one below
Layout
Lets look at the CSS first which is very simple.
/** container styles **/
   .ra-p, .ra-cp {
    padding: 0 0 0.1em;
   }
   /**** heading styles ****/
   .ra-p h2, .ra-cp h2 {
     margin: 0px;
     padding: 0.2em;
     cursor: pointer;
     color: #fff;
     background-color: #3d80b0;
   }
   /**** collapsed heading styles ****/
   .ra-cp h2  {
     Background-color: #3d80b0;
   }
   /**** content styles *****/
   .ra-p .text, .ra-cp .text { overflow: hidden; }
   /**** hide collapsed content ****/
   .ra-cp .text { display: none; }
This all you need. Lets go through it.
First you've '.ra-p' and '.ra-cp' which are applied to the container ['ra-p' for expanded one, 'ra-cp' for collapsed one]. Then we've styles for the heading/title, 'ra-p h2', 'ra-cp h2'. As we are going to use 'h2' as heading tags we've styles for 'h2'. Finally we've styles for the content block, '.ra-p .text' and '.ra-cp .text'.
And now we'll go through the java script need for it.
Accordion = function(el, delay, steps) {
    if (!el) {
     alert('Element required to make it a Accordion.');
     return;
    }
    this.init(el, delay, steps);
   }
  
This code block is self explanatory. This is a constructor which initializes an Accordion. It takes three parameters.
  • el - The element [block which need to work as an accordion].
  • delay - Time delay in animating the accordion.
  • steps - Number of steps for the animation.
First it checks whether the element is available, if not then gives an alert message and returns. If the element exists then continues by calling the 'init()' method. Before looking into the 'init()' method, lets add some supportive methods needed by the code.
For convention I am going to use '_' as prefix to the method names which are internal to the code, so that you can easily identify which methods are internal and which are available for external code. We need
  • A method to check for applied CSS styles.
  • A method to get individual elements from the block which works as Accordion.
  • A method to calculate step height for animation.
First lets see the method to check for applied CSS styles.
_hasClass : function(c, className) {
    return c.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
   }
  
As this is an internal method it is prefixed with '_' as mentioned little earlier. It takes two parameters.
  • The html tag element/node for which we need to check for applied styles.
  • The CSS class name.
This method uses Regular Expression to check CSS styles applied for efficiency. The regular expression used is "new RegExp('(\\s|^)' + className + '(\\s|$)')". In this regular expression '\s' denotes any white-space character, '|' is for alternation [logical OR], '^' is to match for string starting, and the CSS class name we wanted to check for [method parameter 'className'], followed by any white-space character '\s', '|' for alternation [logical OR], and '$' to math for end of string.
I think this explanation gives you good idea how the regular expression works. Now if we see the one line in the method '_hasClass' it uses the DOM attribute 'className' to get the applied CSS classes to the element passed and uses regular expression to find matches. If it finds a match then it returns true, false otherwise.
The next method we have is the one to get elements from an html block [method to get matching child element from the given container element]. Let's name it as '_getElement'.
_getElement : function(parent, childTag, className) {
    var elements = this.el.getElementsByTagName(childTag);
    if (!className)
     return elements[0];
    for (var i=0; i < elements.length; i++) {
     if (this._hasClass(elements[i], className))
      return elements[i];
    }
   }
  
This method takes three parameters.
  • The parent/container element in which we are going to look for matching child elements.
  • The type of the child tag we need to look for.
  • CSS class name, which is applied to the child tag we are looking for.
If we do not pass any CSS class then, this method return the first element matching the given child tag type. We use 'getElementsByTagName ()' method which takes the tag type to be looked for and returns all the matching child tags as an array.
After we get the array of matching child tags, we loop through them and check each element, whether it has the given CSS class applied to it. When it finds a match it returns the element.
Now we need a method to calculate the step height for animation of accordion, let's name it as '_getStepHeight'
_getStepHeight : function () {
    // make sure the content is visible before getting its height
    this.contentEl.style.display = "block";
    // get the height of the content
    var contentHeight = this.contentEl.offsetHeight;
    return (contentHeight / this.steps);
   },
  
This method is very simple; first it gets the height of the block [accordion block] and divides it by the number of steps. To get the height of the block, first we set the 'display' attribute of it to 'block' so that we get the proper height even if the block is in closed status.
Now lets move on to the 'init()'method.
init : function(el, delay, steps) {
    /** Accordion element**/
    this.el = el;
    /** Accordion heading tag - mouse events will be attached to this tag **/
    this.headingTag = "h2";
    /** delay for each step of animation **/ 
    this.delay = delay;
    /** no.of animation steps **/
    this.steps = steps;
    /** CSS classes of container and heading panels **/
    this.panelClass = "ra-p";
    this.cPanelClass = "ra-cp";
    this.textClass = "text";

    /** attach mouse click events **/
    if (this._hasClass(el, this.panelClass)) {
     this.contentEl = this._getElement(el, "div", this.textClass);
     this.headerEl = this._getElement(el, "h2");
     /** calculate animation step size **/
     this.stepHeight = this._getStepHeight();
     if (this.contentEl && this.headerEl) {
      var panel = this;
      this.headerEl.onclick = function() { 
       panel.animateToggle(panel); 
      };
     }
    }
   }
  
Most part of this method is just initialization of variables. First half of the method we'll initialize variables used for accordion animation. Important part is the one where we add mouse click event to the block heading. Before adding mouse event to the heading we verify that the container had the necessary CSS ['ra-p' in this case] applied to it. You can see these values initialized in the first half of the method.
Then we'll get the content block element reference and keep its reference ['contentEl' variable], and we'll get the heading element reference ['headerEl' variable]. After this we'll calculate the step height [height in pixels for each step of animation] for animation.
Before proceeding to add mouse click event, we'll verify both content element and header elements are available, finally we'll add mouse click event handler and attaché 'animateToggle ()' method. That's it we are almost done with this. Only thing we are left with is the 'animateToggle ()' method, lets see what it has.
animateToggle : function(panel) {
    // check if you are in the middle of an operation
    if (this.key) {
     clearInterval(this.key);
     this.key = null;
    }
    // make sure the content is visible before getting its height
    this.contentEl.style.display = "block";
    var expanding = panel._hasClass(this.el, this.cPanelClass);
    // if panel is collapsed and expanding, we must start with 0 height
    if (expanding)
     panel.contentEl.style.height = "0px";
    this.iteration = 1;
    this.key = setInterval(function(){panel._animate(panel, expanding)}, panel.delay);
   }
  
Surprisingly nothing complex here!; first we'll check whether we are in the middle of an existing opration [expanding/collapsing], if we are, then we'll stop it by calling 'clearInterval()' method and continue with the current one.
Then we'll make sure that the content block is visible by setting its 'display' property to 'block', and then see whether we're expanding the block or collapsing it by checking CSS style applied to it. If it has 'ra-p' then block is expanded and we need to collapse it, expand otherwise. If we're expanding set the content block's height to '0px' and start increasing its height. To increase the height step by step we'll call an internal function '_animate()' repeatedly after specific delay using 'setInterval()' method. 'setInterval()' method is available in java script which can be used to call a method repeatedly after some delay until we call it to stop, using 'clearInterval()'.
Lets see what '_animate()' method has in it.
_animate : function (panel, expanding) {
    if (panel.iteration < panel.steps) {
     panel.contentEl.style.height = Math.round(
       ((expanding) ? panel.iteration : 
        panel.steps - panel.iteration) * panel.stepHeight) +"px";
     panel.iteration++;
    } else {
     // set class for the panel
     panel.el.className = (!expanding) ? panel.cPanelClass : panel.panelClass;
     // clear inline styles
     panel.contentEl.style.display = panel.contentEl.style.height = "";
     // stop animation
     clearInterval(this.key);
     this.key = null;
    }
   }
  
At the beginning we specified the number of steps for the animation and this is what we use to stop animation. Fist we check whether the iteration is less that number of steps. If yes we'll proceed with increasing/decreasing the height of the content block. Else we'll reset the CSS styles for the content block and stop animation.
You can download the complete code using the link bellow. Click here.
This post might look lengthy, but when you see the code, it's very small and light in size. Hope it's useful for some folks out there looking for similar code.
By the way it's my first posting, I'd love to get some feedback. Do drop a note!

Comments

Popular posts from this blog

Hosting Multiple Domains In Tomcat

Tomcat allows us to host multiple domains in one instance, using multiple ' Host ' tags. In this article I will explain how to do it on Tomcat. This is very simple configuration using ' Host ' tags in your server.xml . A novice can also understand this configuration very easily. Before going into the details of the configuration first lets have a look at the ' Host ' tag, ' Context ' tag and ' Alias ' tags first. <Host name="domain1" appBase="[application base]" autoDeploy="[true/false]" unpackWARs="[true/false]"> <Alias>...</Alias> <Context path="" docBase="" reloadable="[true/false]"/> </Host> First lets have a look at ' Alias ' tag. This tag is used to provide aliases for your actual domain. For example you have a domain called 'domain1.com', and you want to run the same application for 'www.do

File Uploading Using Servlets, JSP and Commons File Upload API

I’ve seen many developers who are at the early stages of their career have problems with this topic and seen many posts in forums asking how to do it – File Uploading using Servlets, JSP!; this article will provide an example using Commons File Upload API. I tried to make the example as simple as possible, hope it helps those early birds. Example uses JSP to provide the pages with form where user can select the file to upload with other form fields, Commons File Upload API to process submitted form and read form fields separately, and Servlets as middle layer between JSP and Commons File Upload API, example also has ANT build script to generate the distributables. All the code can be downloaded, links to these resources are provided at the end of this post, lets get on with the example then. The flow in this example is as depicted in the following picture. As you can see, user selects the file to upload and provides normal form data as well, using "index.jsp" and submi