|
|
|
|
JavaScript: The Definitive Guide, 3rd EditionBy David Flanagan3rd Edition June 1998 1-56592-392-8, Order Number: 3928 800 pages, $39.95 |
Sample Chapter 13:
Windows and Frames
Contents:
Window Overview
Simple Dialogs
The Status Line
Timeouts and Intervals
The Navigator Object
The Screen Object
Window Control Methods
The Location Object
The History Object
Multiple Windows and FramesChapter 12, JavaScript in Web Browsers, 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 as illustrated in Figure 12.1, 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!
13.1 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 reference section contains complete coverage of Window object properties and methods.
The most important properties of the Window object are:
closedA boolean value that is
trueonly if the window has been closed.defaultStatus,statusThese properties specify the text that appears in the status line of the browser.
documentA reference to the Document object that represents the HTML document displayed in the window. The Document object is covered in detail in Chapter 14, The Document Object Model.
frames[]An array of Window objects that represent the frames (if any) within the window.
historyA reference to the History object that represents the user's browsing history for the window.
innerHeight,innerWidth,outerHeight,outerWidthThe inner and outer dimensions of the window; not available in Internet Explorer 4.
locationA 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.
locationbar,menubar,personalbar,scrollbars,statusbar,toolbarReferences to Bar objects that specify the visibility of the various parts of the Navigator window; not available in IE 4.
nameThe name of the window. Can be used with the
TARGETattribute of the HTML<A>tag, for example.openerA reference to the Window object that opened this one, or
nullif this window was opened by the user.pageXOffset,pageYOffsetThe amounts that the document has been scrolled to the right and down within the window; not available in IE 4.
parentIf the current window is a frame, a reference to the frame of the window that contains it.
selfA self-referential property; a reference to the current Window object. A synonym for
window.topIf the current window is a frame, a reference to the Window object of the top-level window that contains the frame. Note that
topis different fromparentfor frames nested within other frames.windowA 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()andprompt(), get the user's response.close()Close the window.
find(),home(),print(),stop()Duplicate the functionality of buttons in the Navigator button bar; these methods not available in IE 4.
focus(),blur()Request or relinquish keyboard focus for the window; these methods not available in IE 3.
moveBy(),moveTo()Move the window.
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.
13.2 Simple Dialogs
Three commonly used Window methods are
alert(),confirm(), andprompt(). 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. Andprompt()asks the user to enter a string. Sample dialogs produced by these three methods are shown in Figure 13.1.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. The only formatting you can do is with spaces, newlines, and various punctuation characters. Adjusting the layout generally requires trial and error. Bear in mind, though, that the dialogs 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.
Note that the word "JavaScript" appears in the titlebar or upper left corner of all dialog boxes produced by
alert(),confirm(), andprompt(). Although it is annoying, there is no way to get rid of it; it is there to prevent you from writing Trojan horse code that spoofs system dialogs and tricks users into entering their passwords or doing other things that they shouldn't do. (Note that Internet Explorer includes the words "Internet Explorer" in the titlebar of its dialogs, which is not as strong a measure to prevent Trojan horses.)The
confirm()andprompt()methods block - that is, those methods do not return until the user dismisses the dialogs they display. This means that when you pop one up, 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. Thealert()method is also modal, except in Navigator on Unix platforms. On these platforms, JavaScript displays the dialog and then moves on to the next statement. In practice, this minor incompatibility does not cause many 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 28.8K 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 + "</h1><hr>");13.3 The Status Line
There is a status line at the bottom of every browser window (except for those we explicitly create without one). This is a location where the browser can display messages to the user. When the user moves the mouse over a hypertext link, for example, the browser displays the URL that the link points to. And when the user moves the mouse over a browser control button, the browser displays 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:
statusanddefaultStatus.I've just said that browsers display the URL of a hypertext link when the user passes the mouse pointer over the link. This is generally the case, but in your excursions through the web, you may have found some links that don't behave this way - links that display some text other than the link's URL. This is done with the
statusproperty of the Window object and theonMouseOverevent 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
onMouseOverevent handler in the previous example must returntrue. 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 returntrue, 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 15, Events and Event Handling.When the user moves the mouse pointer over a hyperlink, the browser displays the URL for the link and then erases it when the mouse moves off the hyperlink. The same is true when you use an
onMouseOverevent handler to set the Windowstatusproperty - your custom message is displayed while the mouse is over the hyperlink, and then it is erased when the mouse moves off the link. Or that is the way it is supposed to work, anyway. In the Windows version of Navigator 3 (but not the Mac or X11 versions), the status line is not automatically cleared when you set thestatusproperty from anonMouseOverevent handler. To force it to be erased, you can use theonMouseOutevent handler, like this:<A HREF="sitemap.html" onMouseOver="status='Go to Site Map'; return true;" onMouseOut="status='';"> Site Map </A>The
statusproperty is intended for exactly the sort of transient message we saw above. 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 thedefaultStatusproperty of the Window - 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
defaultStatusproperty like this to provide a friendly and helpful message to real beginners:<SCRIPT> defaultStatus = "Welcome! Click on underlined blue text to navigate."; </SCRIPT>If your web page contains an HTML form, you might change thedefaultStatusproperty as the user enters data in the form in order to display step-by-step instructions for completing it.13.4 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. TheclearTimeout()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 usessetTimeout()to schedule itself to be called again, we get a process that repeats without any user intervention. JavaScript 1.2 has added thesetInterval()andclearInterval()methods, which are likesetTimeout()andclearTimeout(), 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 thestatusordefaultStatusproperties 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 a few status bar animation techniques, however, which 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 only occurs 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
onLoadevent handler to perform the first call to thedisplay_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 usessetTimeout()to schedule itself to be called every 60 seconds so that it can update the displayed time. TheonLoadevent handler is specified here as an attribute of the<BODY>tag.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 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 am or pm? 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 1 minute. setTimeout("display_time_in_status_line()", 60000); // 60000 ms is 1 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 ofsetTimeout(). In this case, thesetTimeout()call would be removed from thedisplay_time_in_status_line()method, and theonLoadevent handler would be changed to callsetInterval()to schedule an invocation of that method that automatically repeats once every 60,000 milliseconds.13.5 The Navigator Object
The
Window.navigatorproperty refers to a Navigator object that contains information about the web browser as a whole (such as the version and the list of data formats it can display). The Navigator object is named after Netscape Navigator, but it is also supported by Internet Explorer. IE also supportsclientInformationas a vendor-neutral synonym fornavigator. Unfortunately, Navigator 4 does not support this property.The Navigator object has six main properties that provide version information about the browser that is running:
appNameThe simple name of the web browser.
appVersionThe version number and/or other version information for the browser.
userAgentThe string that the browser sends in its
USER-AGENTHTTP header. This property typically contains all the information in bothappNameandappVersion.appCodeNameThe code name of the browser. Navigator uses this property for the code name "Mozilla," but, in general, this property does not provide any information not provided by other Navigator properties.
platformThe hardware platform on which the browser is running. This property is new in JavaScript 1.2.
languageThe language this version of the browser supports. This is typically a standard two-letter language code, such as "en" for English or "fr" for French. This property is new in Navigator 4. IE 4 defines similar
userLanguageandsystemLanguageproperties instead.The following lines of JavaScript code display each of these Navigator object properties in a dialog box. Figure 13.2 shows the dialogs displayed when the code is run on typical versions of Navigator 4 and Internet Explorer 4:
var browser = "BROWSER INFORMATION:\n"; for(var propname in navigator) { browser += propname + ": " + navigator[propname] + "\n" } alert(browser);Figure 13.2: Navigator object properties for two different browsers
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 only interested in the first digit of the
appVersionproperty, for example. When using the Navigator object to test browser information, we often use methods likeparseInt()andString.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 namedbrowser. These properties, in their processed form, are easier to use than the rawnavigatorproperties. The general term for code like this is a "sniffer," and you can find more complex and general-purpose sniffer code on the Internet. 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 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.isNavigator = false; browser.isIE = false; if (navigator.appName.indexOf("Netscape") != -1) browser.isNavigator = true; else if (navigator.appName.indexOf("Microsoft") != -1) browser.isIE = true;13.5.1 The MimeType and Plugin Objects
As you probably noticed, Figure 13.2 shows that the Navigator object has properties other than those we have discussed so far. Many of these properties are incompatible extensions in Internet Explorer and are not discussed here. If you want to make use of the properties, be sure that you first use code like that in Example 13.3 to ensure that your code is running in Internet Explorer 4 or later. Two properties,
mimeTypesandplugins, do bear further discussion, however. These properties are supported in Navigator 3 and later. In IE 4, the properties are defined for compatibility but are not truly supported: the arrays they refer to are always empty.The
navigator.mimeTypes[]property is an array of MimeType objects, each of which describes one MIME data format (text/htmlandimage/gif, for example) that the web browser can display (either directly, with an external helper application, or with a plugin). The MimeType object itself contains properties that describe the data format.The
mimeTypes[]array is indexed numerically, but it is also an associative array, indexed by the name of the MIME type. Thus, you can easily check for support of a given data format on the browser:// Check to see if the browser can display MPEG files. var show_movie = (navigator.mimeTypes["video/mpeg"] != null);The
navigator.plugins[]property is an array of Plugin objects, each of which represents one plugin module that has been installed in the browser. The properties of the Plugin object provide various details about the plugin. The Plugin object also contains array elements, which are MimeType objects describing each of the data formats supported by that particular plugin. Note that this array is different than thenavigator.mimeTypes[]array described earlier.You can use the
plugins[]property as an associative array, just as you can themimeTypes[]property. This lets you check for the existence of a particular plugin without having to loop through the array numerically and check every element:// Check to see if the browser has the Shockwave plugin installed. var shocked = (navigator.plugins["Shockwave"] != null);13.5.2 Navigator Methods
In addition to its properties, the Navigator object defines methods that provide further information about the browser. In JavaScript 1.1 and later, the
javaEnabled()method returnstrueif the browser supports Java and if Java support is enabled; otherwise it returnsfalse. Navigator 4 adds thepreference()method, which allows signed scripts to query and set user preferences.13.6 The Screen Object
In JavaScript 1.2, the
screenproperty of a Window object refers to a Screen object. This Screen object provides information about the size of the user's display and the number of colors available. Thewidthandheightproperties specify the size of the display in pixels. TheavailWidthandavailHeightproperties specify the display size that is actually available: they exclude the space required by features like the Windows 95 taskbar. You can use these properties to help you decide what size images to include in a document, for example, or in a program that creates multiple browser windows, what size windows to create.The
colorDepthproperty 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, thescreen.colorDepthproperty would be 8. In some circumstances, however, the browser may restrict itself to a subset of the available colors, and you might find ascreen.colorDepthvalue 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 thiscolorDepthproperty to decide which version to include in a document.13.7 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, to control window position and size, to request and relinquish keyboard focus, and to scroll the contents of a window. We conclude with an example that demonstrates several of these features.
13.7.1 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 toopen()is the URL of the document to display in the new window. If this argument is omitted (or isnullor 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 theTARGETattribute of a<FORM>or<A>tag. If you specify the name of a window that already exists,open()simply uses 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 menubar, 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 menubar, toolbar, or locationbar, you could use the following line of JavaScript:var w = window.open("smallwin.html", "smallwin", "width=400,height=350,status=yes,resizeable=yes");Note that when you specify this third argument, any features you do not explicitly specify are omitted. The reference section documents 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 objectwindowto 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, theopenerproperty 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, theopenerproperty isnull.An important point about the
open()method is that it is almost always invoked aswindow.open(), even thoughwindowrefers to the global object and should therefore be entirely optional. The reason thatwindowis specified explicitly is that the Document object also has anopen()method, so specifyingwindow.open()helps to make it very clear what we are trying to do. This is not only a helpful habit; it is required in some circumstances, because, as we'll learn in Chapter 15, 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 theopen()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 an example of the
open()method in Example 13.4.13.7.2 Closing Windows
Just as the
open()method opens a new window, theclose()method closes one. If we've created a Window objectw, 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 thewindowidentifier to disambiguate theclose()method of the Window object from theclose()method of the Document object.You can only automatically close 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 prevents inconsiderate web sites from closing your 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
closedproperty. This property istrueif 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 an example of doing just this in Example 13.4.13.7.3 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()andresizeBy()resize the window by an absolute or relative amount; they are also new in JavaScript 1.2. Note that in Navigator 4, there are some security restrictions on how you can move and resize windows. We'll see more about this in Chapter 21, JavaScript Security, but basically, you are not allowed to move a window off screen or to make it too small. This is so that you can't create a hidden window that the user might forget about.13.7.4 Keyboard Focus
The
focus()andblur()methods also provide high-level control over a window. Callingfocus()requests that the system give keyboard focus to the window andblur()relinquishes keyboard focus. These methods are defined in JavaScript 1.1 and later.13.7.5 Scrolling
Window also contains some methods that control the scrollbars of the window, rather than the window itself.
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, thescroll()method performs the same function as the JavaScript 1.2scrollTo()method.scrollTo()is the preferred method, butscroll()remains for backwards compatibility.In JavaScript 1.2, the elements of the
anchors[]array of the Document object are Anchor objects. Each Anchor object hasxandyproperties that specify the location of the anchor within the document. Thus, you can use these values in conjunction with thescrollTo()method to scroll to known locations within the document.13.7.6 Window Methods Example
Example 13.4 demonstrates the Window
open(),close(), andmoveTo()methods and several other window programming techniques that we've discussed. It creates a new window and then usessetInterval()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!</H1>"', "", "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; } // Have we reached the right or left edge? if ((x+dx > (screen.availWidth - w)) || (x+dx < 0)) dx = -dx; // Have we 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>13.8 The Location Object
The
locationproperty of a window is a reference to a Location object - a representation of the URL of the document currently being displayed in that window. Thehrefproperty of the Location object is a string that contains the complete text of the URL. Other properties of this object, such asprotocol,host,pathname, andsearch, specify the various individual parts of the URL.This
searchproperty 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-purposegetArgs()function that you can use to extract arguments from thesearchproperty of a URL. It also shows how thisgetArgs()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. } 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
hrefproperty of the object (because the Location object has a suitabletoString()method). What is far more interesting, though, is that you can assign a new URL string to thelocationproperty of a window. Assigning a URL to the Location object like this has a very 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 thelocationproperty like this:// If Java isn't enabled, go to a page that displays a message // saying that you can't run this page without Java. if (!navigator.javaEnabled()) location = "needsjava.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
locationproperty of a window is the supported technique to accomplish this.Although the Location object does not have a method that serves the same function as assigning a URL directly to the
locationproperty of a window, this object does support two methods (added in JavaScript 1.1). Thereload()method reloads the currently displayed page from the web server. Thereplace()method loads and displays a URL that you specify. But invoking this method for a given URL is different than assigning that URL to thelocationproperty of a window. When you callreplace(), 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 usereplace()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 to thelocationproperty. For web sites that use frames and display a lot of temporary pages (perhaps generated by a CGI script), usingreplace()is often useful. By not storing temporary pages in the history list, the Back button becomes more useful to the user.Finally, don't confuse the
locationproperty of the Window object, which refers to a Location object, with thelocationproperty of the Document object, which is simply a read-only string with none of the special features of the Location object.Document.locationis a synonym forDocument.URL, which in Navigator 3 is the preferred name for this property (because it avoids the potential confusion). In most cases,document.locationis the same aslocation.href. When there is a server redirect, however,document.locationcontains the actual URL as loaded, andlocation.hrefcontains the URL as originally requested.13.9 The History Object
The
historyproperty of the Window object refers to a History object for the window. The History object is an array of the URLs in the browsing history of the window or frame. For a top-level Navigator window, the History object is a representation of the contents of the browser's Go menu.A user's browsing session history is private information, so, for security reasons, there are heavy restrictions on how the History object can be used. In Navigator 4, the elements of the
historyarray are accessible to signed scripts. In other versions of Navigator and in Internet Explorer, the elements of the array are never accessible, and the History object is less useful.The History object supports three methods, which can be used by unsigned scripts in all versions of Navigator and Internet Explorer. The
back()andforward()methods perform the same action as clicking on the Back and Forward browser buttons. The third method,go(), suffers from bugs in Navigator 2 and 3 and has incompatible behavior in Internet Explorer 3; it is best avoided.Example 13.6 shows how you might use the
back()andforward()methods of the History object and the Location object 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 is covered in detail in Chapter 16, Forms and Form Elements.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 one above. 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 // that 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>13.10 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, most interesting JavaScript applications 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 these windows.
13.10.1 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 anopenerproperty 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 theframes[],parent, andtopproperties of the Window object.Every window has a
framesproperty. 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, theframes[]array is empty andframes.lengthis zero.) Thus, a window (or frame) can refer to its first subframe asframes[0], its second subframe asframes[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
parentproperty, 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,parentsimply refers to the window itself:parent == self; // For any top-level windowIf 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. Thetopproperty is a general-case shortcut, however: no matter how deeply a frame is nested, itstopproperty refers to the top-level containing window. If a Window object represents a top-level window,topsimply refers to the window itself. For frames that are direct children of a top-level window, thetopproperty is the same as theparentproperty.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, andtopproperties. With this understanding of the relationships between windows, you may want to revisit Example 13.6, paying particular attention this time to the way the second frame refers to thehistoryandlocationproperties of the first.Figure 13.4: Relationships between frames
13.10.2 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 theNAMEattribute. An important reason to specify names for windows and frames is that those names can be used as the value of theTARGETattribute of the<A>,<MAP>, and<FORM>tags. This tells the browser where you want the results of activating a link, clicking on an image map, or submitting a form to be displayed.For example, if you have two windows, one named
table_of_contentsand the other namedmainwin, you might have HTML like the following in thetable_of_contentswindow:<A HREF="chapter01.html" TARGET="mainwin"> Chapter 1, Introduction </A>When the user clicks on this hyperlink, the browser loads the specified URL, but instead of displaying the URL in the same window as the link, it displays it in the window namedmainwin. If there is no window with the namemainwin, clicking the link creates a new window with that name and loads the specified URL into it.The
TARGETandNAMEattributes 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 aframes[]array that contains references to each of its frames. This array contains all 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_contentsThis 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
nameproperty of any Window object contains the name of that window. In JavaScript 1.0, this property is read-only. In JavaScript 1.1, 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 Navigator starts up, the initial window has no name, so it cannot be used with theTARGETattribute. If you set thenameproperty of the window, however, you can then use that name inTARGETattributes.13.10.3 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 by using the
frames,parent, andtopproperties. So, although JavaScript code in different frames is executed with different scope chains, this does not prevent the code in one frame from referring to, and using, 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.iNow suppose that frame A has a sibling frame B that wants to set the value of the variableidefined by the code in frame A. If frame B just sets a variablei, it merely succeeds in creating a new property of its own Window object. So instead, it must explicitly refer to the propertyiin its sibling frame with code like this:parent.frames[0].i = 4;Recall that the
functionkeyword that defines functions declares a variable just like thevarkeyword does. If JavaScript code in frame A declares a functionf, 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 tofas 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 asf(), 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 example above, if the function
frefers to global variables, these variables are looked up as properties of frame A, even when the function is invoked from frame B.[1][1] If you have read Chapter 11, Further Topics in JavaScript, you can probably think of a way to use the
Closure()constructor of Navigator 4 to define a version of the functionfthat executes in the scope of 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 astop.debug(). Whenever this function is invoked, however, it looks up the variablenamein 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 only defined for a single window. Recall the Complex class we defined in Chapter 8, Objects, 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 frameInstead, 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);Unlike user-defined constructors, predefined constructors are automatically pre-defined 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 theString.prototypeobject. So, if you write a new method for manipulating JavaScript strings and make it a method of the String class by assigning it to theString.prototypeobject 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 which window the string was actually created in.13.10.4 Example: Colored Frames
Example 13.7, a frame set that defines a grid of nine frames, demonstrates some of the techniques we've been discussing in this chapter. The
<HEAD>section of the frame set includes a<SCRIPT>that defines a JavaScript function namedsetcolor(). TheonLoadevent handler of the<FRAMESET>tag invokessetcolor()once for each of the nine frames.
setcolor()is passed a Window object as its argument. It generates a random color and uses it as the new value of thebgColorproperty of the Document object. (We'll see more about the Document object and its properties in Chapter 14.) Finally,setcolor()uses thesetTimeout()method to schedule itself to be called again in one second. This call tosetTimeout()is the most interesting part of the example. Notice especially how it uses theparentandnameproperties 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.random() * 256).toString(16); var g = (Math.random() * 256).toString(16); var b = (Math.random() * 256).toString(16); var colorString = "#" + r + g + b; // Set the frame background to the random color. w.document.bgColor = colorString; // 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>