|
back
Sample Chapter 13:
Chapter 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!
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:
closed
-
A boolean
value that is true only if the window has
been closed.
defaultStatus, status
-
These properties specify the text that appears in the
status line of the browser.
document
-
A
reference to the Document object that represents the HTML document
displayed in the window. The Document object is covered in detail in
Chapter 14, The Document Object Model.
frames[]
-
An array
of Window objects that represent the frames (if any) within the
window.
history
-
A reference to the History object that represents the
user's browsing history for the window.
innerHeight, innerWidth,
outerHeight, outerWidth
-
The
inner and outer dimensions of the window; not available in Internet
Explorer 4.
location
-
A reference to the Location object that represents the
URL of the document displayed in the window. Setting this property
causes the browser to load a new document.
locationbar, menubar,
personalbar, scrollbars,
statusbar, toolbar
-
References
to Bar objects that specify the visibility of the various parts of the
Navigator window; not available in IE 4.
name
-
The name
of the window. Can be used with the TARGET
attribute of the HTML <A> tag, for
example.
opener
-
A
reference to the Window object that opened this one, or null
if this window was opened by the user.
pageXOffset, pageYOffset
-
The
amounts that the document has been scrolled to the right and down
within the window; not available in IE 4.
parent
-
If the
current window is a frame, a reference to the frame of the window that
contains it.
self
-
A
self-referential property; a reference to the current Window object. A
synonym for window.
top
-
If the
current window is a frame, a reference to the Window object of the
top-level window that contains the frame. Note that top
is different from parent for frames
nested within other frames.
window
-
A
self-referential property; a reference to the current Window object. A
synonym for self.
The Window
object also supports a number of important methods:
alert(), confirm(),
prompt()
-
Display simple dialog boxes to the user, and, for confirm()
and prompt(), get the user's response.
close()
-
Close the window.
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.
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 dialogs 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.
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(), and prompt().
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() and prompt()
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. The alert()
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.
// 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>");
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: status
and defaultStatus.
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 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
the previous 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 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 onMouseOver
event handler to set the Window status property -
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 the status property from an onMouseOver
event handler. To force it to be erased, you can use the onMouseOut
event handler, like this:
<A HREF="sitemap.html"
onMouseOver="status='Go to Site Map'; return true;"
onMouseOut="status='';">
Site Map
</A>
The status property 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
the defaultStatus property 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 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>
If your web page contains an HTML form, you might change the defaultStatus
property as the user enters data in the form in order to display step-by-step
instructions for completing it.
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 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 onLoad event
handler 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. The onLoad event handler is
specified here as an attribute of the <BODY>
tag.
<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 of setTimeout(). In this case, the setTimeout()
call would be removed from the display_time_in_status_line()
method, and the onLoad event handler would be
changed to call setInterval() to schedule an
invocation of that method that automatically repeats once every 60,000
milliseconds.
The
Window.navigator property 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 supports clientInformation as a
vendor-neutral synonym for navigator.
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:
appName
-
The simple name of the web browser.
appVersion
-
The version number and/or other version information for
the browser.
userAgent
-
The string
that the browser sends in its USER-AGENT HTTP
header. This property typically contains all the information in both appName
and appVersion.
appCodeName
-
The code name
of the browser. 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.
platform
-
The hardware
platform on which the browser is running. This property is new in
JavaScript 1.2.
language
-
The 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 userLanguage
and systemLanguage properties 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);
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 appVersion
property, for example. When using the Navigator object to test browser
information, we often use methods like 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 "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.
/*
* 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;
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, mimeTypes
and plugins, 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/html
and image/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
the navigator.mimeTypes[] array described
earlier.
You can use the plugins[]
property as an associative array, just as you can the mimeTypes[]
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);
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 returns true
if the browser supports Java and if Java support is enabled; otherwise it
returns false. Navigator 4 adds the preference()
method, which allows signed scripts to query and set user preferences.
In
JavaScript 1.2, the screen property 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. 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 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
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.
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.
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 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 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. The reason that window
is specified explicitly is that 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 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 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 an example of the open()
method 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.
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 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 an example of
doing just this 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
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.
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. These methods are defined in JavaScript 1.1 and
later.
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, the scroll()
method performs the same function as the JavaScript 1.2 scrollTo()
method. scrollTo() is the preferred method, but
scroll() remains for backwards 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.
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.
<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>
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.
This
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.
/*
* 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 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 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 the location
property 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 location property 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 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 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. By not storing temporary pages in the history list, the Back
button becomes 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 Navigator
3 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
actual 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 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 history
array 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()
and forward() 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() and forward()
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.
<!-- 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>
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.
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 the window itself. For frames that are direct children of a
top-level window, the top property is the same
as the parent property.
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. 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 the history and location
properties of the first.
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 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_contents
and the other named mainwin, you might have
HTML like the following in the table_of_contents
window:
<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 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 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, 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 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 by using
the frames, parent,
and top properties. 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.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 example above, 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.[1]
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 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 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 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 the String.prototype
object. So, if you write a new method for manipulating JavaScript strings
and 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 which window the string was actually created in.
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 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 as the new
value of the bgColor property of the Document
object. (We'll see more about the Document object and its properties in
Chapter 14.) 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.
<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>
|