SiteExperts.com Logo Home | Community | Developer's Paradise | Jobs
User Groups | Site Tools | Site Information | Search

Inside Technique : Popup Menus in IE 5.5 : Implementing a Popup Menu

Armed with this knowledge, we set out to create a simple menu-system that would work anywhere including in a small frame. While we were eventually sucessful this turned out to be much more challenging than we expected. Our hope was to simply set the innerHTML to the menu text, display it, and let the user select the link having the new page display. This was unfortunately much simpler said than done. So as we continue, keep in mind how cool and exciting the end-result is even though we had to go through a few hoops to get there.

Based on our simple assumptions and from reading the documentation, we hoped to define the menu as an invisible HTML fragment on the page and then copy this fragment to the popup window to be displayed. Below is our original failed attempt to accomplish this:

<STYLE>
  .menu {display: none}
</STYLE>
<A HREF="#" ONCLICK="showMenu(this,submenu)">Go</A>

<!-- These define the menus. 
        They are invisible in the current page -->
<TABLE CLASS=menu ID=submenu>
  <TR><TD NOWRAP>
    <A HREF="/home.asp" TARGET="content">Home</A>
  </TD></TR>
  <TR><TD NOWRAP>
    <A HREF="/jobs/home.asp" TARGET="content">Jobs</A>
  </TD></TR>
  <TR><TD NOWRAP>...</TD></TR>
</TABLE>


<SCRIPT LANGUAGE="JScript">
function showMenu(e,menu) {
	var oPopup = window.createPopup();
	var oPopupBody = oPopup.document.body;
	oPopupBody.style.border = "1px black solid"
	oPopupBody.innerHTML = menu.outerHTML
	// null not supported - see below
	oPopup.show(0, e.offsetHeight, null, null, e)
}
</SCRIPT>

Unfortunately, this did not work. The first problem was with the required width and height arguments of the show() method. One of the characteristics of HTML is that you usually do not need to know the size of your content - with HTML the content just flows. In our menu system, we did not know the height nor the width of our contents. We wanted to let the content define the flow hence our illegal use of null in the show() method. At a minimum, I would have hoped the height attribute was optional (width is questionable as it defines when the content should wrap). Our work-around is to render the popup menu offscreen so we can retrieve the content's actual width and height. To make sure the popup menu stays invisible we position it way offscreen in the negative coordinate space. We need to position the element offscreen rather than just hide the element because hidden elements have no size. We changed the style definition and our use of the show() method.

<STYLE>
  .menu {position: absolute; top: -1000; left: -1000}
</STYLE>
<A HREF="#" ONCLICK="showMenu(this,sub)">Go</A>

<!-- These define the menus. They are invisible in the frameset -->
<TABLE CLASS=menu ID=sub>
<TR><TD NOWRAP><A HREF="/home.asp" TARGET="content">Home</A></TD></TR>
<TR><TD NOWRAP><A HREF="/jobs/home.asp" TARGET="content">Jobs</TD></TR>
<TR><TD NOWRAP>...</TD></TR>
</TABLE>


<SCRIPT LANGUAGE="JScript">
function showMenu(e,menu) {
	var oPopup = window.createPopup();
	var oPopupBody = oPopup.document.body;
	oPopupBody.style.border = "1px black solid"
	oPopupBody.innerHTML = menu.outerHTML
	oPopup.show(0, e.offsetHeight, menu.offsetWidth, menu.offsetHeight, e)
}
</SCRIPT>

With this fix we should have been all set. However, there apparently is a problem where we could not get links to work. The href attribute has no effect. We were determined not to let this stop us so we decided to try and script around this issue. First we tried a very simple solution which involved adding inline onclick handlers to each link

<A HREF="/home.asp" 
   ONCLICK="location=this.href; return false" 
   TARGET="content">Home
</A>

Unfortunately this did not work. The location object cannot be manipulated from the popup nor could we find an easy way to reference the source window. This did not seem like a big deal as we also know to use function pointers to assign event handlers. We decided to assign the onclick handler of the popup's body element to a function in the originating document. With this we can take advantage of event bubbling to detect clicks on any link but process the click in the context of the originating window. This becomes clearer when you see the script.

Before showing the popup window, we assign a function reference to the popup's body element in the showMenu() function.

  oPopupBody.onclick = doClick

The doClick function is defined in the same script block as the showMenu() function. This function is fairly simple. It determines if the user clicked on an A element in the popup and then assigns the HREF property of the link to the location of the current window:

function doClick() {
  // Get the event object of the popup window
  var ev = this.document.parentWindow.event 

  // If a link, set the location of the current window
  // (not the popup)
  if (ev.srcElement.tagName=="A")
	self.location = ev.srcElement.href 
}

Since this function exists in the document that opens the popup, the self reference refers to the browser window, not the popup. Since we are now setting the real location of the window and not manipulating the popup window, this avoids the issue of setting the location within the context of the popup.

Finally, getting back to our original goal, below is the complete script for doing the popup window:

<A HREF="#" ONCLICK="showMenu(this,submenu)">Main Menu</A>

<TABLE CLASS=menu ID=submenu>
  <TR><TD NOWRAP>
    <A HREF="/home.asp" TARGET="content">Home</A>
  </TD></TR>
  <TR><TD NOWRAP>
    <A HREF="/jobs/home.asp" TARGET="content">Jobs</A>
  </TD></TR>
  <TR><TD NOWRAP>...</TD></TR>
</TABLE>

<SCRIPT>
var oPopup = window.createPopup();

function getA(el) {
	while (el!=null) {
		if (el.tagName=="A") return el
		el = el.parentElement
	}
	return null
}

function doClick() {
  // Get the event object of the popup window
  var ev = this.document.parentWindow.event 

  // If a link, set the location of the current window
  // We use a smart test that looks for a link anywhere
  // above the clicked item. This allows additional 
  // formatting tags within the link. (eg., B, I, etc.)
  var el = getA(ev.srcElement)
  if (el)
	self.location = el.href 
}
function showMenu(e,menu) {
  var oPopupBody = oPopup.document.body;
  oPopupBody.style.border = "1px black solid"
  oPopupBody.innerHTML = menu.outerHTML
  oPopupBody.onclick = doClick
  oPopup.show(0, e.offsetHeight, menu.offsetWidth, menu.offsetHeight,e)
}
</SCRIPT>

We have finally achieved our original goal - to create a popup menu that can extend beyond the boundaries of the document window. If you are running Internet Explorer 5.5, go to the top of this page and click on "Main Menu" in the upper-left corner. This displays our popup menu. If you make sure your browser window is fairly small you will see the menu extend beyond the window's boundaries.

Discuss and Rate this Article

Page 1:Popup Menus in IE 5.5
Page 2:Implementing a Popup Menu