I am working on a website where we need to present a large amount of information in a hierarchical list. The list will have several thousand elements so it seems best not to present it all immediately. Instead, we want to use a collapsible list so that users can expand the elements they are interested in one at a time. I looked around on the Internet for ways to accomplish this and found a number of solutions. The Internet being what it is, many of these only partially work because they contain bugs , are based on outdated norms or are complicated to use (e.g. based on AJAX). However, I decided to use the last one. One problem with it is that it does not support links in the list by default. This is because the JS hijacks the click events so that clicking links has no effect. When people complained about this in the comments, the author posted a new version that gets around this problem.

However, the code for using links in the list has a bug. The <a> elements get hidden as well. The problem is that the menu collapse button hides the <a> elements (because it hides all elements), so the link text becomes hidden. The solution is to always specify in the call to children().hide() that it is only the <ul> elements that should be hidden. The images below show the problem:

list_all_expandedlist_all_collapsedlist_top_expanded

In the first, all the items are expanded via the button. In the second, all are collapsed via the button. Notice how Item A is now missing, but it’s space is still taken up. This is because the <a> element is hidden but the <li> is not, but the <li> is empty and creates the line. The third shows what happens if one clicks Item A from the second situation. However, if one reloads the page and clicks Item A, then it stays there.

Another problem is that the author uses images to make the + and symbols which he uses as bullets for the list for the expandable items. These can be made using CSS too. I have done this in my version below via a solution found on Stackoverflow (because that’s how we roll).

Full JS code:

/**************************************************************/
 /* Prepares the cv to be dynamically expandable/collapsible   */
 /**************************************************************/
 function prepareList() {
 $('#expList').find('li:has(ul)')
 .click( function(event) {
 if (this == event.target) {
 $(this).toggleClass('expanded');
 $(this).children('ul').toggle('medium');
 }
 return false;
 })
 .addClass('collapsed')
 .children('ul').hide();

//Hack to add links inside the cv
 $('#expList a').unbind('click').click(function() {
 window.open($(this).attr('href'));
 return false;
 });

//Create the button funtionality
 $('#expandList')
 .unbind('click')
 .click( function() {
 $('.collapsed').addClass('expanded');
 $('.collapsed').children('ul').show('medium');
 })
 $('#collapseList')
 .unbind('click')
 .click( function() {
 $('.collapsed').removeClass('expanded');
 $('.collapsed').children('ul').hide('medium');
 })

};

/**************************************************************/
 /* Functions to execute on loading the document               */
 /**************************************************************/
 $(document).ready( function() {
 prepareList()
 });

CSS code:

/********************/
 /* GENERAL SETTINGS */
 /********************/
 body {
 background-color: #AAAAAA;
 font-size: 16px;
 }

#menu {
 list-style: none;
 padding: 0;
 margin: 0;
 }

.clear {
 clear: both;
 }

/********************/
 /* EXPANDABLE LIST  */
 /********************/
 #listContainer{
 margin-top:15px;
 }

#expList ul, li {
 list-style: none;
 margin:0;
 padding:0;
 cursor: pointer;
 }
 #expList p {
 margin:0;
 display:block;
 }
 #expList p:hover {
 background-color:#121212;
 }
 #expList li {
 line-height:140%;
 text-indent:0px;
 background-position: 1px 8px;
 padding-left: 20px;
 background-repeat: no-repeat;
 }

/* Collapsed state for list element */
 #expList .collapsed {
 list-style-type: "+";
 }
 /* Expanded state for list element
 /* NOTE: This class must be located UNDER the collapsed one */
 #expList .expanded {
 list-style-type: "-";
 }
 #expList {
 clear: both;
 }

.listControl{
 margin-bottom: 15px;
 }
 .listControl a {
 border: 1px solid #555555;
 color: #555555;
 cursor: pointer;
 height: 1.5em;
 line-height: 1.5em;
 margin-right: 5px;
 padding: 4px 10px;
 }
 .listControl a:hover {
 background-color:#555555;
 color:#222222;
 font-weight:normal;
 }

HTML code:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Expanding list test</title>
        <link rel="stylesheet" href="style.css" type="text/css" media="screen, projection">
        <script type="text/javascript" src="jquery-1.4.2.min.js">
        </script>
        <script type="text/javascript" src="scripts.js">
        </script>
    </head>
    <body>
        <h1><b>Expanding list test</b></h1>
        <div id="listContainer">
            <div class="listControl">
                <a id="expandList">Expand All</a>
                <a id="collapseList">Collapse All</a>
            </div>
  <ul id="expList">
    <li><a href="">Item A</a>
      <ul>
        <li>Item A.1
          <ul>
            <li>
              <span>Blah</span>
            </li>
          </ul>
        </li>
        <li>Item A.2</li>
        <li>Item A.3
          <ul>
            <li>
              <span>Blah</span>
            </li>
          </ul>
        </li>
      </ul>
    </li>
    <li>Item B</li>
    <li>Item C
      <ul>
        <li>Item C.1</li>
        <li>Item C.2
          <ul>
            <li>
              <span>Blah</span>
            </li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</div>
        </div>
    </body>
</html>