Chapter 10
Working with Multiple Pages and
Data
CONTENTS
In this chapter, you'll look at some of the more complex capabilities
of JavaScript. You learned the basics of using frames in Chapter
9, "Using Frames, Cookies, and Other Advanced Features;"
you'll look at some more sophisticated examples here. You'll also
explore techniques for storing data and learn about the data tainting
feature, which enables you to overcome some of Netscape's security
restrictions.
As you learned in Chapter 9 you can use nested framesets to create
a complex framed document. When you combine nested framesets with
JavaScript, you have to be careful about how you refer to fields
in other frames.
For this section, let's return to the Fictional Software home
page created in Chapter 9 turning it into a complex site using
nested framesets. You'll find it looks much more like typical
sites on the Web.
Let's start by creating the frameset document, shown in Listing
10.1.
Listing 10.1. (NESTED.asp) The frameset document for the nested
framesets example.
<HTML>
<FRAMESET ROWS="30%,*,52">
<FRAME NAME="contents" SRC="index.asp">
<FRAME NAME="main" SRC="fscmain2.asp">
<FRAMESET COLS="300,*">
<FRAME NAME="imagemap" SRC="map.asp">
<FRAME NAME="description" SRC="descrip.asp">
</FRAMESET>
</FRAMESET>
</HTML>
This listing uses the following frames:
- The top frame is the navigation bar created in Chapter 8
"Improving a Web Page with JavaScript."
- The next frame covers most of the page, and it is the main
description of the company and links.
- The third frame is on the lower left, and it includes a client-side
image map for navigation.
- The fourth frame is on the lower right. It provides a text
field that will be used to display descriptions as the user moves
over a link.
Listing 10.2 shows the document for the second frame, which is
adapted from the version used in Chapter 9.
Listing 10.2. (FSCMAIN2.asp) The second frame for the nested
frames example.
<HTML>
<HEAD>
<TITLE>Fictional Software Company</TITLE>
</HEAD>
<BODY>
Welcome to our web page! Fictional Software Company
specializes in creating innovative, user-friendly software
applications with descriptions filled with industry
buzzwords.
<P>
We have a wide range of products (3 of them) to meet
the needs of you and your company. Follow the links
below for more information.
<P>
<UL>
<LI><A HREF="spread.asp"
onMouseOver="parent.description.document.form1.text1.value='Information about
the spreadsheet';return true;">
Fictional Spreadsheet 7.0</A>
<LI><A HREF="word.asp"
onMouseOver="parent.description.document.form1.text1.value='Information about
the word processor';return true;">
Fictional Word Processor 6.0</A>
<LI><A HREF="data.asp"
onMouseOver="parent.description.document.form1.text1.value='Information about
the database';return true;">
Fictional Database 7.0</A>
</UL>
<HR>
<I>1998 FSC - designed by the FSC staff</I>
</BODY>
</HTML>
The third frame is an image map, which can be used for navigation.
It uses event handlers to place informative messages in the text
field in the fourth frame. Listing 10.3 shows the image map document.
Listing 10.3. (MAP.asp) The image map document for the nested
frames example.
<HTML>
<BODY>
<MAP NAME="map1">
<AREA SHAPE=RECT COORDS="6,7,61,43" HREF=support.asp
onMouseOver="parent.description.document.form1.text1.value='Support for our
products';return true;">
<AREA SHAPE=RECT COORDS="73,8,129,42" HREF=compinfo.asp
onMouseOver="parent.description.document.form1.text1.value='About our
Company';return true;">
<AREA SHAPE=RECT COORDS="140,7,200,42" HREF=order.asp
onMouseOver="parent.description.document.form1.text1.value='Order
Products';return true;">
<AREA SHAPE=RECT COORDS="211,6,276,43" HREF=customer.asp
onMouseOver="parent.description.document.form1.text1.value='Customer
Service';return true;">
<AREA SHAPE=default HREF=fscmain2.asp>
</MAP>
<IMG SRC="fscmap.gif" USEMAP="#map1">
</BODY>
</HTML>
Finally, the fourth frame contains a simple text field, which
is used in place of the status bar to display descriptions as
the user moves over links. Listing 10.4 shows this document.
Listing 10.4. (DESCRIP.asp) The description text field document
for the nested frames example.
<HTML>
<BODY>
<FORM NAME="form1">
<INPUT TYPE="TEXT" NAME="text1" SIZE="40" VALUE="Look Here for help.">
</BODY>
</HTML>
This is a good example of how complicated frames programming can
get. Both the middle frame and the image map frame address the
text field in the fourth frame to display descriptions. The final
document, as displayed in Netscape, is shown in Figure 10.1.
Figure 10.1 : The nested frames example, as displayed
by Netscape.
One of the most talked-about issues on the Web today is the integration
of databases with Web content. JavaScript isn't a very powerful
language for database applications, mainly because it's limited
to client-side use. However, there are some tricks you can use
to store data in a database-like format in JavaScript. You'll
look at several such techniques in the following sections.
The first technique may seem simple, but it's often overlooked.
Because a JavaScript variable can hold any type of data, it can
be used as a string array. For example, the following code creates
a three-element string array and assigns values to its elements:
strings = new Array(3);
strings[0] = "This is the first element.";
strings[1] = "This is the second element.";
strings[2] = "This is the third element.";
Each element of the array can be used as an ordinary string object.
For example, this statement displays a substring from the third
element of the array defined previously:
document.write(strings[2].substring(5,10));
If you've worked with Perl, a popular language for CGI programs,
you've probably used associative arrays. An associative array
is an array with names instead of indexes for each of its elements.
JavaScript doesn't officially include associative arrays, but
you can easily simulate them using an object. The following statements
define an object and assign values to three elements:
animals = new Object();
animals["frog"] = 2;
animals["bear"] = 3;
animals["chicken"] = 4;
This technique is useful if you are storing a number of named
values, especially if the names are created during the course
of your program.
One thing missing from JavaScript is two- or three-dimensional
arrays. However, you can easily simulate them by creating an array
of arrays. You can expand this technique to store any number of
items-either numbered or named-in an array. This makes it easy
to store just about anything.
Tip |
The solitaire game in Chapter 15, "Real-Life Examples III," uses several of these techniques to store information about the cards in use.
|
In the first versions of JavaScript (Netscape 2.0 and 2.01), properties
of a document were always available to JavaScript code in other
documents. For example, if you loaded a page in one frame, a JavaScript
program in another frame could access properties of the page-its
links, anchors, and form elements.
Although reading properties of a document doesn't sound like much
of a security risk, some clever folks found ways to exploit it.
If there's a security risk of any kind on a Web page, you can
be sure someone out there will find it and take advantage. Here
are a few of the tricks that were possible due to this feature:
- A document could open a file URL, such as file://c:\dos\,
in a frame, which would display a list of files in the frame.
A JavaScript program in another frame could then read the contents
(available in the links array)
and send them to a server.
- A Web page could open an invisible frame with a JavaScript
program and leave it running while you visited other sites; after
watching you for a while, it could send a list of the sites you
visited to its server.
Although minor, these were risks to security and privacy. The
public tends to worry about such things, particularly on the Internet
(and Microsoft was encouraging them to). To deter these rumors
and problems, Netscape quickly released a fixed version, Netscape
2.02.
The fix in Netscape 2.02 was to prevent a document from accessing
properties of another document, unless it came from the same server.
Thus, if your document loaded Netscape's home page in a frame,
it couldn't access the links, anchors, or even the address of
the Netscape page.
Although the fixed version prevented these problems, it also removed
a useful feature. If you could access properties of a document
in another frame, for example, you could create a "link summary"
frame with a quick reference to all the links on the page.
Luckily, Netscape found a solution to make everyone happy, beginning
with Navigator 3.0b5, which introduced data tainting. This
enables you to access properties of a document in another frame,
but not without evidence.
As an analogy, consider the security devices used in a modern
record or video store. The simple solution to prevent theft is
to keep all the items in locked cabinets, but that would prevent
customers from browsing them. Instead, magnetic strips are attached
to each item, and can't be removed. Thus, you can take an item
off the shelf and look it over-but come near the exit, and alarms
go off all over the place.
Data tainting does the same thing for data from other servers.
Data from another server is marked, or tainted. The data is still
useful, but it is marked. No matter what you do with the data-assign
it to variables, use it in calculations, and so on-it remains
tainted.
When a JavaScript program attempts to send data to a server-either
by submitting form data or by using an URL-it is checked for tainting.
If any tainted data is present, the user is alerted and allowed
to cancel the operation.
Note |
To send data to a server using an URL, the application could use the data as a document name or as a parameter. In either case, a CGI script could receive the data on the server.
|
The actual tainting is done by using a special taint code
in storing the value. The taint code is unique for each server.
Thus, you can freely send data to the same server it was originally
taken from, but the user is warned if you attempt to send it to
a different server.
Note |
When data tainting is enabled, you can also access the value of password objects in a form. Because their value is tainted, though, you can't send this information to a server.
|
To use the data tainting feature, you need to enable it using
an environmental variable. The following command can be used to
set the variable to enable tainting:
SET NS_ENABLE_TAINT=1
If you are using Windows 3.1, you can exit to DOS, type this command,
then return to Windows. In Windows 95, the easiest method is to
add the command to your C:\AUTOEXEC.BAT
file, then reboot the computer. For Macintosh systems, you need
to create a resource called NS_ENABLE_TAINT.
You can check whether the user has enabled data tainting with
the navigator.taintEnabled ( )
method.
You can exercise some control over data tainting with two JavaScript
functions to convert between tainted and nontainted values:
- taint adds taint to a
value, using the current program's taint code.
- untaint removes the taint
from a value.
These functions return a tainted or untainted result, but do not
modify the original value. The main use for untaint
is to make values available to other scripts without security
restrictions.
Note |
Although you can add taint to any value with taint(), you can only untaint values that have the current program's taint code. There is no way to remove taint from a value that originates from another window.
|
As an example of a multiserver application that takes advantage
of data tainting, let's create a framed document that displays
a link summary for an existing document-at any URL. The simplest
part of this task is the frameset document, shown in Listing 10.5.
Listing 10.5. (MULTSERV.asp) The frameset document for the
link summary application.
<HTML>
<FRAMESET COLS="20%,80%">
<FRAME name="summary" SRC="linksumm.asp">
<FRAME name="destination" SRC="doc1.asp">
</FRAMESET>
</HTML>
Next, let's create the link summary document for the first frame,
which will use JavaScript to display a summary of the document
in the second frame. This document is shown in Listing 10.6.
Listing 10.6. (LINKSUMM.asp) The main HTML document for the
link summary JavaScript application.
<HTML>
<HEAD>
<TITLE>Link Summary</TITLE>
<SCRIPT LANGUAGE="JavaScript">
function newloc() {
// send other frame to new URL
parent.frames[1].location.href = document.form1.text1.value;
// update link summary
self.location.reload();
}
</SCRIPT>
</HEAD>
<BODY>
</BODY>
<H3>Link Summary</H3>
<HR>
<FORM NAME="form1">
<INPUT TYPE="text" NAME="text1" VALUE="enter new URL">
<INPUT TYPE="button" VALUE="GO" onClick="newloc();">
</FORM>
<SCRIPT LANGUAGE="JavaScript">
// list links in other frame
len = parent.frames[1].document.links.length;
document.write("<B>Total links: " + len + "</B>");
// begin numbered list
document.write("<BR>\n<OL>");
// reproduce each link here
for (i=0; i < len; i++) {
document.write("<LI><A HREF='");
document.write(parent.frames[1].document.links[i].href);
document.write("'>");
document.write(parent.frames[1].document.links[i].pathname);
document.write("</A>\n");
}
document.write("</OL>");
</SCRIPT>
<HR>
</HTML>
This is where the real action happens. The JavaScript functions
in this document create a summary of the links in the second document.
The links are listed in a numbered list, with each linked to its
corresponding document name.
Of course, when you first load the document, there will be no
document in the second frame. You can use the text field and form
in the link summary frame to load a new document, and the link
information will be displayed.
Thanks to data tainting, this should work with any Web document.
It will not currently work with framed documents, because it hasn't
provided for multiple frames. The output of this program, as displayed
by Netscape, is shown in Figure 10.2. In the figure, I've loaded
Netscape's page, and the links on the page are listed.
Figure 10.2 : The output of the link summary, application.
Often, your program will need to maintain state information; you
may display a series of pages and need to remember something between
pages. Quizzes, questionnaires, and games often need to maintain
state. There are two ways to do this:
- Using cookies, which you looked at in Chapter 9
- Keeping a frame open and using its variables and functions
Both of these have their advantages and disadvantages. If your
application also uses CGI, for example, you may find cookies more
useful. The next task is an example of using a frame to maintain
state information.
As a complex example of using frames to keep track of state between
pages, let's create a questionnaire. This program asks several
questions; after you answer each question, it stores the answer
in an array. The array is part of the script in the top frame;
the bottom frame is used to show the questions. The frameset document
for this example is shown in Listing 10.7.
Listing 10.7. (QUIZ.asp) The frameset document for the questionnaire.
<HTML>
<FRAMESET ROWS="15%,*"
onLoad="setTimeout('parent.MainFrame.NextQuestion();',1000);">
<FRAME NAME="MainFrame" SRC="quizmain.asp">
<FRAME NAME="QuizFrame" SRC="doc1.asp">
</FRAMESET>
Listing 10.8 shows the main program for the questionnaire. When
you load the page, the questions are asked one at a time. After
the last question, a summary of your answers is displayed. The
final output of this program is shown in Figure 10.3.
Figure 10.3 : The questionnaire is complete, and the
results are displayed.
Listing 10.8. (QUIZMAIN.asp) The main JavaScript program for
the quiz example.
<HTML>
<HEAD><TITLE>Questionnaire Example</TITLE>
<SCRIPT LANGUAGE="JavaScript">
// global variables
var answers = new Array(5);
var questions = new Array(5);
questions[0] = "What is your name";
questions[1] = "What is your age";
questions[2] = "What is your phone number";
questions[3] = "How many beans make 5";
var current = 0;
var quest;
// function to ask a question in other frame
function NextQuestion() {
if (current > 0) {
ans = parent.QuizFrame.document.form1.question.value;
answers[current-1] = ans;
}
if (current + 1 < questions.length) {
text = questions[current];
parent.QuizFrame.document.open();
parent.QuizFrame.document.write("<HTML><BODY>\n");
parent.QuizFrame.document.write("<h1>" + "Question #" + current + "</
h1>");
parent.QuizFrame.document.write("<hr>");
parent.QuizFrame.document.write("<b>" + text + "?</b><br>");
parent.QuizFrame.document.write("<FORM NAME=\"form1\">\n");
parent.QuizFrame.document.write("<INPUT TYPE=\"text\" NAME=\"question\">
");
parent.QuizFrame.document.write("<BR><INPUT TYPE=\"BUTTON\"
VALUE=\"Submit Answer\" ");
parent.QuizFrame.document.write("onClick=\"parent.MainFrame.NextQuestion();\"
>");
parent.QuizFrame.document.write("</BODY></HTML>");
parent.QuizFrame.document.close();
current++;
}
else {
parent.QuizFrame.document.open();
parent.QuizFrame.document.write("<HTML><BODY>\n");
parent.QuizFrame.document.write("<h1>Your answers:</h1><hr>");
for (i=0; i<(questions.length-1); i++) {
parent.QuizFrame.document.write("<B>" + questions[i] + "</B>: " +
answers[i] + "<BR>");
}
parent.QuizFrame.document.write("</BODY></HTML>");
parent.QuizFrame.document.close();
}
}
</SCRIPT>
</HEAD>
<BODY>
<H1>Questionnaire</H1>
</BODY>
</HTML>
This program uses the questions
array to store the questions and the answers
array to store the user's answers. When the page is loaded, the
NextQuestion() function is
called to display a question.
Each question is displayed in the bottom frame. The document you
create in this frame also has a JavaScript event handler, which
calls the NextQuestion()
function (in the top frame) when the question is entered.
In this chapter, you learned some of the more complex aspects
of JavaScript and how it can work with complicated pages and data:
- How to integrate documents in nested frames and refer between
them
- How to store data with string arrays and associative arrays
- How to use the data tainting feature to access properties
of pages from different servers
- How to use frames to store the current state of a JavaScript
application and keep track of variables between pages
When you master the techniques in this chapter, you've come a
long way toward becoming a JavaScript expert. Continue your studies
with one of the following:
- For simpler examples of using frames in Web pages, turn to
Chapter 9 "Using Frames, Cookies, and Other Advanced Features."
- For examples that illustrate the techniques you learned in
this chapter, see Chapter 11, "Real-Life Examples II."
- To learn techniques for debugging JavaScript applications,
see Chapter 14, "Debugging JavaScript Programs."
- To learn about using Java to further enhance your page, turn
to Chapter 16, "Integrating JavaScript with Java."
Q: | Using data tainting, I can access properties of another document, such as links and anchors. Is there any way to read the HTML source of the document itself?
|
A: | Currently, there is no way to do this. The properties made available in the object hierarchy (explained in Chapter 5 "Accessing Window Elements as
Objects") are all you can access via JavaScript. This is not expected to change.
|
Q: | Can a JavaScript program in one frame read variables (not properties) defined by a JavaScript program in another frame?
|
A: | Yes. Just treat the variable name as a child of the frame's window object. For example, parent.frame1.score refers to the score variable in the frame1
frame.
|
Q: | What happens if the document I load into a frame is a framed document itself? Will this cause an error?
|
A: | This will not cause an error-in fact, it works fine. The frames of the document are created within the frame in which it is loaded. Functionally, this is treated the same as a nested
frameset.
|
|