Adept Development System

Adept - Application Developer Enterprise to Personal Transition - A system to leverage corporate design (graphic, interface and code) and development skills to produce shrink-wrap software packages.

http://marringtons.com

Monday, August 08, 2005

The Static Active Tree

The reason for the unusual title is because I've created the visual tree code for Adept. The first use is in the options system. I've chosen the large-input method of tree on left and panels on right favoured by X-Windows over the panel-and-tab approach that Windows programs enjoy. It provides for a much richer selection set - but looks rather silly when there are only a few panels. The options system will be the subject of another article later (when this feature is ready).

Most of us are familiar with this form of visual tree representation. Windows Explorer and its ilk use it to display the list of folders on the left, for example. Each line is a branch or leaf. If a branch it has a + to open and view sub-branches, or - to close and hide them again.

In truth, I was surprised at how easy a visual tree was to develop using DHTML and CSS. My first version, written years ago for an online art gallery was for older browsers. This made it more complicated and less elegant than the current generation.

To create a tree, place it inside an activated FORM or SPAN element. It is an unordered list (UL) with a class of tree.

 ul class="tree">
   li>One
     ul>
       li>1.1
       li>1.2
       li>1.3
         ul>
           li>1.3.1
           li>1.3.2
           li>1.3.3
           li>1.3.4
         ul>
       li>1.4
     ul>
     li>Two
 ul>

 

This gives a result shown below. Clicking on the ± will expand/hide the node. In practice each node will consist of links, images or other active elements. Implementation was so remarkably easy that I have included it here in full. They style sheet, as usual is all about presentation. The only part critical to the tree is to hide inner unordered lists by default so that the tree starts closed.

When a branch is clicked on it will investigate the open attribute for the node and hide or show the underlying unordered list.

ul.tree, ul.tree ul
  {
    margin: 0px 0px 0px 16px;
    padding:0;
    font-size: small;
    color: gray;
  }
ul.tree li
  {
    list-style-image: url(/tree/no.leaf.gif);
  }
ul.tree ul
  { display:none; }
ul.tree a
  {
    text-decoration: none;
    color: black;
  }

 

The supporting code is a little longer, but not much. The activation system calls Tree.activate(), passing it the unordered list element that has a class of "tree". Only a single event listener is required for the whole tree as it uses the node clicked as the target to be processed. It will look for an open attribute and open any branch node that has one. The image displayed for each node is set by the activation code.
/** Called by ActiveComponent.js to activate a tree component. */
Tree.activateTree = function( ul)
  {
    System.linkStyleSheet( ul.ownerDocument, "/tree/Tree.css");
    ul.addEventListener( "click", Tree.toggleTreeEvent, false);
    Tree.activateTreeLevel( ul);
  }
  
Tree.activateTreeLevel = function( ul)
  {
    for (var li = ul.firstChild;  li;  li = li.nextSibling)
      if (li.nodeType == 1  &&  li.nodeName == "LI")
        {
          innerULs = li.getElementsByTagName( "ul");
          if (innerULs.length > 0)
            {
              /*
               * We have an inner UL, so treat li as a branch
               */
              li.ul = innerULs[0];
              Tree.activateTreeLevel( li.ul);
              li.style.cursor = "default";
              li.open = (li.getAttribute( "open") != null);
              Tree.showTree( li, li.open);
            }
          else
            {
              li.style.cursor = "default";
               li.style.listStyleImage = "url(/tree/leaf.gif)";
            }
        }
      /*
       * Work-around because style images don't work
       * without a refresh in FireFox.
       */
      ul.style.display = "none";
      ul.style.display = "block";
    }

Tree.toggleTreeEvent = function( evnt)
  {
    Tree.showTree( evnt.target, ! evnt.target.open);
  }
  
Tree.showTree = function( li, open)
  {
    if (! li.ul) return;
    if (open)
      {
         li.style.listStyleImage = "url(/tree/minus.gif)";
         li.ul.style.display = "block";
         li.open = true;
      }
    else
      {
         li.style.listStyleImage = "url(/tree/plus.gif)";
         li.ul.style.display = "none";
         li.open = false;
      }
  }

 

Extensions

The first obvious extension is to add keyboard control. Once the key is clicked it should gain focus and process the four arrow keys to move up and down or in and out of the tree structure. This is not important for windows-explorer-like applications (and the options system is such). After all, who uses the keyboard interface on Windows Explorer? I had to try it just then to confirm that it had one. Other applications, however, work differently. If the tree is part of a system editing system where the hands spend more time on the keyboard than on the mouse, keyboard control becomes an important time-saver.

And this brings us to the next extension. The current tree is static. The server provides an unordered list and this code displays it. What if the user wants to add, move or delete nodes? This will need to be controlled by more keystrokes (shift-arrow for moves), and to implement drag and drop functionality.

I don't need this additional functionality today, but I do have plans that will require it in the near future. When it's ready I will write another article.