Tom is a student in the engineering physics department at the University of Alberta, Canada. He can be reached at tessier@ee .ualberta.ca.
JavaScript is a cross-platform object scripting language that lets you glue together HTML documents, Java applets, and Netscape plug-ins on both clients and servers. One way of differentiating between Java and JavaScript is that Java is typically used by programmers to create new objects and applets, while JavaScript is used by HTML page authors to dynamically script the behavior of those objects.
Although it was developed jointly by Netscape and Sun, JavaScript (which is based on LiveScript, Netscape's HTML scripting language) has already been licensed by a number of software companies, including Spyglass, Oracle, Metrowerks, Sega, Borland, Adobe, and Sybase.
Implemented only within Netscape Navigator 2.0, Beta 2 and up, JavaScript still is in its very early stages and is more a complement to ordinary Java applets than a stand-alone replacement. However, even in its current, primitive form, this scripting language transforms ordinary HTML into a powerful, client-based interpreter. For example, JavaScript, embedded in a Web page, can recognize and respond to user events such as mouse clicks, proofread form inputs before sending data off to servers, and more.
JavaScript resembles Java in that support for most of Java's expression syntax and flow-control features is available, as well as numeric, Boolean, and string types. But unlike Java, which is a compiled language, JavaScript is executed on the fly by the Netscape interpreter. JavaScript is relatively secure in that no writes to a user's hard drive can occur. Also, JavaScript programs can be run from any page, without requiring root or similar file-access privileges. This is perhaps the main advantage of JavaScript--average users can store complex CGI-like scripts on their current page and still only pay the usual monthly Web rental fee.
Take a look at Example 1 and the resulting page in Figure 1. The <SCRIPT LANGUAGE="LiveScript"> tag is used to initiate the JavaScript session. Alternatively, a URL filename may be specified by using SRC in the tag <SCRIPT SRC="script.ls''>. The script is evaluated once after the page loads. Functions, however, are stored in memory, allowing for repeated execution upon user events. Notice the HTML comment tag (<!--) within the JavaScript. This prevents older browsers from dumping the script contents onto the page. Also, the <SCRIPT> tag was placed directly after the <HEAD> tag. Since everything between <HEAD> is loaded first, placing the script tag right after it ensures that the JavaScript code is available before the user has a chance to trigger any event handlers. Keep in mind that the entire page is loaded before any script tags are evaluated.
The document object is used in Example 1. Although you cannot create your own objects, the built-in objects are still quite useful. Figure 2 presents some of the available objects and their properties, methods, and event handlers, while Example 2(a) illustrates their uses. It is important that you think of treating a form's input text areas as outputs. The first and second form inputs are used to interact with users, while the last exists solely to provide an output-text area. This works well if you can get used to using a form input as an output. As you can see in Example 2(a), event handlers are actually embedded within normal HTML code, inside the declaration of tags such as <FORM>, <INPUT>, or <A HREF>. These event handlers only activate when the desired action occurs--a mouse click, mouse over text area, and the like. <SCRIPT> tags are not required for event handlers.
The onChange handler passes the properties of the calculation text area to a custom restore function, which acts as the output text area watcher. If the user changes the calculation area, the function restores the text to its default value and displays a warning. In the onClick handler, calc(form) is called with the argument this.form. Such an identifier indicates to pass all form input properties to the desired function, allowing for smaller argument lists. Although not recommended, all of the required identifiers could have been passed instead, as in Example 2(b).
The eval command in Example 2(a) is a built-in function that evaluates the mathematical expression stored in brackets. There are four different types of built-in functions, three of which are listed in Figure 3.
If you need to change the value of a form object not included in a function-input list, the properties must be accessed directly. In the case of forms, the properties are referred to as forms[0], forms[1], and so on. For example, referring to a text field named "response" in a document's third form requires using the object document.forms[2].response. To change the value of the text in that form, you would do something like document.forms[2].response.value = "New text." This type of direct access also is illustrated in Example 2(c). Notice that the restore function has been replaced with actual JavaScript code, semicolons separating each command.
In its current implementation, JavaScript doesn't offer built-in array creation. You must define your own "make-array" function to set up the required properties and methods as in Example 3.
Listing One utilizes all of the concepts I've discussed so far. When executed, the program presents the client browser with an interactive form, asking users to select an answer out of three options; see Figure 4. In essence, it is a multiple-choice test. When users select a wrong answer, alert is used to display the correct result. A counter keeps track of the total number of questions correctly answered.
Note in Listing One, that trying to call a function involving document.write after a script has been loaded is illegal; see Example 4. This is the reason why form-text-area inputs are used as outputs throughout Listing One. Also, simply reloading the JavaScript multiple-choice page into Netscape via Ctrl-R isn't enough to reset the form, since all default values are changed by the code. You either have to add a Reset button or a link (a link is used in Listing One; if index.html is the name of the JavaScript testing program, then the page will be reset).
While Listing One contains everything within a single HTML file, there are two other ways to output new questions to the client browser. Both techniques allow for the creation of dynamic Web pages, whereby new images can be placed alongside new text (and not have all the text appear in form inputs). But both methods require multiple HTML files to work.
The first technique, easily implemented using the concepts I've presented here, demands a new URL be loaded every time a radio button is pressed. This involves setting up separate URLs for each question--a running total of the number of correct answers cannot be maintained without using some CGI.
The second method needs only three HTML files, and allows you to easily keep track of the number of correct answers. This second technique makes use of a relatively new concept called "frames" (again available only in Netscape 2.0 and up). Simply by taking advantage of Netscape's ability to share data between frames and using a "hidden" frame as a temporary storage area, you can compute new data for the main "display" frame. For example, frame 0 (hidden from the user's view) would contain a form named "code," which is just an empty scratch text area. The JavaScript code in frame 1 must access the code from frame 0 (using parent.frames[0].document.forms[0]. code.value) and use document.write to display it (first reload the display frame with parent. frames[0].location = "sameurl"). The technique is complicated, and since Netscape's current implementation of frames and data sharing is very unstable, I won't go into it any further. After all, it's difficult to debug a page when GPFs randomly occur. In fact, in the current version I am using (2.0, Beta 4), simply resizing a Netscape window containing frames can crash the system.
JavaScript is a powerful, interpreted Web language. Used in conjunction with normal HTML, Java applets, and CGI, interactive Web content can be vastly improved. Load can be transferred off of Web servers, easing strains and improving performance. With some imagination and ingenuity, anyone can create exciting content. Good luck and happy surfing!
(a)
Properties:
parent -- parent frame.
frames[index] -- array of frame objects, one per frame.
frames.length -- number of frame objects in the window.
status -- enables you to set the message in the status bar at the bottom of the client window.
Example:
<A HREF="" onClick="this.href=getURL()"
onMouseOver="window.status='Stay on target'; return true">
Go!</A>
Methods:
alert("string") -- pop up a window displaying "string" .
confirm("string") -- pop up a window displaying "string" .
Returns True if Okay clicked, False, if Cancel clicked.
prompt("string",default) -- pop up a window displaying "string" and prompt the user for input, where default is the default-input value.
Example:
<INPUT TYPE="text" NAME="inputarea" SIZE=10
onMouseOver="document.forms[0].inputarea.value= prompt('Enter a number:',0)">
(b)
Properties:
forms[index] -- array of form objects, one per form.
forms.length -- number of form objects in document.
links[index] -- array of HREF link objects.
links.length -- number of link objects in document.
Methods:
write("HTML") -- write the raw HTML commands to the current window .
Example:
document.write("<HTML><TITLE>Digital Signal Processing</TITLE></HTML>")
writeln("HTML") -- same as write(), but adds a carriage return.
to the end.
clear() -- clears the window.
(c)
Properties:
action -- string value of the ACTION attribute.
Event Handlers:
onSubmit() -- executed when the form is submitted.
Example:
<FORM action=post onSubmit="javafunc()">
Methods:
submit() -- submits the form.
(d)
Properties:
name -- the value of the NAME attribute (a string).
value -- the contents of the field (string).
defaultValue -- the initial contents of the field (string).
Event Handlers:
onChange -- executes after the user modifies the text contained within the box.
onFocus -- executes when input focus enters the field.
onBlur -- executes when input focus leaves the field.
onSelect -- executes when something is selected inside the field .
(e)
Properties:
name -- the value of the NAME attribute (string).
value -- the value of the VALUE attribute (string).
Event Handlers:
onClick -- execute when button clicked.
Methods:
click() -- select a radio button.
Note: click() does not actually click on a radio button, just changes its value to on. It cannot be used to activate an event handler.
(a)
Properties:
E -- value of the constant E (precision equal to that of a real
number)
LN10 -- value of LN 10
LN2 -- value of LN 2
PI -- value of the constant PI
SQRT2 -- value of the SQRT of 2
Methods: (standard math functions)
abs(value)
acos(value)
asin(value)
atan(value)
cos(value)
sin(value)
tan(value)
exp(value)
log(value)
round(value)
sqrt(value)
Example:
Math.cos(Math.PI/2) gives 0.
Apply the "with" command when using math functions. This special command allows Math objects to be written without the "Math" reference.
Example:
with Math {
a = cos(PI/2)
b = sin(PI/2)
}
(b)
Example:
to define a string, simply write the following JavaScript code:
mystring = "What String Equals"
Properties:
length -- defines the length of a string
Methods:
substring(i, j) -- takes a substring from i to j inside the string
Example:
result = mystring.substring(0,4) gives "What"
toLowerCase() -- converts mystring to lowercase
toUpperCase() - converts mystring to uppercase
Example:
result = mystring.toUpperCase gives "WHAT STRING EQUALS"
(c)
eval(expression) - calculates the result of expression.
<script language="LiveScript">
// This function defines an array such that the first
// property, length, (with index of zero), represents
//the number of elements in the array. The remaining
// properties have an integer index of one or greater,
// and are initialized to zero.
function MakeArray(n) {
this.length = n;
for (var i = 1; i <= n; i++) {
this[i] = 0 }
return this }
array = new MakeArray(2);
array[1] = "Apple"
array[2] = "Orange"
var i = 0
while (i < 2) {
i++
document.writeln(array[i]) }
</script>
<HEAD>
<SCRIPT LANGUAGE="LiveScript">
<!-- Use a comment to hide the script contents from unsupported browsers.
var n = 19
var max = 100
document.writeln("Hello world.")
document.writeln("Let's count from ", n+1, " to ",
max, ":<BR>")
count(n, max)
function count(ninput, maximum) // count from ninput to maximum and
// display in document
{
while( ninput < maximum )
{
ninput ++
document.write(ninput," ") // write the current value of n, placing a
// space between each number
}
document.writeln("Done writing.")
}
// End old browser hiding here. -->
</SCRIPT>
</HEAD>
<BODY>
This is the usual HTML text body.
<SCRIPT LANGUAGE="LiveScript">
<!-- hide from old browsers
document.write("<BR>And this is a link coded into ")
document.writeln("the body via JavaScript.")
document.writeln('<A HREF="http://www.ee.ualberta.ca">EE Page</A>')
// -->
</SCRIPT>
</BODY>
</HTML>
(a)
<HTML>
<HEAD>
<SCRIPT LANGUAGE="LiveScript">
function calc(form)
{
form.calculation.value = eval(form.matharea.value)
form.calculation.defaultValue = form.calculation.value
// and set the default value in case have to restore the
// output textarea (ie: if the user puts some text into it)
}
function restore(input)
{
input.value = input.defaultValue
alert("Please do not touch the result window.")
}
</SCRIPT>
</HEAD>
<BODY>
<FORM>
Enter a mathematical expression, such as 3.14 + 5 * 20.333 / 40.
<INPUT TYPE="text" NAME="matharea" SIZE=30>
<INPUT TYPE="button" VALUE="Calculate the math" onClick="calc(this.form)">
<br>Calculated result:
<INPUT TYPE="text" NAME="calculation" onChange="restore(calculation)"><BR>
</FORM>
</BODY>
</HTML>
(b)
replace
function calc(form)
with
function calc(finalresult, mathinput)
{
finalresult.value = eval(mathinput.value)
finalresult.defaultValue = finalresult.value
}
replace
<INPUT TYPE="button" VALUE="Calculate the math" onClick="calc(this.form)">
with
<INPUT TYPE="button" VALUE="Calculate the math" onClick="calc(this.form.
calculation, this.form.matharea)">
(c)
replace
<INPUT TYPE="text" NAME="calculation" onChange=restore(calculation)>
with
<INPUT TYPE="text" NAME="calculation"
onChange='calculation.value=calculation.defaultValue;
alert("Please do not touch the result window.")'>
Although not necessary in this case, onChange='document.forms[0].calculation.value=
document.forms[0].calculation.defaultValue;alert("...
may have been used instead.
<script language="LiveScript">
// This function defines an array such that the first
// property, length, (with index of zero), represents
//the number of elements in the array. The remaining
// properties have an integer index of one or greater,
// and are initialized to zero.
function MakeArray(n) {
this.length = n;
for (var i = 1; i <= n; i++) {
this[i] = 0 }
return this }
array = new MakeArray(2);
array[1] = "Apple"
array[2] = "Orange"
var i = 0
while (i < 2) {
i++
document.writeln(array[i]) }
</script>
<SCRIPT LANGUAGE="LiveScript">
function writedoc () {
document.clear()
document.writeln("<HTML><BODY>Hi man.</BODY></HTML>")
}
</SCRIPT>
<BODY>
<A HREF="la.html" onMouseOver="writedoc()">Place
mouse over me</A>
</BODY>
<html>
<head>
<title>Sample JavaScript Testing Application</title>
<script language="LiveScript">
<!-- hide this script tag's contents from old browsers
// editable variables
var totalnum = 3 // total # of questions
var totalans = 3 // total # of answers per question (used to
// generate the question/answer array below)
var correctans = 1 // the question # (from 0 to N-1) of the
// correct answer to the first question. ***NOTE***: Be sure to
// initialize this to the correct answer of the first question.
// fixed variables: don't touch these
var count = 0
var arrayind = 1 // index into the questans array - must
// start at 1 always, since that's where the first array string is
var rightans = "none"
var totalright = 0 // total # of questions answered right
// This function defines an array such that the first property, length, (with
// index of zero), represents the number of elements in array. The remaining
// properties have an integer index of 1 or greater, and are initialized to 0.
function MakeArray(n) {
this.length = n;
for (var i = 1; i <= n; i++) {
this[i] = 0 }
return this
}
// formula for # of array elements:
// total number of questions times total answers allowed per question
// + 2 (plus two because have to have a string for the actual
// question itself and for the value indicating the correct answer).
// eval used to convert (totalnum)*(totalans+2) into a number usable
// to pass to MakeArray... need eval since the expression is inside a
// function call (ie: inside the MakeArray call)
questans = new MakeArray(eval((totalnum)*(totalans+2)));
// array for question list (start with question #2 - define question
// one in the document's html code below)
questans[1] = "Pyconuclear reactions are:"
questans[2] = "reactions which require high density."
questans[3] = "reactions which depend on heat."
questans[4] = "reactions which require low density."
questans[5] = 0 // correct answer here is "reactions which require
// high density" (list answers from 0 to ans#-1)
questans[6] = "A white dwarf maintains its compact shape via:"
questans[7] = "coulombic repulsion."
questans[8] = "fermi neutron pressure."
questans[9] = "fermi electron pressure."
questans[10] = 2 // correct answer: "fermi electron pressure."
// end of editable questions. Don't change the below (just the text
// displayed at the end of the test)
questans[11] = "The test is complete. Thank you."
questans[12] = ""
questans[13] = ""
questans[14] = ""
questans[15] = 255 // correct answer: none
// create new form outputs in response to mouse click on a radio button
function checkout(form, questionnum)
{
if ( count > totalnum) // if user clicks a radio button after the
// test is complete, display an alert
{
alert('To return to the main page, click on "Return to Main Page."')
}
else
{
if ( questionnum == correctans) // if the currently selected
// question is the correct response, say so
{
totalright++
alert("Correct.")
}
else // if wrong answer, display the correct one
{
if ( correctans == 0)
{ rightans = form.answer1.value }
if ( correctans == 1)
{ rightans = form.answer2.value }
if ( correctans == 2)
{ rightans = form.answer3.value }
alert("Incorrect. The right answer is:\n"+rightans)
}
count++ // increment count so can goto next question
if ( totalright == 1) // if only one right, make sure use the
// word "answer" instead of the plural form "answers" (To Appease the
// Pro Literacy Net Movement)
{
form.completed.value = "You have completed "+count+" of "+
totalnum+" questions, with "+totalright+" correct answer."
}
else
{
form.completed.value = "You have completed "+count+" of "+
totalnum+" questions, with "+totalright+" correct answers."
}
form.completed.defaultValue=form.completed.value // and set the
// default value for the completed text area to the same as the current
// value (in case user enters garbage into the completed text
// area, so can restore it)
// increment arrayind after each use below
form.question.value = questans[arrayind++]
form.question.defaultValue=form.question.value // and set the
// default value to the new value in case the web user clicks on the
// actual Question text and changes it... so can restore the new
// values from the questans array
form.answer1.value = questans[arrayind++]
form.answer1.defaultValue=form.answer1.value
form.answer2.value = questans[arrayind++]
form.answer2.defaultValue=form.answer2.value
form.answer3.value = questans[arrayind++]
form.answer3.defaultValue=form.answer3.value
correctans = questans[arrayind++] // and set the new correct
// answer
if ( count == totalnum) // if count = the total # of questions
{
count++ // then increment count again so is greater than
// totalnum so can activate the alert above if user tries to click on
// a radio button after the test is complete
}
}
}
// restore a form input to its default value if the user messed with it
function restoreval(input)
{
input.value=input.defaultValue
alert("Please click on the radio buttons only.")
}
<!-- done hiding from old browsers -->
</script>
</head>
<body>
<h1>JavaScript Multiple Choice Test</h1>
<form method="post">
<TEXTAREA name="question" rows=1 cols=50 wrap=soft onChange=
"restoreval(question)">
Question 1: When our star (Sol) dies it will most likely become:
</TEXTAREA>
<BR>
<BR>
<BR>
<LI><INPUT TYPE="radio" onClick="checkout(this.form, 0)">
<!-- parameter this.form means pass all parameters from this whole
form to the checkout JavaScript function. The second parameter
indicates the question # the user clicked on (from 0 to (Number of
questions)-1) -->
<TEXTAREA name="answer1" rows=1 cols=50 wrap=soft onChange=
"restoreval(answer1)">
A black hole.
</TEXTAREA>
<BR>
<BR>
<LI><INPUT TYPE="radio" onClick="checkout(this.form, 1)">
<!--<input name="answer2" size=40 value="Mary Lou">-->
<TEXTAREA name="answer2" rows=1 cols=50 wrap=soft onChange=
"restoreval(answer2)">
A white dwarf.
</TEXTAREA>
<BR>
<BR>
<LI><INPUT TYPE="radio" onClick="checkout(this.form, 2)">
<!--<input name="answer3" size=40 value="Harry Fesie">-->
<TEXTAREA name="answer3" rows=1 cols=50 wrap=soft onChange=
"restoreval(answer3)">
A neutron star.
</TEXTAREA>
<br>
<br>
<br>
<TEXTAREA name="completed" rows=1 cols=50 wrap=soft onChange=
"restoreval(completed)">
You have completed 0 of 3 questions, with 0 correct answers.
</TEXTAREA>
</form>
<br>
<br>
<LI><A HREF="index.html">Return to main page.</A>
</body>
</html>
DDJ