Copyright ©1996, Que Corporation. All rights
reserved. No part of this book may be used or reproduced in any
form or by any means, or stored in a database or retrieval system
without prior written permission of the publisher except in the
case of brief quotations embodied in critical articles and reviews.
Making copies of any part of this book for any purpose other than
your own personal use is a violation of
United States copyright
laws. For information, address
Que Corporation, 201 West 103rd
Street, Indianapolis, IN 46290 or at support@mcp.com.
Notice: This material is excerpted from Special Edition Using JavaScript, ISBN: 0-7897-0758-6. This material is provided "as is" without any warranty of any kind. Please see this DISCLAIMER.
by Mark Reynolds
This chapter describes the creation of dynamic documents and windows as well as the interaction between windows and their components. Before you begin this chapter, you might want to reread the section on "Browser and HTML Objects" in chapter 4. This chapter presumes that you now have a working knowledge of Javascript syntax; the material covered is somewhat more complex than in earlier chapters.
The first theme of the chapter will be the creation of popup windows.
The entire content of those windows will be defined by a creation
function, rather than a URL. We will examine various examples,
including popups with text, popups with buttons, and editable
popups.
We will next examine the history, status and location objects. We will see how we can create a hurl the user to a specific URL on the history list, how to examine the various part of the location object, and how to store and retrieve information using the search property of the location object. Finally, you will learn how to create dynamic documents. In fact, we will create a page entirely from JavaScript. We will also learn how to rewrite pages on the fly.
At the end of this chapter you will be able to:
We have already learned a lot about objects in JavaScript. In
fact, this previous four chapters have been devoted to exploring
the various JavaScript objects and their uses. You have already
been exposed to the various built-in objects and HTML objects
which JavaScript provides. In order to go
furtherand explore dynamic
HTML creation we must firsttake a closer look at the hierarchy
of objects in JavaScript.
If you are familiar with any object oriented languages, you will
expect an object Hierarchy to begin with a generic object from
which all other objects are descendents or children. Unfortunately,
the JavaScript Object Hierarchy does not really follow this model.
It might be best described as a system of ownership, and, even
then, the analogy is not really exact. For example, a window which
creates another window could be thought of as the parent of the
new window. However, if you try to refer to the original window
from the child by saying "parent.someobject", it will
not work. On the other hand, frames within a
frameset have a parent-child
relationship with the original window and asking for "parent.
someobject"
will likely yield the object. Other ownership relationships are
not characterized by a parent-child relationship at all. For example,
form elements belong to a form but in order to obtain the form,
you use " this.form" -- not "this.parent".
With these disconcerting thoughts in mind, let's attempt to sort
out the dependencies among Netscape Navigator objects.
The navigator is, in a way, the parent of all other Javascript
objects. It is the executable which runs the browser. The navigator
is responsible for the creation of all browser windows. It is
also responsible for responding to general window events. The
navigator is not a
visual object. You cannot see it. You only
interact with it through its visual construct -, its windows.
Most Navigator window components can only be manipulated in a
yes / no fashion at the time of window creation. These include
the menu, the button bar, the location display, the status display,
scrollbars, history list display, and scroll bars. At time of
window creation, you can also determine whether the window can
be resized, as well as finding its dimensions.
This might seem like a significant restriction. By rewriting the document, however, you can change the contents of a window. This technique allows you to change the values of form elements, the content of the status bar, the position of the pointer in the history list, and the location (the URL which the window contains) at any time. Table 8.1 lists these various elements, when they may be modified, and how they may be modified. It should be noted that the last two items in this table are not really 'window"elements. They control what is displaye, but are not explicitly displayed themselves.
|
|
|
|
---|---|---|---|
Button Bar | window creation | Yes/No | NA |
Menu | window creation | Yes/No | NA |
Location Display | window creation | Yes/No | NA |
Status Bar | window creation | Yes/No | NA |
History | window creation | Yes/No | NA |
Document | during rewrite | Complete | NA |
Many form | Any time | Complete | No |
element properties | |||
Status bar content | Any time | Complete | No |
Location | Any time | Complete | Yes |
History List | Any time | Complete | Yes |
Pointer |
One of the more advanced projects later in this book is the creation
of a 'sticky notes' application. In order to do that, we will
need to have a small note window in which to present the note.
Let's create a primordial note window now. In order to do that,
you must already have a window open with an element which will
allow you to call a Javascript function. The base window will
be the parent of the child note window. The child can always find
its parent with self.parent but the parent can only refer to the
child by its name. There is no 'self.child[]' reference nor is
there a windows array available to Javascript, because of security
concerns.
Netscape Navigator is a mimic. If you create a window under script control, the next window which is created by the navigator will have the same dimensions as the last window created by JavaScript.
The element which we will use will be an image which will behave
as a button, which will betriggered by a 'HREF=Javascript:myfunc'
included in the link tag. This works very well if you need call
only one function and you need no return value. When you try to
use this mechanism in a window which you have constructed on the
fly, however, the image refuses to display. In fact, any image
which uses relative addressing refuses to display in a dynamic
window. The solution is to either use a complete
static reference
for the image or to set the base
directory of your page with <BASE>path</BASE>
in the header. This latter approach will help
JavaScript find
the image. If you need an object which will be accessed later,
you might want to use a form input element, rather than one of
these button images. Javascript will have less trouble finding
it.
Three steps are necessary in to or use an image as a button to
execute a Javascript function. First we must write an appropriate
HTML declaration for the desired image. Next, we must enclose
this HTML declaration within reference tags. Finally, we must
resolve the HREF to a JavaScript function declaration. These three
steps are shown in
Listing 8.1
Listing 8.1 Creating a Button Image
<IMG WIDTH=23 HEIGHT= 22 VSPACE=2 HSPACE= 2 ALIGN=LEFT SRC="images/book/gobtn.gif" BORDER=0> <A HREF='xxxxxx'><IMG WIDTH=23 HEIGHT= 22 VSPACE=2 HSPACE= 2 ALIGN=LEFT SRC="images/book/gobtn.gif" BORDER=0><A> <A HREF='Javascript: openNote'><IMG WIDTH=23 HEIGHT= 22 VSPACE=2 HSPACE= 2 ALIGN=LEFT SRC="images/book/gobtn.gif" BORDER=0><A>
The function used in this example is the openNote() function,
the source for which is given below. Before we plunge into this
code, it is worthwhile to notice that the border has explicitly
been set to 0. This is the only way you can keep Navigator for
drawing a border around your image if it is within a reference
statement. Listing 8.2 contains the HTML for the base window with
the image button. It includes the openNotes() function in a header
script. Note that it has been placed in a table to make it look
neater. Once you open a note window, make sure you close it before
it gets lost. Navigator will not open a second window by the same
name, it will just update the first one.
Listing 8.2 The Complete Source for Creating A New Window in JavaScript
<HTML> <HEAD> <TITLE>Opening a Window with Javascript</TITLE> <SCRIPT> //window globals var aNoteWin function openNote(topic) { aPopUp= window.open('','Note','toobar=no,location=no, directories=no,status=no,scrollbars=yes,resizable=yes, copyhistory=no,width=300,height=200') ndoc= aPopUp.document astr ='<HTML><HEAD><BR><TITLE>' + topic + '</TITLE>' astr +='</HEAD>' astr +='<BODY>' astr +=topic + '<BR>' astr +='</BODY></HTML>' ndoc.write(astr) ndoc.close() self.aNoteWin = aPopUp } function closeNote(which) { self.aNoteWin.close() } </SCRIPT> </HEAD> <BODY> <H3><BR><HR><BR></H3> <CENTER> <FONT SIZE=5 COLOR='darkred'><B>Example 1</B></FONT>: <FONT SIZE=5 COLOR='darkblue'><B>Opening a New Window</B></FONT> <FORM NAME='noteForm'> <INPUT TYPE='button' NAME='makeBtn' VALUE='Make Note' onclick='openNote(""JoAnn Murphy at 7:00; bring salad")'> <INPUT TYPE='button' NAME='closeBtn' VALUE='Close note' onclick='closeNote()'> </FORM> </CENTER> <H3><BR><HR><BR></H3> </BODY> </HTML>
In a windows.open statement, there are three things tobear in mind.
Your first dynamically created window in Javascript.
This small script illustrates several points. First, you can set a window global by defining it outside of any function and preceeded it with var. Here we set the window global aNoteWin with
var aNoteWin
This variable is global so that we could use it to refer to aNoteWin
in other functions. Although we did not do so here, we might want
to save a number of notes in an array.Second, when you create
a window de novo via a script, and no URL is specified, the window
document is still open and you can write to it. Here we wrote
the note topic and then closed the document.
A window which you create can be a simple as the note window.
However, you can also make this window quite complex. In order
to do so, you must write everything to the document, including
form elements, images, and Javascript functions, before you close
it. Listing 8.3 shows a second version of the openNote() function.
This more elaborate version furnishes the note window with two
buttons, including onClick handlers, the topic text, a warning
message, and two Javascript functions. Note that the save function
is stubbed; we will discuss this in a later chapter. All the
display
elements are neatly wrapped in a table.
Listing 8.3 (C8-1.htm) A More Sophisticated Notes Window
function openNote(topic) { aPopUp= window.open('','Note','toobar=no,location=no, directories=no,status=no,scrollbars=yes,resizable=yes, copyhistory=no,width=300,height=200') ndoc= aPopUp.document ndoc.close() ndoc.open() astr ='<HTML><HEAD><BR><TITLE>' + topic + '</TITLE>' astr +='</HEAD>' astr +='<SCRIPT>' astr +='function closeNote(aName){' astr +='self.close()' astr +='}' astr +='function saveNote(aName){' astr +='}' astr +='<\/SCRIPT>' astr +='<BODY>' astr +='<FORM>' astr +='<TABLE ALIGN=LEFT BORDER><TR ALIGN=CENTER><TD>' astr +='\<INPUT TYPE=button NAME=saveBtn VALUE="Save" ONCLICK="saveNote()" \>' astr +='</TD>' astr +='<TD ROWSPAN=4>' + topic astr +='</TD>' astr +='</TR><TR ALIGN=CENTER><TD>' astr +='\<INPUT TYPE=button NAME=closeBtn VALUE="Close" ONCLICK="closeNote()" \>' astr +='</TD></TR>' astr +='<TR><TD><BR></TD></TR>' astr +='<TR><TD><BR></TD></TR>' astr +='</TABLE>' astr +='</FORM>' astr +='<BR CLEAR=ALL><H3><BR></H3>' astr +='Note: Save button is not active yet' astr +='</BODY></HTML>' ndoc.write(astr) ndoc.close() self.aNoteWin = aPopUp }
Netscape keeps you appraised of which link or button your cursor
is over via its status bar. Occasionally, it sends you other messages
via that method, too. Perhaps you have seen it busily scrolling
text to catch your attention.The status (not the status bar, itself)
is a property of a window and it is accessible to you as self.status
= 'Some message'. When you change the status, Navigator immediately
displays it in the status bar. You can also set a property called
defaultStatus, which is the default message displayed in the status
bar. Listing 8.4 illustrates the use of the status property in
Netscape Navigator.
Listing 8.4 (c8-2.htm) Manipulating the Status Bar
<HTML> <HEAD> <TITLE>Manipulating the Status Bar</TITLE> <SCRIPT> // set up a window global so that the new window can be accessed from all functions. var aStatWin = null function openStatus(defmsg,msg) { aStatWin=window.open('','statWin','toobar=no,location=no, directories=no,status=yes,scrollbars=no,resizable=yes, copyhistory=no,width=550,height=2') if (aStatWin != null) { aStatWin.document.write('<FORM NAME="dform"> <INPUT TYPE=TEXT NAME="dummy"></FORM>') aStatWin.document.close aStatWin.defaultStatus = defmsg aStatWin.status = msg setFocus() } } function setStatus() { if(self.aStatWin == null ) alert('Status window is closed!') else { self.aStatWin.status = document.statForm.statMsg.value setFocus() } } function setFocus() { self.aStatWin.document.dform.dummy.focus() } function close() { self.aStatWin.close() aStatWin = null } //This function is a workaround to make sure that the table overlay is drawn correctly. function fixup() { blankWin=window.open('','blankWin','toobar=no,location=no, directories=no,status=yes,scrollbars=no,resizable=no, copyhistory=no,width=600,height=450') blankWin.close() } </SCRIPT> </HEAD> <!-- fixup forces redraw of window after everything, including images, has loaded. The redraw is necessary to enforce correct drawing of table overlays. --> <BODY onLoad='fixup()'> <H3><BR><HR><BR></H3> <CENTER> <FONT SIZE=5 COLOR='darkred'><B>Example : </B></FONT> <FONT SIZE=5 COLOR='darkblue'><B>Setting the Contents of the Status Bar</B></FONT> <H3><BR><HR><BR></H3> <H3><BR></H3> </CENTER> <CENTER> <FORM NAME='statForm'> <TABLE WIDTH=520 ALIGN=CENTER BORDER><TR ALIGN=CENTER><TD> <TABLE WIDTH=500 ALIGN=CENTER > <TR ALIGN=CENTER> <TD WIDTH=35 ALIGN=CENTER> <IMG WIDTH=485 HEIGHT=50 VSPACE=2 HSPACE= 2 ALIGN=CENTER SRC="images/book/gotray.gif"> </TD> <TD> <!-- <INPUT TYPE=button VALUE='Make Status Window' onClick='openStatus("Status is GO!", document.statForm.statMsg.value)'> --> <A HREF='Javascript: openStatus("Status is GO!", document.statForm.statMsg.value)'> <IMG WIDTH=23 HEIGHT=22 VSPACE=2 HSPACE=2 ALIGN=absMiddle SRC="images/book/gobtn1.gif" BORDER=0> Open Status Window</A> </TD> <TD ALIGN=LEFT > <A HREF='Javascript: setStatus()'> <IMG WIDTH=23 HEIGHT=22 VSPACE=2 HSPACE=2 ALIGN=absMiddle SRC="images/book/gobtn2.gif" BORDER=0> Set Status</A> </TD> <TD ALIGN=CENTER > <A HREF= 'Javascript: close()'> <IMG WIDTH=31 HEIGHT=30 VSPACE=2 HSPACE= 2 ALIGN=absMiddle BORDER=0 SRC="images/book/okbtn.gif"> Close Status </A> </TD> </TR> <TR ALIGN=CENTER> <TD ALIGN=CENTER COLSPAN=4> Msg <INPUT TYPE=text NAME='statMsg' VALUE='Howdy!' SIZE= 50 MAXLENGTH=80> </TD> </TR> </TABLE> </TD></TR></TABLE> </FORM> </CENTER> <H3><BR></H3> </BODY> </HTML>
This example builds a window with a status bar included. Just
to make things interesting, we will set the content of the status
bar from the parent window. In addition, this script provides
an example of Table Overlays, which will be discussed later, and
an onLoad() handler which provides a workaround for
Netscape's
unpredictable order of drawing images.
The status window is created with a 'Javascript: openStatus(defMsg,msg)' link attached to an image. This function openStatus(defMsg, msg) performs several tasks.
We will set the status of the status window with a call to setStatus().
This call is made from a 'Javascript: setStatus(document.statForm.statMsg.value)' link attached to an image.
The setStatus() function also checks to see if aStatWin exists
before it tries to address one of its objects. If aStatWin does
exist, setStatus changes the content of its status bar setStatus()
then sets the focus on the dummy button in aStatWin in order to
bring the window to the front. This is done by a call to the setFocus()
method.
Finally, we close the window with an Image link Javascript call to close(). Close simply closes aStatWin and makes sure that its value is reset to null.
The function fixup() is worth looking at in more detail.
The order in which Netscape draws images depends upon where the
images are coming from, whether they are in the cache, and whether
their height and width are given explicit. It may also depend
on the size of the images. Extremely attractive presentations
can be made by overlaying text and graphics over other
graphics
via a feature of
Netscape tables. However, Netscape will invariably
draw your bottom image last when the page first loads. Scrolling
the screen will cause the correct redraw, but you cannot expect
or require your users to do that. One way to force the redraw
is to open and quickly close another window over your page. You
need to do this AFTER all of the page elements have been loaded.
When this occurs, Navigator sends an
onLoad event, which you can
capture in the <BODY> tag. The fixup() function does to
insure that all of the IMAGE buttons are visible. While this is
far from an ideal solution, it is effective.
Always check that a newly createdwindow exists before you try to use address its properties or methods. Do this by checking to see if the window is null. If it is null use alert to inform the user.
A status bar window created dynamically by the parent window.
The location object essentially holds information about the URL
to which the browser points. The browser reads this marked up
text from the server's disk and interprets it just like Microsoft
Word reads and interprets a file on your disk.In addition to the
URL, the location object will also contain any post parameters
of a form submitted via a submit button or your call to submit().
Because of this, you can use the location object for temporary
storage.
In Netscape the location consists of the following parts:
protocol//hostname: (port) pathname search hash
Protocol is the type of protocol used for this file. Examples
are http, ftp, gopher, telnet, andfile (for files on the local
dist). Hostname and port are only valid when the document is on
a remote server. They contain the domain name/IP address of the
server and the server port, respectively. They are not usually
a visible part of a URL. The Web port is a number denoting the
type of service, and is usually 80 for
http. The Pathname is the
path to the file which the browser will display. Search includes
any post parameters which are compiled when a form is submitted.
Hash is usually a link to a local anchor.
The location object also has a host property, which consists of the combination of hostname and port. The location object also has an extremely important property, known as href, which contains the entire URL.
This next example is a page which has no body. It is written entirely
by the header script. As the page writes, it dissects the location
object and lists all of its properties in a table. In order to
see a non-empty location.search, you will have to submit the little
form included after the table. To see a non-empty location.search,
click on the dummy link and then Netscape's Reload button. The
Host, port, and hostname properties will be non-empty only if
you have loaded some page from a server. Listing 8.5 shows the
code for the
Location Display script.
Listing 8.5 (C8-3.htm) Displaying the Properties of the Location Object
<HTML> <HEAD> <!- Created 08 Feb 1996 a2:41 PM 02:41 PM --> <TITLE>Parts of the Location Object</TITLE> <SCRIPT> var aline = '<H3><BR></H3><HR><H3><BR></H3>' var skip='<H3><BR></H3>' document.write('<CENTER>') document.write('<FONT SIZE=5 COLOR="darkred"><B>Example : </B></FONT> <FONT SIZE=5 COLOR="darkblue"><B>What\'s in the Location Object?</B></FONT>') document.write('<BR>') document.write('<BLOCKQUOTE><BLOCKQUOTE>If you are viewing this document from your hard disk, host, hostname, and port will be empty.</BLOCKQUOTE></BLOCKQUOTE>') document.write('<BR>') document.write('<CENTER><TABLE ALIGN= CENTER BORDER CELLPADDING=3>') document.write('<TR><TD><B>Property</B></TD><TD ALIGN=CENTER> <B>Value</B></TD></TR>') document.write('<TR><TD>href</TD><TD>' + location.href + '</TD></TR>') document.write('<TR><TD>protocol</TD><TD>' + location.protocol + '</TD></TR>') document.write('<TR><TD>hostname</TD><TD>' + location.hostname + '</TD></TR>') document.write('<TR><TD>host</TD><TD>' + location.host + '</TD></TR>') document.write('<TR><TD>port</TD><TD>' + location.port + '</TD></TR>') document.write('<TR><TD>pathname</TD><TD>' + location.pathname + '</TD></TR>') document.write('<TR><TD>search</TD><TD>' + location.search + '</TD></TR>') document.write('<TR><TD>hash</TD><TD>' + location.hash + '</TD></TR>') document.write('</TABLE></CENTER>') document.write(aline) document.write('<CENTER>') document.write('<FORM NAME="nameForm" >') document.write('Your name\: <INPUT TYPE=text NAME="yourName" VALUE="John Smith" WIDTH=30 MAXLENGTH=30>') document.write('<INPUT TYPE=submit VALUE="Click Me to Add a Search Parameter!" >') document.write('</FORM>') document.write('<A HREF=' + location.href + '#myAnchor >Click on me and then RELOAD to enter a hash parameter!</A>') document.write(aline) </SCRIPT> </HEAD> </HTML>
Not only can you obtain useful information by examining the Location
object, you can also modify it, and send the user elsewhere. This
is useful if you should want to dynamically generate a URL or
a reference to an anchor. The example shown in Listing 8.6 builds
a URL dynamically and sends the current browser to that URL. This
code implements a "message" center, which retrieves
messages from URLs created via button clicks. Five users have
been created to demonstrate this aspect of the location object.
Listing 8.6 (C8-4.htm) Modifying the Current URL
<HEAD> <!- Created 08 Feb 1996 a4:08 PM 04:08 PM --> <TITLE>Message Center</TITLE> <SCRIPT> function getMessage(who) { loc = self.location document.forms[0].translate.value = loc loc = document.forms[0].translate.value k = loc.lastIndexOf('/') loc = loc.substring(0,k+1) nloc = loc.substring(0,k+1)+ who.value + '.htm' self.location=nloc } </SCRIPT> </HEAD> <BODY> <CENTER><HR> <FONT SIZE=5 COLOR='darkred'><B>Example 4</B></FONT>: <FONT SIZE=5 COLOR='darkblue'><B>Moving Around Dynamically</B></FONT><BR> <HR><FONT SIZE=6 COLOR='darkslateblue'><B>Message Center</B></FONT><BR> </CENTER> <CENTER> <FORM> <TABLE BORDER ALIGN=CENTER><TR><TD> <INPUT TYPE=radio NAME='getMsgR' VALUE='John' onClick='getMessage(this)'>John <INPUT TYPE=radio NAME='getMsgR' VALUE='George' onClick='getMessage(this)'>George <INPUT TYPE=radio NAME='getMsgR' VALUE='Barbara' onClick='getMessage(this)'>Barbara <INPUT TYPE=radio NAME='getMsgR' VALUE='Ken' onClick='getMessage(this)'>Ken <INPUT TYPE=radio NAME='getMsgR' VALUE='Julie' onClick='getMessage(this)'>Julie <INPUT TYPE=hidden NAME='translate' VALUE='' > </TD></TR></TABLE> </FORM> </CENTER> <H3><BR><HR><BR></H3> <H3><BR><HR SIZE=5 WIDTH=80%><BR></H3> </BODY> </HTML>
The script work by first obtaining the current location. It then
strips off the file name and replaces it with the value of the
radiobutton clicked. It also makes sure to tack on the suffix
'.htm'. This presumes that the message HTML files are in the same
directory as the current page. However, it would be easy enough
to build in a subdirectory name just for the messages or, even
have a separate subdirectory for each person. The location object
is then set to the newly constructed URL. Setting the location
retrieves the file at that location. In our example this file
represents the set of messages for the particular user whose button
was pressed.
You can force a page to be reloaded by setting the location object to the URL corresponding to that page.
When you submit a form, all the values of the various form elements
are retrieved, parsed, and concatenated with the location object;
they are placed after the path and preceded by '?' The value of
Location.search is precisely that string, including the '?'. This
string is not just a simple list of element contents, however.
Each element value is placed in the string in the form 'elementName=elementValue'
and followed by '&'. Any non-alphanumeric characters are coded
or escaped. The
ASCII value of the character is changed into a
two digit
hex number preceded by %. If text field or
textarea
elements have multiple words, these words are separated by '+'.
Consequently, when you get the Location.search string, you have
to decode it to get the various form elements which it contains
You can place your own form element values, or anything else, in the location's search property. As long as you precede it with '?', location.search will retrieve it. However, not all non-alphanumeric characters may be placed in the string or retrieved intact. If you are going to concoct a home grown search string, you may either need to encode the parameters yourself or not allow non-alphanumeric characters. Listing 8.7 is a simple page which shows you how to manipulate location.search
Listing 8.7 Using the Search Property of the Location Object
<HTML> <HEAD> <!- Created 08 Feb 1996 a6:10 PM 06:10 PM --> <TITLE>Forcing a Reload with Location</TITLE> <SCRIPT> function reloadMe() { astr = document.nameForm.myName.value astr= self.location.pathname + '?' + astr self.location = astr } function clearUp() { self.location = self.location.pathname } if (self.location.search != null && self.location.search !='') { document.write('<CENTER><FONT SIZE=4 COLOR="darkslategray"><B> Form Entry Data: </B></FONT></CENTER>') document.write('<CENTER><FONT SIZE=4 COLOR="red"><B>' + self.location.search + '</B></FONT></CENTER>') } </SCRIPT> </HEAD> <H3><HR></H3> <CENTER><FONT SIZE=6 COLOR="blue"><B>Forcing a Reload with Location</B></FONT></CENTER> <H3><BR><HR><BR></H3> <CENTER> <FORM NAME=nameForm> <INPUT TYPE=text NAME=myName VALUE='abracadabra&#^$()'> <INPUT TYPE=button NAME=reloadBtn VALUE='Reload Page' onClick='reloadMe()'> <INPUT TYPE=button NAME=submitBtn VALUE= 'Submit Form' onClick='this.form.submit()'> <INPUT TYPE=button NAME=clearBtn VALUE= 'Clear' onClick='clearUp()'> <INPUT TYPE=hidden NAME=hideFld > </FORM> </CENTER> <H3><BR><HR><BR></H3> <H3><BR><HR SIZE=5 WIDTH=80%><BR></H3> </BODY> </HTML>
A script in the <HEAD> part of an HTML document can pick
up the command line parameters with location.search and write
something to the document being loaded based on what it finds.
This example just reads the parameter string and writes it for
you at the head of the page. Note that this write is guarded by
a test to see if location.search is null or empty. If location.search
is not a valid string, and you attempt to parse it into variables
which are used later, you will encounter error after error.Always
test for a null string of empty string.
The code in Listing 8.7 has two useful functions. Clear() simply
strips the search string from the location by setting the location
object to location.path. The reloadMe() function takes the value
from the text box and adds it to location.path. It then sets the
location to that result.
The history object is a list which contains the locations of all
the URL's which you have visited. You can move backwards and forwards
through the history list with history.back and history.forward.
You can also move around in the list in a relative fashion with
history.go(). This function takes a positive or
negative integer
argument, and moves you that many URLs forward or backward in
the history list. The only property of a history list you can
access is its length,the number of items in the list. You can
neither set nor retrieve history list items.
In order to show how to manipule of the history list, we will
build another popup window which boasts only a close button and
four directional buttons. The buttons allow you to manipulate
the history list of the parent window. You can move backwards
and forwards by one step or five. This code is shown in Listing
8.8.
Listing 8.8 (C8-6.htm) Using the History Object in a Popup Window
<HTML> <HEAD> <!- Created 08 Feb 1996 a9:21 PM 09:21 PM --> <TITLE>Running through the History List</TITLE> <SCRIPT> var aNoteWin var myDummyVar = 'Apples, peaches, pumpkin pie...' function openNote(topic) { aPopUp= window.open('','Note','toobar=no,location=no, directories=no,status=no,scrollbars=yes,resizable=yes, copyhistory=no,width=110,height=150') ndoc= aPopUp.document ndoc.close() ndoc.open() astr ='<HTML><HEAD><BR><TITLE>' + topic + '</TITLE>' astr +='<SCRIPT>' astr +='function closeNote(aName){' astr +='self.close()' astr +='}\n' astr +='function saveNote(aName){' astr +='}\n' astr +='function goNext(){' astr +='creator.history.forward()\n' astr +='}\n' astr +='function goBack(){' astr +='creator.history.back()\n' astr +='}\n' astr +='function goStart(){' astr += 'creator.history.go(-5)\n' astr +='}\n' astr +='function goEnd(){' astr +='creator.history.go(5)\n' astr +='}\n' astr +='<\/SCRIPT>' astr +='</HEAD>' ndoc.write(astr) astr ='<BODY>' astr +='<FORM NAME="popForm">' astr +='<TABLE ALIGN=LEFT BORDER>' astr +='</TR><TR ALIGN=CENTER><TD>' astr +='\<INPUT TYPE=button NAME=closeBtn VALUE="Close" ONCLICK="closeNote()" \>' astr +='</TD>' astr +='</TR>' astr +='<TR><TD>' astr +='<INPUT TYPE="button" NAME="startBtn" VALUE=<< onclick="goStart()">' astr +='<INPUT TYPE="button" NAME="backBtn" VALUE=< onclick="goBack()">' astr +='<INPUT TYPE="button" NAME="nextBtn" VALUE=> onclick="goNext()">' astr +='<INPUT TYPE="button" NAME="endBtn" VALUE=>> onclick="goEnd()">' astr +='</TD></TR>' astr +='<TR><TD>' astr +='<INPUT TYPE="hidden" NAME="IAm" VALUE="0">' astr +='</TD></TR>' astr +='</TABLE>' astr +='</FORM>' astr +='<BR CLEAR=ALL><H3><BR></H3>' astr +='</BODY></HTML>' ndoc.write(astr) ndoc.close() self.aNoteWin = aPopUp self.aNoteWin.creator = self aNoteWin.document.popForm.startBtn.focus() } function closeNote(which) { self.aNoteWin.close() } </SCRIPT> </HEAD> <BODY > <CENTER> <FONT SIZE=5 COLOR='darkred'><B>Example 6</B></FONT>: <FONT SIZE=5 COLOR='darkblue'><B>Running through the History List </B></FONT> </CENTER> <H3><BR><HR><BR></H3> <BODY> <H3><BR><HR><BR></H3> <CENTER> <FORM NAME='noteForm'> <INPUT TYPE='button' NAME='makeBtn' VALUE='Make Popup' onclick='openNote("JoAnn Murphy at 7:00; bring salad.")'> <INPUT TYPE='button' NAME='closeBtn' VALUE='Close Popup' onclick='closeNote()'> </FORM> </CENTER> <H3><BR><HR><BR></H3> <H3><BR><HR><BR></H3> <H3><BR><HR SIZE=5 WIDTH=80%><BR></H3> </BODY> </HTML>
This popup is a variation on our old friend, aNoteWin. It can
access its parents variables through an artificial property of
aNoteWin, the creator property.At the end of the openNote() function,
which creates and draws the window, aNoteWin.creator is set to
self. This automatically creates a new
property of the aNoteWinThis
allows use to have ready access to the parent
window's variables,
functions, and objects.
Although it would be useful to retrieve history list items, this
functionality has been removed from Javascript. Unfortunately,
each history list entry contains the entire location, including
the search string. If this information could be retrieved, the
possibility exists that
credit card or other personal information
might be gleaned by malicious individuals.
You may have wondered why there is no windows array. This, also, does not exist in JavaScript. If it did, a script from one window might reach into another unrelated window and scavenge information form its form elements or its location object. Again, what might be perceived as a limitation in Javascript has been imposed to protect you.
The document object encapsulated all JavaScript objects which
correspond to the
HTML elements. It is the parent of forms, links,
and anchors. These objects occur as arrays and are accessed as
document.Forms[xx], document.Links[xx], and document.anchors[xx].
The document object also has several other useful properties.
It has a property, for example, for all of the standard object
colors such as the background color , text color, and link colors.
You cannot change the property of a static closed document, however,
so these are useful only when building a document. Four properties
which are useful in keeping your documents up to date are the
location, title, lastModified, and
referrer properties. These
are used to dynamically write a header or a footer for your documents.
Listing 8.9 shows a typical way in which you could use the document
object in this way. Note that the properties of the document object
are read-only. The attempt to set document.title in this listing
will fail.
Listing 8.9 Writing the Document Header
<HTML> <HEAD> <!- Created 08 Feb 1996 a11:32 PM 11:32 PM --> <TITLE>Writing a Document Header</TITLE> <SCRIPT> document.bgcolor = 'linen' document.text = 'darkslateblue' document.link = 'coral' document.vlink='peach' document.alink='red' document.title='Dynamic Headers' document.write('<TABLE ALIGN=RIGHT WIDTH=300 BORDER=1>') document.write('<TR><TD>') document.write('<FONT SIZE=7 COLOR= "navy">') document.write('<CENTER>' +document.title + '</CENTER>') document.write('</TD></TR></TABLE>') document.write('<CENTER><B>') document.write('<HR>') document.write('This document was last modified on<BR> <FONT COLOR="red">' + document.lastmodified + '</FONT><BR>') document.write('Save this URL: <BR><FONT COLOR="red">' + document.location + '</FONT><BR>') document.write('You arrived here from <BR><FONT COLOR="red">' + document.referrer + '</FONT><BR>') document.write('<BR><HR><BR>') document.write('</B></CENTER>') document.write('') </SCRIPT> </HEAD> <H3><BR><HR><BR></H3> <H3><BR><HR SIZE=5 WIDTH=80%><BR></H3> </BODY> </HTML>
You can get input from your users via form elements (sometimes
known as widgets). You can have many forms on a page. If they
are named (by an HTML NAME directive) you can refer to them by
name. You can also refer to a form by its index into the zero
based forms array. Each form can have any one of the standard
HTML form elements. These include single-line text,
radio,
checkbox,
hidden, password, reset, and submit. There is also a select widget,
which can be either a
dropdown list or a plain list, and a
textarea
widget which can be used to collect large amounts of text spanning
multiple lines.
Listing 8.10 is a page with several forms on it. This page demonstrates
the interaction of form elements. It also has a script which iterates
through all of the forms in the document and prints out their
element names and values.
A script can only access elements which have been created. They may not be accessd in a document <HEAD> since the forms and their elements will have yet exist.
Links are the bread and butter of any hypertext system, especially
HTML. In JavaScript the links array can be canvassed in order
to provide a list of links in a document. Links have only one
property, the target. The target is
NOT the URL pointed to by
the link. Rather, it is the window into which that URL will be
loaded. Any link is also a location object, so that you can dissect
the link in the same way in which you can dissect a location object.
Links can have an onClick handler just as buttons do. However, when, the onClick handler finishes, the URL specified by the link will be loaded. If you do not want the link to go anywhere, just specify the current URL as its destination. If you want to use a link to execute a function, use HREF= 'Javascript : myfunc()' . Myfunc() can call a function or contain Javascript code. For example, HREF='Javascript : self.close()', will immediately close the current window.
A Javascript:xxx call replaces a URL. The browser will try to interpret any return from the function call in that fashion. If the browser does not understand the return value, it will reload the current page. When that occurs, any window globals which you set as during the xxx function call will be lost.
Both text and images can be links. You can include both in the same link if you want, so that clicking on either the text or the image will activate the link's events. Because images can be links, you can use them as buttons to call functions if you use Javascript:xxx as the HREF, instead of a URL. Unfortunately, since you cannot replace images dynamically, you cannot modify the appearance of thie image to simulate a button down effect.
Links can also trap the onMouseOver event. However, it traps this
event at the boundaries of the link. What that means is that if
you run your cursor over a link, the event will be fired twice:
once when you enter the bounding box of the link, and once when
you leave it. If you use the onMouseOver event to write to the
status bar, you will find that if you move your mouse to quickly
you will miss the status write. This is because it is rapidly
replaced by the browser's own response to the event. When you
exit, the status write will stay there until you encounter another
link. If you want to use the content of your link in a function
called from an onMouseOver handler, you can pass the function
'this'. Links are treated like any other
Javascript Object. Figure 8.3
shows a
onMouseOver event being triggered, just as the mouse moves
over the link.
Fig8.3
MouseOver is only fired when the cursor enters or leaves the linked object.
Listing 8.11includes examples of trapping onClick and onMouseOver events from links. It also includes a script which uses the document.links array to write a list of all of the links on the page. Examples of using a Javascript:xxx replacement for a URL in a link can be found inListing 8.2, which uses linked images as buttons.
Listing 8.11 Event Processing for the Link Object
<HTML> <HEAD> <!- Created 09 Feb 1996 a2:45 AM 02:45 AM --> <TITLE>What's Your Link</TITLE> <SCRIPT> function checkOut() { a = (confirm("This file is 10 megs in size. It will take 2 hours to download it. Are your sure you want to do that at this time?")) if (a == true) { alert ('loading file...') } else { alert('NOT loading file!') self.location = self.location } } function enhance(what) { astr = what.href self.status = 'Click here to go to a page of backgrounds.' } </SCRIPT> </HEAD> <BODY> <TABLE ALIGN=RIGHT WIDTH=250 BORDER=1> <TR><TD> <FONT SIZE=7 COLOR= "darkcorel"> <CENTER>What's Your Link</CENTER> </TD></TR></TABLE> <FONT SIZE=5 >Example 9</FONT><BR> This page demonstrates onClick and mouseOver events for links <HR> <H3><BR><HR><BR></H3> <TABLE ALIGN=LEFT BORDER WIDTH=250> <TR><TD> <A HREF='C8-8.HTM' onMouseOver='enhance(this)'> <IMG WIDTH=100 HEIGHT=100 VSPACE=2 HSPACE= 2 ALIGN=Left SRC="images/grkaleid.jpg"> <CENTER><FONT SIZE=5>Enhanced Status</FONT></CENTER></A> </TD></TR></TABLE> This Linked image has an onMouseOver event in its Link tag. This event appears to be fired twice, once when the mouse passes into the boundary of the image and once when it passes out. Move the mouse SLOWLY to see the effect of the event handler.<BR CLEAR ALL> <H3><BR></H3> <TABLE ALIGN=LEFT BORDER WIDTH=250> <TR><TD> <A HREF='C8-9.HTM' onClick='checkOut()'> <IMG WIDTH=32 HEIGHT= 50 VSPACE=2 HSPACE= 2 BORDER=2 ALIGN=Left SRC="images/answer_u.gif"> <CENTER><FONT SIZE=5>File Information</FONT></CENTER></A> </TD></TR></TABLE> This onClick routine asks you if you want to load a very large file. <FONT SIZE=4 COLOR='red'>Note to MME: abort does not work. Checking it out. Remember to fix.</FONT> <BR CLEAR ALL> <H3><BR><HR><BR></H3> <H3><BR><HR SIZE=5 WIDTH=80%><BR></H3> </BODY> <SCRIPT> k = document.links.length document.write('This page has ' + k + ' links. They are: <BR>') for (i = 0 ; i < k ; i++) { document.write(i + ' ' + document.links[i] + '<BR>') } </SCRIPT> </HTML>
Anchors consist of text or images in your document which are marked,
usually named, and can be referenced by a link within the same
document (known as a local link). The document object has an anchors
array but at this time its use is quite limited. You can find
out how many anchors are on the page using the length property.
The property arrays anchors[i].value or anchors[i].name may be
accessed without error, but will also be empty.
Dynamic documents created using JavaScript provide all the functionality
of static documents written in
HTML. If you can write it in HTML
your can write it on the fly in a document script. In fact, you
can write your whole document in a script. We have already seen
an example of this in Listing 8.8, which converts document properties
into a formatted area which can be placed at the beginning of
any document which you write. You can also have a script after
the <BODY> tag which will write a document footer in a similar
fashion.
Unfortunately, you cannot change anything on your current page once the document is finished. If you want to write to the current document, you will have to open it and write to it via scripts placed at the beginning and end of the document. You can also choose to rewrite the entire document.
Using the document.write method is like printing on a dot matrix printer. It is top down only. This makes it particularly hard to do graphing or other innovative work which requires accurate positioning of objects on the page, since you cannot know in advance where they will end up.
You must also be careful when enclosing an HTML statement within a document.write clause. This innocent statement will give you at least one error:
HTML : <FONT SIZE=5 COLOR='red'>Mozilla is GREAT!</FONT> Document.write('<FONT SIZE=5 COLOR='red'>Mozilla is GREAT!</FONT>')
Many HTML parameters need to be quoted, and it is much easier
to use single quotes than double quotes. Unfortunately, the write
statement above will terminate with the leading quote of 'red'.
The closing parenthesis of the write statement will not be found
and an error will occur. The problem is easily fixed in this case:
just use double quotes around the word "red".
But what do you do if there is a need for a third level of nested quotes?Take this HTML statement for example:
<INPUT TYPE='radio' NAME='mycolor' VALUE='red' onClick='Alert("You chose red!")'>
This statement already has nested quotes. If you wish to enclose this within a document.write() clause, you have two choices:
document.write(<INPUT TYPE="radio" NAME="mycolor" VALUE="red" onClick="Alert(\"You chose red!\")">
astr = "You chose red!" document.write(<INPUT TYPE="radio" NAME="mycolor" VALUE="red" onClick="Alert(astr)">
If you have more than three levels of quotes then you MUST use the second option.
In order to provide a canvas for yourself to write on, use the document.open() command. It is a good idea to precede this with a document.close() command. You can open or reopen a document in the current window or any window for which you have a reference. Opening a document in that window clears anything which is already there. Once the document is open, you can write to it.
If you issue a document.open() command from within a script in the current page and it is not preceded by a window reference, the current page will be opened. Whatever is on the page is GONE! This can be quite a surprise. Don't worry. You can recover the page with a reload, but your users might not know that. Check all document.open() references carefully.
A document is open until you specifically close it or until the browser runs out of things to write on it. When you first open a window, the document will also be opened. You can then write to it with a header script. Then the browser will write all of the body, if there is one, and anything it finds in the footer script, if there is one. When it reaches the end of the HTML text, it automatically closes the document. Note that if you open a new window with myNewWin = self.open('', 'NewWin'), you do not have to issue a document.open(). Just start writing on the blank page.
We have already noted some items to be careful of in using document write statements. If you try to write to a document which is not open, nothing will happen. No error will occur - the command will simply fail. You can write anything to a nascent document, including scripts. If you are creating a window from scratch then you will have to write everything. The first two examples of this chapter illustrate this approach.
Troubleshooting
I opened a fresh document using document.open(), wrote to it for quite a long time using document.write(), and nothing happened. Where did I go wrong?
Document.write() places all of its output into an ASCII stream. Think of it as one big string which exists somewhere in memory. The browser does not get to interpret the stream until you specifically say, "That's all, folks!" with a document.close(). Once you close the document, everything which you have written (hopefully) will be rendered in the browser window. This also means than any script errors will not be noticed until the document is actually closed.
If you examine Listing 8.9 of this chapter, you will see that this document is written totally within the header. Let's revisit this document and add a little to it. Suppose that we have a number of images which have the prefix, and differ only in a final numerical suffix. Let us also suppose that the numbers are sequential. With a very little Javascript we can dynamically generate the image citations and write them to the page.
Listing 8.12 takes this approach. The for loop, is particularly worthy of close examination It keeps adding citation information to a continuously growing string. When it is finished, it uses one document.write() statement to put the string on the document stream. There are several reasons to use this type of an iterative construct:
Listing 8.12 Generating an Entire Document in JavaScript
<HTML> <HEAD> <!- Created 08 Feb 1996 a11:32 PM 11:32 PM --> <TITLE>Writing a Document with Javascript</TITLE> <SCRIPT> document.bgcolor = 'linen' document.text = 'darkslateblue' document.link = 'coral' document.vlink='peach' document.alink='red' document.write('<TABLE ALIGN=RIGHT WIDTH=350 BORDER=1>') document.write('<TR><TD>') document.write('<FONT SIZE=7 COLOR= "indianred">') document.write('<CENTER>' +document.title + '</CENTER>') document.write('</TD></TR></TABLE>') document.write('<BR>') document.write('<LEFT>') document.write('This page is an example of a document written completely within a script. The reference for each image is generated dynamically') document.write('</LEFT>') document.write('<BR CLEAR ALL>') document.write('<HR>') bstr = 'BLACE' kstr = '' for (i = 1 ; i <= 10 ; i++) { var xstr = bstr + i + '.jpg' kstr += '<IMG SRC=\"NewImages\/' + xstr + '" ' kstr += 'HEIGHT=100 WIDTH=100 VSPACE=5 HSPACE=5 BORDER=2 ALIGN=LEFT>' kstr += '<H3><BR><H3>' kstr += '<CENTER><FONT SIZE=4 COLOR="coral">' + xstr + '</FONT></CENTER>' kstr += '<HR><BR CLEAR ALL>' } document.write(kstr) document.write('<BR><HR><BR>') document.write('This document was last modified on<BR> <FONT COLOR="red"><B>' + document.lastModified + '</FONT><BR>') document.write('<BR><HR><BR>') document.write('</B></CENTER>') document.write('') </SCRIPT> </HEAD>
There are many reason why you might wish to rewrite you page dynamically. You may want to try out various background images and colors, for example. In order to do to obtain the current information which you will be modifying, and also save that information somewhere. Obtaining the information is easy. Storing and retrieving it is not because Javascript does not let you read or write files (another security restriction).
One approach is to use a form and submit that form with no action listed. This will place the form's parameters in the search property of the location object. This requires quite a bit of parsing, though. We have already shown in Listing 8.7 that you can write your own location.search string. This approach will often be simpler than using submit, with it awkward results, provided that you have no escaped characters and are able to will process the parameters in order. Listing 8.13 shows an example of this. It can be easily extended to include a number of different items. In fact, you can build a simple page with it.
Listing 8.13 Using JavaScript to Dynamically Rewrite a Document
<HTML> <HEAD> <!- Created 08 Feb 1996 a6:10 PM 06:10 PM --> <TITLE>Dynamic Modification of Documents</TITLE> <SCRIPT> function reloadMe() { self.location = self.location astr= self.location.pathname + '?' astr += document.forms[0].aname.value + '*' astr += document.forms[0].mytext.value + '*' astr += document.forms[0].mylink.value + '*' astr += document.forms[0].myvlink.value + '*' astr += document.forms[0].myimage.value + '*@' self.location = astr } function clearUp() { self.location = self.location.pathname } function doTC(what) { what.form.mytext.value = what.value } function doLC(what) { what.form.mylink.value = what.value } function doVC(what) { what.form.myvlink.value = what.value } function doImage(what) { document.forms[0].myimage.value = 'DBLACE' + what + '.jpg' } function doname(what) { what.form.aname.value = what.value } function createArray(n) { this.length = n return this } function arrayParms(astr) { var k = astr.length astr = astr.substring(1,k) var n = 0 var a = 1 var i = 1 var counter=0 while(a > 0) { a = astr.indexOf('*',n) var bstr = astr.substring(n,a) if (bstr != '@' && counter <10 ) { parms[i] = bstr n=a+1 i++ } else a = 0 counter++ } } var parms = new createArray(5) var astr = location.search arrayParms(astr) astr = '<BODY BACKGROUND = "NewImages/' + parms[5] + '" ' astr += 'TEXT="'+ parms[2] + '" ' astr += 'LINK="'+ parms[3] + '" ' astr += 'VLINK="'+ parms[4] + '" ' astr += 'ALINK="red" ' astr += '>\n' document.write(astr) document.write('<TABLE ALIGN=RIGHT WIDTH=350 BORDER=1>') document.write('<TR><TD>') document.write('<FONT SIZE=7 COLOR= "indianred">') document.write('<CENTER>' +document.title + '</CENTER>') document.write('</TD></TR></TABLE>') document.write('<LEFT><B>') document.write('This page is an example of dynamically revised by a header script which acts on information stored in the command line. That information is based on user\' choices.') document.write('</B></LEFT>') document.write('<BR CLEAR ALL>') document.write('<HR>') astr ='<CENTER><FONT SIZE=7 COLOR="' + parms[3] + '"><B> ' astr += parms[1] + '</B></FONT></CENTER>' document.write(astr) document.write('<HR><BR>') </SCRIPT> </HEAD> <CENTER> <FORM NAME=nameForm> Enter your first name here:<BR> <INPUT TYPE=text NAME=myname SIZE= 20 onChange='doname(this)'><H3><BR></H3> <CENTER> <TABLE ALIGN=LEFT WIDTH=100 BORDER CELLPADDING=5> <TR ALIGN=LEFT><TD> <CENTER><B>Text</B></CENTER> <INPUT TYPE="RADIO" NAME="tc" VALUE='white' ONCLICK='doTC(this)'>white <INPUT TYPE="RADIO" NAME="tc" VALUE='yellow' ONCLICK='doTC(this)'>yellow <INPUT TYPE="RADIO" NAME="tc" VALUE='navy' ONCLICK='doTC(this)'>navy <INPUT TYPE="RADIO" NAME="tc" VALUE='blue' ONCLICK='doTC(this)'>blue <INPUT TYPE="RADIO" NAME="tc" VALUE='orange' ONCLICK='doTC(this)'>orange <INPUT TYPE="RADIO" NAME="tc" VALUE='red' ONCLICK='doTC(this)'>red <INPUT TYPE="RADIO" NAME="tc" VALUE='black' ONCLICK='doTC(this)'>black </TD></TR></TABLE> <TABLE ALIGN=LEFT WIDTH=100 BORDER CELLPADDING=5> <TR ALIGN=LEFT><TD> <CENTER><B>Link</B></CENTER> <INPUT TYPE="RADIO" NAME="lc" VALUE='white' ONCLICK='doLC(this)'>white <INPUT TYPE="RADIO" NAME="lc" VALUE='yellow' ONCLICK='doLC(this)'>yellow <INPUT TYPE="RADIO" NAME="lc" VALUE='navy' ONCLICK='doLC(this)'>navy <INPUT TYPE="RADIO" NAME="lc" VALUE='blue' ONCLICK='doLC(this)'>blue <INPUT TYPE="RADIO" NAME="lc" VALUE='orange' ONCLICK='doLC(this)'>orange <INPUT TYPE="RADIO" NAME="lc" VALUE='red' ONCLICK='doLC(this)'>red <INPUT TYPE="RADIO" NAME="lc" VALUE='black' ONCLICK='doLC(this)'>black </TD></TR></TABLE> <TABLE ALIGN=LEFT WIDTH=100 BORDER CELLPADDING=5> <TR ALIGN=LEFT><TD> <CENTER><B>VLink</B></CENTER> <INPUT TYPE="RADIO" NAME="vc" VALUE='white' ONCLICK='doVC(this)'>white <INPUT TYPE="RADIO" NAME="vc" VALUE='yellow' ONCLICK='doVC(this)'>yellow <INPUT TYPE="RADIO" NAME="vc" VALUE='navy' ONCLICK='doVC(this)'>navy <INPUT TYPE="RADIO" NAME="vc" VALUE='blue' ONCLICK='doVC(this)'>blue <INPUT TYPE="RADIO" NAME="vc" VALUE='orange' ONCLICK='doVC(this)'>orange <INPUT TYPE="RADIO" NAME="vc" VALUE='red' ONCLICK='doVC(this)'>red <INPUT TYPE="RADIO" NAME="vc" VALUE='black' ONCLICK='doVC(this)'>black </TD></TR></TABLE> </CENTER> <BR CLEAR ALL> <HR> <CENTER><FONT SIZE=4>Click on the image that you want for a background.</FONT></CENTER> <H3><BR></H3> <A HREF='Javascript:doImage(1)'> <IMG WIDTH=100 HEIGHT= 100 VSPACE=5 HSPACE= 5 BORDER=2 ALIGN=Left SRC="NewImages/DBLACE1.jpg"></A> <A HREF='Javascript:doImage(2)'> <IMG WIDTH=100 HEIGHT= 100 VSPACE=5 HSPACE= 5 BORDER=2 ALIGN=Left SRC="NewImages/DBLACE2.jpg"></A> <A HREF='Javascript:doImage(3)'> <IMG WIDTH=100 HEIGHT= 100 VSPACE=5 HSPACE= 5 BORDER=2 ALIGN=Left SRC="NewImages/DBLACE3.jpg"></A> <A HREF='Javascript:doImage(4)'> <IMG WIDTH=100 HEIGHT= 100 VSPACE=5 HSPACE= 5 BORDER=2 ALIGN=Left SRC="NewImages/DBLACE4.jpg"></A> <A HREF='Javascript:doImage(5)'> <IMG WIDTH=100 HEIGHT= 100 VSPACE=5 HSPACE= 5 BORDER=2 ALIGN=Left SRC="NewImages/DBLACE5.jpg"></A> <BR CLEAR ALL> <HR> </CENTER> <TABLE ALIGN= RIGHT WIDTH=300><TR ALIGN=RIGHT><TD> Name: <INPUT TYPE=text NAME=aname ><BR> My Image: <INPUT TYPE=text NAME=myimage ><BR> My Text: <INPUT TYPE=text NAME=mytext ><BR> My Link: <INPUT TYPE=text NAME=mylink ><BR> My VLink: <INPUT TYPE=text NAME=myvlink ><BR> </TD></TR></TABLE> <INPUT TYPE=button NAME=reloadBtn VALUE='Rewrite Page' onClick='reloadMe()'> <INPUT TYPE=button NAME=clearBtn VALUE= 'Clear' onClick='clearUp()'> <INPUT TYPE=hidden NAME=hideFld > </FORM> <HR> Click on Netscape's reload button before you click on the Rewrite Page button. <H3><BR><HR><BR></H3> <H3><BR><HR SIZE=5 WIDTH=80%><BR></H3> </BODY> </HTML>
This page illustrates several points which we have touched upon before. It uses a script in the document HEAD to write to the document as it is being created. It dynamically changes the location.search string to store parameter information. Finally, it uses that information during the next rewrite operation to pick up where it had previously left off. Figure 8.4 shows the result of loading this page. The page starts out in the default Netscape colors. It gives the user the opportunity to select text and link colors, a background tile, and a caption. The user is in the process of selecting his chosen tile.
Fig8.4
A Dynamically Rewritten Page Stores information for use in the next rewrite operation.
This script does several things which are worth noting. First, it creates a global array named parms. It does not bother to initialize the array because the program knows which parts of the array it is going to use later. Second, it has a simple routine named reloadMe() to gather up all of the form elements and write them to the location object. Third, it has a simple parsing routine named arrayParms() to dissect location.search and place the pieces in an array. Fourth, its header script acts on the information provided by the user to dynamically reconstruct the page. Figure 8.5 shows all the choices being placed in the summary form as the user clicks and enters text. Figure 8.6 shows the browser window after it has been rewritten dynamically to use the colors and tiles chosen by the user. Note that the summary form's contents have been added to the command line.
Fig8.5
Using the Rewrite Page to Enter Text.
Fig8.6
The Rewrite Page after the User has made some Choices.
You can write to any page which appears in your window hierarchy. You cannot write to a page which is not a part of that hierarchy. Consequently, a parent can write to a child window and a child can write to its parent. If you want a parent window to write to a newly created child window you need to keep a handle to that window. window.open() returns such a handle; you should store this value as a global variable. It may then be used subsequently to access the child. If you do not use a global variable then you will not be able to access the child window from any function other than the one which created it.
Let's examine the very first in this chapter again now that we have more knowledge and experience. The parent window has two buttons, one to open the window and one to close it. These buttons call the parent windows' functions, openNote() and closeNote() respectively. The parent storesthe handle to the openNote window in the global variable aNoteWin. The responsibilities of the open routine are to:
The open routine also exemplifies a common and useful time saving
practice, known as aliasing. The full hierarchichal respresentation
of aNoteWin's document (self.aNoteWin.document) is collected in
the short and easy to write variable ndoc.
Finally, it is worth pointing out the utility of having the child
window's close button in the parent window, especially if the
child window is small. The open() method will not create a second
window of the same name, although it can do some updating if you
make a second open() call with different parameters. If you click
on the 'Make note' button and nothing happens, it is possible
that you did not get rid of the last window you made.
Murphy's
Law dictates that the forgotten child will be at the very bottom
of the eight other large windows on your screen. Being able to
close the window from the parent is a great timesaver and an excellent
debugging tool.
In this sectionwe will write a simple text graph using an
appraoch
similar to that of "
A Document Generated Entirely in JavaScript".
We will provide entry fieldson a form where the user can enter
some numbers. We will then use those numbers to dynamically generate
the text graph using X's. Because we need to keep the size of
the graph so that it will fit within a, we will check all of the
input in the form. This code will be found on the CD-ROM with
Special Edition Using JavaScript, in file TextGraph.htm.
Figure 8.7 shows the initial appearance
of the form.
Fig8.7
The Text Graph generate allows the user to enter data which
will be graphed.
Data are entered into the form on the right. Up to 10 numbers can added. As the data are entered, the program automatically does summary statistics. This is really useful if you want to see whether data outlier can be gracefully thrown away. Once the data are entered, the user heas the option of drawing the graph.
There are several points to examine in this example. The first thing to notice is that data is parsed and saved in exactly the same way as the previous example. Second, the code which draws the graph is conditional on location.search being nonempty. If no parameters have been added then no graph is displayed; instructions are given instead.
As the user enters data it is checked to see if it falls within an acceptable range. As this is done cumulative statistics are also kept. These are presented in a table adjacent to the data itself. Finally, if the location is reset then all the fields in the form will be cleared. This approach is much more polite than using Netscape's reload button. Note that a footer script is used to reload the data fields. Figure 8.8 shows the result after user input has been entered and a graph drawn.
Fig8.8
When data enter is complete the text graph is drawn.
Because Netscape is extremely security conscious, they have made it extremely difficult to store or load data, even between document reloads. We have presented one method of storing simple data between document reloads via a string saved in location.search. Scripts can get and use this information to redraw documents, as we have seen. Are there any other options?
If you have a lot of data, you might appreciate having it accumulated and stored for you. You can use a submit widget or form.submit() to have the browser collect up all of your data, encode it, and store it in the command line. As we pointed out before, the output is very hard to read, and looks like scrambled text.
Another possibility is to store data in dynamic arrays in a window
or, better, in the frameset document. Unfortunately, this, too,
is labile. If you have database like data, it must all be hand
coded into the document which will host it, although you could
use an
HTML builder or other
programming tool to automatically
create the
HTML document.
The only possibility which offers any permanence is Netscape Cookies.
Cookies are lines in a file which Netscape will allow you to write
to disk. You can see your cookies in the file, 'cookies.txt' in
your Netscape directory/folder. Cookies are limited in size and
number. Nevertheless, cookies are extremely useful. There are
frequently used in the same was as you a Windows ini file, a Mac
Preferences file, or a Unix .
rc file The next two subsections
will examine both the command line approach and the cookie approach.
Using the submit() routine to store form field values in the command line leads to a location which looks like this:
?myname=Mona+M.+Everett%2C+Ph.D.&tc=navy&lc=blue&vc=orange&aname=Mona+M.+Everett%2C+Ph.D.&myimage=DBLACE4.jpg&mytext=navy&mylink=blue&myvlink=orange&myurl=http%3A%2F%2Fwww2.best.com%2F%7Edsiegel%2Ftips%2Ftips_home.html%22&cmmt=Welcome+to+my+little+home+page+builder.++There%27s+no+tellin%27+just+how+much+this+page+can+be+expanded.++What+do+you+think%3F%0D%0A&subbtn=Submit
If you examine this output, you may be able to discern some patterns.
First of all, it begins with the characteristic '?' character
which delinerates the location.search string. Second, most of
the information seems to occur in pairs, with the field name as
the lefthand member and the field value as the
righthand member.
The pairs are separared by the character '&'.
There are absolutely no spaces in this output. Every space has been replaced by the '+' character. As we mentioned much earlier in this chapter various escape sequences containing the '%' character occur in this string. These sequences are used to encode nonalphanumeric characters in the input. In particular, any punctuation has been replaced by such a sequence.
Javascript does not give you a lot of tools with which to dissect this sequence. Let's see how we can use the ones we have.First, there are a pair of built in functions to handle the escape sequences. These are escape(), which will take a non-alphanumeric and hand you back the coded sequence, and unescape(), which reverses the process. Second, we can use the substring method of the String object to 'walk' through a string. A statement of the form
myString.substring(start,stop)
extracts all the characters starting from position start and ending at the last character just before the position stop. This allows us examine each character in the string one at a time, if we wish. Based on the character encountered, we can replace it or take some action.
The file C8-13.htm on the CD-ROM with Special Edition Using JavaScript
has two functions which use this approach. The first one, which
decodes the command line search string, provides the core of the
page rewrite code. The second changes all of the '<' and '>'
to '<' and '>', respectively, so that you can write
out HTML to the page. This function is not actively called in
the page but is used for writeline debugging. The latter function
is also really useful if you want to write HTML dynamically to
your page to show your user how to do something.
Let's examine the function which decodes the command line search string, arraySubParms(). It is shown in Listing 8.14. In order to use it, you must first have created and initiated the parms array using a function such as this:
function createArray(n) { this.length = n for (i=1 ; i <=n; i++) this[i] = '' return this }
The parms arrays must not only be created, it must also be initialized
before you use it. The function createArray() does this for you.
The array must be declared as a global variable with the statement
'
var parms = new createArray[10]'
Listing 8.14 (c8-13.htm [extract]) Extracting command line information
function arraySubParms(astr) { k = astr.length astr = astr.substring(1,k) lstr = '' rstr = '' bstr = '' crstr = '' counter = 1 for (i = 0 ; i <= k ; i++) { ccStr ='' ccStr = astr.substring(i,i+1) if (ccStr == '+') ccStr = ' ' if (ccStr == '%') { var xx = astr.substring(i,i+3) ccStr = unescape(xx) i += 2 } // car if (ccStr == '=') { parms[counter] = bstr bstr = '' continue } //right hand member of pair if (ccStr == '&') { parms[parms[counter]] = bstr counter++ bstr = '' ccStr='' continue } bstr += ccStr } }
When arraySubParms(astr) receives a string it immediately chops
off the first character, which is the '?' that starts the location.search
string. It then finds the length of the string and begins a loop
which will cycle through every character in the string with the
statement 'cc = astr.substring(i,i+1)'. The variable cc is then
checked to see if it equal to the character '+'. If it is then
that character is replaced with a space. If cc is a percent sign
the function uses the substring function again to grab three characters,
starting from its current position. It then uses the built-in
function, unescape, to turn these three characters back into an
ASCII character. Since three characters instead of one are used
up, the pointer into the string, I, must be advanced by 3 with
the statement 'I+=3'. In either of these two cases (+ or &)
the next two conditional tests will fail and cc, which may have
been modified , is added to
bstr.
The browser places all of the form element names and their values
into the command line as name=value pairs. Each pair is terminated
by '&'. The next two conditional tests extract the left and
right members of the pair and place them into the left and right
members of the associative array parms. If the function finds
that = character, it knows that bstr, which has steadily been
accumulating characters, now holds the name of the element. Another
variable, counter, is used to keep track of the current index
into the parms array. The left member is set with the
statement'parms[counter]
= bstr'. The variable
bstr is set to the empty string at this
point, so that it can start accumulating characters anew. The
value of counter is not advanced. A 'Continue' statement is used
to bypass the rest of the loop so that the = character which was
just seen is not added into the new value of
bstr.
Javascript associative arrays are one dimensional arrays of pairs. The left and right members of a pair are set differently. Set the left member using a numberical index into the array; e.g., myArray[n] =lvar. Set the right member with an index equal to the value which you placed into the left member; e.g., myArray[myArray[n]] = rvar. You can also use myArray[lvar] = rvar if lvar has not changed between setting the left and right sides of the pair.
If the next test, for the '&' character, yields true, the
function knows that it has now accumulated the right hand member
of the array in bstr. It sets the right hand member with the statement
'parms[parms[counter]] = bstr.' Remember that you set the right
hand member with the index as the name of the left hand member,
not the index itself. The processing of the name=value pair is
now complete. Again, bstr is set to the empty string in anticipation
of the next loop
interation. In this particular case, however,
counter is now incremented with the 'counter++' statement.
When the function finally reaches the end of the location.search string, you will then have all of the variables in a the global parms array. You can now use them anywhere within the current window or in any window which you create.
The second noteworthy function from the c8-18.htm file is the
toprint() function, which changes all occurrences of the '<'
and '>' characters in any string into their corresponding control
codes. This seemingly trivial operation is, in fact, very important.
This is because the < and > characters are interpreted by
HTML. If you wish to write HTML to your document you must somehow
prevent them from being interpreted as HTML delimiters. Converting
them to control codes will do the trick. This function is very
useful for debugging, or to show
HTML example code on your pages.
Listing 8.15 shows the code for this function.
Listing 8.15 (c8-13.htm [extract]) A Function to Print HTML safely
function toprint(it) { var bstr='' var cc='' for(i=0; i< it.length; i++ ) { cc = it.substring(i,i+1) if (cc == '<') cc = '<' if (cc == '>') cc = '>' bstr += cc } return bstr }
We will conclude this discuss of command line parameters by examining the remaining code from file C8-13.htm. It will use the global array you have just prepared to rewrite your page according you specifications. Listing 8.16 shows the page rewrite code itself.
Listing 8.16 File c8-13.html [extract] Rewrite a
Web page used
command line data
var astr = location.search if (astr != null && astr != ''){ // start conditional var parms = new createArray(12) arraySubParms(astr) astr = '<BODY BGCOLOR="linen" ' astr += 'TEXT="'+ parms['mytext'] + '" ' astr += 'LINK="'+ parms['mylink'] + '" ' astr += 'VLINK="'+ parms['myvlink'] + '" ' astr += 'ALINK="red" ' astr += 'BACKGROUND = "NewImages/' + parms['myimage'] + '" ' astr += '><BR>' document.write(astr) //document.write(toprint(astr)) } // end conditional else document.write('<BODY>') document.write('<TABLE ALIGN=RIGHT WIDTH=350 BORDER=1>') document.write('<TR><TD>') document.write('<FONT SIZE=7 COLOR= "indianred">') document.write('<CENTER>' +document.title + '</CENTER>') document.write('</TD></TR></TABLE>') document.write('<LEFT><B>') document.write('This page is an example of dynamically revised by a header script which acts on information stored in the command line. That information is based on user\' choices.') document.write('</B></LEFT>') document.write('<BR CLEAR ALL>') document.write('<HR>') var astr = location.search if (astr != null && astr != ''){ // start conditional astr ='<CENTER><FONT SIZE=7 COLOR="' + parms['link'] + '"><B> ' astr += parms['aname'] + '</B></FONT></CENTER>' document.write(astr) } // end conditional document.write('<HR><BR>')
Let us examine the operation of this script in a little more detail. There are several points worth noting. First of all, the property location.search is examined to make sure that it is not null or the empty string. If location.search does not contain a valid string then most of the script processing is skipped. Two if..else statements are used for this purpose.
After the search string has been obtained and the parms array
filled in by the call to arraySubParms(), the script starts building
the BODY statement.. Note that it builds it into a string and
does not write it immediately with document.write. Note, too,
the commented out call to printit(), which was used during debugging
to see if the string was built correctly.
Accumulate your HTML output in a string rather than using individual document.write() calls. This will facilitate debugging.
Once the string has been assembled, the <BODY...> statement, which sets the background image and colors is written to the document. If there was no search string, a plain <BODY> statement is written.
The script then writes a nice header for the document. The script
next uses a second conditional clause to write your name in large
letters. It had to check for the existance of a search string
in order to do so. If the search string is present, you get your
name; if it is absent, go get brief directions on using the page.
When the header script is complete the HTML on the page will be
interpreted by the browser.
Since all the form elements may be cleared with a submit, this program is polite and restores all of them from the global parms array. Instead of writing each one separately, it iterates through the form.elements array. Remember that the array was created with the element name as the left hand member of the array. This makes it easy to get the correct variable in the form element. This could have also been done using numerical indexing. The routine shown is particularly useful if you have a large number of elements to restore.
Notice that a couple of form elements were included which were not used to construct the page. We included them here to provide a lot of escaped characters and a longer string of text with which to test the script.
The only method you can use to store variables between invocations of Netscape Navigator is the cookie approach. This is also the only approach which will work with windows in different hierarchies. Cookies were originally designed to allow a server to save state information on the client's disk. When the client contacted that same host at a later time the previously saved cookie would be sent back to the server. Cookies are therefore useful if a browser connection is interrupted and you want to pick up where you left off. It is also useful in case the server crashes and later wishes to pick up where it left off. Cookies are now available for general use in Javascript.
Cookies have five parameters:
Only the first one, which is a the familiar name value pair, is required. All of the others are optional. However, if you do not save an expiration date, the cookie will automatically expire when you close Netscape--not something you want to happen if you want to keep information from session to session. The various parameters are separated by semicolons. If you create a cookie with the same name and path as a cookie already in existence the new one you will overwrite the existing one.
Although servers can write named cookies one at a time, Javascript cannot. You can set an individual named cookie with the statement document.cookie='cookiename=xxxx', but when you retrieve document.cookie, you will get a string consisting of all of the cookies. Currently the only way to retrieve an individual cookie is too search through the entire set of cookies obtained from document.cookie. Consequently, it helps to prefix or suffix the names of your cookies with a little-used character(s). This makes them easy to find with IndexOf().
Let's examine each of the cookie parameters in turnAs stated,
the NAME=VALUE parameter is an associative pair. This means that
it lends itself nicely to being stored in arrays and placed in
form elements. This is the only required element of the cookie.
The expires=DATE parameters is used to describe the expiration
date for the cookie. As defined by Netscape, the date format must
be 'Wdy,
DD-Mon-YY HH:MM:SS GMT' , with the separators exactly
as given. If you do not want persistent data between browser invocations,
leave out this expiration date. If you want your cookie to never
expire, give it a date several years in the future.
The path=PATH parameter is uses to limit the search path of a
server which can see your cookies. This is analagous to specifying
a document BASE in an HTML document. If you use '/' then everything
in the domain can use your cookies.
The domain=DOMAIN_NAME parameter is only useful if the server
is setting the cookie or, if for some reason, you want to generate
a cookie which is available to the server. If the server generated
the cookie, then the default domain is the
domain name of the
server which generated it. Finally, the parametersecure indiceates
that the cookie should only be sent if there is a secure client-server
relationship.
Listing 8.16 shows the cookie versions of the routines for saving
and restoringa persistent information. In this case the information
comes to and goes from the document cookie, rather than the command
line search string. These routines have been liberally modified
from those presented by Bill Dortch and placed in the
public domain.
Listing 8.16 c8-14.htm Saving and Restoring document cookie information
function fixSep(what) // escapes any semicolons you might have in your data { n=0 while ( n >= 0 ) { n = what.indexOf(';',n) if (n < 0) return what else { what = what.substring(0,n) + escape(';') + what.substring(n+1,what.length) n++ } } return what } function toCookie() { document.cookie = '' nform = document.data for (i=0 ; i<nform.length; i++) { expr =makeYearExpDate(1) astr = fixSep(nform.elements[i].value) //astr = nform.elements[i].value astr= nform.elements[i].name + '=' + astr + ';expires=' + expr + ';path=/' document.cookie=astr } } function makeYearExpDate(yr) { var expire = new Date (); expire.setTime (expire.getTime() + ((yr *365) *24 * 60 * 60 * 1000)); expire = expire.toGMTString() return expire } function getCookieAt(n) { e = document.cookie.indexOf (";", n); if (e == -1) e = document.cookie.length rstr= unescape(document.cookie.substring(n,e)) return rstr } function fromCookie() //restores summary fields from cookie { nform = document.data astr = document.cookie alert(astr) cl = astr.length counter=0 for (i = 0 ; i < nform.length ; i++) //for (i = 0 ; i < 1 ; i++) { nstr = nform.elements[i].name + '=' ll = nstr.length jx = 0; while (jx < cl) { k = jx + ll; xstr = astr.substring(jx,k); //alert(nstr + '\n' + xstr + '\ni=' + i + '\nj=' + jx + '\nk=' + k + '\nll=' + ll + '\ncl=' + cl); if (xstr == nstr) { nform.elements[i].value = getCookieAt(k); break ; } jx = document.cookie.indexOf(" ", jx) + 1; if (jx == 0) break ; } } } function arrayFromCookie() // fills global array from cookie { astr = document.cookie cl = astr.length k=0 //for (i = 0 ; i < nform.length ; i++) jx = 0; for (i = 0 ; i < 6 ; i++) { jx=astr.indexOf(' ',jx) k = astr.indexOf('=',jx); xstr = astr.substring(jx+1,k); //alert(xstr + '\ni=' + i + '\nj=' + jx + '\nk=' + k + '\ncl=' + cl); parms[i]=xstr; parms[parms[i]] = getCookieAt(k+1); jx = astr.indexOf(";", jx) + 1; if (jx <= 0 || i > 10) break ; }
The function makeYearExpDate() allows you to set the expiration date for some number of years in the future. It was designed for really persistent cookies. If you want a shorter time, you can easily modify this routine. Note that this function uses on the Date object heavily. The static method Date.getTime() returns a neatly formatted date string, while the method and Date.toGMTime() returns the date converted to Greenwich Mean Time, which is what the cookie expiration mechanism expect your cookies to contain.
Thefunction fixSep() escapes any semicolons which you might have in your variables. It is highly undesirable to store semicolon in the cookie parameters, since semicolon is the parameter separator.. You could, in fact, escape all the non-alphanumeric characters in the entire string. However, this would make it difficult to read, especially if you simply want to look at the cookie.
The functionGetCookieAt(n) retrieves the cookie value starting at an offset of n characters into the cookie string. It replaces all escape sequences with their ASCII values. The functionFromCookie() restores all of the summary forms variables from the cookie. It is really an undo function.
The final function, arrayFromCookie() is called by the page rebuilding routines to build the global array, parms, from which the page is rewritten. Notice that we did not have to change the page rebuilding code from that of Listing 8.15. We only changed the routine to build the parms array.