|
Related Reading
|
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!
Opening Windows
Closing Windows
Window Geometry
Keyboard Focus and Visibility
Scrolling
Window Methods ExampleRelationships Between Frames
Window and Frame Names
JavaScript in Interacting Windows
Example: Colored Frames
|
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:
closedtrue only if the window has been closed.defaultStatus, statusdocumentframes[]historylocationnametarget attribute of the HTML <a> tag, for example.openernull if this window was opened by the
user.parentselfwindow.toptop is different from parent for frames nested within other frames.windowself.The Window object also supports a number of important methods:
alert( ), confirm( ), prompt( )confirm( ) and prompt(
), get the user's response.close( )focus( ), blur( )focus( ) method also ensures that the window
is visible by bringing it to the front of the stacking order.moveBy( ), moveTo( )open( )print( )resizeBy( ), resizeTo( )scrollBy( ), scrollTo( )setInterval( ), clearInterval( )setTimeout( ), clearTimeout( )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.
|
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.
|
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>");
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>
|
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.
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 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:
appNameappVersionuserAgentUSER-AGENT HTTP header. This property typically
contains all the information in both appName and
appVersion.appCodeNameplatformThe 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.
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;
|
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.
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.
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.
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.
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.
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.
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.
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 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 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.
|
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>
|
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.
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.
|
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.
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.
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);
|
|
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
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 © 2007 O'Reilly Media, Inc.