Web DevCenter    
 Published on Web DevCenter (http://www.oreillynet.com/javascript/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: JavaScript: The Definitive Guide, 4th Edition

JavaScript: Windows and Frames

Related Reading

JavaScript: The Definitive Guide
By David Flanagan

by David Flanagan

This beta excerpt is Chapter 13 from JavaScript: The Definitive Guide, 4th Edition, to be released in November, 2001 by O'Reilly.

Chapter 12 described the Window object and the central role it plays in client-side JavaScript. We've seen that the Window object serves as the global object for client-side JavaScript programs, and it is also the root of the client-side object hierarchy.

Besides these special roles, the Window object is an important object in its own right. Every web browser window and every frame within every window is represented by a Window object. The Window object defines quite a few properties and methods that are important in client-side JavaScript programming. This chapter explores those properties and methods and demonstrates some important techniques for programming with windows and frames. Note that because the Window object is so central to client-side programming, this chapter is quite long. Don't feel you have to master all this material at once--you may find it easier to study this chapter in several shorter chunks!

Contents

Window Overview

Simple Dialog Boxes

The Status Line

Timeouts and Intervals

Error Handling

The Navigator Object

The Screen Object

Window Control Methods

Opening Windows
Closing Windows
Window Geometry
Keyboard Focus and Visibility
Scrolling
Window Methods Example

The Location Object

The History Object

Multiple Windows and Frames

Relationships Between Frames
Window and Frame Names
JavaScript in Interacting Windows
Example: Colored Frames



Window Overview

We begin this chapter with an overview of some of the most commonly used properties and methods of the Window object. Later sections of the chapter explain this material in more detail. As usual, the client-side reference section contains complete coverage of Window object properties and methods.

The most important properties of the Window object are the following:

closed
A boolean value that is true only if the window has been closed.
defaultStatus, status
The text that appears in the status line of the browser.
document
A reference to the Document object that represents the HTML document displayed in the window. The Document object is covered in detail in Chapter 14.
frames[]
An array of Window objects that represent the frames (if any) within the window.
history
A reference to the History object that represents the user's browsing history for the window.
location
A reference to the Location object that represents the URL of the document displayed in the window. Setting this property causes the browser to load a new document.
name
The name of the window. Can be used with the target attribute of the HTML <a> tag, for example.
opener
A reference to the Window object that opened this one, or null if this window was opened by the user.
parent
If the current window is a frame, a reference to the frame of the window that contains it.
self
A self-referential property; a reference to the current Window object. A synonym for window.
top
If the current window is a frame, a reference to the Window object of the top-level window that contains the frame. Note that top is different from parent for frames nested within other frames.
window
A self-referential property; a reference to the current Window object. A synonym for self.

The Window object also supports a number of important methods:

alert( ), confirm( ), prompt( )
Display simple dialog boxes to the user and, for confirm( ) and prompt( ), get the user's response.
close( )
Close the window.
focus( ), blur( )
Request or relinquish keyboard focus for the window. The focus( ) method also ensures that the window is visible by bringing it to the front of the stacking order.
moveBy( ), moveTo( )
Move the window.
open( )
Open a new top-level window to display a specified URL with a specified set of features.
print( )
Print the window or frame--same as if the user had selected the Print button from the window's toolbar (Netscape 4 and later and IE 5 and later only).
resizeBy( ), resizeTo( )
Resize the window.
scrollBy( ), scrollTo( )
Scroll the document displayed within the window.
setInterval( ), clearInterval( )
Schedule or cancel a function to be repeatedly invoked with a specified delay between invocations.
setTimeout( ), clearTimeout( )
Schedule or cancel a function to be invoked once after a specified number of milliseconds.

As you can see from these lists, the Window object provides quite a bit of functionality. The remainder of this chapter explores much of that functionality in more detail.

Simple Dialog Boxes

Three commonly used Window methods are alert( ), confirm( ), and prompt( ). These methods pop up simple dialog boxes. alert( ) displays a message to the user, confirm( ) asks the user to click an Ok or Cancel button to confirm or cancel an operation, and prompt( ) asks the user to enter a string. Sample dialog boxes produced by these three methods are shown in Figure 13-1.

Screen shot.
Figure 13-1. alert( ), confirm( ), and prompt( ) dialog boxes

Note that the text displayed by these dialog boxes is plain text, not HTML-formatted text. You can format these dialog boxes only with spaces, newlines, and various punctuation characters. Adjusting the layout generally requires trial and error. Bear in mind, though, that the dialog boxes look different on different platforms and in different browsers, so you can't always count on your formatting to look right on all possible browsers.

Some browsers (such as Netscape 3 and 4) display the word "JavaScript" in the titlebar or upper-left corner of all dialog boxes produced by alert( ), confirm( ), and prompt( ). Although designers find this annoying, it should be considered a feature instead of a bug: it is there to make the origin of the dialog box clear to users and to prevent you from writing Trojan-horse code that spoofs system dialog boxes and tricks users into entering their passwords or doing other things that they shouldn't do.

The confirm( ) and prompt( ) methods block--that is, those methods do not return until the user dismisses the dialog boxes they display. This means that when you pop up one of these boxes, your code stops running and the currently loading document, if any, stops loading until the user responds with the requested input. There is no alternative to blocking for these methods--their return value is the user's input, so they must wait for the user before they can return. In most browsers, the alert( ) method also blocks and waits for the user to dismiss the dialog box. In some browsers, however (notably Netscape 3 and 4 on Unix platforms), alert( ) does not block. In practice, this minor incompatibility rarely causes problems.

Example 13-1 shows some typical uses of these methods.

Example 13-1: Using the alert( ), confirm( ), and prompt( ) methods

// Here's a function that uses the alert(  ) method to tell the user
// that form submission will take some time and that the user should
// be patient. It would be suitable for use in the onsubmit event handler
// of an HTML form. 
// Note that all formatting is done with spaces, newlines, and underscores.
function warn_on_submit(  )
{
    alert("\n_________________________________________________  _\n\n" +
          "                   Your query is being submitted...\n"    +
          "_________________________________________________  _\n\n"   +
          "Please be aware that complex queries such as yours\n"     +
          "     can require a minute or more of search time.\n\n"    +
          "                         Please be patient.");
}
 
// Here is a use of the confirm(  ) method to ask if the user really
// wants to visit a web page that takes a long time to download. Note that
// the return value of the method indicates the user response. Based
// on this response, we reroute the browser to an appropriate page.
var msg = "\nYou are about to experience the most\n\n" +
          "                -=| AWESOME |=-\n\n" +
          "web page you have ever visited!!!!!!\n\n" +
          "This page takes an average of 15 minutes to\n" +
          "download over a 56K modem connection.\n\n" +
          "Are you ready for a *good* time, Dude????";
 
if (confirm(msg)) 
    location.replace("awesome_page.html");
else
    location.replace("lame_page.html");
 
// Here's some very simple code that uses the prompt(  ) method to get
// a user's name and then uses that name in dynamically generated HTML.
n = prompt("What is your name?", "");
document.write("<hr><h1>Welcome to my home page, " + n + "</h2><hr>");

The Status Line

Web browsers typically display a status line at the bottom of every window (except for those explicitly created without one), where the browser can display messages to the user. When the user moves the mouse over a hypertext link, for example, the browser usually displays the URL to which the link points. And when the user moves the mouse over a browser control button, the browser may display a simple context help message that explains the purpose of the button. You can also make use of this status line in your own programs. Its contents are controlled by two properties of the Window object: status and defaultStatus.

Although web browsers usually display the URL of a hypertext link when the user passes the mouse pointer over the link, you may have encountered some links that don't behave this way--links that display some text other than the link's URL. This effect is achieved with the status property of the Window object and the onmouseover event handler of hypertext links:

<!-- Here's how you set the status line in a 
hyperlink. -- Note that the event handler *must* return 
true for this to work. -->
Lost? Dazed and confused? Visit the
<a href="sitemap.html" onmouseover="status='Go to Site 
Map'; return true;">
  Site Map
</a>
 
<!-- You can do the same thing for client-side image maps.-->
<img src="images/imgmap1.gif" usemap="#map1">
<map name="map1">
  <area coords="0,0,50,20" href="info.html"
    onmouseover="status='Visit our Information Center';
    return true;">
  <area coords="0,20,50,40" href="order.html"
    onmouseover="status='Place an order'; return true;">
  <area coords="0,40,50,60" href="help.html"
    onmouseover="status='Get help fast!'; return true;">
</map>

The onmouseover event handler in this example must return true. This tells the browser that it should not perform its own default action for the event--that is, it should not display the URL of the link in the status line. If you forget to return true, the browser overwrites whatever message the handler displays in the status line with its own URL. Don't worry if you do not fully understand the event handler in this example. We'll explain events in Chapter 19.

When the user moves the mouse pointer over a hyperlink, the browser displays the URL for the link, then erases it when the mouse moves off the hyperlink. The same is true when you use an onmouseover event handler to set the Window status property--your custom message is displayed while the mouse is over the hyperlink and is erased when the mouse moves off the link.

The status property is intended for exactly the sort of transient message we saw in the previous example. Sometimes, though, you want to display a message that is not so transient in the status line--for example, you might display a welcome message to users visiting your web page or a simple line of help text for novice visitors. To do this, you set the defaultStatus property of the Window object; this property specifies the default text displayed in the status line. That text is temporarily replaced with URLs, context help messages, or other transient text when the mouse pointer is over hyperlinks or browser control buttons, but once the mouse moves off those areas, the default text is restored.

You might use the defaultStatus property like this to provide a friendly and helpful message to real beginners:

<script>
defaultStatus =
 "Welcome!  Click on underlined blue text to navigate.";
</script>

Timeouts and Intervals

The setTimeout( ) method of the Window object schedules a piece of JavaScript code to be run at some specified time in the future. The clearTimeout( ) method can be used to cancel the execution of that code. setTimeout( ) is commonly used to perform animations or other kinds of repetitive actions. If a function runs and then uses setTimeout( ) to schedule itself to be called again, we get a process that repeats without any user intervention. JavaScript 1.2 has added the setInterval( ) and clearInterval( ) methods, which are like setTimeout( ) and clearTimeout( ), except that they automatically reschedule the code to run repeatedly; there is no need for the code to reschedule itself.

The setTimeout( ) method is commonly used in conjunction with the status or defaultStatus properties to animate some kind of message in the status bar of the browser. In general, animations involving the status bar are gaudy, and you should shun them! There are, however, a few status-bar animation techniques that can be useful and in good taste. Example 13-2 shows such a tasteful status-bar animation. It displays the current time in the status bar and updates that time once a minute. Because the update occurs only once a minute, this animation does not produce a constant flickering distraction at the bottom of the browser window, like so many others do.

Note the use of the onload event handler of the <body> tag to perform the first call to the display_time_in_status_line( ) method. This event handler is invoked once when the HTML document is fully loaded into the browser. After this first call, the method uses setTimeout( ) to schedule itself to be called every 60 seconds so that it can update the displayed time.

Example 13-2: A digital clock in the status line

<html>
<head>
<script>
// This function displays the time in the status line
// Invoke it once to activate the clock; it will call itself from then on
function display_time_in_status_line(  )
{
 var d = new Date(  );               
   // Get the current time
 var h = d.getHours(  );             
   // Extract hours: 0 to 23
 var m = d.getMinutes(  );           
   // Extract minutes: 0 to 59
 var ampm = (h >= 12)?"PM":"AM";  
   // Is it a.m. or p.m.?
 if (h > 12) h -= 12;             
   // Convert 24-hour format to 12-hour
 if (h == 0) h = 12;                 
   // Convert 0 o'clock to midnight
 if (m < 10) m = "0" + m;         
   // Convert 0 minutes to 00 minutes, etc.
 var t = h + ':' + m + ' ' + ampm;   
   // Put it all together
 
    defaultStatus = t;                 
      // Display it in the status line
 
    // Arrange to do it all again in one minute
    setTimeout("display_time_in_status_line(  )", 60000);
      // 60000 ms is one minute
}
</script>
</head>
<!-- Don't bother starting the clock till everything 
is loaded. The status line will be busy with other 
messages during loading, anyway. -->
<body onload="display_time_in_status_line(  );">
<!-- The HTML document contents go here -->
</body>
</html>

In JavaScript 1.2, Example 13-2 could be written using setInterval( ) instead of setTimeout( ). In this case, the setTimeout( ) call would be removed from the display_time_in_status_line( ) method, and we'd remove the onload event handler. Instead, after defining display_time_in_status_line( ), our script would call setInterval( ) to schedule an invocation of the function that automatically repeats once every 60,000 milliseconds.

Error Handling

The onerror property of a Window object is special. If you assign a function to this property, the function will be invoked whenever a JavaScript error occurs in that window: the function you assign becomes an error handler for the window.

Three arguments are passed to an error handler. The first is a message describing the error that occurred. This may be something like "missing operator in expression", "self is read-only", or "myname is not defined". The second argument is a string that contains the URL of the document containing the JavaScript code that caused the error. The third argument is the line number within the document where the error occurred. An error handler can use these arguments for any purpose it desires. A typical error handler might display the error message to the user, log it somewhere, or force the error to be ignored.

In addition to those three arguments, the return value of the onerror handler is significant. Browsers typically display an error message in a dialog box or in the status line when an error occurs. If the onerror handler returns true, it tells the system that the handler has handled the error and that no further action is necessary--in other words, the system should not display its own error message. For example, if you do not want your users to be pestered by error messages, no matter how buggy the code you write is, you could use a line of code like this at the start of all your JavaScript programs:

self.onerror = function( ) { return true; }

Of course, doing this will make it very difficult for users to give you feedback when your programs fail silently without producing error messages.

We'll see a sample use of an onerror handler in Example 14-1. That example uses the onerror handler to display the error details to the user and allow the user to submit a bug report containing those details.

Note that the onerror error handler is buggy in Netscape 6. Although the function you specify is triggered when an error occurs, the three arguments that are passed are incorrect and unusable. Netscape 6 and other browsers that support JavaScript 1.5 have an alternative means of catching and handling errors, however: they can use the try/catch statement. (See Chapter 6 for details.)

The Navigator Object

The Window.navigator property refers to a Navigator object that contains information about the web browser as a whole, such as the version and a list of the data formats it can display. The Navigator object is named after Netscape Navigator, but it is also supported by Internet Explorer. IE also supports clientInformation as a vendor-neutral synonym for navigator. Unfortunately, Netscape and Mozilla do not support this property.

The Navigator object has five main properties that provide version information about the browser that is running:

appName
The simple name of the web browser.
appVersion
The version number and/or other version information for the browser. Note that this should be considered an "internal" version number, since it does not always correspond to the version number displayed to the user. For example, Netscape 6 reports a version number of 5.0, since there never was a Netscape 5 release. Also, IE Versions 4 through 6 all report a version number of 4.0, to indicate compatibility with the baseline functionality of fourth-generation browsers.
userAgent
The string that the browser sends in its USER-AGENT HTTP header. This property typically contains all the information in both appName and appVersion.
appCodeName
The code name of the browser. Netscape uses the code name "Mozilla" as the value of this property. For compatibility, IE does the same thing.
platform
The hardware platform on which the browser is running. This property was added in JavaScript 1.2.

The following lines of JavaScript code display each of these Navigator object properties in a dialog box:

var browser = "BROWSER INFORMATION:\n";
for(var propname in navigator) {
    browser += propname + ": "
    + navigator[propname] + "\n"
}
alert(browser);

Figure 13-2 shows the dialog box displayed when the code is run on IE 6.

Screen shot.
Figure 13-2. Navigator object properties

As you can see from Figure 13-2, the properties of the Navigator object have values that are sometimes more complex than we are interested in. We are often interested in only the first digit of the appVersion property, for example. When using the Navigator object to test browser information, we often use methods such as parseInt( ) and String.indexOf( ) to extract only the information we want. Example 13-3 shows some code that does this: it processes the properties of the Navigator object and stores them in an object named browser. These properties, in their processed form, are easier to use than the raw navigator properties. The general term for code like this is a "client sniffer," and you can find more complex and general-purpose sniffer code on the Internet. (See, for example, The Ultimate JavaScript Client Sniffer.) For many purposes, however, something as simple as that shown in Example 13-3 works just fine.

Example 13-3: Determining browser vendor and version

/*
 * File: browser.js
 * Include with: <script SRC="browser.js"></script>
 * 
 * A simple "sniffer" that determines browser version and vendor.
 * It creates an object named "browser" that is easier to use than
 * the "navigator" object.
 */
// Create the browser object
var browser = new Object(  );
 
// Figure out the browser's major version
browser.version = parseInt(navigator.appVersion);
 
// Now figure out if the browser is from one of the two
// major browser vendors. Start by assuming it is not.
browser.isNetscape = false;
browser.isMicrosoft = false;
if (navigator.appName.indexOf("Netscape") != -1) 
    browser.isNetscape = true;
else if (navigator.appName.indexOf("Microsoft") != -1)
    browser.isMicrosoft = true;

The Screen Object

In JavaScript 1.2, the screen property of a Window object refers to a Screen object that provides information about the size of the user's display and the number of colors available on it. The width and height properties specify the size of the display in pixels. The availWidth and availHeight properties specify the display size that is actually available; they exclude the space required by features such as the Windows taskbar. You can use these properties to help you decide what size images to include in a document, for example, or what size windows to create in a program that creates multiple browser windows.

The colorDepth property specifies the base-2 logarithm of the number of colors that can be displayed. Often, this value is the same as the number of bits per pixel used by the display. For example, an 8-bit display can display 256 colors, and if all of these colors were available for use by the browser, the screen.colorDepth property would be 8. In some circumstances, however, the browser may restrict itself to a subset of the available colors, and you might find a screen.colorDepth value that is lower than the bits-per-pixel value of the screen. If you have several versions of an image that were defined using different numbers of colors, you can test this colorDepth property to decide which version to include in a document.

Example 13-4, later in this chapter, shows how the Screen object can be used.

Window Control Methods

The Window object defines several methods that allow high-level control of the window itself. The following sections explore how these methods allow us to open and close windows, control window position and size, request and relinquish keyboard focus, and scroll the contents of a window. We conclude with an example that demonstrates several of these features.

Opening Windows

You can open a new web browser window with the open( ) method of the Window object. This method takes four optional arguments and returns a Window object that represents the newly opened window. The first argument to open( ) is the URL of the document to display in the new window. If this argument is omitted (or is null or the empty string), the window will be empty.

The second argument to open( ) is the name of the window. As we'll discuss later in the chapter, this name can be useful as the value of the target attribute of a <form> or <a> tag. If you specify the name of a window that already exists, open( ) simply returns a reference to that existing window, rather than opening a new one.

The third optional argument to open( ) is a list of features that specify the window size and GUI decorations. If you omit this argument, the new window is given a default size and has a full set of standard features: a menu bar, status line, toolbar, and so on. On the other hand, if you specify this argument, you can explicitly specify the size of the window and the set of features it includes. For example, to open a small, resizeable browser window with a status bar but no menu bar, toolbar, or location bar, you could use the following line of JavaScript:

var w = window.open("smallwin.html", "smallwin",
                    "width=400,height=350,status=yes,resizable=yes"); 

Note that when you specify this third argument, any features you do not explicitly specify are omitted. See Window.open( ) in the client-side reference section for the full set of available features and their names.

The fourth argument to open( ) is useful only when the second argument names an already existing window. This fourth argument is a boolean value that specifies whether the URL specified as the first argument should replace the current entry in the window's browsing history (true) or create a new entry in the window's browsing history (false), which is the default behavior.

The return value of the open( ) method is the Window object that represents the newly created window. You can use this Window object in your JavaScript code to refer to the new window, just as you use the implicit Window object window to refer to the window within which your code is running. But what about the reverse situation? What if JavaScript code in the new window wants to refer back to the window that opened it? In JavaScript 1.1 and later, the opener property of a window refers to the window from which it was opened. If the window was created by the user instead of by JavaScript code, the opener property is null.

An important point about the open( ) method is that it is almost always invoked as window.open( ), even though window refers to the global object and should therefore be entirely optional. window is explicitly specified because the Document object also has an open( ) method, so specifying window.open( ) helps to make it very clear what we are trying to do. This is not just a helpful habit; it is required in some circumstances, because, as we'll learn in Chapter 19, event handlers execute in the scope of the object that defines them. When the event handler of an HTML button executes, for example, the scope chain includes the Button object, the Form object that contains the button, the Document object that contains the form, and, finally, the Window object that contains the document. Thus, if such an event handler refers merely to the open( ) method, this identifier ends up being resolved in the Document object, and the event handler opens a new document rather than opening a new window!

We'll see the open( ) method in use in Example 13-4.

Closing Windows

Just as the open( ) method opens a new window, the close( ) method closes one. If we've created a Window object w, we can close it with:

w.close(  ); 

JavaScript code running within that window itself could close it with:

window.close(  ); 

Again, note the explicit use of the window identifier to disambiguate the close( ) method of the Window object from the close( ) method of the Document object.

Most browsers allow you to automatically close only those windows that your own JavaScript code has created. If you attempt to close any other window, the user is presented with a dialog box that asks him to confirm (or cancel) that request to close the window. This precaution prevents inconsiderate scripters from writing code to close a user's main browsing window.

In JavaScript 1.1 and later, a Window object continues to exist after the window it represents has been closed. You should not attempt to use any of its properties or methods, however, except to test the closed property. This property is true if the window has been closed. Remember that the user can close any window at any time, so to avoid errors, it is a good idea to check periodically that the window you are trying to use is still open. We'll see this done in Example 13-4.

Window Geometry

In JavaScript 1.2, moveTo( ) moves the upper-left corner of the window to the specified coordinates. Similarly, moveBy( ) moves the window a specified number of pixels left or right and up or down. resizeTo( ) and resizeBy( ) resize the window by an absolute or relative amount; they are also new in JavaScript 1.2. Note that in order to prevent security attacks that rely on code running in small or offscreen windows that the user does not notice, browsers may restrict your ability to move windows offscreen or to make them too small.

Keyboard Focus and Visibility

The focus( ) and blur( ) methods also provide high-level control over a window. Calling focus( ) requests that the system give keyboard focus to the window, and blur( ) relinquishes keyboard focus. In addition, the focus( ) method ensures that the window is visible by moving it to the top of the stacking order. When you use the Window.open( ) method to open a new window, the browser automatically creates that window on top. But if the second argument specifies the name of a window that already exists, the open( ) method does not automatically make that window visible. Thus, it is common practice to follow calls to open( ) with a call to focus( ).

focus( ) and blur( ) are defined in JavaScript 1.1 and later.

Scrolling

The Window object also contains methods that scroll the document within the window or frame. scrollBy( ) scrolls the document displayed in the window by a specified number of pixels left or right and up or down. scrollTo( ) scrolls the document to an absolute position. It moves the document so that the specified document coordinates are displayed in the upper-left corner of the document area within the window. These two methods are defined in JavaScript 1.2. In JavaScript 1.1, the scroll( ) method performs the same function as the JavaScript 1.2 scrollTo( ) method. scrollTo( ) is the preferred method, but the scroll( ) method remains for backward compatibility.

In JavaScript 1.2, the elements of the anchors[] array of the Document object are Anchor objects. Each Anchor object has x and y properties that specify the location of the anchor within the document. Thus, you can use these values in conjunction with the scrollTo( ) method to scroll to known locations within the document. Alternatively, in IE 4 and later and Netscape 6 and later, document elements all define a focus( ) method. Invoking this method on an element causes the document to scroll as needed to ensure that the element is visible.

Window Methods Example

Example 13-4 demonstrates the Window open( ), close( ), and moveTo( ) methods and several other window-programming techniques that we've discussed. It creates a new window and then uses setInterval( ) to repeatedly call a function that moves it around the screen. It determines the size of the screen with the Screen object and then uses this information to make the window bounce when it reaches any edge of the screen.

Example 13-4: Moving a window

<script>
// Here are the initial values for our animation
var x = 0, y = 0, w=200, h=200;  
  // Window position and size
var dx = 5, dy = 5;              
  // Window velocity
var interval = 100;              
  // Milliseconds between updates
 
// Create the window that we're going to move around
// The javascript: URL is simply a way to display a 
// short document. The final argument specifies 
// the window size
var win = window.open('javascript:"<h1>BOUNCE!</h2>"', "", 
                      "width=" + w + ",height=" + h);
 
// Set the initial position of the window
win.moveTo(x,y);
 
// Use setInterval(  ) to call the bounce(  ) method every interval 
// milliseconds. Store the return value so that we can stop the
// animation by passing it to clearInterval(  ).
var intervalID  = window.setInterval("bounce(  )", interval);
 
// This function moves the window by (dx, dy) every interval ms
// It bounces whenever the window reaches the edge of the screen
function bounce(  ) {
  // If the user closed the window, stop the animation
  if (win.closed) {
      clearInterval(intervalID);
      return;
  }
 
  // Bounce if we have reached the right or left edge
  if ((x+dx > (screen.availWidth - w)) || (x+dx < 0)) dx = -dx;
 
  // Bounce if we have reached the bottom or top edge
  if ((y+dy > (screen.availHeight - h)) || (y+dy < 0)) dy = -dy;
 
  // Update the current position of the window
  x += dx;
  y += dy;
 
  // Finally, move the window to the new position
  win.moveTo(x,y);
}
</script>
 
<!-- Clicking this button stops the animation! -->
<form>
<input type="button" value="Stop" 
       onclick="clearInterval(intervalID); win.close(  );">
</form>

The Location Object

The location property of a window is a reference to a Location object--a representation of the URL of the document currently being displayed in that window. The href property of the Location object is a string that contains the complete text of the URL. Other properties of this object, such as protocol, host, pathname, and search, specify the various individual parts of the URL.

The search property of the Location object is an interesting one. It contains any portion of a URL following (and including) a question mark. This is often some sort of query string. In general, the question-mark syntax in a URL is a technique for embedding arguments in the URL. While these arguments are usually intended for CGI scripts run on a server, there is no reason why they cannot also be used in JavaScript-enabled pages. Example 13-5 shows the definition of a general-purpose getArgs( ) function that you can use to extract arguments from the search property of a URL. It also shows how this getArgs( ) method could have been used to set initial values of the bouncing window animation parameters in Example 13-4.

Example 13-5: Extracting arguments from a URL

/*
 * This function parses comma-separated name=value 
 * argument pairs from the query string of the URL. 
 * It stores the name=value pairs in 
 * properties of an object and returns that object.
 */
function getArgs(  ) {
    var args = new Object(  );
    var query = location.search.substring(1);     
      // Get query string
    var pairs = query.split(",");
     // Break at comma
    for(var i = 0; i < pairs.length; i++) {
        var pos = pairs[i].indexOf('=');
          // Look for "name=value"
        if (pos == -1) continue;
          // If not found, skip
        var argname = pairs[i].substring(0,pos);
          // Extract the name
        var value = pairs[i].substring(pos+1);
          // Extract the value
        args[argname] = unescape(value);
         // Store as a property
       // In JavaScript 1.5, use decodeURIComponent(  ) 
       // instead of escape(  )
    }
    return args;     // Return the object
}
 
/*
 * We could have used getArgs(  ) in the previous bouncing window example
 * to parse optional animation parameters from the URL
 */
var args = getArgs(  );             // Get arguments
if (args.x) x = parseInt(args.x);   // If arguments are defined...
if (args.y) y = parseInt(args.y);   // override default values
if (args.w) w = parseInt(args.w);
if (args.h) h = parseInt(args.h);
if (args.dx) dx = parseInt(args.dx);
if (args.dy) dy = parseInt(args.dy);
if (args.interval) interval = parseInt(args.interval);

In addition to its properties, the Location object can be used as if it were itself a primitive string value. If you read the value of a Location object, you get the same string as you would if you read the href property of the object (because the Location object has a suitable toString( ) method). What is far more interesting, though, is that you can assign a new URL string to the location property of a window. Assigning a URL to the Location object this way has an important side effect: it causes the browser to load and display the contents of the URL you assign. For example, you might assign a URL to the location property like this:

// If the user is using an old browser that 
// can't display DHTML content, redirect to a page 
// that contains only static HTML
if (parseInt(navigator.appVersion) < 4)
    location = "staticpage.html";

As you can imagine, making the browser load specified web pages into windows is a very important programming technique. While you might expect there to be a method you can call to make the browser display a new web page, assigning a URL to the location property of a window is the supported technique for accomplishing this end. Example 13-6, later in this chapter, includes an example of setting the location property.

Although the Location object does not have a method that serves the same function as assigning a URL directly to the location property of a window, this object does support two methods (added in JavaScript 1.1). The reload( ) method reloads the currently displayed page from the web server. The replace( ) method loads and displays a URL that you specify. But invoking this method for a given URL is different than assigning that URL to the location property of a window. When you call replace( ), the specified URL replaces the current one in the browser's history list, rather than creating a new entry in that history list. Therefore, if you use replace( ) to overwrite one document with a new one, the Back button does not take the user back to the original document, as it does if you load the new document by assigning a URL to the location property. For web sites that use frames and display a lot of temporary pages (perhaps generated by a CGI script), using replace( ) is often useful. Since temporary pages are not stored in the history list, the Back button is more useful to the user.

Finally, don't confuse the location property of the Window object, which refers to a Location object, with the location property of the Document object, which is simply a read-only string with none of the special features of the Location object. document.location is a synonym for document.URL, which in JavaScript 1.1 is the preferred name for this property (because it avoids the potential confusion). In most cases, document.location is the same as location.href. When there is a server redirect, however, document.location contains the URL as loaded, and location.href contains the URL as originally requested.

The History Object

The history property of the Window object refers to a History object for the window. The History object was originally designed to model the browsing history of a window as an array of recently visited URLs. This turned out to be a poor design choice, however; for important security and privacy reasons, it is almost never appropriate to give a script access to the list of web sites that the user has previously visited. Thus, the array elements of the History object are never actually accessible to scripts (except when the user has granted permission to a signed script in Netscape 4 and later). The length property of the History object is accessible, but it does not provide any useful information.

Although its array elements are inaccessible, the History object supports three methods (which can be used by normal, unsigned scripts in all browser versions). The back( ) and forward( ) methods move backward or forward in a window's (or frame's) browsing history, replacing the currently displayed document with a previously viewed one. This is similar to what happens when the user clicks on the Back and Forward browser buttons. The third method, go( ), takes an integer argument and can skip forward or backward in the history list by multiple pages. Unfortunately, go( ) suffers from bugs in Netscape 2 and 3 and has incompatible behavior in Internet Explorer 3; it is best avoided prior to fourth-generation browsers.

Example 13-6 shows how you might use the back( ) and forward( ) methods of the History and Location objects to add a navigation bar to a framed web site. Figure 13-3 shows what a navigation bar looks like. Note that the example uses JavaScript with multiple frames, which is something we will discuss shortly. It also contains a simple HTML form and uses JavaScript to read and write values from the form. This behavior is covered in detail in Chapter 15.

Screen shot.
Figure 13-3. A navigation bar

Example 13-6: A navigation bar using the History and Location objects

<!--  This file implements a navigation bar, 
designed to go in a frame at the bottom of a window. Include 
it in a frameset like the following:
        <frameset rows="*,75">
        <frame src="about:blank">
        <frame src="navigation.html">
        </frameset>
-->
 
<script>
// The function is invoked by the Back button in 
// our navigation bar
function go_back(  ) 
{
    // First, clear the URL entry field in our form
    document.navbar.url.value = "";
 
    // Then use the History object of the main frame 
    // to go back
    parent.frames[0].history.back(  );
 
    // Wait a second, and then update the URL entry
    // field in the form from the location.href property of
    // the main frame. The wait seems to be necessary to allow 
    // the location.href property to get in sync.
    setTimeout("document.navbar.url.value = 
       parent.frames[0].location.href;", 1000);
}
 
// This function is invoked by the Forward button in the 
// navigation bar; it works just like the previous one
function go_forward(  )
{
    document.navbar.url.value = "";
    parent.frames[0].history.forward(  );
    setTimeout("document.navbar.url.value = 
               parent.frames[0].location.href;", 1000);
}
 
// This function is invoked by the Go button in 
// the navigation bar and also when the form is submitted 
// (when the user hits the Return key)
function go_to(  )
{
    // Just set the location property of the main frame
    // to the URL the user typed in
    parent.frames[0].location = document.navbar.url.value;
}
</script>
 
<!-- Here's the form, with event handlers that 
invoke the functions above -->
<form name="navbar" onsubmit="go_to(  ); return false;">
  <input type="button" value="Back" 
    onclick="go_back(  );">
  <input type="button" value="Forward" 
    onclick="go_forward(  );">
  URL:
  <input type="text" name="url" size="50">
  <input type="button" value="Go" onclick="go_to(  );">
</form>

Multiple Windows and Frames

Most of the client-side JavaScript examples we've seen so far have involved only a single window or frame. In the real world, JavaScript applications often involve multiple windows or frames. Recall that frames within a window are represented by Window objects; JavaScript makes little distinction between windows and frames. In the most interesting applications, there is JavaScript code that runs independently in each of several windows. The next section explains how the JavaScript code in each window can interact and cooperate with each of the other windows and with the scripts running in each of those windows.

Relationships Between Frames

We've already seen that the open( ) method of the Window object returns a new Window object representing the newly created window. We've also seen that this new window has an opener property that refers back to the original window. In this way, the two windows can refer to each other, and each can read properties and invoke methods of the other. The same thing is possible with frames. Any frame in a window can refer to any other frame through the use of the frames, parent, and top properties of the Window object.

Every window has a frames property. This property refers to an array of Window objects, each of which represents a frame contained within the window. (If a window does not have any frames, the frames[] array is empty and frames.length is zero.) Thus, a window (or frame) can refer to its first subframe as frames[0], its second subframe as frames[1], and so on. Similarly, JavaScript code running in a window can refer to the third subframe of its second frame like this:

frames[1].frames[2]

Every window also has a parent property, which refers to the Window object in which it is contained. Thus, the first frame within a window might refer to its sibling frame (the second frame within the window) like this:

parent.frames[1] 

If a window is a top-level window and not a frame, parent simply refers to the window itself:

parent == self; // For any top-level window

If a frame is contained within another frame that is contained within a top-level window, that frame can refer to the top-level window as parent.parent. The top property is a general-case shortcut, however: no matter how deeply a frame is nested, its top property refers to the top-level containing window. If a Window object represents a top-level window, top simply refers to that window itself. For frames that are direct children of a top-level window, the top property is the same as the parent property.

Frames are typically created with <frameset> and <frame> tags. In HTML 4, however, as implemented in IE 4 and later and Netscape 6 and later, the <iframe> tag can also be used to create an "inline frame" within a document. As far as JavaScript is concerned, frames created with <iframe> are the same as frames created with <frameset> and <frame>. Everything discussed here applies to both kinds of frames.

Figure 13-4 illustrates these relationships between frames and shows how code running in any one frame can refer to any other frame through the use of the frames, parent, and top properties. The figure shows a browser window that contains two frames, one on top of the other. The second frame (the larger one on the bottom) itself contains three subframes, side by side.

Screen shot.
Figure 13-4. Relationships between frames

With this understanding of the relationships between frames, you may want to revisit Example 13-6, paying particular attention this time to the way the code (which is written to run in a second frame) refers to the history and location properties of the first frame.

Window and Frame Names

The second, optional argument to the open( ) method discussed earlier is a name for the newly created window. When you create a frame with the HTML <frame> tag, you can specify a name with the name attribute. An important reason to specify names for windows and frames is that those names can be used as the value of the target attribute of the <a>, <map>, and <form> tags. This value tells the browser where you want to display the results of activating a link, clicking on an image map, or submitting a form.

For example, if you have two windows, one named table_of_contents and the other mainwin, you might have HTML like the following in the table_of_contents window:

<a href="chapter01.html" target="mainwin">
  Chapter 1, Introduction
</a> 

The browser loads the specified URL when the user clicks on this hyperlink, but instead of displaying the URL in the same window as the link, it displays it in the window named mainwin. If there is no window with the name mainwin, clicking the link creates a new window with that name and loads the specified URL into it.

The target and name attributes are part of HTML and operate without the intervention of JavaScript, but there are also JavaScript-related reasons to give names to your frames. We've seen that every Window object has a frames[] array that contains references to each of its frames. This array contains all the frames in a window (or frame), whether or not they have names. If a frame is given a name, however, a reference to that frame is also stored in a new property of the parent Window object. The name of that new property is the same as the name of the frame. Therefore, you might create a frame with HTML like this:

<frame name="table_of_contents" src="toc.html">

Now you can refer to that frame from another, sibling frame with:

parent.table_of_contents 

This makes your code easier to read and understand than using (and relying on) a hardcoded array index, as you'd have to do with an unnamed frame:

parent.frames[1]

The name property of any Window object contains the name of that window. In JavaScript 1.0, this property is read-only. In JavaScript 1.1 and later, however, you can set this property, thereby changing the name of a window or a frame. One common reason to do this is to set the name of the initial browser window. When a browser starts up, the initial window has no name, so it cannot be used with the target attribute. If you set the name property of the window, however, you can then use that name in target attributes.

JavaScript in Interacting Windows

Recall what we learned in Chapter 12: the Window object serves as the global object for client-side JavaScript code, and the window serves as the execution context for all JavaScript code it contains. This holds true for frames as well: every frame is an independent JavaScript execution context. Because every Window object is its own global object, each window defines its own namespace and its own set of global variables. When viewed from the perspective of multiple frames or windows, global variables do not seem all that global, after all!

Although each window and frame defines an independent JavaScript execution context, this does not mean that JavaScript code running in one window is isolated from code running in other windows. Code running in one frame has a different Window object at the top of its scope chain than code running in another frame. However, the code from both frames is executed by the same JavaScript interpreter, in the same JavaScript environment. As we've seen, one frame can refer to any other frame using the frames, parent, and top properties. So, although JavaScript code in different frames is executed with different scope chains, the code in one frame can still refer to and use the variables and functions defined by code in another frame.

For example, suppose code in frame A defines a variable i:

var i = 3; 

That variable is nothing more than a property of the global object--a property of the Window object. Code in frame A could refer to the variable explicitly as such a property with either of these two expressions:

window.i
self.i 

Now suppose that frame A has a sibling frame B that wants to set the value of the variable i defined by the code in frame A. If frame B just sets a variable i, it merely succeeds in creating a new property of its own Window object. So instead, it must explicitly refer to the property i in its sibling frame with code like this:

parent.frames[0].i = 4;

Recall that the function keyword that defines functions declares a variable just like the var keyword does. If JavaScript code in frame A declares a function f, that function is defined only within frame A. Code in frame A can invoke f like this:

f(  ); 

Code in frame B, however, must refer to f as a property of the Window object of frame A:

parent.frames[0].f(  ); 

If the code in frame B needs to use this function frequently, it might assign the function to a variable of frame B so that it can more conveniently refer to the function:

var f = parent.frames[0].f; 

Now code in frame B can invoke the function as f( ), just as code in frame A does.

When you share functions between frames or windows like this, it is very important to keep the rules of lexical scoping in mind. A function is executed in the scope in which it was defined, not in the scope from which it is invoked. Thus, to continue with the previous example, if the function f refers to global variables, these variables are looked up as properties of frame A, even when the function is invoked from frame B.

If you don't pay careful attention to this, you can end up with programs that behave in unexpected and confusing ways. For example, suppose you define the following function in the <head> section of a multiframe document, with the idea that it will help with debugging:

function debug(msg) {
  alert("Debugging message from frame: " + name + "\n" + msg);
} 

The JavaScript code in each of your frames can refer to this function as top.debug( ). Whenever this function is invoked, however, it looks up the variable name in the context of the top-level window in which the function is defined, rather than the context of the frame from which it is invoked. Thus, the debugging messages always carry the name of the top-level window, rather than the name of the frame that sent the message, as was intended.

Remember that constructors are also functions, so when you define a class of objects with a constructor function and an associated prototype object, that class is defined only for a single window. Recall the Complex class we defined in Chapter 8, and consider the following multiframed HTML document:

<head>
<script src="Complex.js"></script>
</head>
<frameset rows="50%,50%">
  <frame name="frame1" src="frame1.html">
  <frame name="frame2" src="frame2.html">
</frameset> 

JavaScript code in the files frame1.html and frame2.html cannot create a Complex object with an expression like this:

var c = new Complex(1,2);  // Won't work from either frame

Instead, code in these files must explicitly refer to the constructor function:

var c = new top.Complex(3,4); 

Alternatively, code in either frame can define its own variable to refer more conveniently to the constructor function:

var Complex = top.Complex;
var c = new Complex(1,2);

JavaScript: The Definitive Guide, 4th EditionJavaScript: The Definitive Guide, 4th Edition
By David Flanagan
November 2001 (est.)
0-596-00048-0, Order Number: 0480
904 pages (est.), $44.95 (est.)

Unlike user-defined constructors, predefined constructors are automatically predefined in all windows. Note, however, that each window has an independent copy of the constructor and an independent copy of the constructor's prototype object. For example, each window has its own copy of the String( ) constructor and the String.prototype object. So, if you write a new method for manipulating JavaScript strings and then make it a method of the String class by assigning it to the String.prototype object in the current window, all strings in that window can use the new method. However, the new method is not accessible to strings defined in other windows. Note that it does not matter which window holds a reference to the string; only the window in which the string was actually created matters.

Example: Colored Frames

Example 13-7, a frame set that defines a grid of nine frames, demonstrates some of the techniques we've discussed in this chapter. The <head> section of the frame set includes a <script> that defines a JavaScript function named setcolor( ). The onload event handler of the <frameset> tag invokes setcolor( ) once for each of the nine frames.

setcolor( ) is passed a Window object as its argument. It generates a random color and uses it with the Document.write( ) method to create a new document that is empty except for a background color. Finally, setcolor( ) uses the setTimeout( ) method to schedule itself to be called again in one second. This call to setTimeout( ) is the most interesting part of the example. Notice especially how it uses the parent and name properties of Window objects.

Example 13-7: A frame color animation

<head>
<title>Colored Frames</title>
<script>
function setcolor(w) {
  // Generate a random color
  var r = Math.floor((Math.random(  ) * 256)).toString(16);
  var g = Math.floor((Math.random(  ) * 256)).toString(16);
  var b = Math.floor((Math.random(  ) * 256)).toString(16);
  var colorString = "#" + r + g + b;
 
  // Set the frame background to the random color
  w.document.write("<body bgcolor='" + colorString 
      + "'></body>");
    w.document.close(  );
 
    // Schedule another call to this method in one second.
    // Since we call the setTimeout(  ) method of the frame,
    // the string will be executed in that context, so we 
    // must prefix properties of the top-level window with
    // "parent.".
    w.setTimeout('parent.setcolor(parent.' + w.name 
        + ')', 1000);
 
    // We could also have done the same thing more simply 
    // like this: setTimeout('setcolor(' + w.name + ')', 1000);
}
</script>
</head>
<frameset rows="33%,33%,34%" cols="33%,33%,34%"
  onload="for(var i = 0; i < 9; i++) setcolor(frames[i]);">
<frame name="f1" src="javascript:''">
<frame name="f2" src="javascript:''">
<frame name="f3" src="javascript:''">
<frame name="f4" src="javascript:''">
<frame name="f5" src="javascript:''">
<frame name="f6" src="javascript:''">
<frame name="f7" src="javascript:''">
<frame name="f8" src="javascript:''">
<frame name="f9" src="javascript:''">
</frameset>

View more information about JavaScript: The Definitive Guide, 4th Edition

Return to the JavaScript and CSS DevCenter.

Copyright © 2009 O'Reilly Media, Inc.