Chapter 20

Learning from the Pros: Site Outlines


CONTENTS

One of the ways JavaScript has made navigating Web sites easier is through the use of multiple frames, typically implemented with a static list of links or a toolbar.

However, with JavaScript's capability to manipulate the contents of a window-including clearing and rewriting it-what would happen if the list of links was a dynamic object that could be expanded or collapsed? That's the idea Stefan Raab began to develop when he started to put together an indexing system for the Cue Systems'

Web site that would enable easy navigation and a visual key to its structure (see fig. 20.1). 

Figure 20.1 : Implementing a navigation outline with JavaScript was easier than Stefan Raab of Cue Systems anticipated, requiring only a few days of coding and testing after learning the basics of scripting.

Stefan Raab of Cue Systems, LLC

Using JavaScript as part of a corporate Web site was practically an accident for Stefan Raab, Communications Development Manager for Cue Systems, located on the World Wide Web at http://www.cuesys.com.

The company's mission is to develop integration between Java and OpenDoc for universal document handling, regardless of the hardware platform. "We call it 'reinventing the wheel for the information age,'" he says.

Most of his time is spent on everything except Web site management, including documentation, system support, and patent registration. "This was strictly a spare time enterprise," Raab says. "As I found a few minutes, I started to put the pieces together."

With an original plan revolving around learning CGI and Perl, Raab had planned on spending up to a month getting familiar with the programming before tackling the Web outline project. As betas of Netscape 2.0 were released with working versions of JavaScript, he started working with the new tools for HTML to see what they were capable of.

When it comes to learning new tools, such as JavaScript, reinventing the wheel means sharing ideas and solutions with other programmers and developers by letting the flow of information on the Web help drive program development through shared source code.

"It really frustrates me when someone just lifts a section of code and doesn't try to do anything else with it," Raab says. He contends it defeats the purpose of making common items available and slows down the process that takes simple ideas and transforms them into powerful applications.

"I love just to watch stuff evolve," he says, adding that one of his favorite moments is when people modify his outline and make it work for applications he never thought of. "People take it and do something new, and that's the exciting part about this."

The Collapsing and Expanding Outline

"The outline is more than a menu," Raab explains. "It's a natural way to organize information," which applies to everything from books and magazines to shopping lists and lists of home improvement projects.

When developing Cue Systems' public Web site, he wanted to use an outline model to relay navigational information to the user. His plan was for an application to automatically track the pages and links and assemble the outline based on that information. Each time a user accessed his site, the server would search for folders and documents-using the information to build an outline, which in turn would be used as a hot-linked table of contents.

The initial plan involved CGI scripting to accomplish site searching and page creation to update the outline. In addition to assembling and testing CGI scripts, this option also meant increased traffic and loading on the server.

Using CGI meant increased difficulties on both sides of the development process. First, he had to learn Perl and then develop the script necessary to search a site and build pages. When a functioning script was in place, it would mean a decrease in server performance, as it spent time looking for Web documents and writing the outline.

As the plan for CGI began to fade, Raab still didn't want to abandon the idea. "The outline is the most powerful way to organize information," he says. "It's something people do automatically whether they know it or not."

Getting up to Speed

"Getting this implemented was not a real priority," Raab says. The first thoughts of putting up a site for public consumption were in October 1995, but there was no serious development towards completion. Cue Systems maintains an intranet for its own use, but a site on the World Wide Web was not a pressing concern.

With the first usable version of JavaScript in a beta release of Netscape 2.0 in late fall 1995, Raab saw the possibilities for putting a site outline together without CGI. After trial-and-error experimentation and working with a JavaScript newsgroup, Raab picked up the fundamentals of implementing JavaScript in HTML.

For additional research, Raab and his colleagues went browsing for other pages that used outlines as a navigational tool. "We looked at other sites, but they just didn't use the function of an outline." Raab says other sites had an outline's appearance, but not the usability. In most cases, it was too simple with links to the top page of a general location, or too unwieldy with a list of links to an entire site.

Once Raab knew how his site shouldn't work, he put together an idea of how it could work and began assembling HTML and JavaScript code. With three days of work in January, a mock-up of the outline was completed.

Beginning his work with an immature language, such as JavaScript, and a beta version of Netscape meant sometimes working with an incomplete set of tools that didn't always perform as they should. "It took longer to work around bugs (in JavaScript)," he says. "It wasn't easy to update the page. There were a lot of undocumented things going on. We were spending a lot of time just finding out what was working."

The First Attempt: Forms

The first implementation of a dynamic outline used JavaScript's form manipulation features. This led to some unexpected behavior created by the way forms and form elements are implemented in JavaScript. Each level of the outline was represented by a form element, which worked well at first. The problems surfaced when adding or subtracting form checkbox elements as particular items were removed when the outline was collapsed.

Form elements are implemented in JavaScript as arrays and not true objects. As a form element was removed from the array, the other elements moved up in the array to occupy the vacant position. However, in addition to occupying the space by its neighbor, the checkbox element also assumed the value its neighbor left behind.

So, if a form element was changed from false (for expanded) to true (for collapsed), the following major topics assumed the checkbox values of the newly collapsed items sub-topics, resulting in bizarre behavior. Selecting or deselecting an outline item resulted in an almost random state of outline items below the affected form element.

onClicks and anchors

The next method for implementing was an outline that used the onClick method in conjunction with anchors. This option utilized a set of pages representing the different states of the outline with hashes to ensure the right portion was displayed when selected.

The problem was using hashes. To reference the top of a form, the URL included a hash with no anchor, which again resulted in unpredictable behavior and frequent crashes caused by referral to a "nonexistent" hash.

Those Persistent Objects

By continued experimentation with JavaScript, Raab learned about a quirk that held the key to the final solution: if a value is assigned to an object and the page is reloaded, the object will retain the value of its properties.

The implications were immediately clear. The form of the outline could be stored as objects with properties relating to the current state. When the user changed the state, the page was reloaded and redrawn based on the new object information.

This was accomplished with another "hidden" feature of the language. JavaScript became a valid document type for URLs (see fig. 20.2). By selecting an item in the outline, a JavaScript command is invoked with an event handler using URL format.

Figure 20.2 : The URL displayed in the status line with the mouse over an outline item is a JavaScript command.

The key to implementing the outline lies in the persistent state of objects during page reloading and the capability to embed JavaScript commands in URLs. Note the URL reflected in the status bar, which displays the two functions that update the outline display.

Losing Automatic Outline Building

By choosing an HTML JavaScript solution over CGI scripting, Raab had to give up the option of an outline that automatically updated itself. That meant more up-front work implementing the idea because he now had to organize his site with pencil and paper to see how it would be implemented in JavaScript code.

"The worst part was building the outlines by hand," he says. Organizing the site on paper also led to organizing the site on the server. "It really took planning."

Like a lot of Web sites, most of Raab's pages were lumped together in a common content directory. By breaking out the pages into directories that reflected site organization, the site outline began to materialize. As the outline took shape, so did the site, and he was ready to implement the information into his script.

Raab insists it was a valuable exercise in the development of a coherent and usable site. Taking the time to "build a good plan straight from the beginning" has saved more time later when it has been necessary to update the site. It also leads to the one drawback of his implementation: when a page or a link changes on his site, it must be recorded in the outline HTML document. "Every time I add, change, or remove a link, I have to change the code," he says.

Although this can be an inconvenience, the majority of the links for the Cue Systems site only occur within the outline. If a link is changed, it's usually in the outline document where the outline data can be changed also. If the link is also contained on another page, Raab's organization and outline speed its identification and change.

Simple Idea-Broad Application

After he finished the code, he posted the results on a newsgroup with a "here's my simple outline" note. He didn't think he had accomplished much until the mail started to overflow in his electronic mail box.

"I was just overwhelmed. People were writing things like, 'This is the best use of JavaScript I've ever seen.' It was just message after message. I still get four or five messages a week like that as more people find it." The current version of the outline is included on the CD-ROM.

Some of the uses that have come back to Raab include a table of contents for an online book and an outline of graphics. Other possibilities he's considered, but hasn't yet seen, include personnel and corporate directories for both Internet and intranet sites, departments for electronic stores, and illustrating structures of hardware connections.

Listing 20.1 is the source code for Stefan Raab's dynamic outline menu. Each menu level is implemented with its own object, which can contain other objects for outline sub-topics. To work with the quirks of JavaScript's write method, the object properties are initialized, and the initial display is created using Raab's draw() function. After a user changes the outline, the page is cleared and recreated using a redraw() function.


Listing 20.1  21list01.html  Cue Systems' Expanding and Collapsing Navigation Outline

<!-- So, you think the outline is pretty cool huh? Yeah me too! -->
<!-- Well feel free to take it and use it to you hearts content -->
<!-- I just ask two things of you:                              -->
<!-- 1. Please send me the URL of the page that uses it! I want -->
<!--    to see what other people are doing with it! :)          -->
<!-- 2. Please keep this Comment attached. Feel free to add to  -->
<!--    it as you see fit!                                      -->
<!-- This is version 2.0                                        -->
<!-- Stefan Raab, cue Systems - stefan@cuesys.com             -->
<html>
<head>
   <title>Wheel</title>
</head>
<body>
<script>var cue = false
var site = false
var objects = true
var jod = true
var bul = false
var team = false
var links = false
function update(obj)
{
  if (obj)
   {
     obj = false;
   }
   else
   {
     obj = true;
   }
        
  return obj;
}
function redraw()
{ 
  document.clear()
  draw()
  document.close()
}
function clse()
{
  top.frames[0].history.go(1 - top.frames[0].history.length)
  var place= window.top.frames[1].location
  window.top.location.href = place.href
}
function draw()
{
  document.write('<A HREF="javascript:clse()">
   <IMG SRC="http://www.cuesys.com/cueweb.gif" BORDER=0 WIDTH=195
   Height=20></A><BR>')
  document.write('<A HREF="javascript:top.frames[1].history.go([nd]1)">
   <IMG SRC="http://www.cuesys.com/icons/leftarr.gif" ALIGN="CENTER"
   BORDER="0"></A>')
  document.write('<A HREF="javascript:top.frames[1].history.go(1)">
   <IMG SRC="http://www.cuesys.com/icons/rightarr.gif"
   ALIGN="CENTER" BORDER="0"></A>')
  document.write('<HR>')
  
  document.write('<DL>')
  if (cue)
   {
     document.write('<DT><A HREF="javascript:cue=update(cue);
      redraw()">')
     document.write('<IMG SRC="http://www.cuesys.com/icons/minus.gif"
      BORDER=0 HEIGHT=10 WIDTH=20></a>')
     document.write('<A HREF="http://cuesys.com/cue" Target="document">
      CUE Systems</a>');
     document.write('<DL><DT><IMG SRC="http://www.cuesys.com/icons/
      bul.gif" HEIGHT=10 WIDTH=20>')
     document.write('<A HREF="http://cuesys.com/cue/index.html"
      TARGET="document">Mission Statement</A>')
     document.write('<DT><IMG SRC="http://www.cuesys.com/icons/bul.gif"
      HEIGHT=10 WIDTH=20>')
     document.write('<A HREF="http://cuesys.com/cue/contact.html"
      TARGET="document">Contact Information</A></DL>')
   }
   else
   {
     document.write('<DT><A HREF="javascript:cue = update(cue);
      redraw()">')
     document.write('<IMG SRC="http://www.cuesys.com/icons/plus.gif"
      HEIGHT=10 WIDTH=20 BORDER=0>')
     document.write('</a><A HREF="http://cuesys.com/cue"
      Target="document">CUE Systems</a>');
   }
   if (site)
    {
      document.write('<DT><A HREF="javascript:site=update(site);
       redraw()"><IMG SRC="http://www.cuesys.com/icons/minus.gif"
       BORDER=0 HEIGHT=10 WIDTH=20></a>')
      document.write('<A HREF="http://cuesys.com/site_info.html"
       Target="document">Site Info</a>');
      document.write('<DL> <DT><IMG SRC="http://www.cuesys.com/icons/
       bul.gif" HEIGHT=10 WIDTH=20> 
         <A HREF="http://cuesys.com/site_info.html" TARGET="document"
          >About our Site</A>')
      document.write('<DT><IMG SRC="http://www.cuesys.com/icons/bul.gif"
       HEIGHT=10 WIDTH=20>
         <A HREF="http://cuesys.com/ps.html" TARGET="document"
          >Site Statistics</A></DL>')
    }
    else
    {
      document.write('<DT> <A HREF="javascript:site = update(site);
       redraw()"><IMG SRC="http://www.cuesys.com/icons/plus.gif"
       HEIGHT=10 WIDTH=20 BORDER=0></a>
          <A HREF="http://cuesys.com/site_info.html" Target="document"
           >Site Info</a>');
    }
    if (objects)
     {
       document.write('<DT><A HREF="javascript:objects=update(objects);
        redraw()"><IMG SRC="http://www.cuesys.com/icons/minus.gif"
        BORDER=0 HEIGHT=10 WIDTH=20></a>')
       document.write('<A HREF="http://cuesys.com/objects"
        Target="document">Object Technology</a>');
       document.write('<DL> <DT> <IMG SRC="http://www.cuesys.com/icons/
        bul.gif" HEIGHT=10 WIDTH=20> <A HREF="http://cuesys.com/
        objects/od" TARGET="document">OpenDoc</A>')
       document.write('<DT><IMG SRC="http://www.cuesys.com/icons/
        bul.gif" HEIGHT=10 WIDTH=20>
          <A HREF="http://cuesys.com/objects/java" TARGET="document"
           >Java</A>')
       if (jod)
        {
          document.write('<DT><A HREF="javascript:jod=update(jod);
           redraw()"><IMG SRC="http://www.cuesys.com/icons/
           minus.gif"  BORDER=0 HEIGHT=10 WIDTH=20></a>')
          document.write('<A HREF="http://cuesys.com/objects/jod"
           Target="document">Java-OpenDoc Integration</a>');
          document.write('<DL><DT><BR CLEAR=ALL> <IMG
           SRC="http://www.cuesys.com/icons/bul.gif" HEIGHT=10
           WIDTH=20><A HREF="http://cuesys.com/lists/jod"
           TARGET="document">Discussion List (ListServ)</A>')
          document.write('<DT>  <IMG SRC="http://www.cuesys.com/icons/
           bul.gif" HEIGHT=10 WIDTH=20><A HREF="http://cuesys.com/
           links/index.html#objects" TARGET="document">Other Links<
           /A></DL>')
         }
         else
         {
           document.write('<DT>  <A HREF="javascript:jod = update(jod);
            redraw()"><IMG SRC="http://www.cuesys.com/icons/plus.gif"
            HEIGHT=10 WIDTH=20 BORDER=0></a>
            <A HREF="http://cuesys.com/objects/jod"
            Target="document">Java-OpenDoc Integration</a>');
         }
            document.write('</DL>')
      }
      else
      {
        document.write('<DT> <A HREF="javascript:objects =
         update(objects);redraw()"><IMG SRC="http://www.cuesys.com/
         icons/plus.gif" HEIGHT=10 WIDTH=20 BORDER=0></a>
         <A HREF="http://cuesys.com/objects" Target="document"
         >Object Technology</a>');
       }
  if (team)
   {
     document.write('<DT><A HREF="javascript:team=update(team);
      redraw()">')
     document.write('<IMG SRC="http://www.cuesys.com/icons/
      minus.gif" BORDER=0 HEIGHT=10 WIDTH=20></a>')
     document.write('<A HREF="http://cuesys.com/people"
      Target="document">CUE Team</a>');
     
     document.write('<DL><DT><IMG SRC="http://www.cuesys.com/icons/
      bul.gif" HEIGHT=10 WIDTH=20>')
     document.write('<A HREF="http://cuesys.com/~rich" TARGET="document"
      >Rich Kilmer</A>')
     document.write('<DT><IMG SRC="http://www.cuesys.com/icons/bul.gif"
      HEIGHT=10 WIDTH=20>')
     document.write('<A HREF="http://cuesys.com/~dave" TARGET="document"
      >Dave Craine</A>')
     document.write('<DT><IMG SRC="http://www.cuesys.com/icons/bul.gif"
      HEIGHT=10 WIDTH=20>')
     document.write('<A HREF="http://cuesys.com/~stefan"
      TARGET="document">Stefan Raab</A></DL>')
   }
   else
   {
     document.write('<DT><A HREF="javascript:team = update(team);redraw()">')
     document.write('<IMG SRC="http://www.cuesys.com/icons/plus.gif"
         HEIGHT=10 WIDTH=20 BORDER=0>')
     document.write('</a><A HREF="http://cuesys.com/people" 
         Target="document">CUE Team</a>');
   }
 if (links)
   {
     document.write('<DT><A HREF="javascript:links=update(links);
      redraw()">')
     document.write('<IMG SRC="http://www.cuesys.com/icons/minus.gif"
      BORDER=0 HEIGHT=10 WIDTH=20></a>')
     document.write('<A HREF="http://cuesys.com/people"
      Target="document">Cool Links</a>');
     
     document.write('<DL><DT><IMG SRC="http://www.cuesys.com/icons/
      bul.gif" HEIGHT=10 WIDTH=20>')
     document.write('<A HREF="http://cuesys.com/links/
      index.html#graphics" TARGET="document">Graphics</A>')
     document.write('<DT><IMG SRC="http://www.cuesys.com/icons/bul.gif"
      HEIGHT=10 WIDTH=20>')
     document.write('<A HREF="http://cuesys.com/links/index.html#mac"
      TARGET="document">Mac & Newton</A>')
     document.write('<DT><IMG SRC="http://www.cuesys.com/icons/bul.gif"
      HEIGHT=10 WIDTH=20>')
     document.write('<A HREF="http://cuesys.com/links/index.html#random"
      TARGET="document">Random</A>')
     document.write('<DT><IMG SRC="http://www.cuesys.com/icons/bul.gif"
      HEIGHT=10 WIDTH=20>')
     document.write('<A HREF="http://cuesys.com/links/index.html#unix"
      TARGET="document">UNIX etc</A></DL>')
   }
   else
   {
     document.write('<DT><A HREF="javascript:links = update(links);
      redraw()">')
     document.write('<IMG SRC="http://www.cuesys.com/icons/plus.gif"
      HEIGHT=10 WIDTH=20 BORDER=0>')
     document.write('</a><A HREF="http://cuesys.com/people"
      Target="document">Cool Links</a>');
   }
}
draw()</script>
</p>
</body>
</html>

"What you see (on our site) is a facade," Raab says. "It is a simple tool to aid navigation. But in its simplicity is its power. Like Java, JavaScript is making once-complicated programming applications easier to understand and implement in less space."

He doesn't view the current version of the script as a static product. He has more refinements and revisions planned, including making the code more modular and smaller still. And, there's always the question of what someone else is going to try with it.