JAVASCRIPT UTOPIA  back to
LAUNCH POP-UP WINDOWS ELEGANTLY (VERSION 1.0)  


***PLEASE NOTE***
this article is now out of date. i'm leaving it up here to show the process i went through before i decided to give up on this approach. the new approach is posted here.


If you've ever seen poor IE3 struggle to render a popup window1 you're probably thinking "launch a window elegantly?? hunh??". Okay, so maybe the title of this article should be "Launch Pop-up Windows As Elegantly As Possible".

But then you're probably now thinking that there's a sweet little 4 line script coming your way that pops up anything from anywhere, clean as a whistle. Hmm. Okay, so maybe the title of this article should be "Launch Pop-up Windows As Elegantly As Possible Considering the Bizarre Bugs and Incompatibilities Between Different Browsers and Browser Versions".

I think you get the idea...the window-launcher I'm going to describe here may look elegant to the user, but by necessity, the implementation methods ain't pretty.

Let's get started. (What? you just want the code? Skip down.)

The Desire
What do we want to do? Not much, really. We want to:
  • Launch a window with specific attributes, like height and width.
  • Call a page into that window.
  • Check if that window already exists before we launch it. If it does already exist, we want to bring the it to the front of the screen.
  • Keep our window launcher code portable.
  • Support as many browsers as possible.

The Utopian Approach

Considering that our desires are pretty straightforward, our basic approach is, likewise, pretty straightforward: We have a function that launches a window. An event (eg. a mouse click, a page loading) will ask our function to launch a new window, telling it what the window should look like and what page should be displayed in the window. But before the function launches the window, it will first check whether or not one already exists. If a window does already exist, it will bring it to the front of the screen ("focus" it); if one doesn't, it will launch a new one.

If you read code more easily than english, here's what I'm saying:

function launchwin(winurl,winname,winfeatures)
	{
	if (newwin == null || newwin.closed)
		{
			newwin = window.open(winurl,winname,winfeatures);
		}
	else
		{
		newwin.focus();
		}
	}

Now *that* would be elegant. And theoretically, the above function is all we should need to launch or focus a window successfully.... Theoretically.

Let's see what soils our code.

The Reality

There are five browser problems that prevent us from using the nice clean code above:
  1. IE3 can't assign self or window to a variable. (Want to see for yourself? Try this in IE3: testme = self; alert(testme);. You'll get this error: "Object doesn't support this property or method".) IE3 *does* know what the window object is, so for instance alert(window.document.bgcolor); will work fine, but IE3 won't let you assign the window object to a variable. And that's a real pain because when we launch our new window we need to assign it to a variable so that we can check whether or not it's already open before all subsequent launches. It's also a pain because, as part of another workaround, we'd like to manually set the opener property of our new window like this: newwin.opener = window;. But we can't in IE3.

  2. IE3 doesn't support the opener property of window because opener was introduced with JavaScript 1.12.

  3. IE3 doesn't support the focus() method of window becausefocus() was introduced with JavaScript 1.1.

  4. IE4 doesn't like the taste of if(foowin.closed) in its mouth3. If you try to check the closed property of window in IE4, you'll eventually get this error: "The RPC server is unavailable."

  5. Netscape4 doesn't execute an ONUNLOAD event when a window is closed using the close-window button: X. Every other JavaScript capable browser does, including Netscape3. How odd......no, how annoying.


Ouch. We got a lot a problems.

The Real Approach

Our Utopian approach was simple and clean, but now we have all sorts of problems to work around. We start the real approach by considering the most problematic browser: IE3. IE3 just isn't up to the task of meeting our list of desires. And the workarounds that we have to institute in order to accomodate IE3 become problematic because they cause us to run into the Netscape4 and IE4 issues listed above. The general approach, then, is to do everything we want to do without causing errors in IE3 (if I had a nickel for every time I said that...), and without using ONUNLOAD in Netscape4 or window.closed in IE4. It goes like this:

  1. An event calls the window launching function, and passes the window's url, name, features, and parentname as arguments to the function. (We pass that data as arguments so that, with our single function, we can use any event to open a unique window with its own attributes and starting page.)

  2. Once the function has started, we ask, is the browser IE3? If so, tell it there is no window open, whether or not that's actually true. This is basically how we deal with IE3. We make it make it think that there is never a previous window open so it just opens a new one every time our function is called. That works well enough--any previous window will be replaced by the new one4. But why do we lie to IE3? Read on...

  3. The first real work the function does is to check if a secondary window object already exists. We check this for two reaons: 1) If this is the first time the function is called, the new window object will evaluate to null, so we know we need to launch a window. 2) The state of the window object (whether it's null or not) is the only thing that can tell IE4 if there are any previously launched windows hanging around from old calls to our function. Remember that IE4 has a bug that prevents us from using the more appropriate window.closed property. That's a bigger drag than it might seem. Here's why: When a new window is launched, we store a reference to its object in a variable (eg. newwin = window.open(url,name,features);). That way we can refer to the new window instance whenever we want. Now, when a user or a function closes the window, you might expect that the value of the variable we use to refer to the window would return to null because the window object no longer exists. But that's not the way it works. The variable's value stays set as a reference to the window object even after the window is closed. Which is precisely why the window.closed property was introduced--it gives us a way to check on the open/closed status of any new window objects we create, and relieves us from the duty of manually setting our window variables to null when windows are closed. Handy dandy, don't you think? Yeah, it would work great if it weren't for IE4. In IE4, because of the window.closed bug, we can't determine a window's open/closed status using the window.closed property.

    Whew. Time for a new paragraph.

    So if IE4's bug stops us from using the window.closed property, how do we know when the window is closed in IE4? Well, we're forced to manually set the value of our window variable to null every time the window is closed. And how do we do that? By using the ONUNLOAD event handler of the page that's in our new window to call a function that sets the window variable to null in its parent window (the one that launched it). Like this: opener.newwin = null;. Unfortunately, this means that *every* page in our new window *must* have an ONUNLOAD handler and a short variable-setting function. While that might seem like a painful enough workaround, it gets worse. Remember that Netscape4 doesn't execute commands given in the ONUNLOAD handler if the window is closed using its close button: X. That doesn't turn out to be so bad because Netscape4 *does* let us check the window.closed property, so in Netscape4 it doesn't matter that we can't manually set the window variable to null (though we must be careful to always check window.closed for the benefit of Netscape4). Remember also that IE3 supports neither the opener nor the closed properties of window, so we have no way of telling IE3 that a secondary window has been closed. Except to lie to it, and tell it that the window is always closed.

    Now you know why we lie to IE3. And you also know how we check for the existence of previously launched windows in all other browsers, despite the bugs. This gives us a cross-browser method of determining whether or not a secondary window exists (even if it means lying to IE3). Once we can determine that, the rest of the function is simple:

  4. If we find that a previous window exists, we bring it to the front of the screen using window.focus()

  5. If we find that no window exists, we launch one. In IE3's case, we always launch one. After we have launched the window, we do a couple of things that make it more useful:
    1. If the new window has no opener property, we manually set one. That makes our script work with JavaScript 1.0 (eg. Netscape 2).
      if (newwin.opener == null)
      {
           newwin.opener = window;
      }
    2. We give the new window's parent a name so we can target links into it remotely.
      newwin.opener.name = parentname;



The Code

Despite how convoluted the Real Approach to our window launcher is compared to the Utopian Approach, the code itself is pretty minimal and self contained. To implement the window launcher you need only 4 things: 1) the launching function, 2) an event to call the launching function, 3) the variable clearing function, 4) an ONUNLOAD event to call the variable clearing function. Here's how you combine those things into a working system:

  1. Make the page for the main window.
    This is the page from which your window will be launched. It has both the standard HTML construction and the window launching function. Copy 'n' paste the following code into your text or HTML editor and save it as mypage.html (It's heavily commented for reference. You can safely delete any text that comes after the double forward-slashes--//):
    
    <HTML>
    <HEAD>
    <TITLE>Your Page Title Goes Here</TITLE>
    
    <SCRIPT LANGUAGE="JavaScript">
    <!--
    //Popup Window
    //Version 1.0
    //Last Updated: November 27, 1998
    //Code maintained at: http://www.moock.org/webdesign/javascript/
    //Copy permission granted for non-commercial uses. Written by Colin Moock.
    
    var newwin;
    
    
    function launchwin(winurl,winname,winfeatures,parentname)
    {
    //This launches a new window if one isn't already launched. If one is launched, it focuses it.
    //It works in Nav3+ and MSIE4+, and does not cause errors in IE3. This function requires that the following
    //code be called by the ONUNLOAD handler in all pages that reside in the new window:
    //	if(!document.images){opener.newwin = null}
    
    // Use this when not specifying params from the event handler:
    // var parentname = "mainwindow";
    // var winurl = "content.html"
    // var winname = "newwindow"
    // var winfeatures = "height=150,"
    //			+ "width=300,"
    //			+ "channelmode=0,"
    //			+ "dependent=1,"
    //			+ "directories=0,"
    //			+ "fullscreen=0,"
    //			+ "location=0,"
    //			+ "menubar=0,"	
    //			+ "resizable=0,"
    //			+ "scrollbars=0,"
    //			+ "status=0,"
    //			+ "toolbar=0,"
    //			+ "screenX=0,"
    //			+ "screeny=0,"
    //			+ "left=0,"
    //			+ "top=0";
    			//Only use the following attributes with a signed script:
    			//-------------------------------
    			//			+ "hotkeys=1, "
    			//			+ "alwaysLowered=0, "
    			//			+ "alwaysRaised=0, "
    			//			+ "titlebar=0, "
    			//			+ "z-lock=0, "
    			//-------------------------------
    
    
    
    
    if (!document.images) 	//Quick sniff for IE3. If the browser is IE3, don't check to see if the
    						//window is already open. Just pretend it's always closed.
    	{
    		newwin = null;
    	}
    
    
    	if (newwin == null || newwin.closed)
    		//-check for the existence of the window.
    		//-we check in two ways: 1) is the newwin object null? It should be if we've closed the window
    		// because we set newwin to null in a function called by the onunload handler.
    		// And 2) is the newwin.closed property true? We check this second state because Netscape 4.5
    		// doesn't call the onunload handler when you use the exit button on the window itself. So we
    		// check the closed property for the benefit of Nav 4.5. Why not just check that? Because IE4
    		// chokes on it if you do.  It's probably a good idea to check both anyway because "closed"
    		// wouldn't be set before the first time the user launches the window. IE4 doesn't choke if we
    		// check the status of newwin first.
    						
    		{
    			//we've determined that no window is open, so launch one now
    	
    			newwin = window.open(winurl,winname,winfeatures);
    
    			if (newwin.opener == null) // set the opener property manually for Nav 2.0.
    			{
    				newwin.opener = window;
    			}
    			newwin.opener.name = parentname; // give our parent window a name so we can target it from newwin
    
    		}
    
    	else
    
    
    		{
    			//if a window already exists, bring it to the front using windowname.focus()
    			//(for Nav3+ and IE4+). IE3 will never get here because we force it to think the
    			//window is always closed. IE3 always launches a new window when launchwin() is called.
    			//Interestingly, IE3 will focus the newwin the first time we try to recreate it, but not
    			//any subsequent times.
    
    				alert("This link is already open in another window. Click OK to continue.");
    				newwin.focus(); 
    
    		}
    
    }
    //-->
    </SCRIPT>
    
    </HEAD>
    
    <BODY>
    </BODY>
    </HTML>
    
    
    
  2. Add the event handler(s) that will call the window launcher.
    Any event can call the window launching function. The most common ones you'll use are probably links, ONLOAD, and form buttons. When you call the window launching function, you have to pass the URL of the page you're loading (winurl), the name of the window you're creating (winname), the features of the window you're creating (winfeatures), and the name you're giving the window that launches the new window (parentname). For a list of possible window features, refer to the comments in the code above, or visit WebReference's Feature List. Here's some sample code you can use for your window launching events:

    <A HREF="javascript:launchwin('yourpage.html' , 'newwindow' , 'height=150,width=300' , 'mainwindow')">Your Link Goes Here</A>
    Try it out

    <BODY ONLOAD="launchwin('yourpage.html' , 'newwindow' , 'height=150,width=300' , 'mainwindow')">

    <FORM><INPUT TYPE="BUTTON" VALUE="Launch Window" ONCLICK="launchwin('yourpage.html' , 'newwindow' , 'height=150,width=300' , 'mainwindow')"></FORM>
  3. Make the page that goes in the new window.
    All the pages in your new window must have an ONUNLOAD handler that calls a quick function to set the parent's child-window variable to null. Use the following code as the basis of all pages in your new window (you'll probably want to delete the comments):
    
    <HTML>
    <HEAD>
    <TITLE>Your Page Title Goes Here</TITLE>
    <SCRIPT LANGUAGE="JavaScript">
    <!--
    
    function clearvar()
    {
    
    
    	if(document.images) //quickie IE3 sniffer...
    			//IE3 doesn't know what "self.opener" is,
    			//and gives errors when you try to assign "self" to a variable to simulate
    			//the opener property (as in "newwin.opener = self"). So we simply don't set the
    			//window status to closed in IE3 when this window is closed. We'll force the status to be 
    			//closed every time we launch the window from the parent's script.
    
    		{
    			//In Nav3 and IE4+, we clear the existence of newwin when newwin is closed. We do this
    			//manually because the newwin object will continue to exist even after the physical window
    			//is gone. In theory we should check the .closed property of newwin to determine
    			//whether or not it's still open (eg. "if(newwin.closed)"), but IE4 returns a strange error
    			//when that property is referenced: "The RPC server is unavailable.". So, for IE4's benefit,
    			//we clear the newwin variable completely when the window is closed by the user.
    			//One problem with setting newwin to null manually is that Nav4 doesn't call onUnload when the
    			//user uses the window's close button (X) to close the window. So to accomodate Nav4 *and* IE4,
    			//we actually end up having to check both the existence of newwin and the status of newwin.closed.
    
    			//A general warning against using the onUnload handler on pre MacOS 8 Macs: If you use that
    			//handler in conjunction with closing the browser window in Netscape 3.x, you are likely to
    			//crash or kill the browser, and possibly even the Finder. For details of the bug, see:
    			//http://www.strangemedia.com/dmt/onUnload/
    
    			opener.newwin = null;
    		}	
    }
    
    //-->
    </SCRIPT>
    
    </HEAD>
    
    
    <BODY onUnload="clearvar()">
    </BODY>
    </HTML>
    
    
That's it. Happy Launching!

Known Issues

  1. Resizing the parent browser window in Netscape3 causes window focusing behaviour to stop functioning. This occurs because Netscape3 refreshes the page on resize, meaning our window variable is lost. This could be fixed by setting a cookie that maintains the open/closed state of the child window.

Please report bugs/issues to colin_moock@iceinc.com.


Footnotes

  1. When IE3 opens a new window via the window.open method, it first draws a new window that matches the same size as the opener window, then reconfigures the window to match the features given in the window.open call. The result is that IE3 looks like it's opening a first window, then closing it, then opening a second window, complete with a fireworks display of screen flickering.   ^^

  2. Danny Goodman's Navigator 4 Object Roadmap from his JavaScript Bible, Second Edition Update incorrectly states that opener is supported by MSIE 3 without the JScript.dll Version 2 upgrade. O'Reilly's JavaScript: The Definitive Guide, Third Edition, however, correctly reports opener as having been introduced with JavaScript 1.1 (p. 734).   ^^

  3. I have not found a book yet that mentions this reproducible bug. If you've got any insight on causes or workarounds for it, please mail me.   ^^

  4. Strangely, the first time IE3 tries to re-open a window that already exists, it actually focuses the previously opened window. Subsequent times, however, it leaves the new window behind the main browser window.    ^^


Revision History

  1. November 27, 1998: Posted.
  2. November 28, 1998: Minor edits to article copy.
  3. March 25, 1999: Added missing bracket in HREF cut'n'paste handler example.