spacer

Webref WebRef   Sitemap · Experts · Tools · Services · Newsletters · About i.com

home / web / dev / menus / intro4

Two, Three or More (!) Related Menus

Senior Systems Engineer – Disk-Based Backup/Replication (PA)
Next Step Systems
US-PA-Philadelphia

Justtechjobs.com Post A Job | Post A Resume
Developer News
News Flash: Adobe Has iPhone Workaround
Adobe's Flash 10.1 Goes Mobile (Minus iPhone)
A Salute to Visionary CEOs


Universal Related Popup Menus 2.01

It seems our readers are never satisfied. A few readers have asked for four- and even five-level menus, to help navigate huge Web sites. Some have also asked about having multiple related menus in one page. Ask and ye shall receive. Rather than extend our nonrecursive technique for our relate function (relate calls relate2...) let's explore a more elegant solution to both requests.

Recursive Relate for Unlimited Levels

The problem with our current relate function is that it's not recursive, so each time we add another level we need another relate function. This technique is inefficient, and cries out for recursiveness. By making relate recursive (where it calls itself if there are more levels to relate), we can add as many levels as we want. A recursive relate, however, requires a different array structure (nested) to work. Fortunately, switching to a nested array also helps solve our second request, multiple menus per page.

Passing Arrays for Multiple Menus

Our previous 2- and 3-level technique used a hard coded array, named "a" to define our menus on the fly. This limited us to one related menu per page. Some users asked for the ability to include two or more different related menus per page. By passing our menu array as a parameter, we can make the relate function even more generic, to handle different menu arrays. Passing arrays as parameters has an added benefit:

  1. External Arrays - they can be defined externally and centralized in one location. For example, larger sites could have a site-wide pop-up menu nav bar that could reference one external file, making maintenance and updates easy.

Note: we found a bug in Netscape 3 with external .js files, which are rendered as text. The correct TYPE attribute for the SCRIPT tag is TYPE="application/x-javascript", but IE chokes on this, so most folks use TYPE="text/javascript". Netscape 3 displays external .js files as text, unless you configure your server explicitly to serve .js files as "application/x-javascript" content (as we have). Another solution is to SSI the .js file into your page.

So with these two key changes in mind, let's get to the code. But first, a little demonstration.

Pick an Internet.com Channel:
-> Pick a Web Site:

WebReference.com Nav Bar Example

Choose a subject:
-> Choose a topic:
-> Choose a subtopic:
-> Choose a sub-subtopic:

The first two-level related menu shows an abbreviated version of internet.com, using our new technique. Notice the first menu has a "leaf" (an option with no submenu) that is also live (in this example, Internet Marketing).. The second four-level menu expands our previous three-level WebReference nav bar. Both use the same relate function, but use two different arrays read in from an external file. (the fourth menu is a mockup for illustration, all links point to 3D)

The Code

First I'd like to extend special thanks to Michaël Guitton for his tireless help with refining this code. He took our existing 3-level code and added external arrays, plus a recursive relate. We've been refining the code ever since. Isn't open source great?

First let's look at what's changed in the relate function call, that we use within your SELECT FORM elements. Instead of referencing a hard-coded two- or three-dimensional array (i.e., a[i][j][k]...) we reference the appropriate level of our nested array "m," which represents the entire menu tree (less the first menu, which is hard coded into the HTML). The reference to m is now passed into relate, at the level matching the select menu's level thus:

Much cleaner. The first two parameters are the same (form, elt), the third has changed from a reference to this.selected index to the menu array "m" and the depth of this select menu (1st, 2nd, 3rd in a series). Relate finds a reference to the menu array (m) at the proper depth (1) and calls update (new function, encapsulates the select menu Option updates) to redefine the submenus. To use the relate function on a second level menu you just change the depth to 2. This is a much simplified version of our original rewrite that looked like this (5-level menu):

Yikes! In our next version we simplified this call to look like this:

This encapsulates most of the referencing complexity, but this.form is redundant here, and ref is unnecessary. The current version (2.01) is simplified even more, and the relate function call looks like this:

We've eliminated the ref function here. So to use relate you just have to change the menu array name, and the depth of the menu, the code does the rest. This newest version relegates the referencing complexity to the relate function, and makes for easier select menu setup.

The Relate Function

The relate function itself has changed in our newest version. Relate updates the current menu's submenus based on the selection of this.form. It first finds the level in the menu array where the current pop-up menu is stored, then passes that reference to the menu array to update(), which updates (redefines) the submenus. Since we aren't testing for new Option constructor support (if (v)) each time we dig thru the menu tree this makes for cleaner and faster code.

Let's step through the relate code. get(form) works as before, looping through the document.forms property until it returns the current form's index.

s(elected)index Function

The s(elected)index function returns the selected index of any given form. Specifically it finds the selected index of the forms elt (0) in the neighborhood (offset) of the current form (num = get(this.form)).

Here's how you use sindex.

returns the selected option index for the current form
returns the selected option index for the next form
returns the selected option index for the previous form

Array Referencing

The key to relate is finding the right level in the menu array tree ("a" above) that corresponds to the current pop-up menu's level. The while loop in relate, specifically the code in green, moves towards the leaves of the menu tree until we reach the level where the current pop-up menu data is stored. Here's how it works:

Update Submenus

Once we've a found a reference within the menu array to the right level we're home free. We pass the form's number and the menu array (referenced correctly) to update, which updates the submenus recursively.

Update works similarly to the way relate did before, nulling out the next menu's array and redefining it. Only now, we pass in the nested array (m) referenced at the right place, and recursively redefine subsequent menus.

Once we run out of child menus we return out of relate. If in the course of traversing our menu tree we run into a leaf (a tree with unequal branches, m.length != 0) we bypass the return and act like a live popup. Note that childless option records shouldn't be first in menu records. Pretty nifty, huh?

Now, on to our new external arrays. The new code allows you to define multiple arrays for different related menus in one page. The structure of each array has changed from the old code to the new. Instead of a two- or three-dimensional array (a[i][j][k]...) which must be hard coded into different relate functions we create a nested array, like a C hacker does, thus:

This example shows a simple two-level related menu tree, which is an abbreviated version of internet.com's sites. We define a nested array, i, with the first level array empty, representing the first menu (internet.com channels), in the second level arrays representing each channel. The O method assigns the option's text and value to the current array's element. It also adds another nested dimension for any submenus.

So our menu tree array "m" (passed in as "i" above) is in fact an array of hierarchical menus. relate() maps the child menus to the subsequent form's select elts.

That's it! This technique works for any level related menu, and has been tested up to five levels. To extend this technique up to four levels for example, you would do the following.

First menu:

Second menu:

etc. and define the nested menus like this: (4-D menus here)

What about Frames?

Some of you have asked about using the URPM in a FRAMESET. Not a problem. Frame-enabling these popups just take a couple minor changes related to the jmp() function. We create an 'urpm' object, which points to the current window by default (self). Then in the jmp() function we change 'location = ...' to 'urpm.location = ...' and voila! All you need to do then is to set 'urpm' to the relevant frame using the parent property. All the "4d" code has been changed to work with frames, and you can also see a demonstration (zip file).

Here are the changes we made:

To see how we did it in this page just view source. A stripped down version of this page with only the two related menus is available here. As always it's open source, so if you think of any improvements let us know.


Comments are welcome

internet.commediabistro.comJusttechjobs.comGraphics.com

Search:

WebMediaBrands Corporate Info

Legal Notices, Licensing, Reprints, Permissions, Privacy Policy.
Advertise | Newsletters | Shopping | E-mail Offers | Freelance Jobs

webref The latest from WebReference.com Browse >
Building a Banking Application Home Page with OOP · Mixing Scripting Languages · Review: phpFox, a Social Networking CMS with all the Bells and Whistles
Sitemap · Experts · Tools · Services · Email a Colleague · Contact FREE Newsletters 
 The latest from internet.com
Enterprise 2.0: Social Networking in the Cloud · BroadSoft Marketplace Hastens Pace of Telephony Innovation · Review: HTC Hero for Sprint

Created: Sept. 28, 1999
Revised: Nov. 4, 1999

URL: http://webreference.com/dev/menus/intro4.html