SlideShare a Scribd company logo
Javascript animated collapsible panels without any frameworks                                                1



               Javascript animated collapsible panels
                     without any frameworks
                                                             By Armand Niculescu

   If you need to make collapsible panels that remember their state, but don’t want to use any
javascript framework, this tutorial will help you do this with no headaches - and you’ll learn
some neat javascript tricks too!

      In case you’re wondering why no frameworks, the reason is two-fold.

          • First, although frameworks are nice, they’re BIG. jQuery weighs over 50Kb 1; it’s not
            much if you’re building an entire web app, but if you’re only looking for some specif-
            ic effect, it’s overkill considering that you can achieve the same thing in less that
            3Kb.
          • Second, by doing it yourself, you have the potential to actually learn more about
            javascript (only if you want to — the code I’m presenting is pretty much plug-and-
            play) and reuse the concepts to do other things - like a collapsible tree menu for ex-
            ample.


         Features at a glance
           •   works with any number of panels in a page;
           •   plug-and-play – can be used with little or no tweaks;
           •   shows different arrows and style for the expanded/collapsed state;
           •   animated transitions;
           •   each panel’s state is saved and remembered between sessions;
           •   tested in IE6, IE7, IE8, Firefox 3, Chrome, Safari 4 and Opera 9.


         Start with layout and style
   For this tutorial we’ll use a minimal style; since the article is not about CSS, I will only
briefly cover this.


           HTML
      The required HTML for a panel is very simple:

     1. <div class="panel">
     2.     <h2>One panel</h2>
1     To add insult to injury, if you are using a CMS or blog with plugins, they sometimes use different frameworks,
      so the page ends up loading jQuery, MooTools and Scriptaculous to display some simple effects.

©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                               2
  3.     <div class="panelcontent">
  4.         ... content goes here ...
  5.     </div>
  6. </div>

   We have a DIV that encloses the title (in our case a H2 tag, but you could use something
else) and another DIV that will hold the actual content. We need this kind of nested struc-
ture, but if anyone thinks of a more semantic approach, it can be changed.


       CSS
  For our panel we will use two main classes, .panel (for expanded state) and
.panelcollapsed (for the collapsed state), like this:

  1. .panel, .panelcollapsed
  2. {
  3.     background: #eee;
  4.     margin: 5px;
  5.     padding: 0px 0px 5px;
  6.     width: 300px;
  7.     border: 1px solid #999;
  8.     -moz-border-radius: 4px;
  9.     -webkit-border-radius: 4px;
 10. }
 Feel free to change the layout as you see fit. Note that in the example above Firefox and
Webkit-based browsers (Chrome and Safari) will also get some nice rounded corners.

   For headings I’m using an arrow icon on the background, the rest of the style is pretty
plain:

 11. .panel h2, .panelcollapsed h2
 12. {
 13.     font-size: 18px;
 14.     font-weight: normal;
 15.     margin: 0px;
 16.     padding: 4px;
 17.     background: #CCC url(arrow-up.gif) no-repeat 280px;
 18.     border-bottom: 1px solid #999;
 19.     -moz-border-radius: 3px;
 20.     -webkit-border-radius: 3px;
 21.     border-top: 1px solid #FFF;
 22.     border-right: 1px solid #FFF;
 23.     border-left: 1px solid #FFF;
 24. }

  For a collapsed panel, we want to change the style slightly and change the arrow direction:

 25. .panelcollapsed h2
 26. {
 27.     background: #CCC url(arrow-dn.gif) no-repeat 280px;
 28.     border-color: #CCC;
 29. }


©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                               3
  I also wanted to add a little ‘rollover’ effect on the heading (not visible in IE6):

 30. .panel h2:hover, .panelcollapsed h2:hover { background-color: #A9BCEF; }

  Finally, we’ll add just a little style for the panel content:

 31.   .panelcontent
 32.   {
 33.       background: #EEE;
 34.       overflow: hidden;
 35.   }
 36.
 37.   .panelcollapsed .panelcontent { display: none; }



       Adding the interactivity
  Because I wanted the HTML to be as simple as possible and to work with any number of
panels, I did not add links with onclick handlers (e.g. <a href="#" onclick="toggle()">)
and I also didn’t want to have to add IDs to panels. Therefore, I made the script ’smart’
enough to discover the panels and add the onclick handlers by itself. This is not as hard as it
may seem:

  1.   var PANEL_NORMAL_CLASS    = "panel";
  2.   var PANEL_COLLAPSED_CLASS = "panelcollapsed";
  3.   var PANEL_HEADING_TAG     = "h2";
  4.
  5.   function setUpPanels()
  6.   {
  7.       var headingTags = document.getElementsByTagName(PANEL_HEADING_TAG);
  8.
  9.       for (var i=0; i < headingTags.length; i++)
 10.       {
 11.           var el = headingTags[i];
 12.           if (el.parentNode.className != PANEL_NORMAL_CLASS &&
       el.parentNode.className != PANEL_COLLAPSED_CLASS)
 13.               continue;
 14.           el.onclick = function()
 15.           {
 16.                   var target    = this.parentNode;
 17.                   var collapsed = target.className == PANEL_COLLAPSED_CLASS;
 18.                   target.parentNode.className = collapsed ? PANEL_NORMAL_CLASS :
       PANEL_COLLAPSED_CLASS;
 19.           };
 20.       }
 21.   }
 22.
 23.   // Register setUpPanels to be executed on load
 24.   if (window.addEventListener)
 25.           window.addEventListener("load", setUpPanels, false);
 26.   else
 27.   if (window.attachEvent)
 28.           window.attachEvent("onload", setUpPanels);



©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                                    4
  First, I’m setting up some constants, just in case you’ll want to use different tags for the
heading or different class names. The setUpPanels function works like this:

       1. using getElementsByTagName we get all H2 headings in the page and collect them
          into an array;
       2. we go through each collected element:
              1. test to see if it’s inside a panel by accessing its parent class name, skip if it’s
                 not panel or panelcollapsed.
              2. add an onclick handler for the heading. This will work fine even without a
                 link, although the hover effect will not work in IE6. The function does the
                 following:
                      1. get the heading’s parent, that’s the panel itself.
                      2. get its class name, switch between panel and panelcollapsed.

   Finally, we need a way to make the setUpPanels to execute upon window load. Now, in-
stead of using a <body onload="setUpPanels()"> construct, we’ll use something more clever.
Unfortunately, as always IE plays different, so we’ll have to use two methods: the correct DOM
method via addEventListener and the IE way with attachEvent.


     Making changes persistent
   To be really useful for the user, the expanded/collapsed preferences should be saved in a
cookie, so that the next time the page is loaded, the panels are already expanded or collapsed.
For this, first we’ll use an object called panelsStatus that will keep the expanded/collapsed
status for each panel; it will act as an associative array or a hash. The ‘key’ will be the panel’s
name (the text in the H2 tag) and the value will be “true” for an expanded panel and “false”
for a collapsed one.

  To keep code neat, we’ll also define our cookie name with

 29. var PANEL_COOKIE_NAME = "panels";



       Saving settings
  Saving will take place each time a panel is toggled.

 30. function saveSettings(key, value)
 31. {
 32.     panelsStatus[key] = value;
 33.
 34.     var panelsData = [];
 35.     for (var key in panelsStatus)
 36.         panelsData.push(key+":"+panelsStatus[key]);
 37.
 38.     var today = new Date();
 39.     var expirationDate = new Date(today.getTime() + 365 * 1000 * 60 * 60 * 24);
 40.


©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                                   5
 41.     document.cookie = PANEL_COOKIE_NAME + "=" + escape(panelsData.join("|")) +
     ";expires=" + expirationDate.toGMTString();
 42. }

  Step by step, here’s what the function does:

       1. get the modified key-value pair and put it in the panelsStatus object;
       2. go through all elements in the object and combine entries in a string like this:
          “key:value“, put in a temporary array;
       3. get today date and add one year to it (that will be the cookie expiration date);
       4. join the temporary array elements into one string like “key1:value1|key2:value2|
          key3:value3” and write in the cookie.The cookie string will look like this:
          panels=One%20panel%3Afalse;expires=Sun, 11 Apr 2010 09:40:22 GMT

   For simplicity I’ve used “:” (colon) and “|” (pipe) as separators. If you think you’re likely to
encounter those in the panel name, you can replace them with any weird character you can
think of.


       Loading settings
  Loading will be done once, when the page is loaded.

 43. function loadSettings()
 44. {
 45.     panelsStatus = {};
 46.
 47.     var start = document.cookie.indexOf(PANEL_COOKIE_NAME + "=");
 48.     if (start == -1) return;
 49.     start += PANEL_COOKIE_NAME.length+1;
 50.     var end = document.cookie.indexOf(";", start);
 51.     if (end == -1) end = document.cookie.length-1;
 52.
 53.     var cookieValue = unescape(document.cookie.substring(start, end));
 54.     var panelsData = cookieValue.split("|");
 55.
 56.     for (var i=0; i< panelsData.length; i++)
 57.     {
 58.         var pair = panelsData[i].split(":");
 59.         panelsStatus[pair[0]] = pair[1];
 60.     }
 61. }

  Here’s what the code does:

       1.   create the panelsStatus object;
       2.   find the cookie name in the document.cookie string, return if not found;
       3.   find the end of the cookie value string (before the “;expires=“);
       4.   take the cookie value and split by by the “|” character, put in a temporary array;
       5.   go through each element in the array, split by “:” and save in the panelsStatus ob-
            ject as key/value pair.

©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                               6

     Animating the transition
   For some reason, people think animation is difficult in browser. It’s not. Think of it this
way: any property that you can set via CSS, you can alter dynamically in javascript. To anim-
ate, you just need to call a function repeatedly, and this is most easily achieved with
setTimeout or setInterval().


       Preparing the transition
  First, let’s define some new constants:

 62. var PANEL_CONTENT_CLASS   = "panelcontent";
 63. var PANEL_ANIMATION_DELAY = 20; /*ms*/
 64. var PANEL_ANIMATION_STEPS = 10;

  So, we have defined the class name for the panel content, the delay in milliseconds
between two calls to the animation function and the total number of steps to use for the an-
imation.

   To keep things as flexible as possible, I wanted the script to work easily regardless of the
panel height, so when animating, we’ll dynamically change the height of the panel content
(that’s why it needs the overflow:hidden definition in CSS). If you want all panels to have the
same height, maybe with scrollable content, I suggest adding another DIV in the panel con-
tent and setting its height and overflow.

 65. function animateTogglePanel(panel, expanding)
 66. {
 67.     var elements = panel.getElementsByTagName("div");
 68.     var panelContent = null;
 69.     for (var i=0; i < elements.length; i++)
 70.     {
 71.         if (elements[i].className == PANEL_CONTENT_CLASS)
 72.         {
 73.             panelContent = elements[i];
 74.             break;
 75.         }
 76.     }
 77.
 78.     panelContent.style.display = "block";
 79.     var contentHeight = panelContent.offsetHeight;
 80.
 81.     if (expanding)
 82.         panelContent.style.height = "0px";
 83.
 84.     var stepHeight = contentHeight / PANEL_ANIMATION_STEPS;
 85.     var direction = (!expanding ? -1 : 1);
 86.
 87.     setTimeout(function(){animateStep(panelContent,1,stepHeight,direction)},
     PANEL_ANIMATION_DELAY);
 88. }




©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                                    7
  This function expects two parameters - a reference to the panel that needs to be animated
and whether it should be an expanding or collapsing animation.

       1. First, it finds the DIV that has the panelcontent class by collecting all descendant
          DIVs and going through them until it finds the one that has the right class name.
          This process could have been simplified a little by using getElementsByClassName,
          but that function is not supported by IE (surprise, surprise).
       2. Then, we need to get the height of the panel content, so we make sure it’s displayed
          by setting its display property to “block” and read its height with offsetHeight
          property. offsetHeight return its total height, meaning defined height + paddings
          + border widths, so you should not define any paddings or borders in the
          panelcontent class, or you’ll get inaccurate results.
       3. if the animation is for expanding, we need the content visible, but it has to start
          with a height of 0.
       4. calculate by how much the panel should expand or contract on each step by divid-
          ing the total height by the number of steps, and the direction - positive for expan-
          sion, negative for contraction
       5. set the timeout - here’s a tricky thing: we can’t call animateStep function directly
          because we need to pass the reference to the panel content to it. So we set an an-
          onymous function, which in turn will call animateStep. It’s confusing, but it works -
          for more details, read about setInterval callback arguments on Mozilla Developer.

   The next function is called every 20 milliseconds. It receives a reference to the panel con-
tent, the current step (iteration number), the step height and direction.

 89. function animateStep(panelContent, iteration, stepHeight, direction)
 90. {
 91.     if (iteration < PANEL_ANIMATION_STEPS)
 92.     {
 93.          panelContent.style.height = Math.round(((direction > 0) ? iteration :
     PANEL_ANIMATION_STEPS - iteration) * stepHeight) +"px";
 94.          iteration++;
 95.          setTimeout(function()
     {animateStep(panelContent,iteration,stepHeight,direction)},
     PANEL_ANIMATION_DELAY);
 96.     }
 97.     else
 98.     {
 99.          panelContent.parentNode.className = (direction < 0) ?
     PANEL_COLLAPSED_CLASS : PANEL_NORMAL_CLASS;
100.          panelContent.style.display = panelContent.style.height = "";
101.     }
102. }

  What it does:

       1. checks if the current iteration is smaller than the total number of iterations;
       2. if it is:
                1. change the panel content height to be equal to iteration number multiplied
                    by step height; reverse if it’s a collapsing animation;for example, consider it-

©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                                      8
                    eration 2 with a step height of 5px, expanding. Panel content height becomes
                    2*5=10px; at iteration 3 it will be 3*5=15px and so on;for a collapsing anima-
                    tion, if the total number of steps is 10, at iteration 2 we’d have (10-2)*5 =40px,
                    iteration 3 is (10-3)*5=35px.
                 2. set the timeout to call animateStep function again after the specified delay.
        3. if it’s not:
                 1. switch the class name for the panel (panel content’s parent).
                 2. clear any inline styles we’ve set.


       Putting it all together
  That’s it really. All we need to do now is to integrate the load/save and animation back into
our first code.

  The final javascript will look like this:

  1.   var   PANEL_NORMAL_CLASS      =   "panel";
  2.   var   PANEL_COLLAPSED_CLASS   =   "panelcollapsed";
  3.   var   PANEL_HEADING_TAG       =   "h2";
  4.   var   PANEL_CONTENT_CLASS     =   "panelcontent";
  5.   var   PANEL_COOKIE_NAME       =   "panels";
  6.   var   PANEL_ANIMATION_DELAY   =   20; /*ms*/
  7.   var   PANEL_ANIMATION_STEPS   =   10;
  8.
  9.   function setUpPanels()
 10.   {
 11.       loadSettings();
 12.
 13.         // get all headings
 14.         var headingTags = document.getElementsByTagName(PANEL_HEADING_TAG);
 15.
 16.         // go through all tags
 17.         for (var i=0; i < headingTags.length; i++)
 18.         {
 19.             var el = headingTags[i];
 20.
 21.                 // make sure it's the heading inside a panel
 22.         if (el.parentNode.className != PANEL_NORMAL_CLASS &&
     el.parentNode.className != PANEL_COLLAPSED_CLASS)
 23.             continue;
 24.
 25.         // get the text value of the tag
 26.         var name = el.firstChild.nodeValue;
 27.
 28.         // look for the name in loaded settings, apply the normal/collapsed class
 29.         el.parentNode.className = (panelsStatus[name] == "false") ?
     PANEL_COLLAPSED_CLASS : PANEL_NORMAL_CLASS;
 30.
 31.         // add the click behavor to headings
 32.         el.onclick = function()
 33.         {
 34.             var target    = this.parentNode;
 35.             var name      = this.firstChild.nodeValue;

©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                        9
 36.                var collapsed = target.className == PANEL_COLLAPSED_CLASS;
 37.                saveSettings(name, !collapsed.toString());
 38.                animateTogglePanel(target, collapsed);
 39.           };
 40.       }
 41.   }
 42.
 43.   /**
 44.     * Start the expand/collapse animation of the panel
 45.     * @param panel reference to the panel div
 46.     */
 47.   function animateTogglePanel(panel, expanding)
 48.   {
 49.        // find the .panelcontent div
 50.        var elements = panel.getElementsByTagName("div");
 51.        var panelContent = null;
 52.        for (var i=0; i < elements.length; i++)
 53.        {
 54.            if (elements[i].className == PANEL_CONTENT_CLASS)
 55.            {
 56.                panelContent = elements[i];
 57.                break;
 58.            }
 59.        }
 60.
 61.       // make sure the content is visible before getting its height
 62.       panelContent.style.display = "block";
 63.
 64.       // get the height of the content
 65.       var contentHeight = panelContent.offsetHeight;
 66.
 67.       // if panel is collapsed and expanding, we must start with 0 height
 68.       if (expanding)
 69.           panelContent.style.height = "0px";
 70.
 71.       var stepHeight = contentHeight / PANEL_ANIMATION_STEPS;
 72.       var direction = (!expanding ? -1 : 1);
 73.
 74.     setTimeout(function(){animateStep(panelContent,1,stepHeight,direction)},
     PANEL_ANIMATION_DELAY);
 75. }
 76.
 77. /**
 78. * Change the height of the target
 79. * @param panelContent reference to the panel content to change height
 80. * @param iteration current iteration;
 81. * @param stepHeight height increment to be added/substracted in one step
 82. * @param direction 1 for expanding, -1 for collapsing
 83. */
 84. function animateStep(panelContent, iteration, stepHeight, direction)
 85. {
 86.     if (iteration < PANEL_ANIMATION_STEPS)
 87.     {
 88.         panelContent.style.height = Math.round(((direction > 0) ? iteration :
     PANEL_ANIMATION_STEPS - iteration) * stepHeight) +"px";
 89.         iteration++;
 90.         setTimeout(function()


©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                           10
       {animateStep(panelContent,iteration,stepHeight,direction)},
       PANEL_ANIMATION_DELAY);
 91.       }
 92.       else
 93.       {
 94.            // set class for the panel
 95.            panelContent.parentNode.className = (direction < 0) ?
       PANEL_COLLAPSED_CLASS : PANEL_NORMAL_CLASS;
 96.            // clear inline styles
 97.            panelContent.style.display = panelContent.style.height = "";
 98.       }
 99.   }
100.
101.   // ------------------------------------------------------------------------------
102.   // Load-Save
103.   // ------------------------------------------------------------------------------
104.   /**
105.     * Reads the "panels" cookie if exists, expects data formatted as key:value|
106.     * puts in panelsStatus object
107.     */
108.   function loadSettings()
109.   {
110.        // prepare the object that will keep the panel statuses
111.        panelsStatus = {};
112.
113.       // find the cookie name
114.       var start = document.cookie.indexOf(PANEL_COOKIE_NAME + "=");
115.       if (start == -1) return;
116.
117.       // starting point of the value
118.       start += PANEL_COOKIE_NAME.length+1;
119.
120.       // find end point of the value
121.       var end = document.cookie.indexOf(";", start);
122.       if (end == -1) end = document.cookie.length-1;
123.
124.       // get the value, split into key:value pairs
125.       var cookieValue = unescape(document.cookie.substring(start, end));
126.       var panelsData = cookieValue.split("|");
127.
128.       // split each key:value pair and put in object
129.       for (var i=0; i< panelsData.length; i++)
130.       {
131.           var pair = panelsData[i].split(":");
132.           panelsStatus[pair[0]] = pair[1];
133.       }
134.   }
135.
136.   /**
137.     * Takes data from the panelsStatus object, formats as key:value|key:value...
138.     * and puts in cookie valid for 365 days
139.     * @param key   key name to save
140.     * @paeam value key value
141.     */
142.   function saveSettings(key, value)
143.   {
144.        // put the new value in the object


©2009 RichNetApps.com
Javascript animated collapsible panels without any frameworks                             11
145.      panelsStatus[key] = value;
146.
147.      // create an array that will keep the key:value pairs
148.      var panelsData = [];
149.      for (var key in panelsStatus)
150.          panelsData.push(key+":"+panelsStatus[key]);
151.
152.      // set the cookie expiration date 1 year from now
153.      var today = new Date();
154.      var expirationDate = new Date(today.getTime() + 365 * 1000 * 60 * 60 * 24);
155.      // write the cookie
156.      document.cookie = PANEL_COOKIE_NAME + "=" + escape(panelsData.join("|")) +
     ";expires=" + expirationDate.toGMTString();
157. }
158.
159. // ---------------------------------------------------------------------------
160. // Register setUpPanels to be executed on load
161. if (window.addEventListener)
162. {
163.      // the "proper" way
164.      window.addEventListener("load", setUpPanels, false);
165. }
166. else
167. if (window.attachEvent)
168. {
169.      // the IE way
170.      window.attachEvent("onload", setUpPanels);
171. }

   As you can see, the code is fully commented, so any details I’ve left out you should under-
stand easily.


       Download the code
   You can download a fully working example by going to the online article at
http://www.richnetapps.com/?p=389.




©2009 RichNetApps.com

More Related Content

Viewers also liked

ASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CSASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CS
tutorialsruby
 
treeview
treeviewtreeview
treeview
tutorialsruby
 
symfony_from_scratch
symfony_from_scratchsymfony_from_scratch
symfony_from_scratch
tutorialsruby
 
LibX2.0-Code4Lib-2009AsPresented
LibX2.0-Code4Lib-2009AsPresentedLibX2.0-Code4Lib-2009AsPresented
LibX2.0-Code4Lib-2009AsPresented
tutorialsruby
 
presentation
presentationpresentation
presentation
tutorialsruby
 
catalog
catalogcatalog
catalog
tutorialsruby
 
javascript2
javascript2javascript2
javascript2
tutorialsruby
 
veracruz
veracruzveracruz
veracruz
tutorialsruby
 

Viewers also liked (8)

ASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CSASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CS
 
treeview
treeviewtreeview
treeview
 
symfony_from_scratch
symfony_from_scratchsymfony_from_scratch
symfony_from_scratch
 
LibX2.0-Code4Lib-2009AsPresented
LibX2.0-Code4Lib-2009AsPresentedLibX2.0-Code4Lib-2009AsPresented
LibX2.0-Code4Lib-2009AsPresented
 
presentation
presentationpresentation
presentation
 
catalog
catalogcatalog
catalog
 
javascript2
javascript2javascript2
javascript2
 
veracruz
veracruzveracruz
veracruz
 

Similar to collapsible-panels-tutorial

Untangling7
Untangling7Untangling7
Untangling7
Derek Jacoby
 
Build and save your own Gutenberg Block Patterns
Build and save your own Gutenberg Block PatternsBuild and save your own Gutenberg Block Patterns
Build and save your own Gutenberg Block Patterns
Plasterdog Web Design
 
Perch, Patterns and Old Browsers
Perch, Patterns and Old BrowsersPerch, Patterns and Old Browsers
Perch, Patterns and Old Browsers
Rachel Andrew
 
The Theory Of The Dom
The Theory Of The DomThe Theory Of The Dom
The Theory Of The Dom
kaven yan
 
Angular JS in 2017
Angular JS in 2017Angular JS in 2017
Angular JS in 2017
Ayush Sharma
 
CSS Lessons Learned The Hard Way – Zoe Gillenwater
CSS Lessons Learned The Hard Way – Zoe GillenwaterCSS Lessons Learned The Hard Way – Zoe Gillenwater
CSS Lessons Learned The Hard Way – Zoe Gillenwater
beyond tellerrand
 
CSS Lessons Learned the Hard Way (Beyond Tellerand)
CSS Lessons Learned the Hard Way (Beyond Tellerand)CSS Lessons Learned the Hard Way (Beyond Tellerand)
CSS Lessons Learned the Hard Way (Beyond Tellerand)
Zoe Gillenwater
 
Display Suite: A Themers Perspective
Display Suite: A Themers PerspectiveDisplay Suite: A Themers Perspective
Display Suite: A Themers Perspective
Mediacurrent
 
The Ring programming language version 1.9 book - Part 54 of 210
The Ring programming language version 1.9 book - Part 54 of 210The Ring programming language version 1.9 book - Part 54 of 210
The Ring programming language version 1.9 book - Part 54 of 210
Mahmoud Samir Fayed
 
Hadoop Tutorial
Hadoop TutorialHadoop Tutorial
Hadoop Tutorial
emedin
 
a3.pdf
a3.pdfa3.pdf
a3.pdf
1914848496c
 
Start Using CSS Grid Layout Today - RuhrJS
Start Using CSS Grid Layout Today - RuhrJSStart Using CSS Grid Layout Today - RuhrJS
Start Using CSS Grid Layout Today - RuhrJS
Rachel Andrew
 
The Future State of Layout
The Future State of LayoutThe Future State of Layout
The Future State of Layout
Stephen Hay
 
Wordless, stop writing WordPress themes like it's 1998
Wordless, stop writing WordPress themes like it's 1998Wordless, stop writing WordPress themes like it's 1998
Wordless, stop writing WordPress themes like it's 1998
Filippo Dino
 
LISA Qooxdoo Tutorial Handouts
LISA Qooxdoo Tutorial HandoutsLISA Qooxdoo Tutorial Handouts
LISA Qooxdoo Tutorial Handouts
Tobias Oetiker
 
Cloud and Ubiquitous Computing manual
Cloud and Ubiquitous Computing manual Cloud and Ubiquitous Computing manual
Cloud and Ubiquitous Computing manual
Sonali Parab
 
Data herding
Data herdingData herding
Data herding
unbracketed
 
Data herding
Data herdingData herding
Data herding
unbracketed
 
The Road to Starling 2
The Road to Starling 2The Road to Starling 2
The Road to Starling 2
Daniel Sperl
 
Fewd week6 slides
Fewd week6 slidesFewd week6 slides
Fewd week6 slides
William Myers
 

Similar to collapsible-panels-tutorial (20)

Untangling7
Untangling7Untangling7
Untangling7
 
Build and save your own Gutenberg Block Patterns
Build and save your own Gutenberg Block PatternsBuild and save your own Gutenberg Block Patterns
Build and save your own Gutenberg Block Patterns
 
Perch, Patterns and Old Browsers
Perch, Patterns and Old BrowsersPerch, Patterns and Old Browsers
Perch, Patterns and Old Browsers
 
The Theory Of The Dom
The Theory Of The DomThe Theory Of The Dom
The Theory Of The Dom
 
Angular JS in 2017
Angular JS in 2017Angular JS in 2017
Angular JS in 2017
 
CSS Lessons Learned The Hard Way – Zoe Gillenwater
CSS Lessons Learned The Hard Way – Zoe GillenwaterCSS Lessons Learned The Hard Way – Zoe Gillenwater
CSS Lessons Learned The Hard Way – Zoe Gillenwater
 
CSS Lessons Learned the Hard Way (Beyond Tellerand)
CSS Lessons Learned the Hard Way (Beyond Tellerand)CSS Lessons Learned the Hard Way (Beyond Tellerand)
CSS Lessons Learned the Hard Way (Beyond Tellerand)
 
Display Suite: A Themers Perspective
Display Suite: A Themers PerspectiveDisplay Suite: A Themers Perspective
Display Suite: A Themers Perspective
 
The Ring programming language version 1.9 book - Part 54 of 210
The Ring programming language version 1.9 book - Part 54 of 210The Ring programming language version 1.9 book - Part 54 of 210
The Ring programming language version 1.9 book - Part 54 of 210
 
Hadoop Tutorial
Hadoop TutorialHadoop Tutorial
Hadoop Tutorial
 
a3.pdf
a3.pdfa3.pdf
a3.pdf
 
Start Using CSS Grid Layout Today - RuhrJS
Start Using CSS Grid Layout Today - RuhrJSStart Using CSS Grid Layout Today - RuhrJS
Start Using CSS Grid Layout Today - RuhrJS
 
The Future State of Layout
The Future State of LayoutThe Future State of Layout
The Future State of Layout
 
Wordless, stop writing WordPress themes like it's 1998
Wordless, stop writing WordPress themes like it's 1998Wordless, stop writing WordPress themes like it's 1998
Wordless, stop writing WordPress themes like it's 1998
 
LISA Qooxdoo Tutorial Handouts
LISA Qooxdoo Tutorial HandoutsLISA Qooxdoo Tutorial Handouts
LISA Qooxdoo Tutorial Handouts
 
Cloud and Ubiquitous Computing manual
Cloud and Ubiquitous Computing manual Cloud and Ubiquitous Computing manual
Cloud and Ubiquitous Computing manual
 
Data herding
Data herdingData herding
Data herding
 
Data herding
Data herdingData herding
Data herding
 
The Road to Starling 2
The Road to Starling 2The Road to Starling 2
The Road to Starling 2
 
Fewd week6 slides
Fewd week6 slidesFewd week6 slides
Fewd week6 slides
 

More from tutorialsruby

&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />
tutorialsruby
 
TopStyle Help &amp; &lt;b>Tutorial&lt;/b>
TopStyle Help &amp; &lt;b>Tutorial&lt;/b>TopStyle Help &amp; &lt;b>Tutorial&lt;/b>
TopStyle Help &amp; &lt;b>Tutorial&lt;/b>
tutorialsruby
 
The Art Institute of Atlanta IMD 210 Fundamentals of Scripting &lt;b>...&lt;/b>
The Art Institute of Atlanta IMD 210 Fundamentals of Scripting &lt;b>...&lt;/b>The Art Institute of Atlanta IMD 210 Fundamentals of Scripting &lt;b>...&lt;/b>
The Art Institute of Atlanta IMD 210 Fundamentals of Scripting &lt;b>...&lt;/b>
tutorialsruby
 
&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />
tutorialsruby
 
&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />
tutorialsruby
 
Standardization and Knowledge Transfer – INS0
Standardization and Knowledge Transfer – INS0Standardization and Knowledge Transfer – INS0
Standardization and Knowledge Transfer – INS0
tutorialsruby
 
xhtml_basics
xhtml_basicsxhtml_basics
xhtml_basics
tutorialsruby
 
xhtml_basics
xhtml_basicsxhtml_basics
xhtml_basics
tutorialsruby
 
xhtml-documentation
xhtml-documentationxhtml-documentation
xhtml-documentation
tutorialsruby
 
xhtml-documentation
xhtml-documentationxhtml-documentation
xhtml-documentation
tutorialsruby
 
CSS
CSSCSS
CSS
CSSCSS
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa0602690047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
tutorialsruby
 
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa0602690047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
tutorialsruby
 
HowTo_CSS
HowTo_CSSHowTo_CSS
HowTo_CSS
tutorialsruby
 
HowTo_CSS
HowTo_CSSHowTo_CSS
HowTo_CSS
tutorialsruby
 
BloggingWithStyle_2008
BloggingWithStyle_2008BloggingWithStyle_2008
BloggingWithStyle_2008
tutorialsruby
 
BloggingWithStyle_2008
BloggingWithStyle_2008BloggingWithStyle_2008
BloggingWithStyle_2008
tutorialsruby
 
cascadingstylesheets
cascadingstylesheetscascadingstylesheets
cascadingstylesheets
tutorialsruby
 
cascadingstylesheets
cascadingstylesheetscascadingstylesheets
cascadingstylesheets
tutorialsruby
 

More from tutorialsruby (20)

&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />
 
TopStyle Help &amp; &lt;b>Tutorial&lt;/b>
TopStyle Help &amp; &lt;b>Tutorial&lt;/b>TopStyle Help &amp; &lt;b>Tutorial&lt;/b>
TopStyle Help &amp; &lt;b>Tutorial&lt;/b>
 
The Art Institute of Atlanta IMD 210 Fundamentals of Scripting &lt;b>...&lt;/b>
The Art Institute of Atlanta IMD 210 Fundamentals of Scripting &lt;b>...&lt;/b>The Art Institute of Atlanta IMD 210 Fundamentals of Scripting &lt;b>...&lt;/b>
The Art Institute of Atlanta IMD 210 Fundamentals of Scripting &lt;b>...&lt;/b>
 
&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />
 
&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />&lt;img src="../i/r_14.png" />
&lt;img src="../i/r_14.png" />
 
Standardization and Knowledge Transfer – INS0
Standardization and Knowledge Transfer – INS0Standardization and Knowledge Transfer – INS0
Standardization and Knowledge Transfer – INS0
 
xhtml_basics
xhtml_basicsxhtml_basics
xhtml_basics
 
xhtml_basics
xhtml_basicsxhtml_basics
xhtml_basics
 
xhtml-documentation
xhtml-documentationxhtml-documentation
xhtml-documentation
 
xhtml-documentation
xhtml-documentationxhtml-documentation
xhtml-documentation
 
CSS
CSSCSS
CSS
 
CSS
CSSCSS
CSS
 
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa0602690047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
 
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa0602690047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
0047ecaa6ea3e9ac0a13a2fe96f4de3bfd515c88f5d90c1fae79b956363d7f02c7fa060269
 
HowTo_CSS
HowTo_CSSHowTo_CSS
HowTo_CSS
 
HowTo_CSS
HowTo_CSSHowTo_CSS
HowTo_CSS
 
BloggingWithStyle_2008
BloggingWithStyle_2008BloggingWithStyle_2008
BloggingWithStyle_2008
 
BloggingWithStyle_2008
BloggingWithStyle_2008BloggingWithStyle_2008
BloggingWithStyle_2008
 
cascadingstylesheets
cascadingstylesheetscascadingstylesheets
cascadingstylesheets
 
cascadingstylesheets
cascadingstylesheetscascadingstylesheets
cascadingstylesheets
 

Recently uploaded

Crafting Excellence: A Comprehensive Guide to iOS Mobile App Development Serv...
Crafting Excellence: A Comprehensive Guide to iOS Mobile App Development Serv...Crafting Excellence: A Comprehensive Guide to iOS Mobile App Development Serv...
Crafting Excellence: A Comprehensive Guide to iOS Mobile App Development Serv...
Pitangent Analytics & Technology Solutions Pvt. Ltd
 
Essentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation ParametersEssentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation Parameters
Safe Software
 
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid ResearchHarnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Neo4j
 
"Choosing proper type of scaling", Olena Syrota
"Choosing proper type of scaling", Olena Syrota"Choosing proper type of scaling", Olena Syrota
"Choosing proper type of scaling", Olena Syrota
Fwdays
 
Generating privacy-protected synthetic data using Secludy and Milvus
Generating privacy-protected synthetic data using Secludy and MilvusGenerating privacy-protected synthetic data using Secludy and Milvus
Generating privacy-protected synthetic data using Secludy and Milvus
Zilliz
 
Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |
AstuteBusiness
 
Mutation Testing for Task-Oriented Chatbots
Mutation Testing for Task-Oriented ChatbotsMutation Testing for Task-Oriented Chatbots
Mutation Testing for Task-Oriented Chatbots
Pablo Gómez Abajo
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
Chart Kalyan
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
Miro Wengner
 
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
Alex Pruden
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
Zilliz
 
What is an RPA CoE? Session 1 – CoE Vision
What is an RPA CoE?  Session 1 – CoE VisionWhat is an RPA CoE?  Session 1 – CoE Vision
What is an RPA CoE? Session 1 – CoE Vision
DianaGray10
 
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
Fwdays
 
Apps Break Data
Apps Break DataApps Break Data
Apps Break Data
Ivo Velitchkov
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
Neo4j
 
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
saastr
 
GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)
Javier Junquera
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
Neo4j
 
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
Jason Yip
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
panagenda
 

Recently uploaded (20)

Crafting Excellence: A Comprehensive Guide to iOS Mobile App Development Serv...
Crafting Excellence: A Comprehensive Guide to iOS Mobile App Development Serv...Crafting Excellence: A Comprehensive Guide to iOS Mobile App Development Serv...
Crafting Excellence: A Comprehensive Guide to iOS Mobile App Development Serv...
 
Essentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation ParametersEssentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation Parameters
 
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid ResearchHarnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
 
"Choosing proper type of scaling", Olena Syrota
"Choosing proper type of scaling", Olena Syrota"Choosing proper type of scaling", Olena Syrota
"Choosing proper type of scaling", Olena Syrota
 
Generating privacy-protected synthetic data using Secludy and Milvus
Generating privacy-protected synthetic data using Secludy and MilvusGenerating privacy-protected synthetic data using Secludy and Milvus
Generating privacy-protected synthetic data using Secludy and Milvus
 
Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |
 
Mutation Testing for Task-Oriented Chatbots
Mutation Testing for Task-Oriented ChatbotsMutation Testing for Task-Oriented Chatbots
Mutation Testing for Task-Oriented Chatbots
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
 
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
 
What is an RPA CoE? Session 1 – CoE Vision
What is an RPA CoE?  Session 1 – CoE VisionWhat is an RPA CoE?  Session 1 – CoE Vision
What is an RPA CoE? Session 1 – CoE Vision
 
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
 
Apps Break Data
Apps Break DataApps Break Data
Apps Break Data
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
 
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
 
GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
 
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
 

collapsible-panels-tutorial

  • 1. Javascript animated collapsible panels without any frameworks 1 Javascript animated collapsible panels without any frameworks By Armand Niculescu If you need to make collapsible panels that remember their state, but don’t want to use any javascript framework, this tutorial will help you do this with no headaches - and you’ll learn some neat javascript tricks too! In case you’re wondering why no frameworks, the reason is two-fold. • First, although frameworks are nice, they’re BIG. jQuery weighs over 50Kb 1; it’s not much if you’re building an entire web app, but if you’re only looking for some specif- ic effect, it’s overkill considering that you can achieve the same thing in less that 3Kb. • Second, by doing it yourself, you have the potential to actually learn more about javascript (only if you want to — the code I’m presenting is pretty much plug-and- play) and reuse the concepts to do other things - like a collapsible tree menu for ex- ample. Features at a glance • works with any number of panels in a page; • plug-and-play – can be used with little or no tweaks; • shows different arrows and style for the expanded/collapsed state; • animated transitions; • each panel’s state is saved and remembered between sessions; • tested in IE6, IE7, IE8, Firefox 3, Chrome, Safari 4 and Opera 9. Start with layout and style For this tutorial we’ll use a minimal style; since the article is not about CSS, I will only briefly cover this. HTML The required HTML for a panel is very simple: 1. <div class="panel"> 2. <h2>One panel</h2> 1 To add insult to injury, if you are using a CMS or blog with plugins, they sometimes use different frameworks, so the page ends up loading jQuery, MooTools and Scriptaculous to display some simple effects. ©2009 RichNetApps.com
  • 2. Javascript animated collapsible panels without any frameworks 2 3. <div class="panelcontent"> 4. ... content goes here ... 5. </div> 6. </div> We have a DIV that encloses the title (in our case a H2 tag, but you could use something else) and another DIV that will hold the actual content. We need this kind of nested struc- ture, but if anyone thinks of a more semantic approach, it can be changed. CSS For our panel we will use two main classes, .panel (for expanded state) and .panelcollapsed (for the collapsed state), like this: 1. .panel, .panelcollapsed 2. { 3. background: #eee; 4. margin: 5px; 5. padding: 0px 0px 5px; 6. width: 300px; 7. border: 1px solid #999; 8. -moz-border-radius: 4px; 9. -webkit-border-radius: 4px; 10. } Feel free to change the layout as you see fit. Note that in the example above Firefox and Webkit-based browsers (Chrome and Safari) will also get some nice rounded corners. For headings I’m using an arrow icon on the background, the rest of the style is pretty plain: 11. .panel h2, .panelcollapsed h2 12. { 13. font-size: 18px; 14. font-weight: normal; 15. margin: 0px; 16. padding: 4px; 17. background: #CCC url(arrow-up.gif) no-repeat 280px; 18. border-bottom: 1px solid #999; 19. -moz-border-radius: 3px; 20. -webkit-border-radius: 3px; 21. border-top: 1px solid #FFF; 22. border-right: 1px solid #FFF; 23. border-left: 1px solid #FFF; 24. } For a collapsed panel, we want to change the style slightly and change the arrow direction: 25. .panelcollapsed h2 26. { 27. background: #CCC url(arrow-dn.gif) no-repeat 280px; 28. border-color: #CCC; 29. } ©2009 RichNetApps.com
  • 3. Javascript animated collapsible panels without any frameworks 3 I also wanted to add a little ‘rollover’ effect on the heading (not visible in IE6): 30. .panel h2:hover, .panelcollapsed h2:hover { background-color: #A9BCEF; } Finally, we’ll add just a little style for the panel content: 31. .panelcontent 32. { 33. background: #EEE; 34. overflow: hidden; 35. } 36. 37. .panelcollapsed .panelcontent { display: none; } Adding the interactivity Because I wanted the HTML to be as simple as possible and to work with any number of panels, I did not add links with onclick handlers (e.g. <a href="#" onclick="toggle()">) and I also didn’t want to have to add IDs to panels. Therefore, I made the script ’smart’ enough to discover the panels and add the onclick handlers by itself. This is not as hard as it may seem: 1. var PANEL_NORMAL_CLASS = "panel"; 2. var PANEL_COLLAPSED_CLASS = "panelcollapsed"; 3. var PANEL_HEADING_TAG = "h2"; 4. 5. function setUpPanels() 6. { 7. var headingTags = document.getElementsByTagName(PANEL_HEADING_TAG); 8. 9. for (var i=0; i < headingTags.length; i++) 10. { 11. var el = headingTags[i]; 12. if (el.parentNode.className != PANEL_NORMAL_CLASS && el.parentNode.className != PANEL_COLLAPSED_CLASS) 13. continue; 14. el.onclick = function() 15. { 16. var target = this.parentNode; 17. var collapsed = target.className == PANEL_COLLAPSED_CLASS; 18. target.parentNode.className = collapsed ? PANEL_NORMAL_CLASS : PANEL_COLLAPSED_CLASS; 19. }; 20. } 21. } 22. 23. // Register setUpPanels to be executed on load 24. if (window.addEventListener) 25. window.addEventListener("load", setUpPanels, false); 26. else 27. if (window.attachEvent) 28. window.attachEvent("onload", setUpPanels); ©2009 RichNetApps.com
  • 4. Javascript animated collapsible panels without any frameworks 4 First, I’m setting up some constants, just in case you’ll want to use different tags for the heading or different class names. The setUpPanels function works like this: 1. using getElementsByTagName we get all H2 headings in the page and collect them into an array; 2. we go through each collected element: 1. test to see if it’s inside a panel by accessing its parent class name, skip if it’s not panel or panelcollapsed. 2. add an onclick handler for the heading. This will work fine even without a link, although the hover effect will not work in IE6. The function does the following: 1. get the heading’s parent, that’s the panel itself. 2. get its class name, switch between panel and panelcollapsed. Finally, we need a way to make the setUpPanels to execute upon window load. Now, in- stead of using a <body onload="setUpPanels()"> construct, we’ll use something more clever. Unfortunately, as always IE plays different, so we’ll have to use two methods: the correct DOM method via addEventListener and the IE way with attachEvent. Making changes persistent To be really useful for the user, the expanded/collapsed preferences should be saved in a cookie, so that the next time the page is loaded, the panels are already expanded or collapsed. For this, first we’ll use an object called panelsStatus that will keep the expanded/collapsed status for each panel; it will act as an associative array or a hash. The ‘key’ will be the panel’s name (the text in the H2 tag) and the value will be “true” for an expanded panel and “false” for a collapsed one. To keep code neat, we’ll also define our cookie name with 29. var PANEL_COOKIE_NAME = "panels"; Saving settings Saving will take place each time a panel is toggled. 30. function saveSettings(key, value) 31. { 32. panelsStatus[key] = value; 33. 34. var panelsData = []; 35. for (var key in panelsStatus) 36. panelsData.push(key+":"+panelsStatus[key]); 37. 38. var today = new Date(); 39. var expirationDate = new Date(today.getTime() + 365 * 1000 * 60 * 60 * 24); 40. ©2009 RichNetApps.com
  • 5. Javascript animated collapsible panels without any frameworks 5 41. document.cookie = PANEL_COOKIE_NAME + "=" + escape(panelsData.join("|")) + ";expires=" + expirationDate.toGMTString(); 42. } Step by step, here’s what the function does: 1. get the modified key-value pair and put it in the panelsStatus object; 2. go through all elements in the object and combine entries in a string like this: “key:value“, put in a temporary array; 3. get today date and add one year to it (that will be the cookie expiration date); 4. join the temporary array elements into one string like “key1:value1|key2:value2| key3:value3” and write in the cookie.The cookie string will look like this: panels=One%20panel%3Afalse;expires=Sun, 11 Apr 2010 09:40:22 GMT For simplicity I’ve used “:” (colon) and “|” (pipe) as separators. If you think you’re likely to encounter those in the panel name, you can replace them with any weird character you can think of. Loading settings Loading will be done once, when the page is loaded. 43. function loadSettings() 44. { 45. panelsStatus = {}; 46. 47. var start = document.cookie.indexOf(PANEL_COOKIE_NAME + "="); 48. if (start == -1) return; 49. start += PANEL_COOKIE_NAME.length+1; 50. var end = document.cookie.indexOf(";", start); 51. if (end == -1) end = document.cookie.length-1; 52. 53. var cookieValue = unescape(document.cookie.substring(start, end)); 54. var panelsData = cookieValue.split("|"); 55. 56. for (var i=0; i< panelsData.length; i++) 57. { 58. var pair = panelsData[i].split(":"); 59. panelsStatus[pair[0]] = pair[1]; 60. } 61. } Here’s what the code does: 1. create the panelsStatus object; 2. find the cookie name in the document.cookie string, return if not found; 3. find the end of the cookie value string (before the “;expires=“); 4. take the cookie value and split by by the “|” character, put in a temporary array; 5. go through each element in the array, split by “:” and save in the panelsStatus ob- ject as key/value pair. ©2009 RichNetApps.com
  • 6. Javascript animated collapsible panels without any frameworks 6 Animating the transition For some reason, people think animation is difficult in browser. It’s not. Think of it this way: any property that you can set via CSS, you can alter dynamically in javascript. To anim- ate, you just need to call a function repeatedly, and this is most easily achieved with setTimeout or setInterval(). Preparing the transition First, let’s define some new constants: 62. var PANEL_CONTENT_CLASS = "panelcontent"; 63. var PANEL_ANIMATION_DELAY = 20; /*ms*/ 64. var PANEL_ANIMATION_STEPS = 10; So, we have defined the class name for the panel content, the delay in milliseconds between two calls to the animation function and the total number of steps to use for the an- imation. To keep things as flexible as possible, I wanted the script to work easily regardless of the panel height, so when animating, we’ll dynamically change the height of the panel content (that’s why it needs the overflow:hidden definition in CSS). If you want all panels to have the same height, maybe with scrollable content, I suggest adding another DIV in the panel con- tent and setting its height and overflow. 65. function animateTogglePanel(panel, expanding) 66. { 67. var elements = panel.getElementsByTagName("div"); 68. var panelContent = null; 69. for (var i=0; i < elements.length; i++) 70. { 71. if (elements[i].className == PANEL_CONTENT_CLASS) 72. { 73. panelContent = elements[i]; 74. break; 75. } 76. } 77. 78. panelContent.style.display = "block"; 79. var contentHeight = panelContent.offsetHeight; 80. 81. if (expanding) 82. panelContent.style.height = "0px"; 83. 84. var stepHeight = contentHeight / PANEL_ANIMATION_STEPS; 85. var direction = (!expanding ? -1 : 1); 86. 87. setTimeout(function(){animateStep(panelContent,1,stepHeight,direction)}, PANEL_ANIMATION_DELAY); 88. } ©2009 RichNetApps.com
  • 7. Javascript animated collapsible panels without any frameworks 7 This function expects two parameters - a reference to the panel that needs to be animated and whether it should be an expanding or collapsing animation. 1. First, it finds the DIV that has the panelcontent class by collecting all descendant DIVs and going through them until it finds the one that has the right class name. This process could have been simplified a little by using getElementsByClassName, but that function is not supported by IE (surprise, surprise). 2. Then, we need to get the height of the panel content, so we make sure it’s displayed by setting its display property to “block” and read its height with offsetHeight property. offsetHeight return its total height, meaning defined height + paddings + border widths, so you should not define any paddings or borders in the panelcontent class, or you’ll get inaccurate results. 3. if the animation is for expanding, we need the content visible, but it has to start with a height of 0. 4. calculate by how much the panel should expand or contract on each step by divid- ing the total height by the number of steps, and the direction - positive for expan- sion, negative for contraction 5. set the timeout - here’s a tricky thing: we can’t call animateStep function directly because we need to pass the reference to the panel content to it. So we set an an- onymous function, which in turn will call animateStep. It’s confusing, but it works - for more details, read about setInterval callback arguments on Mozilla Developer. The next function is called every 20 milliseconds. It receives a reference to the panel con- tent, the current step (iteration number), the step height and direction. 89. function animateStep(panelContent, iteration, stepHeight, direction) 90. { 91. if (iteration < PANEL_ANIMATION_STEPS) 92. { 93. panelContent.style.height = Math.round(((direction > 0) ? iteration : PANEL_ANIMATION_STEPS - iteration) * stepHeight) +"px"; 94. iteration++; 95. setTimeout(function() {animateStep(panelContent,iteration,stepHeight,direction)}, PANEL_ANIMATION_DELAY); 96. } 97. else 98. { 99. panelContent.parentNode.className = (direction < 0) ? PANEL_COLLAPSED_CLASS : PANEL_NORMAL_CLASS; 100. panelContent.style.display = panelContent.style.height = ""; 101. } 102. } What it does: 1. checks if the current iteration is smaller than the total number of iterations; 2. if it is: 1. change the panel content height to be equal to iteration number multiplied by step height; reverse if it’s a collapsing animation;for example, consider it- ©2009 RichNetApps.com
  • 8. Javascript animated collapsible panels without any frameworks 8 eration 2 with a step height of 5px, expanding. Panel content height becomes 2*5=10px; at iteration 3 it will be 3*5=15px and so on;for a collapsing anima- tion, if the total number of steps is 10, at iteration 2 we’d have (10-2)*5 =40px, iteration 3 is (10-3)*5=35px. 2. set the timeout to call animateStep function again after the specified delay. 3. if it’s not: 1. switch the class name for the panel (panel content’s parent). 2. clear any inline styles we’ve set. Putting it all together That’s it really. All we need to do now is to integrate the load/save and animation back into our first code. The final javascript will look like this: 1. var PANEL_NORMAL_CLASS = "panel"; 2. var PANEL_COLLAPSED_CLASS = "panelcollapsed"; 3. var PANEL_HEADING_TAG = "h2"; 4. var PANEL_CONTENT_CLASS = "panelcontent"; 5. var PANEL_COOKIE_NAME = "panels"; 6. var PANEL_ANIMATION_DELAY = 20; /*ms*/ 7. var PANEL_ANIMATION_STEPS = 10; 8. 9. function setUpPanels() 10. { 11. loadSettings(); 12. 13. // get all headings 14. var headingTags = document.getElementsByTagName(PANEL_HEADING_TAG); 15. 16. // go through all tags 17. for (var i=0; i < headingTags.length; i++) 18. { 19. var el = headingTags[i]; 20. 21. // make sure it's the heading inside a panel 22. if (el.parentNode.className != PANEL_NORMAL_CLASS && el.parentNode.className != PANEL_COLLAPSED_CLASS) 23. continue; 24. 25. // get the text value of the tag 26. var name = el.firstChild.nodeValue; 27. 28. // look for the name in loaded settings, apply the normal/collapsed class 29. el.parentNode.className = (panelsStatus[name] == "false") ? PANEL_COLLAPSED_CLASS : PANEL_NORMAL_CLASS; 30. 31. // add the click behavor to headings 32. el.onclick = function() 33. { 34. var target = this.parentNode; 35. var name = this.firstChild.nodeValue; ©2009 RichNetApps.com
  • 9. Javascript animated collapsible panels without any frameworks 9 36. var collapsed = target.className == PANEL_COLLAPSED_CLASS; 37. saveSettings(name, !collapsed.toString()); 38. animateTogglePanel(target, collapsed); 39. }; 40. } 41. } 42. 43. /** 44. * Start the expand/collapse animation of the panel 45. * @param panel reference to the panel div 46. */ 47. function animateTogglePanel(panel, expanding) 48. { 49. // find the .panelcontent div 50. var elements = panel.getElementsByTagName("div"); 51. var panelContent = null; 52. for (var i=0; i < elements.length; i++) 53. { 54. if (elements[i].className == PANEL_CONTENT_CLASS) 55. { 56. panelContent = elements[i]; 57. break; 58. } 59. } 60. 61. // make sure the content is visible before getting its height 62. panelContent.style.display = "block"; 63. 64. // get the height of the content 65. var contentHeight = panelContent.offsetHeight; 66. 67. // if panel is collapsed and expanding, we must start with 0 height 68. if (expanding) 69. panelContent.style.height = "0px"; 70. 71. var stepHeight = contentHeight / PANEL_ANIMATION_STEPS; 72. var direction = (!expanding ? -1 : 1); 73. 74. setTimeout(function(){animateStep(panelContent,1,stepHeight,direction)}, PANEL_ANIMATION_DELAY); 75. } 76. 77. /** 78. * Change the height of the target 79. * @param panelContent reference to the panel content to change height 80. * @param iteration current iteration; 81. * @param stepHeight height increment to be added/substracted in one step 82. * @param direction 1 for expanding, -1 for collapsing 83. */ 84. function animateStep(panelContent, iteration, stepHeight, direction) 85. { 86. if (iteration < PANEL_ANIMATION_STEPS) 87. { 88. panelContent.style.height = Math.round(((direction > 0) ? iteration : PANEL_ANIMATION_STEPS - iteration) * stepHeight) +"px"; 89. iteration++; 90. setTimeout(function() ©2009 RichNetApps.com
  • 10. Javascript animated collapsible panels without any frameworks 10 {animateStep(panelContent,iteration,stepHeight,direction)}, PANEL_ANIMATION_DELAY); 91. } 92. else 93. { 94. // set class for the panel 95. panelContent.parentNode.className = (direction < 0) ? PANEL_COLLAPSED_CLASS : PANEL_NORMAL_CLASS; 96. // clear inline styles 97. panelContent.style.display = panelContent.style.height = ""; 98. } 99. } 100. 101. // ------------------------------------------------------------------------------ 102. // Load-Save 103. // ------------------------------------------------------------------------------ 104. /** 105. * Reads the "panels" cookie if exists, expects data formatted as key:value| 106. * puts in panelsStatus object 107. */ 108. function loadSettings() 109. { 110. // prepare the object that will keep the panel statuses 111. panelsStatus = {}; 112. 113. // find the cookie name 114. var start = document.cookie.indexOf(PANEL_COOKIE_NAME + "="); 115. if (start == -1) return; 116. 117. // starting point of the value 118. start += PANEL_COOKIE_NAME.length+1; 119. 120. // find end point of the value 121. var end = document.cookie.indexOf(";", start); 122. if (end == -1) end = document.cookie.length-1; 123. 124. // get the value, split into key:value pairs 125. var cookieValue = unescape(document.cookie.substring(start, end)); 126. var panelsData = cookieValue.split("|"); 127. 128. // split each key:value pair and put in object 129. for (var i=0; i< panelsData.length; i++) 130. { 131. var pair = panelsData[i].split(":"); 132. panelsStatus[pair[0]] = pair[1]; 133. } 134. } 135. 136. /** 137. * Takes data from the panelsStatus object, formats as key:value|key:value... 138. * and puts in cookie valid for 365 days 139. * @param key key name to save 140. * @paeam value key value 141. */ 142. function saveSettings(key, value) 143. { 144. // put the new value in the object ©2009 RichNetApps.com
  • 11. Javascript animated collapsible panels without any frameworks 11 145. panelsStatus[key] = value; 146. 147. // create an array that will keep the key:value pairs 148. var panelsData = []; 149. for (var key in panelsStatus) 150. panelsData.push(key+":"+panelsStatus[key]); 151. 152. // set the cookie expiration date 1 year from now 153. var today = new Date(); 154. var expirationDate = new Date(today.getTime() + 365 * 1000 * 60 * 60 * 24); 155. // write the cookie 156. document.cookie = PANEL_COOKIE_NAME + "=" + escape(panelsData.join("|")) + ";expires=" + expirationDate.toGMTString(); 157. } 158. 159. // --------------------------------------------------------------------------- 160. // Register setUpPanels to be executed on load 161. if (window.addEventListener) 162. { 163. // the "proper" way 164. window.addEventListener("load", setUpPanels, false); 165. } 166. else 167. if (window.attachEvent) 168. { 169. // the IE way 170. window.attachEvent("onload", setUpPanels); 171. } As you can see, the code is fully commented, so any details I’ve left out you should under- stand easily. Download the code You can download a fully working example by going to the online article at http://www.richnetapps.com/?p=389. ©2009 RichNetApps.com