Jason Smith
Technology Engineer, Microsoft Developer Network
July 8, 1996
When we started designing the Site Builder Workshop site, our goal was to create an elegant, easy-to-use Web site that provided quick access to information across the site. We decided to categorize the Workshop content by task (programming, authoring, design, site administration, and planning/production), but we also wanted users to easily navigate between these functional boundaries and find product- or technology-specific information quickly, regardless of its location on the site. Providing that kind of access to information required a solution that was simple, pervasive, and elegant.
We considered the use of drop-down list boxes containing links to sitewide information. Although this solution was easy to implement, it lacked finesse. Instead, we decided to use the pop-up menu control (available from the ActiveX™ Component Gallery on this Web site), which could be embedded in an HTML page to display a pop-up menu. To create a user interface that was familiar to most users, we incorporated the pop-up menu control in a navigation frame that appeared on all Workshop pages. To see the results of our work, click Products or Technologies in the blue frame at the top of this page.
To implement the pop-up menu control, our controls guru, Garrison McCully, used the ActiveX Control Pad to insert the proper OBJECT tag syntax for the control, and then manually entered the PARAM tags to populate the menu:
<OBJECT ID="TasksMenu" WIDTH=100 HEIGHT=51 CLASSID="CLSID:7823A620-9DD9-11CF-A662-00AA00C066D2" CODEBASE="/ie/download/activex/iemenu.ocx"> <PARAM NAME="Menuitem[0]" value="Authoring/Editing"> <PARAM NAME="Menuitem[1]" value="Design/Creative"> <PARAM NAME="Menuitem[2]" value="Programming"> <PARAM NAME="Menuitem[3]" value="Site Administration"> <PARAM NAME="Menuitem[4]" value="Planning/Production"> <PARAM NAME="Menuitem[5]" value="Demo/How-To Site"> </OBJECT>
Note: It is also possible to set the menu's contents during the OnLoad event handler for a page, but we experienced some minor problems doing so. Hence, a word of warning for Microsoft Internet Explorer 3.0 Beta 1 users: If a control has not been installed on the client machine, an attempt to initialize the properties (in this case, the menu's contents) of the control during the document's OnLoad subroutine will generate an error. Because the OnLoad script begins running immediately after the page has been downloaded, you need to place the initialization code elsewhere. Our solution was to move it within the OBJECT description.
The CODEBASE specifies the server location of the control for downloading purposes, if the control is not already installed on the user's machine.
After adding the menu, Garrison defined an action for each menu item. (Note that the ActiveX Control Pad provides an easy way to create a skeleton message handler that can be expanded manually.)
Sub TasksMenu_Click(byVal X) Select Case X Case 1 top.location="/workshop/author/default.htm" Case 2 top.location="/workshop/design/default.htm" Case 3 top.location="/workshop/prog/default.htm" Case 4 top.location="/workshop/admin/default.htm" Case 5 top.location="/workshop/prod/default.htm" Case 6 top.location="/workshop/demo/default.htm" Case Else End Select End Sub
You've probably noticed the disparities between the indexing methods in the two functions above. When we initially populated the menu, we used a zero-based array. The TasksMenu_Click handler, on the other hand, expects a one-based array. In other words, Menuitem thinks the task menu has indices from zero to five, while TasksMenu_Click needs the same data mapped to an array with indices from one to six. This seems a little quirky, but it works fine. Just remember that the methods associated with the pop-up menu control, as well as the value passed to the control's Click handler, are all one-based arrays.
When we got the menu hooked up right, we had to decide which page element would instantiate (display) the pop-up menu. (Command buttons are an obvious choice, but they are visually displeasing to everyone except programmers who wear plaid with stripes.) Our site's designer, Craig Kosak, decided to use a menu bar embedded in a persistent navigation frame, which would be available from all pages of the Workshop.
To create the impression of a menu bar, Craig used an HTML table in the navigation frame. Each table cell was populated with a graphic consisting of the menu name and a small arrow pointing down, and tagged as an anchor with an HREF pointing to the current page (to create the default "no operation" behavior if the user cancels the menu); for example:
<TABLE WIDTH="166" BORDER="0" CELLSPACING="0" CELLPADDING="0"> <TR> ...some HTML code to fill in other cells <TD ALIGN=LEFT VALIGN=TOP> <A HREF="/workshop/home-nav.htm" ID="Tech" > <IMG SRC="/workshop/images/tech-home.gif" WIDTH="104" HEIGHT="32" BORDER="0" ALT="Technologies"> </a> </TD> </TR>
The empty HREF parameter identifies the graphic as a clickable item, while the ID parameter (NAME works equally well) provides a name for resolving user events into function calls. For example, when the user clicks the graphic represented by tech-home.gif above, the browser combines the anchor ID and the event name specified to form a function name: Tech_OnClick. This message handler function then performs the necessary tasks to display the menu:
Sub Tech_OnClick TechMenu.Popup 217,35 End Sub
As with any function or subroutine, you can insert additional code into this subroutine to perform additional tasks.
To complete the implementation of the pop-up menu, we needed to define an action for each menu item. When the user selects a menu item, the control calls its Click method, passing the index of the item selected. Again, the actual function name is the concatenation of the object's ID value and the event name (that is, TechMenu_Click). In the Site Builder Workshop, the menu items are simply redirections to other Web pages:
Sub TechMenu_Click(byVal X) Select Case X Case 1 top.Location="/intdev/msconf/" Case 2 top.Location="/intdev/controls/controls-f.htm/" Case 3 top.Location="/intdev/sdk/" Case 4 top.Location="/intdev/sdk/actlogo-f.htm" Case 5 top.Location="/intdev/signcode/" Case 6 top.Location="/intdev/sdk/docs/com/comintro-f.htm" Case 7 top.Location="/intdev/security/cryptapi-f.htm" Case 8 top.Location="/intdev/prog-gen/dcom-f.htm" ...You get the idea... Case Else End Select End Sub
So far, I've explained how we implemented an ActiveX control (the pop-up menu control) on the Site Builder Workshop Web pages. Unfortunately, not everyone has a browser that supports ActiveX controls. We subsequently needed a solution that would allow us to take advantage of the ActiveX support in Internet Explorer 3.0 while still providing an acceptable user experience (that is, no errors or blank screens) on other browsers.
To solve the cross-browser issue, we decided to add a script that would (a) determine the current browser, and (b) dynamically customize the HTML page to do the right thing for that browser. For example, the menu bar is tagged as a table with graphic hotspots for Internet Explorer users, as I explained above. For Netscape® Navigator®, we decided to use the same table, but we linked each cell to an HTML page instead of the pop-up menu control. (The HTML page duplicates the list of links included in the pop-up menu.) To enable this kind of customization, we wrote a subroutine in JavaScript, which is the scripting language common to all of our target browsers:
<SCRIPT LANGUAGE="JavaScript"> <!-- var uagent=navigator.userAgent; if (uagent.indexOf("MSIE") == 25) { document.writeln ('<TABLE WIDTH="166" BORDER="0" CELLSPACING="0" CELLPADDING="0">'); document.writeln('<TR>'); document.writeln('<TD ALGIN=LEFT VALIGN=TOP>'); document.writeln('<IMG SRC="/workshop/images/microsoft-home.gif" WIDTH="104" HEIGHT="33" BORDER="0" ALT="Microsoft">'); document.writeln('</TD>'); document.writeln('<TD ALIGN=LEFT VALIGN=TOP>'); document.writeln('<IMG SRC="/workshop/images/space-blue.gif" WIDTH="102" HEIGHT="1" BORDER="0" ALT="">'); document.writeln('</TD>'); document.writeln('<TD ALIGN=LEFT VALIGN=TOP>'); document.writeln('<A HREF="/workshop/home-nav.htm" ID="Tech" > <IMG SRC="/workshop/images/tech-home.gif" WIDTH="104" HEIGHT="32" BORDER="0" ALT="Technologies"></a></TD>'); ...The rest of the menu bar is formatted just like the "Tech" menu. } else { document.writeln ('<TABLE WIDTH="166" BORDER="0" CELLSPACING="0" CELLPADDING="0">'); document.writeln('<TR>'); document.writeln('<TD ALGIN=LEFT VALIGN=TOP>'); document.writeln('<IMG SRC="/workshop/images/microsoft-home.gif" WIDTH="104" HEIGHT="33" BORDER="0" ALT="Microsoft">'); document.writeln('</TD>'); document.writeln('<TD ALIGN=LEFT VALIGN=TOP>'); document.writeln('<IMG SRC="/workshop/images/space-blue.gif" WIDTH="102" HEIGHT="1" BORDER="0" ALT="">'); document.writeln('</TD>'); <!-- Link the Technologies hotspot to the approp. menu page --> document.writeln('<TD ALIGN=LEFT VALIGN=TOP>'); document.writeln('<A HREF="tech-frames.htm" Target="text" > <IMG SRC="/workshop/images/tech-home.gif" WIDTH="104" HEIGHT="32" BORDER="0" ALT="Technologies"></a></TD>'); <!-- Link the Products hotspot to the approp. menu page --> document.writeln('<TD ALIGN=LEFT VALIGN=TOP>'); document.writeln('<A HREF="products-frames.htm" Target="text" > <IMG SRC="/workshop/images/products-home.gif" WIDTH="70" HEIGHT="32" BORDER="0" ALT="Products"></a></TD>'); <!-- Link the Search hotspot to the approp. menu page --> document.writeln('<TD ALIGN=LEFT VALIGN=TOP>'); document.writeln('<A HREF="/search/" target="text"> <IMG SRC="/workshop/images/search-home.gif" WIDTH="59" HEIGHT="32" BORDER="0" ALT="Search" ></a></TD>'); document.writeln('<TD ALIGN=LEFT VALIGN=TOP> <IMG SRC="/workshop/images/devnav-home.gif" WIDTH="109" HEIGHT="32" BORDER="0" ALT="download DevNav"></TD></TR></TABLE>'); } // --> </SCRIPT>
Whew! The really important code that checks the version of the browser is located at the beginning of this script. After that, it is important to remember that you are in a script, so everything you enter between the <SCRIPT> and </SCRIPT> tags needs to be valid syntax for that scripting language. Using straight HTML within the script above will result in an error, hence the use of the document.writeln calls. We encountered some line-length limitations in the JavaScript customization code for the menu bar. Tagging each cell in the table resulted in some fairly lengthy HTML commands, which we had to break up into several document.writeln calls.
One of our most important criteria for the Site Builder Workshop was that it degrade gracefully when viewed by browsers other than Internet Explorer version 3.0. Obviously, we wanted to take advantage of the useful features Internet Explorer offered, but we didn't want to leave everyone else out in the cold. As a result, we added the JavaScript code described above to provide customization based on the user's browser.
As I mentioned previously, the initialization of controls that have not already been downloaded can also present some problems. This is because the scripting engine does not wait for the download to finish before executing code in the OnLoad subroutine, which is the most logical place for initialization to occur. To avoid this initialization problem, you must move the troublesome code elsewhere. Our solution was to move it within the OBJECT description, which was the only place to initialize the control between the OnLoad and OnClick user events. If you decide to follow this example, remember that the first element in the PARAM array is at index zero--not at index one, as it is for other methods such as AddItem and RemoveItem.
In the original version of the pop-up menu control (available previously on the Site Builder Workshop Web site), we could not easily set the position of the pop-up window, because the optional x and y position parameters for the Popup function were relative to the screen and not to the ActiveX document or its container. By default, the pop-up menu appeared with the upper-left corner at the mouse click position and subsequently covered up part of the menu name--a user interface taboo. As you will notice on the Site Builder Workshop site, we've switched to a newer version of the control, which allows the menu to be placed relative to the application window.
The controls that we used on this site are all part of the ActiveX Component Gallery, included in the Site Builder Workshop. Be sure to visit the gallery to download the pop-up menu control, and to browse through the other ActiveX controls supplied by Microsoft and its partners.
Back to Tips on Using This Site
Back to Site Builder Workshop home page