Chapter 5

Client-Side JavaScript


CONTENTS


One of the distinctions of Netscape ONE technology is the emphasis on dynamic content in a site-transforming static pages into a live application. One of the most powerful methods for including dynamic content is JavaScript.

JavaScript is a distant cousin of Java, the Web program-ming language that was developed by Sun Microsystems. Developers at Netscape recognized that the object-oriented features of Java were a double-edged sword-they made it easier to write powerful, extensible programs but demanded more sophistication from the programmer. The Netscape developers were building a simpler language, similar in some respects to Java, but reachable by people who were not professional programmers. The result was JavaScript.

A Primer on Object-Oriented Programming

Among software engineers, the hottest terms today are object-oriented analysis (OOA), object-oriented design (OOD), and object-oriented programming (OOP). Indeed, one wag has coined the phrase Object-Oriented Everything. This section describes object-oriented programming by showing what software development was like before objects became popular.

The Bad Old Days: Life Before Software Engineering

Software development has gone through three major phases over the past 30 years. Early programmers just wrote code, with little emphasis on analysis and design and not much structure to their products. That approach ran out of steam by the late '60s. New languages like Pascal, C, and Ada were invented to support a new approach-structured analysis, design, and programming.

The structured methods laid an excellent foundation, but by the 1990s, engineers were looking for a better way. They found that way in the object-oriented techniques. New languages, such as C++ and Eiffel, were developed to take advantage of these methods. Old languages, such as Ada, were updated to introduce object-oriented capabilities. Today, much of the newest software is developed by using object-oriented techniques.

In August 1988, Ed Yourdon, one of the leaders of the structured revolution, published an article in American Programmer entitled "Sayonara, Structured Stuff." By this time, many software engineers were feeling the limitations of the structured methods and were adopting a much older technology that had suddenly become popular: object-oriented methods.

Object-oriented methods take the principles of information hiding and encapsulation, developed as part of structured analysis and design, and add to them the concept of a class hierarchy. This approach has brought software engineering full-circle-programmers are again encouraged to use routines written by their colleagues, but this time those routines are encapsulated in objects with well-defined interfaces so that changes in the implementation have little or no effect on the interface.

Figure 5.1 shows the architecture of a typical object-oriented design. Each object instance has data; each class of object has methods, which may manipulate the data. Much of an object's work is done by sending messages to other objects. These messages are implemented by the class's methods-one object cannot usually read or change the data in another object.

Figure 5.1: Object-oriented methods emphasize information hiding.

Classes and Instances

Central to the idea of object-oriented programming is the idea of classes. Classes represent a concept. For example, in an aircraft simulator example the developer may identify the Boeing 727 as a type of aircraft. When the program runs, it may build one 727 or a fleet of hundreds. Each aircraft shares common behavior-after all, they're all 727s-but each one is a separate instance. The class contains a pattern for the aircraft. For example, it has an altitude variable. If two instances of the aircraft are built at runtime, they may both start off with an altitude of 0 feet-they're on the ground. If one aircraft is told to take off and climb to 30,000 feet and the other aircraft is left parked, the two instances end up with very different values in their altitude variable: 30,000 feet for the one and 0 for the other. Figure 5.2 shows how different instances take on different values.

Figure 5.2: Two instances of the class Boeing727 have different values in their instance variables.

Abstract Base Classes  Some concepts are too abstract to form real instances. For example, the Boeing 747, the McDonnel-Douglas DC-10, and the Lockheed L1011 are all examples of a wide-body commercial jet aircraft. You could build a fleet of 747s, DC-10s, and L1011s, but you could never build a generic wide-body commercial jet. Programmers call these "generic" classes abstract. Some programming languages, like C++, enable a programmer to tell the compiler which classes are abstract, so the compiler warns the programmer if he or she attempts to instantiate an abstract class.

The proper use of abstract classes, of course, is to serve as the basis for other classes. Figure 5.3 shows a hierarchy consisting mostly of abstract classes, with concrete classes as the leaves of the hierarchy.

Figure 5.3: Many designers prefer to use only abstract classes as base classes and never derive from a concrete class.

Mix-In Classes  Sometimes, real-world concepts do not fit into a pure hierarchy. For example, aircraft come with a variety of engines. For simplicity, say there are only two: jets and turboprops. If you have to distinguish between these two types of engine for each node of the hierarchy, you end up with the complex hierarchy shown in Figure 5.4.

Figure 5.4: This complex hierarchy captures aircraft and engine information.

You may prefer to have two simpler hierarchies, one for the aircraft and one for the engine, as shown in Figures 5.5 and 5.6.

Figure 5.5: This simpler hierarchy deals only with aircraft.

Figure 5.6: Engine information is kept in its own hierarchy.

Some languages, like C++, enable the designer to mix two or more classes into the definition of another. This capability is called multiple inheritance (see Figure 5.7).

Figure 5.7: Multiple inheritance enables the programmer to use mix-in classes.

Note
Software engineering companies commonly adopt style guides and naming conventions so that their staff can tell at a glance what sort of entity a name refers to. In one common naming convention, an initial T means a class, and an initial M means a mix-in class. These naming conventions are not used by the compiler but serve to make the engineers' lives easier.

Properties and Methods

Each class has variables that should be associated with each instantiated object. As shown earlier, each aircraft has an altitude. It also might have a position, a course, and a speed. Depending on the application, it may need to know the captain's name, the number of people on board, or how much fuel remains in the tanks. Each fact known about an instance of the class is stored in an instance variable. The facts known about an instance of the class are sometimes called attributes or data members.

The class also provides methods by which other classes operate on it. For example, the aircraft may be told to climb, descend, and turn by using its Climb(), Descend(), and Turn() methods. Each of these methods will typically take parameters: Climb() and Descend() may take a destination altitude and a rate of change. Turn() may take a new heading.

Figure 5.8 shows a common notation for representing instance variables and methods.

Figure 5.8: In this notation, the class name is given in the top box, data members are listed in the second box, and methods are listed in the third box.

Now you begin to see the power of object-oriented programming. Within the classification hierarchy, methods and data members can be added, modified, or overridden. Basic flight operations like Climb() can be written once for all types of aircraft-Climb( ) would appear in the TAircraft abstract base class. Data members most relevant to commercial aircraft, such as NumberOfPassengers, may appear in TCommercialAircraft, whereas some data members apply only to military aircraft (such as Armament).

Many methods benefit from defaults. For example, the default rate of climb in Climb() on class TFighter may have a much higher value than the default in the same method on a commercial aircraft. A programmer using instances of a fighter and a commercial aircraft can tell both aircraft to climb to 30,000 feet, confident that each will execute the maneuver in the appropriate way.

JavaScript has built-in objects that reflect the hierarchical structure of an HTML document. For example, the window object at the top of the hierarchy has methods like open and close (which open and close a browser window). It also has the methods alert (which puts up an alert dialog) and confirm (which puts up a confirmation dialog). Frames are also part of the windows object. The child frames of a window are stored in an array: the first frame is window.frames[0], the second is window.frames[1], and so forth.

The document object has the useful method write. Using document.write, you can send HTML to the current document.

Each form is a child of the document object, and each element is a child of its form. Thus, the account_number field on the form test is document.test.account_number. After you know the name of an object, you can call its methods and access its data members. Thus, the value of the account_number field is given by document.test.account_number.value.

ON THE WEB
http://home.netscape.com/comprod/products/navigator/version_2.0/
script/
  For a full list of the methods of the built-in JavaScript objects, visit this Netscape site.

Properties and Access Methods

From time to time, you may change how you compute an instance variable, or you may want to take some action whenever the instance variable is changed or read. If you allow functions outside the class to read and write the instance variables directly, you give up that kind of control.

A better design is to identify which instance variables need to be read or set by software outside the class. Then, build access methods to grant the necessary access to those variables. For example, no one outside the class should be able to set the Altitude variable-it gets set by the Climb() and Descend() methods. But other classes may need to read the altitude. Write a small method that just returns the altitude. Similarly, the CaptainsName variable should be readable and "writeable" by users of the class. Write a CaptainsName() access method that returns the CaptainsName variable and a SetCaptainsName(string) method that takes its string parameter and copies it to the CaptainsName variable.

Some object-oriented languages, like C++, enable the designer to specify some methods and data members as private or protected-they cannot be accessed from outside the class.

Methods: Static and Otherwise  Sometimes a method does not apply to the instances-it applies to the class as a whole. For example, you may need a Count() method to tell you how many aircraft are in your fleet. Many object-oriented languages enable the developer to specify that a method or data member is static-it applies to the whole class and not to any one instance. Even when you work in a language that does not support static members, you can still design for such members and implement them by using a small related class with one instance.

Where Did All the Pointers Go?

Object-oriented languages enable the programmer to make new instances by running the class constructor. Objects run the class destructor when they are destroyed. The constructor typically returns a special variable called a pointer-a variable that stores the address in memory where the object begins.

Pointers are quite convenient; instead of passing around the whole object, the programmer can just pass the pointer. Unfortunately, the pointer sometimes gives the programmer too much power. The ability to point to any memory location at random can be abused. Furthermore, pointers become invalid when the object they reference is deleted. Many of the problems in object-oriented code come when a programmer attempts to use a pointer to a deleted object.

The designers of Java and JavaScript decided that pointers were too dangerous to be allowed in their languages. They have combined many of the benefits of pointers, however, in the objects themselves.

Arrow Notation  In languages like C++, programmers access the members of an object by following the pointer to that object with an arrow that is made up of a dash and a right angle bracket. Thus, a C++ programmer can make a new aircraft and pfly it up to 10,000 feet by writing:

TAircraft* theAircraft = new TBoeing747();
theAircraft->Climb(10000);

Dot Notation  If the programmer doesn't have a pointer to an object, he or she can call the methods of the object directly by using dot notation. In C++, the same example as the one above can be written:

TAircraft theAircraft = *(new TBoeing747());
theAircraft.Climb(10000);

Because Java and JavaScript do not use pointers, those languages use the dot notation exclusively. Programmers who have experience in C++ find this notational change to be one of the biggest differences between C++ and Java.

Defining Functions

Many languages that were introduced about the time structured programming came into vogue are strongly typed. This statement means that the programmer tells the compiler how each variable or function will be used, and the compiler enforces this restriction. For example, if a C++ programmer writes

TAircraft* theAircraft = new TBoeing747();
TEngine* theEngine = new TGeneralElectric();
theAircraft = theEngine;

the compiler complains that it cannot convert a TEngine pointer to a TAircraft pointer.

The advantage of a strongly typed language is that this sort of type mismatch is not likely to show up at runtime-when the user is relying on the software and the programmer is nowhere to be found. The disadvantage of a strongly typed language is that it can be difficult to get all the types right. Even experienced programmers sometimes argue with the compiler about which types are compatible.

The developers of Java decided that their audience-experienced programmers-would benefit from strong typing. The developers of JavaScript took the opposite point of view. Instead of being strongly typed, JavaScript has generous conversion functions that operate transparently to change text strings to numbers and back again. Thus, in JavaScript, a programmer can say

var a = 10
var b = "100"
c = a + b

with the expectation that c will end up with a value of 110.

Where to Put Functions

In most programming languages, the programmer can wrap a set of statements into a package called a function, with defined inputs called parameters and an output called the return value. Good programmers use functions to improve the readability and maintainability of their code.

Recall that in JavaScript, programmers integrate the client-side code into the HTML file. For best results, define all functions in the <HEAD> section. Because the <HEAD> is processed before the body is downloaded, this practice makes it impossible for a user to call a function that has not yet been defined.

Returning Results

Because JavaScript is not strongly typed, it is not necessary to declare the return type of the function. Listing 5.1 is an example of a simple JavaScript function that calculates and returns a random number, along with enough code to actually call it and display the results.


Listing 5.1  -A Simple Random Number Generator

<HTML>
<HEAD>
<TITLE>Random Number Demo</TITLE>
<SCRIPT LANGUAGE="JavaScript">
<!--
function RandomNumber()
{
  today = new Date();
  num = Math.abs(Math.sin(today.getTime()));
  return num;
}
// -->
</SCRIPT>
</HEAD>
<BODY>
<SCRIPT LANGUAGE="JavaScript">
<!--
  document.write("The random number is " + RandomNumber());
// -->
</SCRIPT>
</BODY>
</HTML>

To understand how this script works, take it a few lines at a time:

<SCRIPT LANGUAGE="JavaScript">
<!--
function RandomNumber()
{
  today = new Date();
  num = Math.abs(Math.sin(today.getTime()));
  return num;
}
// -->
</SCRIPT>

To insert client-side JavaScript in the file, wrap it in <SCRIPT>...</SCRIPT> tags. These tags tell the browser which parts of the file to interpret as JavaScript. Everything inside the <SCRIPT>...</SCRIPT> tags should be an HTML comment, so browsers that do not understand JavaScript do not send the script to the user's window.

In the <HEAD>, the script should contain the functions for the page. In this case, there is only one function, which is declared with the keyword function and its name, RandomNumber. Any parameters are declared between the parentheses-RandomNumber has no parameters.

The function starts by making a new instance of the built-in class Date. When called with no parameters, the constructor for Date makes an object that represents the current date and time.

Tip
You have several ways to call the Date constructor. With no parameters, it returns the current date and time. You can set a date by passing just the data parameters. For example, Date(96,5,25) returns an object that represents June 26, 1996. Note that the numbering for the month starts at zero, so June is month 5, not month 6.
You can get a full date and time by passing all of the necessary parameters. Thus Date(96,5,25,9,24,0) represents June 26, 1996 at 10:24:00 AM. (Like months, the counting for hours also starts at zero for the first hour and progresses through hour 23 for midnight.)

Caution
JavaScript represents dates internally as the number of milliseconds that have passed since midnight January 1, 1970, a point in time called the epoch. This representation is similar to the common representation of time on UNIX computers (but most UNIX machines count time in seconds, not milliseconds). Usually you don't have to worry about this internal representation-just bear in mind that you can't represent dates prior to the epoch. Calling Date(69,5,25) crashes the Netscape browser.

The line num = Math.abs(Math.sin(today.getTime())); contains three object references. The built-in object Math is called once to run the sine function and once to get the absolute value. The getTime() method of the date object today is called to get the time portion of the date-time. Because it is being passed to a function (sine) that expects numeric input, JavaScript's built-in conversion routines give the time to (sine) as a number.

The result of sine is a number between -1 and +1; the result of the call to Math.abs is a number between 0 and 1. The next line, return num, returns that pseudorandom number to the calling function.

Calling JavaScript Functions

Listing 5.1 also shows one way of calling a JavaScript function. Inside the <BODY> section of the HTML file, you define another script. This time it calls the write method of the current
document. Document's write method is particularly flexible-it can handle text or numbers. In this case, it is passed as a string ("The random number is ") and the output of RandomNumber(). Recall that RandomNumber() returns a number, but because you use the + operator to concatenate it to the string, JavaScript turns it into a string for you.

<BODY>
<SCRIPT LANGUAGE="JavaScript">
<!--
  document.write("The random number is " + RandomNumber());
// -->
</SCRIPT>
</BODY>

Event Handlers

Another way to invoke JavaScript functions is from an event handler. Just as in conventional programming environments such as Microsoft Windows or the Macintosh operating system, objects such as windows receive events such as Click. Event handlers are pieces of code that are called in response to such events.

When a document is loaded into the browser, several objects are automatically built. These include the window itself, the document, and any forms and form elements defined on the page.

These user controls are implemented as built-in JavaScript objects. These objects each respond to events, which are typically triggered by the user. Figure 5.9 shows the hierarchy of user-accessible objects.

Figure 5.9: The client-side JavaScript object hierarchy.

Suppose the user loads the HTML shown in Listing 5.2 into Netscape Navigator. SimpleDoc.html contains a form named "test" which prompts the user for an account number and a name. For the purposes of this demonstration, the form includes a button named "checkLength" that handles the Click event. When a user clicks the "checkLength" button, the event handler invokes the JavaScript function runVerify(), which confirms that the length of the data in the "account number" text field is correct. Likewise, the event handler for the "checknonBlank" button invokes runVerify(), this time to confirm that the "name" field is not blank.

In real use, the programmer might remove the "checkLength" and "checknonBlank" buttons, and let their work be done from runSubmit(), which is called from the "Submit" button.


Listing 5.2  -A Simple Document

<HTML>
<HEAD>
<TITLE>A Simple Document</TITLE>
<SCRIPT LANGUAGE="JavaScript">
<!--
function runVerify(form, button)
{
  var result = false;
  if (button.name == "checkLength")
    result = test_field_length(form);
  else if (button.name == "checknonBlank")
    result = test_field_nonblank(form);
  if (result)
    alert("Your input is valid.");
}

function test_field_length(form)
{
  thefield = form.account_number;
  if (thefield.value.length != 12)
  {
    require_prompt (thefield, "Invalid account number");
    return (false);
  } 
  else
  {
     return (true);
  }
}

function test_field_nonblank(form)
{
  thefield = form.username;
  if (thefield.value == "")
  {
    require_prompt (thefield, "Please enter your name");
    return (false);
  } 
  else
  {
     return (true);
  }
}

function require_prompt(field, string)
{
  alert(string);
  field.focus();
}

function runSubmit(form, button)
{
  result = true;
  if (!test_field_length(form))
    result = false;
  else  if (!test_field_nonblank(form))
    result = false;
  if (true == result)
  {
    alert("All entries verified OK.");
    document.test.submit();
  }
  return;
}

function initialFocus()
{
  document.test.account_number.focus();
  return;
}
// -->
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="test" ACTION="http://your.company.com/cgi-bin/query" METHOD="GET">
<P>
Enter your 12-digit account number:
<INPUT TYPE="text" NAME="account_number" SIZE=12>
<INPUT TYPE="button" NAME="checkLength" Value="Test Input" onClick="runVerify(this.form, this)">
</P>
<P>
Enter your name (required field):
<INPUT TYPE="text" NAME="username">
<INPUT TYPE="button" NAME="checknonBlank" Value="Test Input" onClick="runVerify(this.form, this)">
</P>
<P>
<INPUT TYPE="button" NAME="Submit" VALUE="Submit" 
onClick="runSubmit(this.form, this)";
</P>
</FORM>
</BODY>
</HTML>

When this page loads, the window, location, and window.document objects are immediately created. Each object has preset properties. For example, document.title is "A Simple Document" and location.href is "http://www.dse.com/demo/SimpleDoc.html." Next, the browser instantiates objects associated with the form named "Test." These objects include document.test, document.test.checklength, document.test.account_number, and so forth. Each of these objects has default properties based on the HTML. For example, document.test.action is "http://your.company.com/cgi-bin/query."

Note
The elements of a form are named in accordance with the names given in the HTML tags. In addition, Navigator maintains arrays of each form and each element within the form. Thus, the form named document.test is equivalent to document.forms[0]. The first element of that form is document.forms[0].elements[0].

Table 5.1 shows the events that occur in JavaScript. In this table, the word "focus" means the attention of the user, as determined by mouse-clicks and key presses. For example, if the user clicks a text field so that the insertion pointer appears in that field, the focus event occurs. When the user clicks elsewhere and the insertion pointer leaves the field, the blur event occurs.

Table 5.1  JavaScript Events Occur As the User Interacts with the Document

EventMeaning Event Handler
blur User moved focus away from the elementonBlur
click User clicks a form element or linkonClick
change User changes the value of the element onChange
focus User gives the form element input focusonFocus
load User loads the page into NavigatoronLoad
mouseover User moves the mouse over the linkonMouseOver
select User selects an input fieldonSelect
submit User presses a form's Submit buttononSubmit
unload User exits the pageonUnload

Note
Netscape has announced plans for additional events, including enable and disable. Watch the Netscape site for more information. One good source is http://home.netscape.com/eng/mozilla/3.0/handbook/javascript/index.html

Table 5.2 shows the events and event handlers associated with each object.

Table 5.2  Events and Their Elements

EventForm Element or HTML Tag
focus text fields, textareas, selections
blur text fields, textareas, selections
change text fields, textareas, selections
click buttons, option buttons, check boxes, submit buttons, reset buttons, links
select text fields, textareas
mouseover links

Caution
Events refer to user actions. Some objects have methods that have the same name as events. (For example, there is a click method for class button.) These methods do not trigger an event. If you want to get the same effect as though the user had taken some action, call the event-handler directly. (For example, call onClick().)

Common Objects

Table 5.3 shows the objects frequently used in client-side JavaScript.

Table 5.3  Each JavaScript Object Has Several Methods and Properties

ObjectHTML Tag or Description
anchor <A NAME="name">...</A>
button <INPUT TYPE="button" NAME="name" VALUE="value">
checkbox <INPUT TYPE="checkbox" NAME="name" VALUE="value">
Date Used to represent dates and times
document The object that represents the document as a whole
form <FORM ...>
history The list of URLs this user has visited
link <A HREF="path">
location An URL
Math Used for its members, such as sin() and PI
password <INPUT TYPE="password" NAME="name">
radioButton <INPUT TYPE="radioButton" NAME="name" VALUE="value">
reset <INPUT TYPE="reset" NAME="name" VALUE="value">
selection <SELECT NAME="name"><OPTION>...</SELECT>
string A text string, such as "This string"
submit <INPUT TYPE="submit" NAME="name" VALUE="value">
text <INPUT TYPE="text" NAME="name" VALUE="value">
textArea <TEXTAREA NAME="name" ROWS=integer COLS=integer>
window The top-level object-the browser window itself

In the SDK…

JavaScript is an object-oriented language with dynamic (rather than strong) typing and a built-in hierarchy ranging from the browser window to the least element in a form. Each of these objects has methods and data members (also known as properties) that can be accessed by the JavaScript programmer.

Client-side JavaScript is built into every Netscape Navigator browser, starting with version 2.0. The Netscape ONE SDK site contains information about JavaScript programming. Some Netscape products, such as LiveWire Pro, are implemented as libraries of JavaScript functions that run on the server. If you have a Netscape server and LiveWire (or LiveWire Pro), you can write JavaScript on the server-a topic taken up in the next chapter.

ON THE WEB
http://home.netscape.com/eng/mozilla/3.0/handbook/javascript/
index.html
  This site contains an updated version of the original (Navigator 2.0) JavaScript handbook. Be sure to check out the "What's New" section.

ON THE WEB
http://home.netscape.com/eng/mozilla/Gold/handbook/javascript/
index.html
  This site contains a different version of the original (Navigator 2.0) JavaScript handbook. There's plenty of background material. Use the frame on the left to dig into the details of JavaScript programming.