|
|
Chapter 8
Frames, Documents, and Windows
CONTENTS
Now that you have learned the basics of JavaScript and how to
work with forms, you are ready to look at another advanced feature
of JavaScript: frames.
Frames provide the ability to divide a document window into distinct
sections, each of which contains different HTML files that can
also be manipulated using JavaScript.
Besides the capability to manipulate frames, JavaScript also provides
the document object, which
provides properties and methods for dealing with anchors, links
and colors, and the window
object-the top level object of a web document window.
In this chapter we cover these topics:
- An introduction to frames
- Working with frames in JavaScript
- The document object
- The window object
- Working with the status bar
- Controlling the timing of scripts with setTimeout()
Frames are one of the most widely used new features of Navigator
2.0 and 3.0.
By using a few simple extensions to the HTML standard, Web authors
are able to achieve sophisticated control over the layout of information
in the Web browser window by dividing the window into rectangular
sections and loading separate HTML files into each section of
the window.
In addition, links in one frame can update another frame, and
the result of processing form data in a CGI script on a server
can be targeted at another frame.
Even without the addition of JavaScript, frames have enabled the
addition of a type of interactivity that wasn't possible before,
using regular HTML. For instance, sites now feature fixed tool
bars and permanent search forms such as the one in Figure 8.1.
Figure 8.1 : Using frames, The Dataphile On-line in Hong Kong has permanent search forms at its site.
A page is divided into frames using the FRAMESET
tag. The tag is used in the top-level document defining a window
containing frames and is used to specify how to divide the document
window.
|
Because windows divided into frames are created from multiple HTML files, it is important to keep the hierarchical relationship of documents in mind. In a document window divided into frames, the top-level document is the HTML document that defines
the frames and files that will load into those frames.
|
The FRAMESET container tag
takes several attributes. The two basic ones are ROWS
and COLS. A FRAMESET
tag takes either one of these or both to divide a document into
a set of rows or columns. For instance,
<FRAMESET COLS="25,*,25">
would define three columns. The two outer columns would each be
25 pixels wide, and the middle column would take the remaining
space depending on the size of the window. In this example the
asterisk (*) represents the
remaining available space after the space is allocated for the
other frames.
In addition to specifying the size of frames in pixels, the size
of columns and rows can be defined using percentages relative
to the space available to the document:
<FRAMESET ROWS="35%,*">
Tip |
The use of percentages to define the size of frames is useful when you consider that different users will have different size monitors running at different resolutions. If you normally use a very high resolution, you may feel it is okay to define the width
of a column as 700 pixels, but to a user running at standard 640¥480 VGA resolution, this frame would be wider than his display allows.
|
The preceding FRAMESET tag
would divide the display into two rows. The top row would be 35
percent of the height of the display area, and the bottom row
would fill the remaining space (using the asterisk again).
Note |
The FRAMESET tag replaces the BODY tag in a file. Files with FRAMESET containers are not used to directly display HTML data in Navigator.
|
Inside a FRAMESET container,
the FRAME tag is used to
specify which files should be displayed in each frame. The URLs
of the files-which can be relative or absolute-should be specified
using the SRC attribute in
the same way as the IMG tag
is used to include images in an HTML document.
|
The terms relative and absolute refer to two different ways of indicating the location of files in HTML. In absolute URLs, the complete protocol (the part before the colon), domain name, and path of a file are provided. For instance,
|
http://www.juxta.com/juxta/docs/prod.htm
is an absolute URL.
In relative URLs, the protocol, domain name, and complete path
are not indicated. Instead, the location of the file relative
to the current file is indicated. If the file indicated the URL
is in the same directory, then just the filename is needed. If
the file is in a subdirectory, then the path from the current
directory is needed.
For example, the following creates a document with two rows.
<FRAMESET ROWS="35%,*">
<FRAME SRC="menu.html">
<FRAME SRC="welcome.html">
</FRAMESET>
The top is 35 percent of the available space, and the bottom takes
up the remaining 65 percent. The file menu.html
is loaded into the top frame, and the file welcome.html
is displayed in the lower frame.
In addition to the SRC attribute,
the FRAME tag can take several
other attributes as outlined in Table 8.1.
Table 8.1. Attributes for the FRAME
tag.
Attribute | Description
|
SRC |
Specifies the URL of the HTML file to be displayed in the frame.
|
NAME |
Specifies the name of the frame so that it can be referenced by HTML tags and JavaScript scripts.
|
NORESIZE
| Specifies that the size of a frame is fixed and cannot be changed by the user.
|
SCROLLING
| Specifies whether scroll bars are available to the user. This can take a value of YES, NO, or AUTO.
|
MARGINHEIGHT
| Specifies the vertical offset in pixels from the border of the frame.
|
MARGINWIDTH
| Specifies the horizontal offset in pixels from the border of the frame.
|
To illustrate these attributes, look at the earlier example. The
user can resize the frames by dragging on the border between the
frames. By adding NORESIZE
to either of the frames, this is prevented:
<FRAMESET ROWS="35%,*">
<FRAME SRC="menu.html" NORESIZE>
<FRAME SRC="welcome.html">
</FRAMESET>
or
<FRAMESET ROWS="35%,*">
<FRAME SRC="menu.html">
<FRAME SRC="welcome.html" NORESIZE>
</FRAMESET>
Typically, if a document fills more space than the frame it is
assigned to, Navigator will add scroll bars to the frame. If you
don't want scroll bars to appear, regardless of the size of the
frame, you can use SCROLLING=NO
to prevent them from being used:
<FRAMESET ROWS="35%,*">
<FRAME SRC="menu.html">
<FRAME SRC="welcome.html" SCROLLING=NO>
</FRAMESET>
As you can see in Figure 8.2, by using SCROLLING=NO,
no scroll bars appear in the lower frame, even though the graphic
is larger than the frame.
Figure 8.2 : Preventing scroll bars in a frame, even when the document is larger than the frame.
Looking at examples of frames on the Web, it quickly becomes obvious
that many sites have more complex layouts than simply dividing
the window into rows or columns. For instance, in Figure 8.1 you
saw an example of a site that has rows and columns combined to
produce a very complex layout.
This is achieved by nesting, or embedding, FRAMESET
containers within each other. For instance, if you want to produce
a document with three frames where you have two rows and the bottom
row is further divided in two columns (to produce three frames),
you could use a structure like this:
<FRAMESET ROWS="30%,*">
<FRAME SRC="menu.html">
<FRAMESET COLS="50%,50%">
<FRAME SRC="welcome.html">
<FRAME SRC="pic.html" SCROLLING=AUTO>
</FRAMESET>
</FRAMESET>
A similar result can be achieved by using separate files. For
instance, if the first file contains
<FRAMESET ROWS="30%,*">
<FRAME SRC="menu.html">
<FRAME SRC="bottom.html">
</FRAMESET>
and the file bottom.html
contains
<FRAMESET COLS="50%,50%">
<FRAME SRC="welcome.html">
<FRAME SRC="pic.html" SCROLLING=AUTO>
</FRAMESET>
then you would get the same result as the previous example where
both FRAMESET containers
appeared in the same file.
To get a better idea of how this works, you can look at the source
code for The Dataphile On-line which you saw in Figure
8.1. The following source code in Listing 8.1 combines the nested
framesets from multiple files into a single file:
Listing 8.1. The source code for The
Dataphile On-line frames.
<FRAMESET ROWS="100,*">
<FRAMESET COLS="500,*">
<FRAME SRC="banner.htm" NORESIZE
MARGINHEIGHT=0
MARGINWIDTH=0
SCROLLING="no">
<FRAMESET ROWS="30,*">
<FRAME SRC="constant.htm"
NORESIZE MARGINHEIGHT=0
MARGINWIDTH=0
SCROLLING="no">
<FRAME SRC="menu.htm"
NORESIZE MARGINHEIGHT=0
MARGINWIDTH=0
SCROLLING="auto">
</FRAMESET>
</FRAMESET>
<FRAMESET COLS="*,250">
<FRAMESET ROWS="*,50">
<FRAME SRC="welcome.htm"
NAME="middle"
SCROLLING="auto">
<FRAME SRC="search.htm"
MARGINHEIGHT=2
MARGINWIDTH=2 SCROLLING="auto">
</FRAMESET>
<FRAMESET ROWS="50,*">
<FRAME SRC="newshead.htm"
SCROLLING="no"
MARGINHEIGHT=0 MARGINWIDTH=0>
<FRAME SRC="newstory.htm"
SCROLLING="auto"
MARGINGHEIGHT=2 MARGINWIDTH=2>
</FRAMESET>
</FRAMESET>
</FRAMESET>
|
You start by dividing the window into two rows. The top row is divided into two columns, and the right column is further divided into two rows. Likewise, the bottom row is divided into two columns. The left column is divided into two rows, as is the right
column.
|
You may have noticed that the one problem with files containing
FRAMESET containers is that
they will go undisplayed on a non-Netscape browser because other
browsers don't support this extension to HTML.
This is addressed by the NOFRAMES
container tag. Any HTML code contained between the NOFRAMES
tags is ignored by the Navigator browser but will be displayed
by any other browser.
For instance, this code
<HTML>
<HEAD>
<TITLE>NOFRAMES Example</TITLE>
</HEAD>
<FRAMESET ATTRIBUTES>
<FRAME SRC="filename">
<FRAME SRC="filename">
</FRAMESET>
<NOFRAMES>
HTML code for other browsers
</NOFRAMES>
</HTML>
could be used to produce output like Figure 8.3 in Navigator 2.0
or 3.0 but like Figure 8.4 in another browser.
Figure 8.3 : Only Navigator 2.0 recognizes the FRAMESET tag.
Figure 8.4 : The NOFRAMES tag provides an alternative page for users of other browsers.
In order to place (or target) the result of links or form submissions
in specific frames, you can name frames using the NAME
attribute of the FRAME tag.
For instance,
<FRAMESET COLS="50%,*">
<FRAME SRC="menu.html" NAME="menu">
<FRAME SRC="welcome.html" NAME="main">
</FRAMESET>
would create two named frames called menu
and main. In the file menu.html,
you could have hypertext references target the main
frame using the TARGET attribute:
<A HREF="choice1.html" TARGET="main">
Likewise, the result of a form submission could be targeted the
same way:
<FORM METHOD=POST ACTION="/cgi-bin/test.pl"
TARGET="main">
The TARGET attribute can
also be used in the BASE
tag to set a global target for all links in a document. For instance,
if an HTML document has this BASE
tag in its header
<BASE TARGET="main">
then all hypertext and results of form processing will appear
in the FRAME named "main".
This global targeting is overridden by using a TARGET
attribute in an A tag or
FORM tag in the body of the
HTML document.
Note
|
Naming and targeting are not relevant to frames only. Windows can also be named and targeted as you learn later in this chapter, in the section about the window object.
|
In addition to targeting named frames, there are several special
terms which can be used in the TARGET
attributes. These are outlined in Table 8.2.
Table 8.2. Special values for the TARGET
attribute.
Value | Description
|
_blank |
Causes a link to load in a new, unnamed window.
|
_self |
Causes a link to load in the same window the anchor was clicked in. (This can be used to override a target specified in a BASE tag.)
|
_parent
| Causes a link to load in the immediate FRAMESET parent.
|
_top |
Causes a link to load in the full body of the window regardless of the number of nested FRAMESET tags.
|
JavaScript provides the frames
property of the window object
for working with different frames from a script.
The frames property is an
array of objects with an entry for each child frame in a parent
frameset. The number of frames is provided by the length
property.
For instance, in a given window or frameset with two frames, you
could reference the frames as parent.frames[0]
and parent.frames[1]. The
index of the last frame could be parent.frames.length.
By using the frames array,
you can access the functions and variables in another frame, as
well as objects, such as forms and links, contained in another
frame. This is useful when building an application that spans
multiple frames but that also must be able to communicate between
the frames.
Note
|
Each frame has a different document, location, and history object associated with it. This is because each frame contains a separate HTML document and has a separate history list. You will learn about the document object
later in this chapter and about the history object in Chapter 10, "Strings, Math, and the History List."
|
For example, if you have two frames, you could create a form in
the first frame to provide the user with a field to enter an expression.
Then you could display the results in a form in the other frame.
This cross-frame communication is achieved by referencing the
document object's forms[]
array in the second frame with parent.frames[1].document.forms[0].
In Listing 8.2 you build a simple calculator to evaluate expressions
entered by users and use frames to display the output.
Listing 8.2. Cross-frame communication.
<!-- HTML CODE FOR PARENT FRAMESET
(this is a separate file) -->
<HTML>
<HEAD>
<TITLE>Listing 8.2</TITLE>
</HEAD>
<FRAMESET COLS="50%,*">
<FRAME SRC="input.html">
<FRAME SRC="output.html">
</FRAMESET>
</HTML>
<!-- HTML FOR INPUT FRAME (this is a separate file called input.html-->
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
function update(field) {
var result = field.value;
var output = "" + result + "
= " + eval(result);
parent.frames[1].document.forms[0].result.value
= output;
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HEAD>
<BODY>
<FORM METHOD=POST>
<INPUT TYPE=text NAME="input" onChange="update(this);">
</FORM>
</BODY>
</HTML>
<HTML>
<!-- HTML FOR OUTPUT FRAME (this is a separate file called
output.html)-->
<BODY>
<FORM METHOD=POST>
<TEXTAREA NAME=result ROWS=2 COLS=20
WRAP=SOFT></TEXTAREA>
</FORM>
</BODY>
</HTML>
|
In this example, it is important to note two things in the update() function. First, the eval() function used to evaluate the expression provided by the user doesn't work properly on the Windows 3.11 version of Navigator 2.0. Second, when
you evaluate the expression and store the result in the variable output
|
var output = "" + result +
" = " + eval(result);
you start the expression with ""
to ensure a string value is assigned to the variable output.
In addition to specifying frames
using the frames array, if you name the frames, you can specify
certain frames using the form parent.framename.
In the example you just saw, if you name the frames input
and output, you could rewrite
the update() function:
function update(field) {
var result = field.value;
var output = "" + result + "
= " + eval(result);
parent.output.form[0].result.value = output;
}
The frameset in this example would look like
<FRAMESET COLS="50%,*">
<FRAME SRC="input.html" NAME="input">
<FRAME SRC="output.html" NAME="output">
</FRAMESET>
The naming of elements can be taken one step further, and the
forms can be named. For instance, if you name the forms inputForm
and outputForm, then the
files input.html and output.html
can look like this:
<!-- HTML FOR INPUT FRAME (this is
a separate file called input.html-->
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
function update(field) {
var result = field.value;
var output = "" + result + "
= " + eval(result);
parent.output.document.outputForm.result.value
= output;
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HEAD>
<BODY>
<FORM METHOD=POST NAME="inputForm">
<INPUT TYPE=text NAME="input" onChange="update(this);">
</FORM>
</BODY>
</HTML>
<HTML>
<!-- HTML FOR OUTPUT FRAME (this is a separate file called
output.html)-->
<BODY>
<FORM METHOD=POST NAME="outputForm">
TEXTAREA NAME=result ROWS=2 COLS=20
WRAP=SOFT></TEXTAREA>
</FORM>
</BODY>
</HTML>
Notice, then, how the output field can be referred to with
parent.output.document.-outputForm.result
With nested frames, cross-frame communication gets a little bit
more complicated.
When building nested framesets, you can use subdocuments for each
frameset. When you do this, the parent will only refer back
to the document containing the parent frameset and not the top-level
frameset.
For example, referring to the previous expression evaluation example,
if you want to divide the display into four equal quarters (as
shown in Figure 8.5) and then use only two of them, you would
have to change the FRAMESET
to be something like this:
Figure 8.5 : Using nested framesets produces complex screen Layouts.
<FRAMESET ROWS="50%,*">
<FRAME SRC="top.html">
<FRAME SRC="bottom.html">
</FRAMESET>
Where top.html and bottom.html
contain further nested framesets:
<!-- HTML FOR top.html -->
<FRAMESET COLS="50%,*">
<FRAME SRC="input.html"
NAME="input">
<FRAME SRC="logo.html">
</FRAMESET>
<!-- HTML FOR bottom.html -->
<FRAMESET COLS="50%,*">
<FRAME SRC="about.html">
<FRAME SRC="output.html"
NAME="output">
</FRAMESET>
If input.html and output.html
are still the files where the work is being done (logo.html
and about.html are cosmetic),
then you can't use the update()
function you were using, because parent.frame[1]
in the script will be referring to the frame containing logo.html-the
parent of the input frame
is the first nested frameset. You want to reference the frame
containing output.html, which
is in the second nested frameset. To reference this document,
you need to go up two parent levels and then down two frames to
reach output.html:
parent.parent.frame[1].frame[1]
With the named frame, this would become parent.parent.frame[1].output.
In addition to referring to variables and objects in other frames,
the same technique can be used to invoke functions in other frames.
For instance, you could add a function to output.html
to handle displaying the results in the appropriate text field.
Then, in input.html you could
simply call the function and pass it the value of the variable
output:
<!-- HTML FOR INPUT FRAME (this is
a separate file called input.html-->
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
function update(field) {
var result = field.value;
var output = "" + result + "
= " + eval(result);
parent.output.displayResult(output);
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HEAD>
<BODY>
<FORM METHOD=POST NAME="inputForm">
<INPUT TYPE=text NAME="input" onChange="update(this);">
</FORM>
</BODY>
</HTML>
<HTML>
<!-- HTML FOR OUTPUT FRAME (this is a separate file called
output.html)-->
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
function displayResult(output) {
document.ouputForm.result.value = output;
}
// STOP HIDING -->
</SCRIPT>
</HEAD>
<BODY>
<FORM METHOD=POST NAME="outputForm">
<TEXTAREA NAME=result ROWS=2 COLS=20
WRAP=SOFT></TEXTAREA>
</FORM>
</BODY>
</HTML>
Bill Dortch's hIdaho Frameset.
|
It quickly becomes obvious that any program can get tangled up in deeply nested frames, all of which must interact with each other to produce an interactive application.
This can quickly lead to confusing references to
parent.parent.frameA.frameB.frameC.form1.fieldA.value
or
parent.frameD.frameE.functionA()
To make this easier, Bill Dortch has produced the hIdaho Frameset. This is a set of freely available JavaScript functions to make dealing with functions in nested framesets easier. Dortch has made the hIdaho Frameset available for others to use in their
scripts. Full information about the Frameset is on-line at
http://www.hidaho.com/frameset/
Using this Frameset, it is possible to register functions in a table and then call them from anywhere in a nested frameset without needing to know which frames they are defined in and without needing to use a long, and often confusing, sequence of objects
and properties to refer to them. In addition, frames and framesets can be easily moved without having to recode each call to the affected functions across all your documents.
The hIdaho Frameset also provides a means of managing the timing of functions so you can ensure that a function has been loaded and registered before attempting to call it. This is especially useful during window and frame refreshes, when documents are
reevaluated.
The source code is reproduced on the CD-ROM:
<script language="JavaScript">
<!-- begin script
//****************************************************************
// The hIdaho Frameset. Copyright (C) 1996 Bill Dortch, hIdaho Design
// Permission is granted to use and modify the hIdaho Frameset code,
// provided this notice is retained.
//****************************************************************
var debug = false;
var amTopFrameset = false;
// set this to true for the topmost frameset
var thisFrame = (amTopFrameset) ? null : self.name;
var maxFuncs = 32;
function makeArray (size) {
this.length = size;
for (var i = 1; i <= size; i++)
this[i] = null;
return this;
}
var funcs = new makeArray ((amTopFrameset) ? maxFuncs : 0);
function makeFunc (frame, func) {
this.frame = frame;
this.func = func;
return this;
}
function addFunction (frame, func) {
for (var i = 1; i <= funcs.length; i++)
if (funcs[i] == null) {
funcs[i] = new makeFunc (frame, func);
return true;
}
return false;
}
function findFunction (func) {
for (var i = 1; i <= funcs.length; i++)
if (funcs[i] != null)
if (funcs[i].func == func)
return funcs[i];
return null;
}
function Register (frame, func) {
if (debug) alert (thisFrame + ":
Register(" + frame + "," + func + ")");
if (Register.arguments.length < 2)
return false;
if (!amTopFrameset)
return parent.Register (thisFrame + "." + frame, func);
if (findFunction (func) != null)
return false;
return addFunction (frame, func);
}
function UnRegister (func) {
if (debug) alert (thisFrame + ": UnRegister(" + func + ")");
if (UnRegister.arguments.length == 0)
return false;
if (!amTopFrameset)
return parent.UnRegister (func);
for (var i = 1; i <= funcs.length; i++)
if (funcs[i] != null)
if (funcs[i].func == func) {
funcs[i] = null;
return true;
}
return false;
}
function UnRegisterFrame (frame) {
if (debug) alert (thisFrame + ": UnRegisterFrame(" + frame + ")");
if (UnRegisterFrame.arguments.length == 0)
return false;
if (!amTopFrameset)
return parent.UnRegisterFrame (thisFrame + "." + frame);
for (var i = 1; i <= funcs.length; i++)
if (funcs[i] != null)
if (funcs[i].frame == frame) {
funcs[i] = null;
}
return true;
}
function IsRegistered (func) {
if (debug) alert (thisFrame + ": IsRegistered(" + func + ")");
if (IsRegistered.arguments.length == 0)
return false;
if (!amTopFrameset)
return parent.IsRegistered (func);
if (findFunction (func) == null)
return false;
return true;
}
function Exec (func) {
if (debug) alert (thisFrame + ": Exec(" + func + ")");
var argv = Exec.arguments;
if (argv.length == 0)
return null;
var arglist = new makeArray(argv.length);
for (var i = 0; i < argv.length; i++)
arglist[i+1] = argv[i];
var argstr = "";
for (i = ((amTopFrameset) ? 2 : 1); i <= argv.length; i++)
argstr += "arglist[" + i + "]" + ((i < argv.length) ? "," : "");
if (!amTopFrameset)
return eval ("parent.Exec(" + argstr + ")");
var funcobj = findFunction (func);
if (funcobj == null)
return null;
return eval ("self." + ((funcobj.frame == null) ? "" :
(funcobj.frame + "."))+ funcobj.func + "(" + argstr + ")");
}
//****************************************************************
// End of hIdaho Frameset code.
//****************************************************************
// end script -->
</script>
The source code should be included in each frameset document in your hierarchy of nested framesets. The only important distinction is that the amTopFrameset variable should be set to false for all framesets except the top.
Each of the functions is used for a different purpose.
The Register() function.
The Register() function is used to register functions in the function table. It is called from the function's frame by referring to the function in the immediate parent frameset as follows:
parent.Register(self.name,"functionName").
self refers to the currently opened frame or window. You learn more about it later in this chapter.
The function will return true if there is room in the function table and the name is not currently registered. Otherwise, it will return false.
The UnRegister() function.
This function does exactly what its name suggests: It removes a specific function from the registration table. It takes a single argument: UnRegister("functionName").
The UnRegisterFrame() function.
This function unregisters all functions registered for a specified frame. It takes the frame name as a single argument.
The IsRegistered() function.
A call to IsRegistered("frameName") returns true if the function is registered and false if it isn't.
The Exec() function.
The Exec() function is used to call a specific function. It takes at least one argument-the name of the function-but can take more in the form of parameters to pass to the called function as arguments. For instance, if you want to call the
function functionA and pass two arguments, arg1 and arg2, you could call
parent.Exec("functionA",arg1,arg2);
The Exec() function returns the value returned by the specified function.
It is not considered harmful to call an unregistered function using Exec(). If you do, a null value is returned. This can cause confusion, of course, if a legitimate value returned by the specified function could be the null
value. This can happen when the frame containing the desired function has not finished loading when another frame's script tries to call it.
One way that Dortch suggests dealing with this timing problem is to use the IsRegsistered() function to ensure a function exists before calling it:
function intialize() {
if (!parent.IsRegistered("functionA")) {
setTimeout("initialize()",250);
return;
}
JavaScript code
parent.Exec("functionA",arg1,arg2);
JavaScript code
}
In this example, the function initialize() will not get past the first if statement unless the function functionA has been registered. The function uses the setTimeout() method to cause a pause for 250 milliseconds after
which
initialize() is to be called again.
setTimeout() is a method of the window object that enables a pause to be specified before executing a command or evaluating an expression. We will look at the setTimeout() method, and the related clearTimeout() method,
later in this chapter when we cover the window object.
The initialize() function is a recursive function that will continue to call itself every quarter second until the desired function is registered.
As indicated on the hIdaho Frameset Web page, Dortch has purposely not written functions to provide access to variables and other objects and properties in other frames because he feels that well-designed, multi-frame applications should use function calls
to access information in other frames. Look for an updated version coming soon to his Web page.
|
Now that you know how to work with frames, you are going to produce
a testing tool that teachers can use to easily produce a test
in any given subject.
To do this, you will use nested framesets. The top-level frameset
will produce three rows: one for the title, one for the work area,
and one for a level selector.
The middle row, the work area, will be split into two equal columns.
The left side will contain a form for the student to enter his
answer as well as a field to display the current score. The right
column will be used to display the questions and the result of
a student's answer.
For these purposes, you only need to look at the source code for
the student entry form and the level selection tool in the bottom
frame. You will use Bill Dortch's hIdaho Frameset to make working
with the nested framesets easier.
The frameset is defined by two files: the top-level test.htm
(Listing 8.3) and work.htm
(Listing 8.4) which defines the workspace in the middle row. work.htm
contains the nested frameset referred to in test.htm
(<FRAME SRC="work.htm" NAME="work">):
Listing 8.3. Top-level frameset (test.htm).
<!-- FRAMESET FROM test.htm -->
<FRAMESET ROWS="20%,*,20%">
<FRAME SRC="title.htm">
<FRAME SRC="work.htm" NAME="work">
<FRAME SRC="level.htm" NAME="level">
</FRAMESET>
Listing 8.4. The nested frameset (work.htm).
<!-- FRAMESET FROM work.htm -->
<FRAMESET COLS="50%,*">
<FRAME SRC="form.htm" NAME="form">
<FRAME SRC="output.htm" NAME="output">
</FRAMESET>
Both test.htm and work.htm
would include the source code of the hIdaho Frameset so that the
programs can easily call functions in other frames. In the file
test.htm, amTopFrameset
should be set to true with
the statement amTopFrameset = true.
All of the functions and information are kept in the file form.htm
(Listing 8.5). form.htm
is one of the frames in the nested frameset.
Listing 8.5. The entry form (form.htm).
<!-- SOURCE CODE OF form.htm -->
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
var currentLevel=1;
var currentQuestion=1;
var toOutput = "";
// DEFINE LEVEL ONE
q1 = new question("1 + 3",4);
q2 = new question("4 + 5",9);
q3 = new question("5 - 4",1);
q4 = new question("7 + 3",10);
q5 = new question("4 + 4",8);
q6 = new question("3 - 3",0);
q7 = new question("9 - 5",4);
q8 = new question("8 + 1",9);
q9 = new question("5 - 3",2);
q10 = new question("8 - 3",5);
levelOne = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE LEVEL TWO
q1 = new question("15 + 23",38);
q2 = new question("65 - 32",33);
q3 = new question("99 + 45",134);
q4 = new question("34 - 57",-23);
q5 = new question("-34 - 57",-91);
q6 = new question("23 + 77",100);
q7 = new question("64 + 32",96);
q8 = new question("64 - 32",32);
q9 = new question("12 + 34",46);
q10 = new question("77 + 77",154);
levelTwo = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE LEVEL THREE
q1 = new question("10 * 7",70);
q2 = new question("15 / 3",5);
q3 = new question("34 * 3",102);
q4 = new question("33 / 2",16.5);
q5 = new question("100 / 4",25);
q6 = new question("99 / 6",16.5);
q7 = new question("32 * 3",96);
q8 = new question("48 / 4",12);
q9 = new question("31 * 0",0);
q10 = new question("45 / 1",45);
levelThree = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE TEST
test = new newTest(levelOne,levelTwo,levelThree);
function newTest(levelOne,levelTwo,levelThree) {
this[1] = levelOne;
this[2] = levelTwo;
this[3] = levelThree;
}
function level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10) {
this[1] = q1;
this[2] = q2;
this[3] = q3;
this[4] = q4;
this[5] = q5;
this[6] = q6;
this[7] = q7;
this[8] = q8;
this[9] = q9;
this[10] = q10;
}
function question(question,answer) {
this.question = question;
this.answer = answer;
}
parent.Register(self.name,"startTest");
function startTest(newLevel) {
currentLevel=newLevel;
currentQuestion=1;
document.forms[0].answer.value="";
document.forms[0].score.value=0;
displayQuestion();
}
function displayQuestion() {
ask = test[currentLevel][currentQuestion].question;
answer = test[currentLevel][currentQuestion].answer;
toOutput = "" + currentQuestion +
". What is " + ask + "?";
document.forms[0].answer.value = "";
window.open("display.htm","output");
}
parent.Register(self.name,"output");
function output() {
return toOutput;
}
function checkAnswer(form) {
answer = form.answer.value;
if (answer == "" || answer == null)
{
alert("Please enter an
answer.");
return;
}
correctAnswer = test[currentLevel][currentQuestion].answer;
ask = test[currentLevel][currentQuestion].question;
score = form.score.value;
if (eval(answer) == correctAnswer) {
toOutput = "Correct!";
score ++;
form.score.value = score;
} else {
toOutput = "Sorry! "
+ ask + " is " + correctAnswer + ".";
}
window.open("display.htm","output");
if (currentQuestion < 10) {
currentQuestion ++;
setTimeout("displayQuestion()",3000);
} else {
toOutput = "You're Done!<BR>You're
score is " + score + " out of 10.";
setTimeout("window.open('display.htm','output')",3000);
form.answer.value="";
form.score.value="0";
}
}
function welcome() {
toOutput = "Welcome!";
window.open("display.htm","output");
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#0000FF"
onLoad="welcome();">
<FORM METHOD=POST>
<CENTER>
<STRONG>Type You're Answer Here:</STRONG><BR>
<INPUT TYPE=text NAME=answer SIZE=30><P>
<INPUT TYPE=button NAME=done VALUE="Check Answer"
onClick="checkAnswer(this.form);"><P>
Correct Answers So Far:<BR>
<INPUT TYPE=text NAME=score VALUE="0" SIZE=10>
</FORM>
</BODY>
</HTML>
The file level.htm (Listing
8.6) provides users with three buttons to select different levels.
level.htm is the bottom frame
in the parent frameset:
Listing 8.6. Level selection controls.
<!-- SOURCE CODE OF level.htm -->
<HTML>
<BODY BGCOLOR="#000000" TEXT="#FFFFFF">
<CENTER>
<STRONG>
Select a level here:
<FORM METHOD=POST>
<INPUT TYPE=button NAME="one" VALUE="Level One"
onClick="parent.Exec('startTest',1);">
<INPUT TYPE=button NAME="two" VALUE="Level Two"
onClick="parent.Exec('startTest',2);">
<INPUT TYPE=button NAME="three" VALUE="Level
Three"
onClick="parent.Exec('startTest',3);">
</FORM>
</STRONG>
</CENTER>
</BODY>
</HTML>
All display in the frame named output
is done by reloading the file display.htm
(Listing 8.7):
Listing 8.7. display.htm
is reloaded to update the output.
<!-- SOURCE CODE OF display.htm -->
<HTML>
<BODY BGCOLOR="#0000FF" TEXT="#FFFFFF">
<H1>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
document.write(parent.Exec("output"));
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</H1>
</BODY>
</HTML>
Finally, title.htm (Listing
8.8) contains the information displayed in the top frame of the
parent frameset:
Listing 8.8. The title frame.
<!-- SOURCE CODE OF title.htm -->
<HTML>
<BODY BGCOLOR="#000000" TEXT="#00FFFF">
<CENTER>
<H1>
<STRONG>
The Math Test
</STRONG>
</H1>
</CENTER>
</BODY>
</HTML>
The final product would look something like Figure 8.6.
Figure 8.6 : Using nested framesets to produce a multilevel math test.
|
As you can see in the source code listings, the file form.htm (Listing 8.5) is the centerpiece of the entire application. It is in this file that all the work of checking answers, displaying questions and results, and resetting the test is done.
|
Let's look at the document section by section.
var currentLevel=1;
var currentQuestion=1;
var toOutput = "";
These are the key global variables in the script which are used
to keep track of the current level being tested, the current question
being tested, and what should next be displayed in the output
frame.
Next the questions and answers for the three levels are defined:
// DEFINE LEVEL ONE
q1 = new question("1 + 3",4);
q2 = new question("4 + 5",9);
q3 = new question("5 - 4",1);
q4 = new question("7 + 3",10);
q5 = new question("4 + 4",8);
q6 = new question("3 - 3",0);
q7 = new question("9 - 5",4);
q8 = new question("8 + 1",9);
q9 = new question("5 - 3",2);
q10 = new question("8 - 3",5);
levelOne = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE LEVEL TWO
q1 = new question("15 + 23",38);
q2 = new question("65 - 32",33);
q3 = new question("99 + 45",134);
q4 = new question("34 - 57",-23);
q5 = new question("-34 - 57",-91);
q6 = new question("23 + 77",100);
q7 = new question("64 + 32",96);
q8 = new question("64 - 32",32);
q9 = new question("12 + 34",46);
q10 = new question("77 + 77",154);
levelTwo = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE LEVEL THREE
q1 = new question("10 * 7",70);
q2 = new question("15 / 3",5);
q3 = new question("34 * 3",102);
q4 = new question("33 / 2",16.5);
q5 = new question("100 / 4",25);
q6 = new question("99 / 6",16.5);
q7 = new question("32 * 3",96);
q8 = new question("48 / 4",12);
q9 = new question("31 * 0",0);
q10 = new question("45 / 1",45);
levelThree = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE TEST
test = new newTest(levelOne,levelTwo,levelThree);
function newTest(levelOne,levelTwo,levelThree) {
this[1] = levelOne;
this[2] = levelTwo;
this[3] = levelThree;
}
function level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10) {
this[1] = q1;
this[2] = q2;
this[3] = q3;
this[4] = q4;
this[5] = q5;
this[6] = q6;
this[7] = q7;
this[8] = q8;
this[9] = q9;
this[10] = q10;
}
function question(question,answer) {
this.question = question;
this.answer = answer;
}
The test consists of three levels with 10 questions each. You
store all this information in a series of objects. The question
object has two properties: question
and answer. The level
object consists of 10 questions as properties. The test
object has three properties-each level of the test.
Notice that you only name the properties in the question
object. This is because you will want to access the level and
particular question using numeric indexes rather than names. For
instance, you could refer to level one as test[1]
and question three of level one as test[1][3]
(notice the use of two indexes next to each other) and the answer
to question 3 of level one as test[1][3].answer.
|
The structure described here is known as a nested object construct. In this example, test is an object. It has a set of properties (all objects in this case) which can be referred to by their numerical index. So, test[1] is a
property of test and an object in its own right. Because test[1] is an object, it can also have properties, in this case, referred to by numerical index. So, test[1][3] is a property of test[1] and, again, this property
is itself an object. Once again, as an object, test[1][3] can have properties-in this case, answer, referenced by name as test[1][3].answer.
|
The next function in Listing 8.5 is the startTest()
function:
parent.Register(self.name,"startTest");
function startTest(newLevel) {
currentLevel=newLevel;
currentQuestion=1;
document.forms[0].answer.value="";
document.forms[0].score.value=0;
displayQuestion();
}
The startTest() function
is one of the functions you register with the parent.Register()
function from the hIdaho Frameset. You do this because you want
to be able to call the function from the level
frame.
The function accepts a single argument-the level of the new test-
and sets currentLevel and
currentQuestion appropriately
as well as clearing the fields of the form. Then the function
calls displayQuestion() to
start the test.
function displayQuestion() {
ask = test[currentLevel][currentQuestion].question;
answer = test[currentLevel][currentQuestion].answer;
toOutput = "" + currentQuestion +
". What is " + ask + "?";
document.forms[0].answer.value = "";
window.open("display.htm","output");
}
This function is used to display each successive question. It
takes no arguments but gets its information from the global variables
currentLevel and currentQuestion.
In this way, it can get the current text of the question by using
test[currentLevel][currentQuestion].question.
The function then stores the complete output in toOutput
and uses the method window.open()
to open display.htm in the
frame named output. As you
will learn later in the section on the window
object, open() can be used
to open files in named frames and windows.
parent.Register(self.name,"output");
function output() {
return toOutput;
}
Like the startTest() function,
you register the output()
function with parent.Register().
The function simply returns the value of toOutput
and is used to update the display in the output frame, as you
will see when you look at the source code of the file display.htm.
function checkAnswer(form) {
answer = form.answer.value;
if (answer == "" || answer == null)
{
alert("Please enter an
answer.");
return;
}
correctAnswer = test[currentLevel][currentQuestion].answer;
ask = test[currentLevel][currentQuestion].question;
score = form.score.value;
if (eval(answer) == correctAnswer) {
toOutput = "Correct!";
score ++;
form.score.value = score;
} else {
toOutput = "Sorry! "
+ ask + " is " + correctAnswer + ".";
}
window.open("display.htm","output");
if (currentQuestion < 10) {
currentQuestion ++;
setTimeout("displayQuestion()",3000);
} else {
toOutput = "You're Done!<BR>You're
score is " + score + " out of 10.";
setTimeout("window.open('display.htm','output')",3000);
form.answer.value="";
form.score.value="0";
}
}
checkAnswer() is where the
bulk of the work is done in the script.
Because checkAnswer() is
called from the form, it takes a single argument for the form
object. The function compares the student's answer stored in the
field form.answer with the
correct answer taken from the test
object.
If the student answered correctly, an appropriate message is stored
in toOutput, and the score
is incremented and displayed. If the answer is wrong, an appropriate
message is stored in toOutput,
but the score is left untouched. Once the answer is checked, the
message is displayed using window.open()
to open display.htm in the
output frame.
The function then uses the condition currentQuestion
< 10 to check if the question just answered is
the last question in the test. If it is not the last question,
then the currentQuestion
variable is increased by one to go to the next question and setTimeout()
is used to wait three seconds (3000 milliseconds) before displaying
the new question with displayQuestion().
Otherwise, the function stores the results of the test in toOutput,
displays them with a similar three-second delay using setTimeout(),
and then clears the answer
and score fields of the form.
function welcome() {
toOutput = "Welcome!";
window.open("display.htm","output");
}
The function welcome() stores
a welcome message in toOutput
and then displays it by loading display.htm
into the output frame.
<BODY BGCOLOR="#FFFFFF"
TEXT="#0000FF" onLoad="welcome();">
After the document finishes loading, the welcome()
function is called using the onLoad
event handler.
Note |
In the preceding segment of the BODY tag, you will notice the use of RGB triplets to define color for the background and text in the document. The RGB triplets (such as FFFFFF and 00FFFF) define colors as combinations of red,
blue, and green. The six hexadecimal digits consist of three pairs of the form: RRGGBB. The use of colors in documents is discussed in more detail later in this chapter in the section about the document object.
|
<FORM METHOD=POST>
<CENTER>
<STRONG>Type You're Answer Here:</STRONG><BR>
<INPUT TYPE=text NAME=answer SIZE=30><P>
<INPUT TYPE=button NAME=done VALUE="Check Answer"
onClick="checkAnswer(this.form);"><P>
Correct Answers So Far:<BR>
<INPUT TYPE=text NAME=score VALUE="0" SIZE=10
onFocus="this.blur();">
</FORM>
This form is where most user interaction takes place-with the
exception of the level frame.
It has a text entry field named answer
where users type their answers to each question, a button they
click on to check their answers, and a text field to display the
current score.
Only two event handlers are used: onClick="checkAnswer(this.form);"
to check the users' answer and onFocus="this.blur;"
to ensure users don't try to cheat by changing their own scores.
The file level.htm (Listing
8.6) contains a simple three button form to start a new test at
any of the three levels:
<FORM METHOD=POST>
<INPUT TYPE=button NAME="one" VALUE="Level One"
onClick="parent.Exec('startTest',1);">
<INPUT TYPE=button NAME="two" VALUE="Level Two"
onClick="parent.Exec('startTest',2);">
<INPUT TYPE=button NAME="three" VALUE="Level
Three"
onClick="parent.Exec('startTest',3);">
</FORM>
Each button has a similar onClick
event handler which uses parent.Exec()
to call the startTest() function
from the form frame and pass
it a single integer argument for the level.
The only other file involving JavaScript scripting is display.htm
which sets up the body of the document and then uses document.write()
to display the result returned by output()
from the form frame. The
function is called using parent.Exec().
In this way, every time the main script in form.htm
reloads display.htm, the
current value of toOutput
is returned by output() and
displayed as the body text for display.htm.
Note |
You could have written the output directly to the output frame using document.write(). You will see an example of this later in the chapter.
|
In any given window or frame, one of the primary objects is the
document object. The document
object provides the properties and methods to work with numerous
aspects of the current document, including information about anchors,
forms, links, the title, the current location and URL, and the
current colors.
You already have been introduced to some of the features of the
document object in the form
of the document.write() and
document.writeln() methods,
as well as the form object
and all of its properties and methods.
The document object is defined
when the BODY tag is evaluated
in an HTML page and the object remains in existence as long as
the page is loaded. Because many of the properties of the document
object are reflections of attributes of the BODY
tag, you should have a complete grasp of all the attributes available
in the BODY tag in Navigator
2.0.
The BODY tag defines the
main body of an HTML document. Its attributes enable the HTML
author to define colors for text and links, as well as background
colors or patterns for the document.
In addition, as you have already learned, there are two event
handlers, onLoad and onUnload,
that can be used in the BODY
tag.
The following is a list of available attributes for the BODY
tag:
- BACKGROUND-Specifies
the URL of a background image.
- BGCOLOR-Specifies a background
color for the document as a hexadecimal RGB triplet or a color
name. Color names available in Navigator 2.0 are listed at the
end of the hapter.
- FGCOLOR-Specifies the
foreground (and text) color as a hexadecimal triplet.
- LINK-Specifies the color
for links as a hexadecimal triplet.
- ALINK-Specifies the color
for an active link (when the user has the mouse clicked on a link
until the user releases the mouse button) as a hexadecimal triplet.
- VLINK-Specifies the color
for a followed link as a hexadecimal triplet.
Table 8.3 outlines the properties of the document
object.
Table 8.3. Properties of the document
object.
Property | Description
|
alinkColor
| The RGB value for the color of activated links expressed as a hexadecimal triplet.
|
anchors
| Array of objects corresponding to each named anchor in a document.
|
applets
| Array of objects corresponding to each Java applet included in a document. The applets array will be discussed in detail in Chapter 14.
|
bgColor
| The RGB value of the background color as a hexadecimal triplet.
|
cookie
| Contains the value of the cookies for the current document. Cookies are discussed in depth in Chapter 9.
|
embeds
| Array of objects reflecting each plug-in in a document. The embeds array will be discussed in detail in Chapter 14.
|
fgColor
| The RGB value of the foreground color as a hexadecimal triplet.
|
forms |
Array of objects corresponding to each form in a document.
|
images
| Array of objects corresponding to each in-line image included in a document.
|
lastModified
| A string containing the last date the document was modified.
|
linkColor
| The RGB value of links as a hexadecimal triplet.
|
links |
An array of objects corresponding to each link in a document. Links can be hypertext links or clickable areas of an imagemap.
|
location
| The full URL of the document. This is the same as the URL property. URL should be used instead of location.
|
referrer
| Contains the URL of the document that called the current document.
|
title |
A string containing the title of the document. |
URL |
The full URL of the document. |
vlinkColor
| The RGB value of followed links as a hexadecimal triplet.
|
Some of these properties are obvious. For instance, in a document
containing the tag
<BODY BGCOLOR="#FFFFFF"
FGCOLOR="#000000"
LINK="#0000FF">
document.bgColor would have
a value of "#FFFFFF",
document.fgColor would equal
"#000000", and
document.linkColor would
be "#0000FF".
In addition, you have already learned to use the forms
array.
However, the anchors, images, and links arrays, along with the
location object deserve a closer look.
The anchors Array
While the <A> tag in
HTML is usually used to define hypertext links to other documents,
it can also be used to define named anchors in a document so that
links within a document can jump to other places in the document.
For instance, in the HTML page
<HTML>
<HEAD>
<TITLE>Anchors Example</TITLE>
</HEAD>
<BODY>
<A NAME="one">Anchor one is here
HTML Code
<A NAME="two">Anchor two is here
More HTML Code
<A HREF="#one">Go back to Anchor One</A><BR>
<A HREF="#two">GO back to Anchor Two</A>
</BODY>
</HTML>
two anchors are defined using <A NAME="anchorName">
(<A NAME="one">
and <A NAME="two">),
and links to those anchors are created using <A
HREF="#anchorName"> (<A
HREF="#one"> and <A
HREF="#two">).
JavaScript provides the anchors
array as a means to access information and methods related to
anchors in the current document. Each element in the array is
an anchor object, and like
all arrays, the array has a length
property.
The order of anchors in the array follows the order of appearance
of the anchors in the HTML document.
Therefore, in the example, document.anchors.length
would have a value of 1 (since
the anchors array, like the
forms array and frames
array starts with a zero index) and document.anchors[0]
would refer to the anchor named one.
The images Array
The images array is implemented
in Navigator 3.0 only. It was unavailable in Navigator 2.0.
Any image included in an HTML with the IMG
tag is accessible through the images
array. Like the anchors array,
the images array has a length
property.
Each entry in the array is an Image
object, which has nine properties that reflect information about
the image and information in the IMG
tag:
- src: A string reflecting
the SRC attribute of the
IMG tag.
- lowsrc: A string reflecting
the LOWSRC attribute of the
IMG tag. The LOWSRC
attribute specifies a low-resolution version of the image file
included in the SRC tag.
- name: A string reflecting
the NAME attribute of the
IMG tag.
- height: An integer reflecting
the height of the image in pixels. This reflects the value of
the HEIGHT attribute of the
IMG tag.
- width: An integer reflecting
the width of the image in pixels. This reflects the value of the
WIDTH attribute in the IMG
tag.
- border: An integer indicating
the width of the image border in pixels. This reflects the value
of the BORDER attribute of
the IMG tag.
- vspace: An integer reflecting
the vertical space, in pixels, around an image. This reflects
the value of the VSPACE attribute
of the IMG tag.
- hspace: An integer reflecting
the horizontal space, in pixels, around an image. This will reflect
the value of the HSPACE attribute
of the IMG tag.
- complete: A Boolean value
indicating whether Navigator has finished downloading the image.
Most of these properties are read-only-that is, their values cannot
be set in a script. src and
lowsrc, however, can be dynamically
changed. The result of changing these values will update the display
with the new image specified.
Note |
When a new image is specified by changing the value of src or lowsrc it is scaled to fit into the space used by the original image.
|
Images also have three event handlers associated with them:
- onLoad: Indicates JavaScript
code to execute when an image finishes loading.
- onError: Indicates JavaScript
code to execute when there is an error loading the image.
- onAbort: Indicates JavaScript
code to execute when the user aborts loading an image (for instance,
when the user clicks on the Stop button).
In addition to instances of the Image
object in the images array,
it is possible to create additional Image
objects using the Image()
constructor.
This causes an image to be loaded across the network, but the
image will not be displayed until the object is assigned to one
of the Image objects in the
images array. To create an
Image object using the constructor
function, use the following syntax:
newImageName
= new Image();
newImageName.src = "filename";
Then, the image could be displayed in place of an image already
rendered to the window by using
document.images[index].src = myImage.src;
The links Array
Just as the anchors array
provides a sequential list of all the anchors in a document, the
links array offers an entry
for each hypertext link defined by <A
HREF="URL"> in an HTML document or as
a clickable area in an imagemap using the AREA
tag.
Also like the anchors array,
each element in the links
array is a link object or
an Area object, and the array
has a length property.
The link object has a several
properties and event handlers, as defined in the following list:
- hash: A string reflecting
the anchor portion of the link URL (the portion after the "#"
symbol)
- host: A string reflecting
the host and domain name of the link URL
- hostname: A string reflecting
the host, domain name, and port number of the link URL (includes
the colon)
- href: A string reflecting
the entire link URL
- pathname: A string reflecting
the path portion of the link URL
- port: A string reflecting
the port number from the link URL
- protocol: A string reflecting
the protocol from the link URL, including the trailing colon
- search: A string reflecting
the query portion of the link URL (the portion after the "?"
symbol)
- target: A string reflecting
the TARGET attribute
- onClick: Specifies code
to execute when the user clicks on the link-use return
false in the event handler's
code to cancel the click event
- onMouseOut: Specifies
code to execute when the user moves the mouse pointer off the
link
- onMouseOver: Specifies
code to execute when the user moves the mouse pointer onto the
link
The Area object offers the
same properties and event handlers with the exception of onClick.
In addition to the write()
and writeln() methods which
you have been using throughout the book, the document
object provides three other methods: open(),
close(), and clear().
The open() method is used
to open the document window for writing a MIME
type. It takes a single argument: the MIME
type (such as text/html).
You can also use the window.open()
method to open a window or frame for writing a document, as you
will see in the section on the window
object.
|
MIME stands for Multi-purpose Internet Mail Extensions. MIME provides a way to exchange files in any format between computers using Internet mail standards. MIME supports pre-defined file types and allows the creation of custom types. MIME types are
specified using two-part codes, such as text/html for HTML files and image/gif for GIF bitmap graphics.
|
Further more, close() closes
the document window for writing, and the clear()
method clears the current document window. Document output is
not actually rendered (displayed) until document.close()
is called.
For this example, you are going to build a simple utility to demonstrate
what different combinations of text, link, and background colors
look like.
The application will use two frames: the top frame contains five
text entry fields for the background color, text color, link color,
active link color, and followed link color, plus a button to enable
users to test their color combinations.
When users press the button, the script loads a simple document,
using the specified colors, into the lower frame. The users should
be able to specify colors by hexadecimal triplets or by name.
To do this, you don't need to use the hIdaho Frameset you used
in Listings 8.3 through 8.8 because you won't be using nested
framesets or making cross-frame function calls.
The parent frameset is defined in Listings 8.9 and 8.10.
Listing 8.9. The parent frameset for the color tester.
<HTML>
<HEAD>
<TITLE>Example 8.9</TITLE>
</HEAD>
<FRAMESET ROWS="45%,*">
<FRAME SRC="pick.htm">
<FRAME SRC="blank.htm" NAME="output">
</FRAMESET>
</HTML>
The source code for the file pick.htm,
where all the processing occurs, is in Listing 8.10.
Listing 8.10. The pick.htm
file.
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FORM OTHER BROWSERS
function display(form) {
doc = open("","output");
doc.document.write ('<BODY BGCOLOR="'
+ form.bg.value);
doc.document.write ('" TEXT="' + form.fg.value);
doc.document.write ('" LINK="' + form.link.value);
doc.document.write ('" ALINK="' +
form.alink.value);
doc.document.write ('" VLINK="' +
form.vlink.value);
doc.document.writeln ('">');
doc.document.write("<H1>This is a
test</H1>");
doc.document.write("You have selected these
colors.<BR>");
doc.document.write('<A HREF="#">
This
is a test link</A>');
doc.document.write("</BODY>");
doc.document.close();
}
// STOP HIDING SCRIPT -->
</SCRIPT>
</HEAD>
<BODY>
<CENTER>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
document.write('<H1>The Colour Picker</H1>');
document.write('<FORM METHOD=POST>');
document.write('Enter Colors:<BR>');
document.write('Background: <INPUT TYPE=text NAME="bg"
VALUE="'
+ document.bgColor + '"> ... ');
document.write('Text: <INPUT TYPE=text NAME="fg"
>VALUE="'
+ document.fgColor + '"><BR>');
document.write('Link: <INPUT TYPE=text NAME="link"
VALUE
="' + document.linkColor + '"> ...');
document.write('Active Link: <INPUT TYPE=text NAME="alink"
VALUE="'
+ document.alinkColor + '"><BR>');
document.write('Followed Link: <INPUT TYPE="text"
NAME="vlink"
VALUE
="' + document.vlinkColor + '"><BR>');
document.write('<INPUT TYPE=button VALUE="TEST"
onClick="display(this.form);">');
document.write('</FORM>');
display(document.forms[0]);
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</CENTER>
</BODY>
</HTML>
The program produces results like those in Figure 8.7.
Figure 8.7 : With JavaScript, you can dynamically test color combinations.
|
As you can see in this example, all the work is being done in the top frame, which contains the document pick.htm.
|
The file has two main components: a JavaScript function and the
body of the document, which is almost entirely generated by another
JavaScript script using the document.write()
and document.writeln() methods.
The interface consists of a single form containing five fields
for each of the color values, plus a button that calls the function
display().
function display(form) {
doc = open("","output");
doc.document.write ('<BODY BGCOLOR="'
+ form.bg.value);
doc.document.write ('" TEXT="' + form.fg.value);
doc.document.write ('" LINK="' + form.link.value);
doc.document.write ('" ALINK="' +
form.alink.value);
doc.document.write ('" VLINK="' +
form.vlink.value);
doc.document.writeln ('">');
doc.document.write("<H1>This is a
test</H1>");
doc.document.write("You have selected these
colors.<BR>");
doc.document.write('<A HREF="#">
This
is a test link</A>');
doc.document.write("</BODY>");
doc.document.close();
}
The function is fairly simple: An empty document is opened in
the output frame using the
window.open() method, and
the name doc is assigned
for JavaScript to refer to that window (frame).
The commands doc.document.write()
and doc.document.writeln()
can then be used to write HTML to the newly opened window. The
values of the five form fields are then used to build a custom
BODY tag that defines all
the colors for the document. After the text has been output, the
method doc.document.close()
is used to close the open document and finish displaying it in
the frame.
With this single function, you can build a simple form in the
body of the document. The form is built by a JavaScript script
that assigns initial values to the five fields using properties
of the document object to
set the values to the current browser defaults. Then the script
calls display() so that an
initial sample is displayed in the lower frame.
As with many programs, there is more than one way to achieve a
desired effect. For instance, the display()
function could be rewritten to change the colors dynamically-without
rewriting the content of the frame-by using the color properties
of the document object:
function display(form) {
parent.output.document.bgColor = form.bg.value;
parent.output.document.fgColor = form.fg.value;
parent.output.document.linkClor = form.link.value;
parent.output.document.alinkColor =
form.alink.value;
parent.output.document.vlinkColor =
form.vlink.value;
}
Then you simply can remove the call to display()
in the body of the HTML document, make the content of the output
frame a separate HTML document, and load the sample document into
the lower frame in the parent frameset.
As you learned in Chapter 1, "Where
Does JavaScript Fit In?" when you were first introduced to
the Navigator Object Hierarchy, the window
object is the parent object of each loaded document.
Because the window object is the parent object for loaded documents,
you usually do not explicitly refer to the window
object when referring to its properties or invoking its methods.
For this reason, window.alert()
can be called by using alert().
Table 8.4 outlines the properties and methods of the window
object. You have seen many of these, including the frames
array and the parent object,
as well as the alert(), confirm(),
open(), prompt(),
and setTimeout() methods.
Table 8.4. Properties and methods of the window
object.
Name | Description
|
frames
| Array of objects containing an entry for each child frame in a frameset document.
|
document
| The document object for the document currently loaded in the window.
|
location
| An object reflecting the current URL loaded in the window.
|
opener
| Refers to the window containing the document that opened the current document. This only has a value if the current window was opened or created with the open() method.
|
parent
| The FRAMESET in a FRAMESET-FRAME relationship.
|
self |
The current window-use this to distinguish between windows and forms of the same name.
|
top |
The top-most parent window. |
status
| The value of the text displayed in the window's status bar. This can be used to display status messages to the user.
|
defaultStatus
| The default value displayed in the status bar.
|
alert()
| Displays a message in a dialog box with an OK button.
|
blur()
| Removes focus from a window. In most versions of Navigator, this sends the window to the background.
|
confirm()
| Displays a message in a dialog box with OK and Cancel buttons. This returns true when the user clicks on OK, false otherwise.
|
close()
| Closes the current window. |
focus()
| Gives input focus to a window. In most versions of Navigator, this brings the window to the front.
|
open()
| Opens a new window with a specified document or opens the document in the specified named window.
|
prompt()
| Displays a message in a dialog box along with a text entry field.
|
scroll()
| Scrolls the window to a coordinate specified by an x,y coordinate passed as arguments to the method.
|
setTimeout()
| Sets a timer for a specified number of milliseconds and then evaluates an expression when the timer has finished counting. Program operation continues while the timer is counting down.
|
clearTimeout()
| Cancels a previously set timeout. |
The location object provides
several properties and methods for working with the location of
the current object.
Table 8.5 outlines these properties and methods.
Table 8.5. Properties and methods of the location
object.
Name | Description
|
hash |
The anchor name (the text following a # symbol in an HREF attribute)
|
host |
The hostname and port of the URL |
hostname
| The hostname of the URL |
href |
The entire URL as a string |
pathname
| The file path (the portion of the URL following the third slash)
|
port |
The port number of the URL (if there is no port number, then the empty string)
|
protocol
| The protocol part of the URL (such as http:, gopher: or ftp:-including the colon)
|
reload()
| Reloads the current URL |
replace()
| Loads the a new URL over the current entry in the history list
|
search
| The form data or query following the question mark (?) in the URL
|
Using the status bar-the strip at the bottom of the Navigator
window where you are told about the current status of document
transfers and connections to remote sites-can be used by JavaScript
programs to display custom messages to the user.
This is primarily done using the onMouseOver
event handler, which is invoked when the user points at a hypertext
link. By setting the value of self.status
to a string, you can assign a value to the status bar (you could
also use window.status or
status here). In the program
<HTML>
<HEAD>
<TITLE>Status Example</TITLE>
</HEAD>
<BODY>
<A HREF="home.html" onMouseOver="self.status='Go
Home!'; return true;">Home</A>
<A HREF="next.html" onMouseOver="self.status='Go
to the next Page!';
return
true;">Next</A>
</BODY>
</HTML>
two different messages are displayed when the user points the
mouse at the links. This can be more informative than the URLs
that Navigator normally displays when a user points at a link.
Note |
Notice that both of the onMouseOver event handlers in the script return a true value after setting the status bar to a new value. This is necessary to display a new value in the status bar using the onMouseOver event handler.
|
By using the open() and close()
methods, you have control over what windows are open and which
documents they contain.
The open() method is the
more complex of the two. It takes two required arguments and an
optional feature list in the following form:
open("URL", "windowName",
"featureList");
Here the featureList
is a comma-separated list containing any of the entries in Table
8.6.
Table 8.6. Windows features used in the open()
method.
Name | Description
|
toolbar
| Creates the standard toolbar |
location
| Creates the location entry field |
directories
| Creates the standard directory buttons |
status
| Creates the status bar |
menubar
| Creates the menu at the top of the window |
scrollbars
| Creates scroll bars when the document grows beyond the current window
|
resizable
| Enables resizing of the window by the user
|
copyhistory
| Indicates whether the history list of the current window should be copied to the new window
|
width |
Specifies the window width in pixels |
height
| Specifies the window height in pixels |
Note |
With the exception of width and height, which take integer values, all of these features can be set to true with a value of yes or 1 or set to false with a value of no or 0.
|
For example, to open a document called new.html
in a new window named newWindow
and to make the window 200 pixels by 200 pixels with all window
features available except resizable,
you could use the command
window.open("new.html","newWindow","toolbar=yes,
location=1,directories=yes,status=yes,menubar=1,
scrollbars=yes,resizable=0,copyhistory=1,width=200,he
ight=200");
which would produce a window like the one in Figure 8.8.
Figure 8.8 : You control the size of new windows, as well as which elements to display.
Note that you can open a window and then write HTML into that
window using document.writeln()
and document.write(). You
saw an example of this in Listings 8.6 through 8.8.
For instance, the function newwindow()
opens a new window and writes several lines of HTML into it.
function newwindow() {
newWindow = open("","New_Window");
newWindow.document.write("<H1>Testing
...</H1>"); newWindow.document.writeln("1...
2...
3...");
newWindow.document.close();
}
Note |
Notice the command newWindow = open("",:New Window"); which opens an instance of the window object and names it newWindow so that you can then use commands such as newWindow.document.write().
|
The close() method is simpler
to use:
window.close();
simply closes the current window.
You already saw an example of using setTimeout()
in Bill Dortch's hIdaho Frameset earlier in this chapter where
he suggests using a setTimeout()
call to make sure a function is registered before trying to call
the function.
The setTimeout() method takes
the form
ID=setTimeout("expression",milliseconds)
where expression is any string
expression, including a call to a function, milliseconds
is the number of milliseconds-expressed as an integer-to wait
before evaluating the expression, and ID
is an identifier that can be used to cancel the setTimeout()
before the expression is evaluated.
clearTimeout() is passed
a single argument: the identifier of the timeout setting to be
canceled.
For instance, if you want to create a page that displays a welcome
message to the user and then automatically goes to a new page
five seconds later if the user hasn't clicked on the appropriate
button, you could write a script like Listing 8.11.
Listing 8.11. Creating an automatic pause.
<HTML>
<HEAD>
<TITLE>Timeout Example</TITLE>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
function go() {
open("new.html","newWindow");
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HEAD>
<BODY onLoad="timeout = setTimeout('go()',5000);">
<IMG SRC="welcome.gif">
<H1>Click on the button or wait five seconds to continue
...</H1>
<FORM METHOD=POST>
<INPUT TYPE=button VALUE="Continue ..." onClick="clearTimeout(timeout);
go();">
</FORM>
</BODY>
</HTML>
In this example, you produce a simple function to implement status
bar help in any HTML document. The function can be called from
any event handler and will display a message in the status bar.
function help(message) {
self.status = message;
return true;
}
With this function, you can then implement full on-line pointers
and help systems. For instance, if you use this in the math test
from Listings 8.3 through 8.8, you can add help messages with
only slight modifications to both form.htm
and level.htm. See Listing
8.12 for the new version.
Listing 8.12. Updating the math test program.
<!-- SOURCE CODE OF form.htm -->
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
var currentLevel=1;
var currentQuestion=1;
var toOutput = "";
// DEFINE LEVEL ONE
q1 = new question("1 + 3",4);
q2 = new question("4 + 5",9);
q3 = new question("5 - 4",1);
q4 = new question("7 + 3",10);
q5 = new question("4 + 4",8);
q6 = new question("3 - 3",0);
q7 = new question("9 - 5",4);
q8 = new question("8 + 1",9);
q9 = new question("5 - 3",2);
q10 = new question("8 - 3",5);
levelOne = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE LEVEL TWO
q1 = new question("15 + 23",38);
q2 = new question("65 - 32",33);
q3 = new question("99 + 45",134);
q4 = new question("34 - 57",-23);
q5 = new question("-34 - 57",-91);
q6 = new question("23 + 77",100);
q7 = new question("64 + 32",96);
q8 = new question("64 - 32",32);
q9 = new question("12 + 34",46);
q10 = new question("77 + 77",154);
levelTwo = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE LEVEL THREE
q1 = new question("10 * 7",70);
q2 = new question("15 / 3",5);
q3 = new question("34 * 3",102);
q4 = new question("33 / 2",16.5);
q5 = new question("100 / 4",25);
q6 = new question("99 / 6",16.5);
q7 = new question("32 * 3",96);
q8 = new question("48 / 4",12);
q9 = new question("31 * 0",0);
q10 = new question("45 / 1",45);
levelThree = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE TEST
test = new newTest(levelOne,levelTwo,levelThree);
function newTest(levelOne,levelTwo,levelThree) {
this[1] = levelOne;
this[2] = levelTwo;
this[3] = levelThree;
}
function level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10) {
this[1] = q1;
this[2] = q2;
this[3] = q3;
this[4] = q4;
this[5] = q5;
this[6] = q6;
this[7] = q7;
this[8] = q8;
this[9] = q9;
this[10] = q10;
}
function question(question,answer) {
this.question = question;
this.answer = answer;
}
parent.Register(self.name,"startTest");
function startTest(newLevel) {
currentLevel=newLevel;
currentQuestion=1;
document.forms[0].answer.value="";
document.forms[0].score.value=0;
displayQuestion();
}
function displayQuestion() {
ask = test[currentLevel][currentQuestion].question;
answer = test[currentLevel][currentQuestion].answer;
toOutput = "" + currentQuestion +
". What is " + ask + "?";
document.forms[0].answer.value = "";
window.open("display.htm","output");
}
parent.Register(self.name,"output");
function output() {
return toOutput;
}
function checkAnswer(form) {
answer = form.answer.value;
correctAnswer = test[currentLevel][currentQuestion].answer;
ask = test[currentLevel][currentQuestion].question;
score = form.score.value;
if (eval(answer) == correctAnswer) {
toOutput = "Correct!";
score ++;
form.score.value = score;
} else {
toOutput = "Sorry! "
+ ask + " is " + correctAnswer + ".";
}
window.open("display.htm","output");
if (currentQuestion < 10) {
currentQuestion ++;
setTimeout("displayQuestion()",3000);
} else {
toOutput = "You're Done!<BR>You're
score is " + score + " out of 10.";
setTimeout("window.open('display.htm','output')",3000);
form.answer.value="";
form.score.value="0";
}
}
function welcome() {
toOutput = "Welcome!";
window.open("display.htm","output");
}
parent.Register(self.name,"help");
function help(message) {
self.status = message;
return true;
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#0000FF"
onLoad="welcome();">
<FORM METHOD=POST>
<CENTER>
<STRONG>Type You're Answer Here:</STRONG><BR>
<INPUT TYPE=text NAME=answer SIZE=30
onFocus="help('Enter
your answer here.');"><P>
<A HREF="#" onClick="checkAnswer(document.forms[0]);"
onMouseOver="return
help('Click here to check your answer.');">
Check
Answer</A><P>
<INPUT TYPE=text NAME=score VALUE="0" SIZE=10 onFocus="this.blur();">
</FORM>
</BODY>
</HTML>
Similarly, level.htm requires
changes:
Listing 8.13. Updating the level.htm
file.
<!-- SOURCE CODE OF level.htm -->
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
function help(message) {
self.status = message;
return true;
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#000000" TEXT="#FFFFFF"
LINK="#FFFFFF"
ALINK="#FFFFFF"
VLINK="#FFFFFF">
<CENTER>
<STRONG>
Select a level here:<BR>
<A HREF="#" onClick="parent.Exec('startTest',1);"
onMouseOver="return
parent.Exec('help','Start
test at level one.');">LEVEL ONE</A>
<A HREF="#" onClick="parent.Exec('startTest',2);"
onMouseOver="return
parent.Exec('help',
'Start
test at level two.');">LEVEL TWO</A>
<A HREF="#" onClick="parent.Exec('startTest',3);"
onMouseOver="return
parent.Exec('help',
'Start
test at level three.');">LEVEL THREE</A>
</STRONG>
</CENTER>
</BODY>
</HTML>
These changes produce results like those in Figure 8.9.
Figure 8.9 : Using onMouseOver and the status property to display help messages in the navigator window's status bar.
|
In order to implement the interactive help in the math test, you have to make only minor changes to both HTML files.
|
In form.htm, you have added
the help() function to the
header and made two changes in the body of the document. You have
added an onFocus event handler
to the answer field. The event handler calls help()
to display a help message.
You have also changed the button to a hypertext link so that you
can use the onMouseOver event
handler to display another help message. There are several points
to note in the following line:
<A HREF="#" onClick="checkAnswer(document.forms[0]);"
onMouseOver="return
help('Click here to check your
answer.');">
Check
Answer</A>
First, in the call to checkAnswer(),
you can't pass the argument this.form
because the hypertext link is not a form element. For this reason,
you use document.forms[0]
to explicitly identify the form.
The second point to notice is that you have an empty anchor in
the attribute HREF="#".
When a user clicks on the link, only the onClick
event handler executes, but because there is no URL specified,
the page doesn't change.
Note |
For changes to the status bar to take effect in an onMouseOver event, you need to return a value of true from the event handler. This isn't true in other event handlers, such as onFocus.
|
Tip |
Instead of using the onClick event handler, you can use a special type of URL to call JavaScript functions and methods:
<A HREF="JavaScript:checkAnswer(document.forms[0])">.
|
In level.htm, you make similar
changes. You simply have added the help function to the file's
header and changed the three form buttons to three hypertext links
with appropriate onMouseOver
event handlers.
Note |
Notice when you try these scripts that status messages stay displayed until another message is displayed in the status bar, even when the condition that caused the message to be displayed has ended.
|
The various HTML color attributes and tags (BGCOLOR,
FGCOLOR, VLINKCOLOR,
ALINKCOLOR, LINKCOLOR,
FONT COLOR) and their related
JavaScript methods (bgColor(),
fgColor(), vlinkColor(),
alinkColor(), linkColor(),
fontColor()) can take both
RGB triplets and selected color names as their values.
Table 8.7 is a list of selected Netscape color words and their
corresponding RGB triplets. The complete list of color words can
be found in the JavaScript document at the Netscape Web site (see
appendix A).
Table 8.7. Color words in Navigator 2.0.
Color Name | RGB Triplet
| Color Name | RGB Triplet
|
antiquewhite
| FA EB D7
| ivory
| FF FF F0
|
aqua |
00 FF FF
| Courier">lemonchiffon
| FF FA CD
|
azure |
F0 FF FF
| lightblue
| AD D8 E6
|
beige |
f5 f5 DC
| lightyellow
| FF FF E0
|
black |
00 00 00
| magenta
| FF 00 FF
|
blue |
00 00 FF
| maroon
| 80 00 00
|
brown |
A5 2A 2A
| mediumpurple
| 93 70 DB
|
chartreuse
| 7F FF 00
| mediumturquoise
| 48 D1 cc
|
cornflowerblue
| 64 95 ED
| moccasin
| FF E4 B5
|
crimson
| DC 14 3C
| navy |
00 00 80
|
darkcyan
| 00 8B 8B
| orange
| FF A5 00
|
darkgray
| A9 A9 A9
| papayawhip
| FF EF D5
|
darkgreen
| 00 64 00
| pink |
FF C0 CB
|
darkpink
| FF 14 93
| rosybrown
| BC 8F 8F
|
firebrick
| B2 22 22
| salmon
| FA 80 72
|
floralwhite
| FF FA F0
| silver
| C0 C0 C0
|
fuchsia
| FF 00 FF
| slateblue
| 6A 5A CD
|
gold |
FF D7 00
| tan |
D2 B4 8C
|
greenyellow
| AD FF 2F
| tomato
| FF 63 47
|
hotpink
| FF 69 B4
| yellow
| FF FF 00
|
indigo
| 4B 00 82
| |
|
In this chapter you have covered several significant topics.
You now know how to divide the Navigator window into multiple
independent sections and how to work with functions and values
in different windows using the hIdaho Frameset.
In addition, you have taken a detailed look at the document
object and learned about its properties, which give you information
about colors used in a document, as well as information about
the last modification date of a document, and the location of
a document.
The window object, which
is the parent object of the document object, provides you the
ability to work with various aspects of windows and frames, including
altering the text displayed in the window's status bar, setting
timeouts to pause before evaluating expressions or calling functions,
and opening and closing named windows.
In the Chapter 9, "Remember Where
You've Been with Cookies," you are going to take a look at
Cookies-a feature of Navigator that enables you to store information
about a page and recall it later when the user returns to the
page.
Command/Extension | Type
| Description |
FRAMESET
| HTML tag | Defines a window or frame containing frames
|
ROWS |
HTML attribute | Defines the number of rows in a FRAMESET tag
|
COLS |
HTML attribute | Defines the number of columns in a FRAMESET tag
|
FRAME |
HTML tag | Defines the source document for a frame defined in a FRAMESET container
|
SRC |
HTML attribute | Indicates the URL of a document to load into a frame
|
NORESIZE
| HTML attribute | Specifies that a frame is fixed in size and cannot be resized by the user
|
SCROLLING
| HTML attribute | Indicates whether scroll bars are to be displayed in a frame (takes the value YES, NO or AUTO)
|
MARGINHEIGHT
| HTML attribute | Specifies the vertical offset in pixels from the border of the frame
|
MARGINWIDTH
| HTML attribute | Specifies the horizontal offset in pixels from the border of the frame
|
TARGET
| HTML attribute | Indicates the frame or window for a document to load into-used with the A, FORM, BASE, and AREA tags
|
NOFRAMES
| HTML tag | Indicates HTML code to be displayed in browsers that don't support frames; used in documents containing the FRAMESET container tags
|
frames
| JavaScript property | Array of objects for each frame in a Navigator window
|
parent
| JavaScript property | Indicates the parent frameset document of the currently loaded document
|
BODY |
HTML tag | Defines the main body of an HTML document
|
BACKGROUND
| HTML attribute | Specifies URL of a background image in the BODY tag
|
BGCOLOR
| HTML attribute | Specifies the background color for a document as a hexadecimal triplet or color name
|
FGCOLOR
| HTML attribute | Specifies the foreground color for a document as a hexadecimal triplet or color name
|
LINK |
HTML attribute | Specifies the color of link text
|
ALINK
| HTML attribute | Specifies the color of active link text
|
VLINK
| HTML attribute | Specifies the color of followed link text
|
alinkColor
| JavaScript property | The color value of active links
|
anchors
| JavaScript property | Array of objects corresponding to each named anchor in a document
|
applets
| JavaScript property | Array of objects corresponding to each Java applet in a document
|
bgColor
| JavaScript property | The background color value of a document
|
embeds
| JavaScript property | Array of objects corresponding to each plug-in in a document
|
fgColor
| JavaScript property | The foreground color value of a document
|
forms
| JavaScript property | Array of objects corresponding to each form in a document
|
images
| JavaScript Property | Array of objects corresponding to each image in a document
|
lastModified
| JavaScript property | As a string, the last date the document was modified
|
linkColor
| JavaScript property | The color value of link text
|
links
| JavaScript property | Array of objects corresponding to each link in a document
|
location
| JavaScript property | Object defining the full URL of the document
|
title
| JavaScript property | Title of the document represented as a string
|
vlinkColor
| JavaScript property | The color value of followed links
|
hash |
JavaScript property | An anchor name (location object)
|
host |
JavaScript property | Hostname and port of a URL (location object)
|
href |
JavaScript property | Hostname of a URL (location object)
|
opener
| JavaScript property | Refers to the window containing the script that opened the current window (window object)
|
pathname
| JavaScript property | File path from the URL (location object)
|
port |
JavaScript property | Port number from the URL (location object)
|
protocol
| JavaScript property | Protocol part of the URL (location object)
|
search
| JavaScript property | Form data or query from the URL (location object)
|
URL |
JavaScript property | The full URL of a document
|
blur()
| JavaScript method | Removes input focus from a window (window object)
|
open()
| JavaScript method | Opens a document for a particular MIME type (document object)
|
close()
| JavaScript method | Closes a document for writing (document object)
|
clear()
| JavaScript method | Clears a document window (document object)
|
focus()
| JavaScript method | Gives input focus to a window (window object)
|
reload()
| JavaScript method | Reloads the current URL
|
replace()
| JavaScript method | Loads a new URL in the place of the current document
|
scroll()
| JavaScript method | Scrolls the window to a specified location
|
self |
JavaScript property | Refers to the current window
|
top |
JavaScript property | The top-most parent window
|
status
| JavaScript property | Text displayed in the status bar represented as a string
|
defaultStatus
| JavaScript property | Default text displayed in the status bar
|
close()
| JavaScript method | Closes the window (window object)
|
open()
| JavaScript method | Opens a document in a named window (window object)
|
setTimeout()
| JavaScript method | Pauses for a specified number of milliseconds and then evaluates an expression
|
clearTimeout()
| JavaScript method | Cancels a previously set timeout
|
onMouseOver
| Event handler | Specifies script to execute when the mouse pointer is over a hypertext link
|
location
| JavaScript property | The location of the document currently loaded in a window
|
Q | If I use frames in my documents, will users of any other Web browsers be able to view my documents?
|
A | At the present time, only Netscape Navigator supports the FRAMESET and FRAMES tags. If you make effective use of the NOFRAMES tag, it is possible to design perfectly reasonable non-frame
alternatives for the users of other browsers' software. Of course, if you are using JavaScript, your users must be using Navigator.
|
Q | Is it possible to force changes in the relative size of frames using JavaScript?
|
A | No. There are no mechanisms to force resizing of frames in JavaScript without reloading the document and dynamically changing the value of the ROWS and COLS attributes of the FRAMESET tag using
document.write() or document.writeln().
|
Q | Is it possible to set the status bar when the user points at a form button?
|
A | No. At the present time, the <INPUT TYPE=button> tag does not support the onMouseOver event, which you use in the <A> tag to set the status bar when the mouse points at the link.
|
- Expand the math test example so that it presents the questions
in a random order each time the user runs through the test.
- Design a program that does the following:
Splits the screen into two frames.
In the first, enables the user to indicate a URL.
Loads the URL into the second frame, and once it's loaded, displays
the following information about it in the first frame: all color
attributes, the title of the document, and the last date of modification.
- What happens on the status bar in this script?
<HTML>
<HEAD>
<TITLE>Exercise 8.3</TITLE>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
function help(message) {
self.status = message;
return true;
}
function checkField(field) {
if (field.value == "")
help("Remember to enter
a value in this field");
else
help("");
return true;
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HEAD>
<BODY>
<FORM METHOD=POST>
Name: <INPUT TYPE=text NAME="name" onFocus="help('Enter
your name');"
onBlur="checkField(this);">
Email: <INPUT TYPE=text NAME="email"
onFocus="help('Enter
your email address');"
onBlur="checkField(this);">
</FORM>
</BODY>
</HTML>
- Extend Listing 8.9 and Listing 8.10 so that the user can specify
any URL to be displayed using the specified color scheme. You
will want to consider using the alternative method for the display()
function.
- In order to add random order to the tests, you need to add
two things to the program: a function to produce a suitable random
number and a method to keep track of which questions have already
been asked. All the changes are to form.htm
and should look like this:
<!-- SOURCE CODE OF form.htm -->
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
var currentLevel=1;
var currentQuestion=1;
var askedQuestion=0;
var toOutput = "";
// DEFINE LEVEL ONE
q1 = new question("1 + 3",4);
q2 = new question("4 + 5",9);
q3 = new question("5 - 4",1);
q4 = new question("7 + 3",10);
q5 = new question("4 + 4",8);
q6 = new question("3 - 3",0);
q7 = new question("9 - 5",4);
q8 = new question("8 + 1",9);
q9 = new question("5 - 3",2);
q10 = new question("8 - 3",5);
levelOne = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE LEVEL TWO
q1 = new question("15 + 23",38);
q2 = new question("65 - 32",33);
q3 = new question("99 + 45",134);
q4 = new question("34 - 57",-23);
q5 = new question("-34 - 57",-91);
q6 = new question("23 + 77",100);
q7 = new question("64 + 32",96);
q8 = new question("64 - 32",32);
q9 = new question("12 + 34",46);
q10 = new question("77 + 77",154);
levelTwo = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE LEVEL THREE
q1 = new question("10 * 7",70);
q2 = new question("15 / 3",5);
q3 = new question("34 * 3",102);
q4 = new question("33 / 2",16.5);
q5 = new question("100 / 4",25);
q6 = new question("99 / 6",16.5);
q7 = new question("32 * 3",96);
q8 = new question("48 / 4",12);
q9 = new question("31 * 0",0);
q10 = new question("45 / 1",45);
levelThree = new level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10);
// DEFINE TEST
test = new newTest(levelOne,levelTwo,levelThree);
function newTest(levelOne,levelTwo,levelThree) {
this[1] = levelOne;
this[2] = levelTwo;
this[3] = levelThree;
}
function level(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10) {
this[1] = q1;
this[2] = q2;
this[3] = q3;
this[4] = q4;
this[5] = q5;
this[6] = q6;
this[7] = q7;
this[8] = q8;
this[9] = q9;
this[10] = q10;
}
function question(question,answer) {
this.question = question;
this.answer = answer;
}
parent.Register(self.name,"startTest");
function startTest(newLevel) {
currentLevel=newLevel;
currentQuestion=1;
clearArray(asked);
askedQuestion = chooseQuestion();
document.forms[0].answer.value="";
document.forms[0].score.value=0;
displayQuestion();
}
function displayQuestion() {
ask = test[currentLevel][askedQuestion].question;
answer = test[currentLevel][askedQuestion].answer;
toOutput = "" + currentQuestion +
". What is " + ask + "?";
document.forms[0].answer.value = "";
window.open("display.htm","output");
}
parent.Register(self.name,"output");
function output() {
return toOutput;
}
function checkAnswer(form) {
answer = form.answer.value;
correctAnswer = test[currentLevel][askedQuestion].answer;
ask = test[currentLevel][askedQuestion].question;
score = form.score.value;
if (eval(answer) == correctAnswer) {
toOutput = "Correct!";
score ++;
form.score.value = score;
} else {
toOutput = "Sorry! "
+ ask + " is " + correctAnswer + ".";
}
window.open("display.htm","output");
if (currentQuestion < 10) {
currentQuestion ++;
askedQuestion = chooseQuestion();
setTimeout("displayQuestion()",3000);
} else {
toOutput = "You're Done!<BR>You're
score is " +
Âscore
+ " out of 10.";
setTimeout("window.open('display.htm','output')",3000);
form.answer.value="";
form.score.value="0";
}
}
function welcome() {
toOutput = "Welcome!";
window.open("display.htm","output");
}
asked = new createArray(10);
function createArray(num) {
for (var j=1; j<=num; j++)
this[j] = false;
this.length=num;
}
function clearArray(toClear) {
for (var j=1; j<=toClear.length; j++)
toClear[j] = false;
}
function chooseQuestion() {
choice = (getNumber() % 10) + 1;
while (asked[choice]) {
choice = (getNumber() % 10)
+ 1;
}
asked[choice] = true;
return choice;
}
function getNumber() {
var time = new Date();
return time.getSeconds();
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#0000FF"
onLoad="welcome();">
<FORM METHOD=POST>
<CENTER>
<STRONG>Type You're Answer Here:</STRONG><BR>
<INPUT TYPE=text NAME=answer SIZE=30><P>
<INPUT TYPE=button NAME=done VALUE="Check Answer"
onClick="checkAnswer(this.form);"><P>
Correct Answers So Far:<BR>
<INPUT TYPE=text NAME=score VALUE="0" SIZE=10>
</FORM>
</BODY>
</HTML>
You've added four functions to the script, as well as made a few
other subtle changes. First, you've added a simple createArray()
function that enables you to create the asked
array. You use this array to keep track of questions already asked.
Each element is set to false
until that question is asked.
The clearArray() function
takes an array as an argument and simply sets each element to
false.
The chooseQuestion() function
adds the ability to randomly select a question. The function uses
the getNumber() function
(which returns the seconds from the current time) to create a
pseudo-random number. The while
loop keeps selecting numbers until it finds one that has a false
entry in the asked array.
Once an available question has been found, the appropriate entry
in asked is set to true,
and the number of the question is returned.
In addition to these three functions, you have made some other
small changes. You have added a global variable called askedQuestion.
This variable indicates the index of the question you have asked.
currentQuestion becomes the
sequential number of the question to be displayed to the user.
In the startTest() function,
you add the lines
clearArray(asked);
askedQuestion = chooseQuestion();
which clear the asked array
to false and then select
a question.
In displayQuestion(), you
have switched to using askedQuestion
in place of currentQuestion
as an index to the test[currentLevel]
object, and in the function checkAnswer(),
you have made the same change.
In checkAnswer(), you have
also changed the way the function selects the next question:
currentQuestion ++;
askedQuestion = chooseQuestion();
setTimeout("displayQuestion()",3000);
This simply chooses a random question and then calls displayQuestion().
- The following frameset and HTML document produce the desired
results, as shown in Figure 8.10.
Figure 8.10 : Testing colors on a document selected by the user.
<!-- MAIN FRAMESET FILE -->
<HTML>
<HEAD>
<TITLE>Exercise 8.2</TITLE>
</HEAD>
<FRAMESET COLS="25%,*">
<FRAME SRC="info.htm">
<FRAME SRC="blank.htm" NAME="display">
</FRAMESET>
</HTML>
The HTML document would look like this:
<!-- SOURCE CODE FOR info.htm -->
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
function loadSite(form) {
var url = form.url.value;
doc = open(url,"display");
form.title.value = doc.document.title;
form.date.value = doc.document.lastModified;
form.bg.value = doc.document.bgColor;
form.fg.value = doc.document.fgColor;
form.link.value = doc.document.linkColor;
form.alink.value = doc.document.alinkColor;
form.vlink.value = doc.document.vlinkColor;
}
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</HTML>
<BODY>
<FORM METHOD=POST>
URL: <INPUT TYPE=text NAME="url"><P>
<INPUT TYPE=button NAME=load VALUE="Load URL"
onClick="loadSite(this.form);"><P>
Title:
<INPUT TYPE=text NAME="title"
onFocus="this.blur();"><P>
Last Modified: <INPUT TYPE=text NAME="date" onFocus="this.blur();"><P>
Background Color: <INPUT TYPE=text NAME="bg"
onFoucs="this.blur();"><P>
Text Color: <INPUT TYPE=text NAME="fg" onFocus="this.blur();"><P>
Link Color: <INPUT TYPE=text NAME="link" onFocus="this.blur();"><P>
Active Link Color: <INPUT TYPE=text NAME="alink"
onFocus="this.blur();"><P>
Followed Link Color: <INPUT TYPE=text NAME="vlink"
onFocus="this.blur();">
</FORM>
</BODY>
</HTML>
You use one simple function to achieve the desired result: loadSite().
The function loads the specified URL into the display frame
using window.open().
Once this is done, you can use the properties of the document
object to display the desired information into fields in the HTML
form.
In the form in the body of the HTML document, you use the onClick
event handler in the button to call the loadSite()
function. The display fields for the information about the document
all have the event handler onFocus="this.blur();"
to make sure the user can't alter the information.
- This script partially addresses the problem in Listings 8.12
and 8.13-that the help messages remain displayed even after you
remove mouse focus from a link or remove focus from a field. This
particular script displays a help message when the user gives
focus to a field. When focus is removed, either a warning message
is displayed or the status bar is cleared.
- The following script makes the necessary changes to enable
the user to specify a URL to be displayed in the lower frame:
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FORM OTHER BROWSERS
function display(form) {
parent.output.document.bgColor = form.bg.value;
parent.output.document.fgColor = form.fg.value;
parent.output.document.linkClor = form.link.value;
parent.output.document.alinkColor = form.alink.value;
parent.output.document.vlinkColor = form.vlink.value;
}
function loadPage(url) {
var toLoad = url.value;
if (url.value == "")
toLoad = "sample.htm";
open (toLoad,"output");
}
// STOP HIDING SCRIPT -->
</SCRIPT>
</HEAD>
<BODY>
<CENTER>
<SCRIPT LANGUAGE="JavaScript">
<!-- HIDE FROM OTHER BROWSERS
document.write('<H1>The Color Picker</H1>');
document.write('<FORM METHOD=POST>');
document.write('Enter Colors:<BR>');
document.write('Background: <INPUT TYPE=text NAME="bg"
VALUE="'
+ document.bgColor + '"> ... ');
document.write('Text: <INPUT TYPE=text NAME="fg"
VALUE="'
+ document.fgColor + '"><BR>');
document.write('Link: <INPUT TYPE=text NAME="link"
VALUE
="' + document.linkColor + '"> ...');
document.write('Active Link: <INPUT TYPE=text NAME="alink"
VALUE="'
+ document.alinkColor + '"><BR>');
document.write('Followed Link: <INPUT TYPE="text"
NAME="vlink"
VALUE
="' + document.vlinkColor + '"><BR>');
document.write('Test URL: <INPUT TYPE="text" SIZE=40
NAME="url"
VALUE=""
onChange="loadPage(this);"><BR>');
document.write('<INPUT TYPE=button VALUE="TEST"
onClick="display(this.form);">');
document.write('</FORM>');
// STOP HIDING FROM OTHER BROWSERS -->
</SCRIPT>
</CENTER>
</BODY>
</HTML>
You have made two simple changes to the script. In
addition to the display()
function, you have added the loadPage()
function, which loads a URL into the lower frame using window.open().
If the user provides no value for the URL, the standard sample
file is loaded.
In the form, you have added a text field for the URL with an onChange
event handler that calls loadPage().
You also need to change the frameset to load the appropriate sample
page into the lower frame:
<HTML>
<HEAD>
<TITLE>Exercise 8.4</TITLE>
</HEAD>
<FRAMESET ROWS="45%,*">
<FRAME SRC="pick.htm">
<FRAME SRC="sample.htm" NAME="output">
</FRAMESET>
</HTML>
|