What is So Dynamic About Dynamic HTML?
Building a Dynamic Thank You Page
An Introduction to Microsoft Layers
You are here: irt.org | Articles | Dynamic HTML (DHTML) | Creating a JavaScript site map [ previous next ]
Published on: Saturday 3rd January 1998 By: Martin Webb
Most Web sites use either simple hypertext links or image links to allow visitors to navigate around the various linked pages. These techniques, although tried and tested, do not necessarily give an indication of the breadth and depth of the site. Using the JavaScript described in this article, you will be able to include a visual representation of your site that can be dynamically expanded and collapsed, enabling visitors to quickly identify which parts of the site they wish to explore.
The site map could be written completely in HTML. But this would require a separate file, and therefore a separate request to the server, for each different view of the site map. This article describes how to implement a dynamic site map using images, frames and JavaScript, using just three files for Netscape Navigator, and for Microsoft Internet Explorer, a few additional, small images. The difference between treatments for the two browsers is due to Netscape Navigator's inclusion of undocumented and unsupported internal-gopher images that we can utilize.
There are nine undocumented internal-gopher images which we can use in Netscape Navigator 4. You may have actually come across them when viewing a directory listing in Netscape Navigator 4:
If you are viewing this article in another broswer, e.g. Microsoft Internet Explorer, FireFox etc then the above images will not be displayed. Therefore, I have created the following .GIF images which can be used instead:
The JavaScript detailed later in this article will use the alternate .GIF images.
To enable the site map to be dynamic and allow the contents to change without downloading any additional files from the server, we need to be able to redisplay/reload the document. Normally, this would overwrite all the contents of the document, including the JavaScript source code and, more importantly, the current values of the JavaScript variables. However, by using frames, we are able to store the JavaScript and JavaScript variables in the document that defines the frameset, i.e., the parent frame, and then only redisplay one or another of the child frames.
We'll use the following simple frameset definition contained within the sitemap.htm file:
<head> <script language="JavaScript"><!-- /* this will contain the JavaScript detailed later */ //--></script> </head> <frameset cols="50%,*"> <frame src="menu.htm" name="menu"> <frame src="dummy.htm" name="content"> </frameset>
The sitemap.htm file also contains 99% of your JavaScript code.
When using JavaScript and a frameset definition in the same document, the JavaScript is usually held between the <head>and </head> tags. Otherwise, it is possible that neither the JavaScript nor the frameset will be processed correctly. The actual JavaScript is detailed in the rest of the article.
menu.htm, loaded into the menu frame, contains the 1% of the JavaScript necessary to request and display the site map. The background color is set to white, and whatever is returned from the parent frames ShowSiteMap() function is output to the document:
<body bgcolor="#FFFFFF"> <script language="JavaScript"><!-- document.write(parent.ShowSiteMap()); //--></script> </body>
dummy.htm, loaded into the content frame, contains just the body tags to ensure that the background color is set to white:
<body bgcolor="#FFFFFF"></body>
The site map will be displayed in the menu frame. When a document is selected in the site map, it will be shown in the content frame. However, the JavaScript source code that enables the site map to be displayed will be held in the parent frame, i.e., the sitemap.htm file.
Below is the JavaScript source code that is required in the sitemap.htm file, as well as the contents of the site map file structure, i.e., the list of files and their connection with one another.
The location of the current window is manipulated to obtain the path of the currently loaded document. The baseHREF will then be used later to give the absolute path to a file/menu in the site map and when changing the text displayed in the status bar. We'll assume that the site map is located in the fictitious directory: http://xxx.yyy.zzz/abc123/
The next three JavaScript functions -- File(), Menu() and Add() -- define the objects and methods used to create a tree hierarchy of files and menus.
The following File() function is actually a constructor function for File objects, as it will be invoked later using the new operator, e.g., new File('index.html','text').
function File(name,type) { this.name = name; // the name of the file to appear in the site map this.type = type; // image/binary/movie/text/sound/telnet/unknown/index this.path = baseHREF; // initial path, built when adding nested objects }
The following Menu() function is a constructor function for Menu objects. It is similar to the File constructor, except that it has three additional properties, as well as Add, a user-defined method (e.g., Menu.Add() ) and Contents, a built-in JavaScript Object.
The use of Object() effectively creates an empty array. Unlike Array() it is fully supported in earlier releases of JavaScript.
The value of the open property is conditional on whether open parameter has a value or is null, i.e., a value has not been passed. This avoids the need to set each and every Menu object's open property; we can omit them, whereby they will default to false.
function Menu(name,ref,open) { this.name = name; // the name of the file/menu to appear in the site map this.type = 'menu'; this.path = baseHREF; // initial path, built up when adding nested objects this.ref = ref; // reference to the object being constructed if (!open) // optional value indicating if object is open or closed this.open = false; else this.open = true; this.index = 0; // initial number of items in Contents this.Add = Add; // Add is a Menu method e.g. Menu.Add() this.Contents = new Object(); // adds an empty built-in JavaScript Object }
The following Add() function is used as a method of Menu, e.g., Menu.Add(). The obj parameter is added to the Contents[] array of the referenced Menu object, and the index property of the referenced Menu is increased by one. The path property of obj is set to the path of the owning Menu object plus the name property of obj.
function Add(obj) { this.Contents[this.index++] = obj; obj.path = this.path + '/' + obj.name; }
What we have implied, but not yet stated, is that the obj parameter is actually an object that has been created somewhere else in the script. Using File(), Menu() and Add() we can create a structure that links files together in a hierarchical tree structure. For example, the following would first create a Menu object called rootMenu, and then a File object which would be added to the Contents array of the rootMenu object:
rootMenu = new Menu('home','rootMenu',true); rootMenu.Add(new File('index.html','index'));
To add another File object to the rootMenu is as simple as:
rootMenu.Add(new File('email.html','text'));
To add a Menu object to the rootMenu require two lines, as we need a variable name for the object to which we can refer later:
docMenu = new Menu('documents','docMenu'); rootMenu.Add(docMenu);
If we had done it in one line, we wouldn't be able to add File objects to the docMenu:
docMenu.Add(new File('readme.txt','text')); docMenu.Add(new File('logo.gif','image'));
So, in theory, our example object structure looks something like this:
rootMenu -+- index.html | +- email.html | +- docMenu ---+- readme.txt | +- logo.gif
The object structure shows the order in which the objects are added. Therefore, if we want all the Menu objects to appear before the File objects in a particular menu, and all the objects to appear in alphabetical order, we just need to make sure that they are added to the Contents array of the owning Menu object in the correct order:
rootMenu = new Menu('home','rootMenu',true); docMenu = new Menu('documents','docMenu'); rootMenu.Add(docMenu); docMenu.Add(new File('logo.gif','image')); docMenu.Add(new File('readme.txt','text')); rootMenu.Add(new File('email.html','text')); rootMenu.Add(new File('index.html','index'));
Now the structure will, in theory, look like this:
rootMenu -+- docMenu ---+- logo.txt | | | +- readme.gif +- email.html | +- index.html
Now we'll show how to output the site map to the menu frame using both images and text links.
The following Show() function iterates around the object stucture building up an HTML table of images and text links of the currently open menus and their contents. It holds the HTML to be displayed in the output variable which is then returned to the menu.htm document for display in the menu frame. The starting point within the object structure will normally be the rootMenu, although this does not always have to be the case. You may prefer to show only a small section of the structure, e.g., the contents of a menu hidden deep in the object structure:
function Show(obj) { if (obj.type == 'menu') var anchor = '<a href="javascript:parent.OpenMenu(parent.' + obj.ref + ')"'; else var anchor = '<a href="' + obj.path + '" target="content"'; anchor += ' onMouseover="window.status=\'' + obj.path + '\'; return true"'; anchor += ' onMouseout="window.status=\'\'; return true">'; var picture = '<img src="' + obj.type + '.gif" align="absbottom" border="0" width="20" height="21">'; var output = '<table border=0><tr>' + '<td valign=top>' + anchor + picture + '</a></td>' + '<td><font face="arial">' + anchor + obj.name + '</a>'; if (obj.open) for (var i=0; i<obj.index; i++) output += Show(obj.Contents[i]); output += '</td></tr></table>'; return output; } function ShowSiteMap() { return Show(rootMenu); } function OpenMenu(obj) { obj.open = !obj.open; window.menu.location.href = window.menu.location.href; }
The Show() function first builds up the anchor to be displayed. If the current obj objects type property is "menu," then the anchor will contain a javascript:url to the OpenMenu() function. It will be something like:
<a href="javascript:parent.OpenMenu(parent.rootMenu)" onMouseover="window.status='http://xxx.yyy.zzz/abc123/';return true" onMouseout="window.status='';return true">
You may now begin to appreciate why we need a ref property for each Menu object. We need the name of the variable holding the Menu object to be able to pass it to the OpenMenu() function. Without this we wouldn't be able to refer to a particular Menu object directly.
Note that, as both the function and variable are defined in the Menu frame's parent frame, they both require a reference to the parent frame. Otherwise, neither the function nor the variable will be found.
If the type property is not "menu," then the anchor will contain a link to the absolute URL of the file, like this:
<a href="http://xxx.yyy.zzz/abc123/index.html" target="content" onMouseover="window.status='http://xxx.yyy.zzz/abc123/index.html';return true" onMouseout="window.status='';return true">
Next, the picture to be displayed in the link is built up:
<img src="menu.gif" align="absbottom" border="0" width="20" height="21">
The beginning of the HTML table is then constructed using two columns, one containing the image link and the other containing the text link. If the obj objects open property is true, then for each entry in the Contents array, the Show() function is invoked to embed yet another table within the current table. Finally, the HTML table is closed and the contents of output are returned to the caller of the OpenMenu() function.
The OpenMenu() function, which completes the JavaScript, when invoked will toggle the open property of the current obj object, and then reload the document in the menu frame.
The ShowSiteMap() function is invoked by the menu.htm document, which in turn invokes the Show() function with the rootMenu, i.e., the initial Menu object:
What actually happens when a menu link is selected is that the Menu object is passed by the menu.htm document to the OpenMenu() function in the sitemap.htm document. The OpenMenu() function then toggles the open property of the Menu object before reloading the contents of the menu frame, which then calls the Show() function in the parent frame to rebuild the site map -- all within the blink of an eye.
You can try out a sample site map: sitemap.htm.
If you use the above three HTML files (sitemap.htm, dummy.htm and menu.htm) on your own site, and then amend the tree structure using the Menu and File functions, you will be able to create a dynamic site map that your visitors can explore without the need for further downloads from the server. This will aid your visitors to locate the area of your site in which they are most interested without having to navigate a multitude of links.
You could further enhance the site map to use your own images or create others for specific filetypes.
The content frame could have an initial document loaded, perhaps explaining how to use the site map. The frameset can be enhanced to remove the border and to make the frames nonresizable.
You could include + and - gifs to indicate how to close and open the menus; this could be combined with additional menu images that show the menu image as open or closed, or you could include an indicator against the menu that contains the document currently being viewed in the content frame, i.e., a "You are Here" type of indicator.
You don't even need to build up the path of each file as it's added to the object structure! You can pass the complete relative or absolute URL as a parameter, which can be a local or a remote file and can, if required, use protocols other than http -- ftp:, mailto: and even javascript:.
What is So Dynamic About Dynamic HTML?
Building a Dynamic Thank You Page
An Introduction to Microsoft Layers
An Introduction to Netscape Layers