Universal Related Popup Menus / Three Related Menus- WebReference.com | WebReference

Universal Related Popup Menus / Three Related Menus- WebReference.com

Three Related Menus

Universal Related Popup Menus

A number of readers have asked for three related menus. We've extended the 2-D array technique to three dimensions to make three related menus. Try changing the first menu and see what happens. Here's a stripped down version for download.

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

Related Select Lists

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

The code is similar to the 2-D version, with the following changes.

    function O(txt,url) { itemsIndex++; a[catsIndex][itemsIndex] = new Array(); a[catsIndex][itemsIndex].text = txt; a[catsIndex][itemsIndex].value = url; subItemsIndex = 0; } function OO(txt,url) { a[catsIndex][itemsIndex][subItemsIndex] = new myOptions(txt,url); subItemsIndex++; }

In O (for second menu option) we create a new array for each element to store a possible third dimension, and add two properties as before (text, value) only we now do it within the O function (using new myOptions here doesn't work).

Function OO fills the third dimension to our 3-D array, and acts like O did in the 2-D version, saving our two variables as properties of the element. Once we have the 3-D array filled up with options, we only need to refer to the array in the right place to extract the menu options we want.

Twice Related

Relating the first to the second menu is similar to our 2-D example, except we also call relate2 to change the third menu based on the second.

    function relate(formName,elementNum,j) { // relate first to second (and third) menus // ie change first menu, changes second, then change third // if(v){ var formNum = getFormNum(formName); if (formNum>=0) { formNum++; // reference next form, assume it follows in HTML with (document.forms[formNum].elements[elementNum]) { for(i=options.length-1;i>0;i--) options[i] = null; // null out in reverse order (bug workarnd) for(i=0;i<a[j].length;i++){ options[i] = new Option(a[j][i].text,a[j][i].value); } options[0].selected = true; } // change third menu fromRelate = 1; relate2(formName,elementNum,0,1); fromRelate = 0; } } else { jmp(formName,elementNum); } }

The one change here from the original relate function is the call to relate2, which updates the third menu, based on the first and second menus. This is where it gets tricky as we have to know the current selection of both menus 1 and 2 to find the option values of menu 3 in our 3-D array.

    function relate2(formName,elementNum,j,fromRelate) { if(v){ var formNum = getFormNum(formName); if (formNum>=0) { // find first menu's selection // fromRelate means "coming from relate function?" // then increment formNum so k refers to first form, // not the nonexistent one before it (-1) if (fromRelate) formNum++; // assumes forms follow each other k = document.forms[formNum-1].elements[elementNum].selectedIndex; if(k<0)k=0; // assume that the first option is selected in the parent menu formNum++; // reference next form, assume it follows in HTML with (document.forms[formNum].elements[elementNum]) { for(i=options.length-1;i>0;i--) options[i] = null; // null out in reverse order (bug workarnd) for(i=0;i<a[k][j].length;i++){ options[i] = new Option(a[k][j][i].text,a[k][j][i].value); } options[0].selected = true; } } } else { jmp(formName,elementNum); } }

Bug Fix v 1.3 - On multiple select lists, there is no default selection, so when you choose an option from the second menu's list, you'd get an error, since there's no selection in the first menu's list. Setting the first option in the first menu's list as the default selection eliminates this bug.

    <SELECT NAME="m1" onChange="relate(this.form,0,this.selectedIndex)" SIZE=2> <OPTION VALUE="/experts/" SELECTED>Experts

Just in case the user forgets to set the first selection to SELECTED in multiple related lists, we'll check for an unselected parent menu option in the code.

    k = document.forms[formNum-1].elements[elementNum].selectedIndex; if(k<0)k=0;

relate2 is called by the second menu (and the first, through the relate function) to change the third menu, and is similar to relate, with two changes: the fromRelate boolean and our third dimension, k. When called on its own, relate2 simply changes the third menu using our 3-D array to create option values. When called from relate, we set fromRelate to true, and we increment our form number to refer to the second menu (without this k would be undefined). In either case k finds the current selection of the first menu, which we use later when referencing our 3-D array.

The first highlighted line above increments the formNum to refer to the second menu if this function is called fromRelate, as we pass in the first menu's name when we call relate from the first menu. The second highlighted line fills k with the selectedIndex of the first menu. The next line does a check on k, if undefined (-1) we assume the first option is selected (k=0). The next line increments formNum to refer to the third menu, whose options we redefine from the 3-D array. (k=first menu selection, (j=second menu selection, and i=third menu options).

Internet Explorer 4 Bug

Internet Explorer 4+ behaved strangely after returning to the form, in version 1.2 of these related menus. After you selected say Contents -> Internet -> FAQs and then hit the back button, the menus were out of synch (this also occured on the 2-D menus to a lesser extent). This we like to call the IE4 MEMORY-CACHE-STORING-ONLY-INDEX-AND-NOT-CONTENT bug.

Internet Explorer 4+ remembers the index of each SELECT menu but not the contents of each SELECT, so it gets it wrong. Explorer's memory cache stores the selected index only of the SELECT menu, and not the content at the time of selection. When you return to a page, it displays the default contents of each SELECT, grabs the stored index from cache and aligns the default contents to that index. Netscape, on the other hand, seems to remember both index and contents. Here's the fix.

Bug Fix v1.3 The way around this is to avoid the problem altogether and reset all your forms. You can either do this onload in the HEAD or at the end of your BODY. We chose the onload method:

    function resetForms() { for (i=0;i<document.forms.length;i++) { document.forms[i].reset(); } } window.onload = resetForms;

Thanks to Peter Belesis (pbel@internet.com) for pointing this out.

Internet Explorer 5 Persistent Menus

There is a workaround for the above back button problem in IE5, that will maintain persistence of the SELECT menus. Here are the steps:

  1. In the HEAD, insert this meta-tag:
    <META NAME="save" CONTENT="history">
  2. In your stylesheet, insert this one line, as is:
    <STYLE>
       	.saveHistory {behavior:url(#default#savehistory);}
    </STYLE>
  3. Give ALL your SELECTs a Unique ID and a CLASS=saveHistory
    ...
    <SELECT....ID=sel4  CLASS=saveHistory....>
    <SELECT....ID=sel5  CLASS=saveHistory....>
    ... 
  4. Make the reset function ONLY for IE4, since IE5 will now remember the SELECT state:
    window.onload = IEsetup;
    function IEsetup(){
    	if(!document.all) return;
    	IE5 = navigator.appVersion.indexOf("5.")!=-1;
    	if(!IE5) {
    		for (i=0;i<document.forms.length;i++) {
    			document.forms[i].reset();
    		}
    	}
    }

We've incorporated these improvements into this page, view source to see how it works. Thanks again to Peter Belesis for pointing this out.

Comments are welcome




Created: Mar. 9, 1997
Revised: July 22, 1999

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