As seemingly complex as the previous chapters examples were, they only begin to scratch the surface of JavaScripts potential. JavaScript has been designed to be extensible and powerful. With its eventual tight integration with Java and Navigator plug-ins, JavaScript promises to be one of the most useful tools available to the Web programmer.
Many of the newer technologies in Navigator are becoming more tightly integrated into JavaScript. The most important of these new technologies are Netscape Frames and Persistent Client State HTTP Cookies. Frames allow your Navigator to divide its pages into separate sections. Each section may have its own content, fixed or dynamic, but more importantly, each frame can have its own JavaScript application running within it. Additionally, the JavaScript applications can communicate and interact between frames.
Persistent Client State HTTP Cookies, or just cookies, allow a server to send information to a client for local storage. They are persistent in that the information will be retained across multiple sessions, and will be retrievable by a server in the future. In other words, you may visit a site, enter your preferences for viewing, and the next day it will know who you are. Also, an online catalog can use cookies to track visitors across the site and allow them to use the shopping cart model for shopping, checking out only when they have made all of their selections. Cookies allow for greater flexibility in interactive content, and JavaScript makes programming them much easier.
For both the Frames section and Cookies section, there are convenience functions that have been made available by Bill Dortch of hIdaho Design. These routines make programming Frames and Cookies under JavaScript much easier, and take much of the burden off the programmer.
The following sections contain code examples and explanations as well as the toolkits to assist you in your development of these more advanced JavaScript applications. The examples show how to use JavaScript in the context of Frames and Cookies, as well as an example of using timers within JavaScript. Following the three examples is a case study of a real-world product using most of the JavaScript features that have been discussed thus far.
Frames and JavaScript blend together almost seamlessly in the way they interact and inter-operate. Many functions that are difficult to accomplish on a single page are relatively easy using frames. For example, having a list of current users in one frame, generated by JavaScript, and a chat session in another frame between all of the users, is something that has already been done using JavaScript and Frames.
Netscape Communications new Web site makes extensive use of Frames and JavaScript in trying to simplify the process of navigating through their extensive Web site. By using JavaScript, they can dynamically build the content of all of their frames, without having many different versions of HTML pages for each possible outcome. Because of the simplicity of JavaScript over Java, situations like Frames and tables are best suited for JavaScript applications: Implementing them in Java would require more overhead in time than creating each HTML page individually. Figure 44.1 shows the new Netscape Web site.
FIGURE 44.1.The Netscape Web site using Frames.
The example provided for this section demonstrates the integration of Frames and JavaScript by creating a system for dynamically selecting background and foreground color attributes and updating the page in real time. The interface contains three frames stacked vertically. The first frame contains sample HTML to view your color options, the second frame contains a form for selecting which color options to change, and the third frame contains a list of JavaScript color names that can be selected to change the currently selected item to that color. Figure 44.2 shows the interface for the Frames sample application.
The example application is not included here due to its length. It is included on the CD-ROM and is available online at http://jrc.livesoftware.com/.
To assist in the development of Frames-based JavaScript applications, the hIdaho Frameset has been made available by Bill Dorch of hIdaho Design. This toolkit greatly simplifies the process of communicating between frames with JavaScript applications. The next section details the usage of the Frameset and provides the complete source code to the toolkit.
FIGURE 44.2.The interface to the JavaScript Frames demo.
When creating multi-frame JavaScript applications, calling functions within other frames can sometimes become tricky and confusing. The problem arises when a function in one frame needs to call a function in another. In this case, the calling function must know the specific location of the function it is calling in the frameset hierarchy. The hIdaho Frameset eliminates this problem by allowing you to register functions and specify in which frame they are located. After registering a function, you can call a function in another frame without specifying the specifics of the location of the function within its frameset hierarchy.
For example, if you had a function in another frame, the following statement could be one hypothetical way on calling the function:
frames[AnotherFrameName].functionInAnotherFrame(param1, param2);
Notice in the above statement the necessity of having to know the name of the frame in the frameset for each function that you need to call. Using the hIdaho Frameset, once you have registered the function, you can call the function by name and not be concerned with the name of the frame that the function is located in. The following statement shows a call to a hypothetical function in another frame:
parent.Exec (functionInAnotherFrame, param1, param2);
The hIdaho Frameset consists of five main functions:
More information on the hIdaho Frameset is available on the hIdaho Web site at http://www.hidaho.com/frameset/. The source code to the hIdado Frameset is shown in Listing 44.1.
<script language=JavaScript> <!-- begin script // ********************************************************************** // The hIdaho Frameset. Copyright (C) 1996 Bill Dortch, hIdaho Design. // Permission is granted to use and modify the hIdaho Frameset code, // provided this notice is retained. // ********************************************************************** var debug = false; var amTopFrameset = false; // set this to true for the topmost frameset var thisFrame = (amTopFrameset) ? null : self.name; var maxFuncs = 32; function makeArray (size) { this.length = size; for (var i = 1; i <= size; i++) this[i] = null; return this; } var funcs = new makeArray ((amTopFrameset) ? maxFuncs : 0); function makeFunc (frame, func) { this.frame = frame; this.func = func; return this; } function addFunction (frame, func) { for (var i = 1; i <= funcs.length; i++) if (funcs[i] == null) { funcs[i] = new makeFunc (frame, func); return true; } return false; } function findFunction (func) { for (var i = 1; i <= funcs.length; i++) if (funcs[i] != null) if (funcs[i].func == func) return funcs[i]; return null; } function Register (frame, func) { if (debug) alert (thisFrame + : Register( + frame + , + func + )); if (Register.arguments.length < 2) return false; if (!amTopFrameset) return parent.Register (thisFrame + . + frame, func); if (findFunction (func) != null) return false; return addFunction (frame, func); } function UnRegister (func) { if (debug) alert (thisFrame + : UnRegister( + func + )); if (UnRegister.arguments.length == 0) return false; if (!amTopFrameset) return parent.UnRegister (func); for (var i = 1; i <= funcs.length; i++) if (funcs[i] != null) if (funcs[i].func == func) { funcs[i] = null; return true; } return false; } function UnRegisterFrame (frame) { if (debug) alert (thisFrame + : UnRegisterFrame( + frame + )); if (UnRegisterFrame.arguments.length == 0) return false; if (!amTopFrameset) return parent.UnRegisterFrame (thisFrame + . + frame); for (var i = 1; i <= funcs.length; i++) if (funcs[i] != null) if (funcs[i].frame == frame) { funcs[i] = null; } return true; } function IsRegistered (func) { if (debug) alert (thisFrame + : IsRegistered( + func + )); if (IsRegistered.arguments.length == 0) return false; if (!amTopFrameset) return parent.IsRegistered (func); if (findFunction (func) == null) return false; return true; } function Exec (func) { if (debug) alert (thisFrame + : Exec( + func + )); var argv = Exec.arguments; if (argv.length == 0) return null; var arglist = new makeArray(argv.length); for (var i = 0; i < argv.length; i++) arglist[i+1] = argv[i]; var argstr = ; for (i = ((amTopFrameset) ? 2 : 1); i <= argv.length; i++) argstr += arglist[ + i + ] + ((i < argv.length) ? , : ); if (!amTopFrameset) return eval (parent.Exec( + argstr + )); var funcobj = findFunction (func); if (funcobj == null) return null; return eval (self. + ((funcobj.frame == null) ? : (funcobj.frame + .))+ funcobj.func + ( + argstr + )); } // ********************************************************************** // End of hIdaho Frameset code. // ********************************************************************** // end script --> </script>
As stated earlier, HTTP cookies are a mechanism for the server to send and retrieve information from the client. This simple mechanism enables you to extend the capabilities of your Web sites tremendously. The code in this section gives an example of how to use cookies to store and retrieve information from the client.
With the mechanism of cookies in place, complex sites can be created. For example, developing applications utilizing the code made available here enables you to create systems for tracking all of your users, tracking how long a user is staying on your site, and creating a full online ordering catalog. The provided toolkit also makes it extremely easy to use cookies in your applications and Web sites. A single command can set and retrieve cookies.
Another added advantage in using cookies is their ability to expire after a predetermined amount of time. In the example to follow, the cookie value you enter expires after 24 hours. You can have the values expire right away, in a week, or whatever time interval you desire. It is also possible to request that a cookie only be retrieved securely, which would ensure the privacy of your users if you were using cookies to transfer passwords.
The sample application, as shown in Figure 44.3, uses a very simple form to illustrate the setting and getting of cookies. Notice how the entire code statement that checks for the users input sets the cookie value and alerts them if the field is blank is all contained within a single <FORM> tag statement.
<FORM NAME=demoForm onSubmit= if(demoForm.name.value.length != 0) { var expdate = new Date (); expdate.setTime(expdate.getTime() + (60 * 60 * 1000)); SetCookie(DemoName, demoForm.name.value, expdate); alert(Cookie has been set to + demoForm.name.value + .); return false; } else { alert(You left the Name field blank.); return false; }>FIGURE 44.3.
The HTTP cookie demo.
Because we have established the preceding code as the response to a Submit, using an ordinary submit button will set the cookie:
<INPUT TYPE=submit VALUE=Set The Cookie>
To proceed to the next page, we use the window.open( ) function. Before moving to the next page, we perform an extra check to make sure that the cookie was set:
<INPUT TYPE=button VALUE=Continue... onClick= if(GetCookie(DemoName) == null) alert(Did you set the cookie?) else window.open(page2.html, _top)>
Once you have submitted the form on the first cookie example page, the second page simply calls the GetCookie( ) function to retrieve the cookie value from the Navigator as this line illustrates:
document.write(Welcome back + GetCookie(DemoName) + .);
Figure 44.4 shows the result of the second page from the cookie example.
Listing 44.2 shows the listing for the first Web page of the cookie demo. Note that the cookie toolkit code (see Listing 44.4) should be inserted at the top of this source code as denoted by the comment <!-- PLACE COOKIE TOOLKIT HERE -->.
FIGURE 44.4.Retrieving a cookie value from the Navigator.
<HTML><HEAD> <!-- PLACE COOKIE TOOLKIT HERE --> <TITLE>Cookie Demo</TITLE></HEAD> <BODY BGCOLOR=FFFFFF TEXT=000000> <CENTER> <FONT SIZE=+2>JavaScript Cookies Demo</FONT><BR> <IMG WIDTH=108 HEIGHT=26 SRC=../images/previewercredit.gif><BR> </CENTER> <HR> <SMALL> Following is an example of how one can use Persistent Client State HTTP Cookies to do some cool things. Once you have set the cookie, select the continue button to move to the next page. That page will read the cookie and say hello. Your new cookie will expire in 24 hours. Cookie functions by Bill Dortch.<BR> </SMALL> <HR> <FORM NAME=demoForm onSubmit= if(demoForm.name.value.length != 0) { var expdate = new Date (); expdate.setTime(expdate.getTime() + (60 * 60 * 1000)); SetCookie(DemoName, demoForm.name.value, expdate); alert(Cookie has been set to + demoForm.name.value + .); return false; } else { alert(You left the Name field blank.); return false; }> <PRE> Your Name: <INPUT TYPE=text NAME=name SIZE=40> </PRE> <P> <CENTER> <INPUT TYPE=submit VALUE=Set The Cookie> <INPUT TYPE=button VALUE=Continue... onClick= if(GetCookie(DemoName) == null) alert(Did you set the cookie?) else window.open(page2.html, _top)> </FORM> </CENTER> <HR> </BODY> </HTML>
Listing 44.3 shows the code for the second Web page of the Cookies Demo. As in the previous listing, the cookie toolkit code (see Listing 44.4) should be inserted at the top of this source code as denoted by the comment <!-- PLACE COOKIE TOOLKIT HERE -->.
<HTML><HEAD> <!-- PLACE COOKIE TOOLKIT HERE --> <TITLE>Cookie Demo</TITLE></HEAD> <BODY BGCOLOR=FFFFFF TEXT=000000> <CENTER> <FONT SIZE=+2>JavaScripts Cookies Demo</FONT><BR> <IMG WIDTH=108 HEIGHT=26 SRC=../images/previewercredit.gif><BR> </CENTER> <HR> JavaScript will now read the cookie information from your browser and try to retrieve the name you entered on the previous page: <center><h3> <SCRIPT> document.write(Welcome back + GetCookie(DemoName) + .); </SCRIPT> </h3></center> Cookies can be used in a wide set of applications, for example, a catalog shopping cart, a timed easter egg hunt, and personalized Web pages.<BR> <HR> </BODY> </HTML>
The source code in Listing 44.4 contains the latest cookie toolkit from hIdaho Design. The source code contains usage information as well as an example of how to use the toolkit. The latest version is available from http://www.hidaho.com/cookies/.
<script language=JavaScript> <!-- begin script // // Cookie Functions - Second Helping (21-Jan-96) // Written by: Bill Dortch, hIdaho Design <[email protected]> // The following functions are released to the public domain. // // The Second Helping version of the cookie functions dispenses with // my encode and decode functions, in favor of JavaScripts new built-in // escape and unescape functions, which do more complete encoding, and // which are probably much faster. // // The new version also extends the SetCookie function, though in // a backward-compatible manner, so if you used the First Helping of // cookie functions as they were written, you will not need to change any // code, unless you want to take advantage of the new capabilities. // // The following changes were made to SetCookie: // // 1. The expires parameter is now optional - that is, you can omit // it instead of passing it null to expire the cookie at the end // of the current session. // // 2. An optional path parameter has been added. // // 3. An optional domain parameter has been added. // // 4. An optional secure parameter has been added. // // For information on the significance of these parameters, and // and on cookies in general, please refer to the official cookie // spec, at: // // http://www.netscape.com/newsref/std/cookie_spec.html // // // Internal function to return the decoded value of a cookie // function getCookieVal (offset) { var endstr = document.cookie.indexOf (;, offset); if (endstr == -1) endstr = document.cookie.length; return unescape(document.cookie.substring(offset, endstr)); } // // Function to return the value of the cookie specified by name. // name - String object containing the cookie name. // returns - String object containing the cookie value, or null if // the cookie does not exist. // function GetCookie (name) { var arg = name + =; var alen = arg.length; var clen = document.cookie.length; var i = 0; while (i < clen) { var j = i + alen; if (document.cookie.substring(i, j) == arg) return getCookieVal (j); i = document.cookie.indexOf( , i) + 1; if (i == 0) break; } return null; } // // Function to create or update a cookie. // name - String object containing the cookie name. // value - String object containing the cookie value. May contain // any valid string characters. // [expires] - Date object containing the expiration date of the cookie. If // omitted or null, expires the cookie at the end of the current session. // [path] - String object indicating the path for which the cookie is valid. // If omitted or null, uses the path of the calling document. // [domain] - String object indicating the domain for which the cookie is // valid. If omitted or null, uses the domain of the calling document. // [secure] - Boolean (true/false) value indicating whether cookie transmission // requires a secure channel (HTTPS). // // The first two parameters are required. The others, if supplied, must // be passed in the order listed above. To omit an unused optional field, // use null as a place holder. For example, to call SetCookie using name, // value and path, you would code: // // SetCookie (myCookieName, myCookieValue, null, /); // // Note that trailing omitted parameters do not require a placeholder. // // To set a secure cookie for path /myPath, that expires after the // current session, you might code: // // SetCookie (myCookieVar, cookieValueVar, null, /myPath, null, true); // function SetCookie (name, value) { var argv = SetCookie.arguments; var argc = SetCookie.arguments.length; var expires = (argc > 2) ? argv[2] : null; var path = (argc > 3) ? argv[3] : null; var domain = (argc > 4) ? argv[4] : null; var secure = (argc > 5) ? argv[5] : false; document.cookie = name + = + escape (value) + ((expires == null) ? : (; expires= + expires.toGMTString())) + ((path == null) ? : (; path= + path)) + ((domain == null) ? : (; domain= + domain)) + ((secure == true) ? ; secure : ); } // Function to delete a cookie. (Sets expiration date to current date/time) // name - String object containing the cookie name // function DeleteCookie (name) { var exp = new Date( ); exp.setTime (exp.getTime( ) - 1); // This cookie is history var cval = GetCookie (name); document.cookie = name + = + cval + ; expires= + exp.toGMTString(); } // end script --> </script>
Another very useful tool in JavaScript is its capability to set timers. With the setTimeout( ) method, you can specify a number of milliseconds to wait before calling a function. This can be used for delays, defaulting to certain selections if the user has not chosen, or even building a real-time, JavaScript-based clock.
The following example implements a real-time digital clock under JavaScript. Figure 44.5 shows the clock in action.
FIGURE 44.5. LA real-time JavaScript digital clock.
The function TOfunc( ), once started, repeatedly sets a timer to call itself every second and display the current time. This simple approach allows JavaScript to display a fully working digital clock that updates itself every second:
function TOfunc( ) { TO = window.setTimeout( TOfunc( ), 1000 ); var today = new Date( ); document.forms[0].elements[0].value = today.toString( ); }
Turning off the clock is accomplished by calling the clearTimeout( ) function that clears all timeouts:
<input type=radio name=rad value=OFF checked onClick= if( enabled ) { clearTimeout( TO ); enabled = 0; }> OFF
Listing 44.5 is the complete listing for the JavaScript clock.
<TITLE>JavaScript Clock</TITLE> </HEAD> <BODY BGCOLOR=FFFFFF TEXT=000000> <CENTER> <FONT SIZE=+2>JavaScript Clock</FONT><BR> <IMG WIDTH=108 HEIGHT=26 SRC=../images/previewercredit.gif> </CENTER> <HR> <center> <SMALL> Click the ON button to start the clock, OFF to stop it. </SMALL> </CENTER> <HR> <form> <center> <input type=text name=disp value= size=25 onFocus=this.blur() > <br> <input type=radio name=rad value=OFF checked onClick= if( enabled ) { clearTimeout( TO ); enabled = 0; }> OFF <input type=radio name=rad value=ON onClick= if( !enabled ) { TO = setTimeout( TOfunc(), 1000 ); enabled = 1; } > ON <br> </center> </form> <HR> </BODY> </HTML>
LiveSite by Live Software is an example of an application that uses JavaScript to extend its core capabilities. The LiveSite system is an application that interfaces with Web servers through the Windows CGI protocol. LiveSite is a site management system that enables users to create Web sites online using their browser and forms-based templates. Without using any HTML, users can create sophisticated Web sites with almost no limit on size or content. The users page information is then stored in a relational database for future modifications and updates. Because the user must enter information using forms, the LiveSite system makes extensive use of JavaScripts capability to validate form input before submitting the form.
When a user is building a page using the LiveSite system, there are many choices for background patterns, stock imagery, rule imagery, and bullet imagery. Each of these could present a daunting list to read through in making a decision for which image to use. By utilizing JavaScript, the program can provide an image previewing system whereby the user can preview the images in a smaller JavaScript-created window without interfering with the form they are currently working on. This allows for greater flexibility and ease of use.
A demonstration version of the LiveSite system is provided on the CD-ROM so that you may experiment with the actual application. Updated versions of LiveSite software are available from http://www.livesite.com/. Figure 44.6 shows one of LiveSites page creation forms that utilizes JavaScript.
FIGURE 44.6.A LiveSite page using form validation through JavaScript.
Inevitably, code in any language is bound to have some bugs. JavaScript has a fairly robust system for reporting errors to you. Generally, when JavaScript finds an error, it will alert you with a window that specifies the error and the associated line. Many timesas any programmer that has ever debugged code would knowthis information is still not enough.
Some of the best steps to take in debugging JavaScript code are to carefully review your logic and make sure you are using correct syntax. Make sure all lines end in a semicolon (;), and make sure all braces are properly closed. When using if...else statements or other statements that allow single lines after the statement without braces, for example:
for(var i=0;i<10;i++) document.write(The number is: + I);
it may make your code easier to debug by adding the braces even when they are not needed; that is, substitute the above example with
for(var i=0;i<10;i++) { document.write(The number is: + I); }
That way, you can more quickly see what the for loop should be containing. There are also many resources available on the Internet for help. Sometimes, some of the best help can come from viewing other peoples source code. A good starting point is Live Softwares JavaScript Resource Center (http://jrc.livesoftware.com/), which contains numerous examples and links to other JavaScript sites. Netscape Communications also maintains a developers site for JavaScript and other technologies. You can reach Netscapes site by contacting http://developer.netscape.com/.
By using some of the more advanced features of Netscape, such as frames and cookies, JavaScript can truly emerge as a uniquely powerful language for Web-based scripting and application development.
Using Frames and JavaScript, applications that previously could only be accomplished with C/C++ or Java can now be fully implemented.
Using HTTP cookies and JavaScript opens many possibilities, including complete online commerce applications using the shopping cart model for user tracking and JavaScript for order taking and order processing.
Commercial applications are already beginning to emerge utilizing JavaScript, and with the information you now have at hand, you too can create dynamic and compelling content and applications using JavaScript.