Sei sulla pagina 1di 280

JavaScript Basics

In this lesson of the JavaScript tutorial, you will learn... 1. 2. 3. 4. 5. 6. 7. 8. To work with the HTML DOM. To follow JavaScript syntax rules. To write JavaScript inline. To write JavaScript in script blocks. To create and link to external JavaScript files. To work with JavaScript objects, methods, and properties. To reference HTML elements with JavaScript. To use event handlers.

The Name "JavaScript"


In this manual, we refer to the language we are learning as JavaScript, which is what it is usually called. However, the name JavaScript is owned by Netscape. Microsoft calls its version of the language JScript. The generic name of the language is EcmaScript.

The HTML DOM


The HTML Document Object Model (DOM) is the browser's view of an HTML page as an object hierarchy, starting with the browser window itself and moving deeper into the page, including all of the elements on the page and their attributes. Below is a simplified version of the HTML DOM.

As shown, the top-level object is window. The document object is a child of window and all the objects (i.e, elements) that appear on the page (e.g, forms, links, images, tables, etc.) are descendants of the document object. These objects can have children of their own. For example, form objects generally have several child objects, including textboxes, radio buttons, and select menus.

JavaScript Syntax
Basic Rules
1. JavaScript statements end with semi-colons. 2. JavaScript is case sensitive. 3. JavaScript has two forms of comments: o Single-line comments begin with a double slash (//). o Multi-line comments begin with "/*" and end with "*/". Syntax
// This is a single-line comment /* This is a multi-line comment. */

Dot Notation
In JavaScript, objects can be referenced using dot notation, starting with the highest-level object (i.e, window). Objects can be referred to by name or id or by their position on the page. For example, if there is a form on the page named "loginform", using dot notation you could refer to the form as follows: Syntax
window.document.loginform

Assuming that loginform is the first form on the page, you could also refer to this way: Syntax
window.document.forms[0]

A document can have multiple form elements as children. The number in the square brackets ([]) indicates the specific form in question. In programming speak, every document object contains an array of forms. The length of the array could be zero (meaning there are no forms on the page) or greater. In JavaScript, arrays are zero-based, meaning that the first form on the page is referenced with the number zero (0) as shown in the syntax example above.

Square Bracket Notation


Objects can also be referenced using square bracket notation as shown below. Syntax
window['document']['loginform'] // and window['document']['forms[0]']

Dot notation and square bracket notation are completely interchangeable. Dot notation is much more common; however, as we will see later in the course, there are times when it is more convenient to use square bracket notation.

Where Is JavaScript Code Written?


JavaScript code can be written inline (e.g, within HTML tags called event handlers), in script blocks, and in external JavaScript files. The page below shows examples of all three.

Code Sample: JavaScriptBasics/Demos/JavaScript.html


<html> <head> <title>JavaScript Page</title> <script type="text/javascript"> window.alert("The page is loading"); </script> </head> <body> <p align="center"> <span onclick="document.bgColor = 'red';">Red</span> | <span onclick="document.bgColor = 'white';">White</span> </p> <script type="text/javascript" src="Script.js"></script> </body> </html>

Code Sample: JavaScriptBasics/Demos/Script.js


document.write("Hello, there!");

Code Explanation

As this page loads, an alert will pop up that says "The page is loading" as shown below.

After the user clicks the OK button, the page will finish loading and will appear as

follows. The text "Hello, there!" is written dynamically by the code in JavaScriptBasics/Demos/Script.js. We will look at the code in this file and in JavaScriptBasics/Demos/JavaScript.html again shortly.

JavaScript Objects, Methods and Properties


JavaScript is used to manipulate or get information about objects in the HTML DOM. Objects in an HTML page have methods (actions, such as opening a new window or submitting a form) and properties (attributes or qualities, such as color and size). To illustrate objects, methods and properties, we will look at the code in JavaScriptBasics/Demos/JavaScript2.html, a slightly modified version of JavaScriptBasics/Demos/JavaScript.html, which we looked at earlier, and at the code in JavaScriptBasics/Demos/Script2.js.

Code Sample: JavaScriptBasics/Demos/JavaScript2.html


<html> <head> <title>JavaScript Page</title> <script type="text/javascript"> //Pop up an alert window.alert("The page is loading"); </script> </head> <body> <p align="center"> <span onclick="document.bgColor = 'red';">Red</span> | <span onclick="document.bgColor = 'white';">White</span> | <span onclick="document.bgColor = 'green';">Green</span> | <span onclick="document.bgColor = 'blue';">Blue</span> </p> <script type="text/javascript" src="Script2.js"></script> </body> </html>

Code Sample: JavaScriptBasics/Demos/Script2.js


/* This script simply outputs "Hello, there!" to the browser. */ document.write("Hello, there!");

Methods
Methods are the verbs of JavaScript. They cause things to happen.

window.alert()
HTML pages are read and processed from top to bottom. The JavaScript code in the initial script block at the top of JavaScriptBasics/Demos/JavaScript2.html calls the alert() method of the window object. When the browser reads that line of code, it will pop up an alert box and will not continue processing the page until the user presses the OK button. Once the user presses the button, the alert box disappears and the rest of the page loads.

document.write()
The write() method of the document object is used to write out code to the page as it loads. In JavaScriptBasics/Demos/Script2.js, it simply writes out "Hello, there!"; however, it is more often used to write out dynamic data, such as the date and time on the user's machine.

Arguments
Methods can take zero or more arguments separated by commas. Syntax
object.method(argument1, argument2);

The alert() and write() methods shown in the example above each take only one argument: the message to show.

Properties
Properties are the adjectives of JavaScript. They describe qualities of objects and, in some cases are writable (can be changed dynamically).

document.bgColor
The bgColor property of the document object is read-write. Looking back at JavaScriptBasics/Demos/JavaScript2.html, the four span elements use the onclick event handler to catch click events. When the user clicks on a span, JavaScript is used to change the value of the bgColor property to a new color.

The Implicit window Object


The window object is always the implicit top-level object and therefore does not have to be included in references to objects. For example, window.document.write() can be shortened to document.write(). Likewise, window.alert() can be shortened to just alert().

The getElementById() Method

A very common way to reference HTML elements is by their ID using the getElementById() method of the document object as shown in the example below.

Event Handlers
In JavaScriptBasics/Demos/JavaScript2.html, we used the onclick event handler to call JavaScript code that changed the background color of the page. Event handlers are attributes that force an element to "listen" for a specific event to occur. Event handlers all begin with the letters "on". The table below lists the HTML event handlers with descriptions. HTML Event Handlers Event Handler onblur onchange onclick Elements Supported a, area, button, input, label, select, textarea input, select, textarea All elements except applet, base, basefont, bdo, br, font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title All elements except applet, base, basefont, bdo, br, font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title a, area, button, input, label, select, textarea All elements except applet, base, basefont, bdo, br, font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title All elements except applet, base, basefont, bdo, br, font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title All elements except applet, base, basefont, bdo, br, font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title frameset body Description the element lost the focus the element value was changed a pointer button was clicked a pointer button was double clicked the element received the focus a key was pressed down a key was pressed and released a key was released all the frames have been loaded the document has been loaded a pointer button was pressed down

ondblclick onfocus onkeydown

onkeypress

onkeyup onload onload

All elements except applet, base, basefont, bdo, br, onmousedown font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title

HTML Event Handlers Event Handler Elements Supported Description a pointer was moved within a pointer was moved away a pointer was moved onto a pointer button was released the form was reset some text was selected the form was submitted all the frames have been removed the document has been removed

All elements except applet, base, basefont, bdo, br, onmousemove font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title All elements except applet, base, basefont, bdo, br, onmouseout font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title All elements except applet, base, basefont, bdo, br, onmouseover font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title All elements except applet, base, basefont, bdo, br, onmouseup font, frame, frameset, head, html, iframe, isindex, meta, param, script, style, title onreset form onselect onsubmit onunload onunload input, textarea form frameset body

Exercise: Using Event Handlers


Duration: 15 to 25 minutes. In this exercise, you will use some of the event handlers from the table above to allow the user to change the background color of the page. 1. Open JavaScriptBasics/Exercises/JavaScript.html for editing. 2. Modify the page so that... o when it is finished loading an alert pops up reading "The page has loaded!" o when the "Red" button is clicked, the background color turns red and an alert pops up reading "The background color is now Red." o when the "Green" button is double-clicked, the background color turns green and an alert pops up reading "The background color is now Green." o when the "Orange" button is clicked down, the background color turns orange and an alert pops up reading "The background color is now Orange."

when the mouse button is released over the "Blue" button, the background color turns blue and an alert pops up reading "The background color is now Blue."

Code Sample: JavaScriptBasics/Exercises/JavaScript.html


<html> <head> <title>JavaScript Page</title> <script type="text/javascript"> window.alert("The page is loading."); </script> </head> <body> <form> Click the button to turn the page <input type="button" value="Red"/> <br/><br/> Double click the button to turn the page <input type="button" value="Green"/> <br/><br/> Click down on the button to turn the page <input type="button" value="Orange"/> <br/><br/> Release the mouse while on the button to turn the page <input type="button" value="Blue"/> </form> <hr/> <script type="text/javascript" src="Script.js"></script> </body> </html>

1. Add functionality so that when the user presses any key, the background color turns white. 2. Add a "Black" button. When the user hovers over this button and presses the mouse button down, the background color should turn black. When the user releases the mouse button, the background color should turn white. Where is the solution?

JavaScript Basics Conclusion


In this lesson of the JavaScript tutorial, you have learned the basics of JavaScript. Now you're ready for more. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Variables, Arrays and Operators


In this lesson of the JavaScript tutorial, you will learn... 1. To create, read and modify JavaScript variables. 2. To work with JavaScript arrays. 3. To work with JavaScript operators.

JavaScript Variables
Variables are used to hold data in memory. JavaScript variables are declared with the var keyword.
var age;

Multiple variables can be declared in a single step.


var age, height, weight, gender;

After a variable is declared, it can be assigned a value.


age = 34;

Variable declaration and assignment can be done in a single step.


var age = 34;

A Loosely-typed Language
JavaScript is a loosely-typed language. This means that you do not specify the data type of a variable when declaring it. It also means that a single variable can hold different data types at different times and that JavaScript can change the variable type on the fly. For example, the age variable above is an integer. However, the variable strAge below would be a string (text) because of the quotes.
var strAge = "34";

If you were to try to do a math function on strAge (e.g, multiply it by 4), JavaScript would dynamically change it to an integer. Although this is very convenient, it can also cause unexpected results, so be careful.

Storing User-Entered Data

The following example uses the prompt() method of the window object to collect user input. The value entered by the user is then assigned to a variable, which is accessed when the user clicks on one of the span elements.

Code Sample: VariablesArraysOperators/Demos/Variables.html


<html> <head> <title>JavaScript Variables</title> <script type="text/javascript"> //Pop up a prompt var USER_COLOR = window.prompt("Enter a color.", ""); </script> </head> <body> <p align="center"> <span onclick="document.bgColor = 'red';">Red</span> | <span onclick="document.bgColor = 'white';">White</span> | <span onclick="document.bgColor = 'green';">Green</span> | <span onclick="document.bgColor = 'blue';">Blue</span> | <span onclick="document.bgColor = USER_COLOR;"> <script type="text/javascript"> document.write(USER_COLOR); </script> </span> </p> </body> </html>

Code Explanation As the page loads, a prompt pops up asking the user to enter a color.

This is done with the prompt() method of the window object. The prompt() method is used to get input from the user. It takes two arguments: 1. The message in the dialog box (e.g., "Enter a color."). 2. The default value that appears in the text box. In the example above this is an empty string (e.g, "").

If the OK button is pressed, the prompt returns the value entered in the textbox. If the Cancel button or the close button (the red X) is pressed, the prompt returns null. The line below assigns whatever is returned to the variable USER_COLOR.
var USER_COLOR = window.prompt("Enter a color.", "");

A script block with a call to document.write() is then used to output the color entered by the user. This output is contained within a span element, which has an onclick event handler that will be used to turn the background color of the page to the user-entered color.
<span onclick="document.bgColor = USER_COLOR;"> <script type="text/javascript"> document.write(USER_COLOR); </script> </span>

Exercise: Using Variables


Duration: 5 to 15 minutes. In this exercise, you will practice using variables. 1. Open VariablesArraysOperators/Exercises/Variables.html for editing. 2. Below the ADD PROMPT HERE comment, write code that will prompt the user for her first name and assign the result to a variable. 3. Add a button below the Ringo button that reads "Your Name". Add functionality so that when this button is pressed an alert pops up showing the user's first name. 4. Test your solution in a browser.

Code Sample: VariablesArraysOperators/Exercises/Variables.html


<html> <head> <title>JavaScript Variables</title> <script type="text/javascript"> //ADD PROMPT HERE </script> </head> <body> <form> <input type="button" value="Paul" onclick="alert('Paul');"/> <br/><br/> <input type="button" value="John" onclick="alert('John');"/> <br/><br/> <input type="button" value="George"

onclick="alert('George');"/> <br/><br/> <input type="button" value="Ringo" onclick="alert('Ringo');"/> <br/><br/> <!--ADD BUTTON HERE--> </form> </body> </html>

Where is the solution?

Arrays
An array is a grouping of objects that can be accessed through subscripts. At its simplest, an array can be thought of as a list. In JavaScript, the first element of an array is considered to be at position zero (0), the second element at position one (1), and so on. Arrays are useful for storing data of similar types. Arrays are declared using the new keyword.
var myarray = new Array();

It is also possible and very common to use the [] literal to declare a new Array object.
var myarray = [];

Values are assigned to arrays as follows.


myarray[0] = value1; myarray[1] = value2; myarray[2] = value3;

Arrays can be declared with initial values.


var myarray = new Array(value1, value2, value3); //or, using the [] notation: var myarray = [value1, value2, value3];

The following example is similar to the previous one, except that it prompts the user for four different colors and places each into the USER_COLORS array. It then displays the values in the USER_COLORS array in the spans and assigns them to document.bgColor when the user clicks on the spans. Unlike in some languages, values in JavaScript arrays do not all have to be of the same data type.

Code Sample: VariablesArraysOperators/Demos/Arrays.html


<html> <head> <title>JavaScript Arrays</title> <script type="text/javascript"> //Pop up four prompts and create an array var USER_COLORS = new Array(); USER_COLORS[0] = window.prompt("Choose a color.", ""); USER_COLORS[1] = window.prompt("Choose a color.", ""); USER_COLORS[2] = window.prompt("Choose a color.", ""); USER_COLORS[3] = window.prompt("Choose a color.", ""); </script> </head> <body> <p align="center"> <span onclick="document.bgColor = USER_COLORS[0];"> <script type="text/javascript"> document.write(USER_COLORS[0]); </script> </span> | <span onclick="document.bgColor = USER_COLORS[1];"> <script type="text/javascript"> document.write(USER_COLORS[1]); </script> </span> | <span onclick="document.bgColor = USER_COLORS[2];"> <script type="text/javascript"> document.write(USER_COLORS[2]); </script> </span> | <span onclick="document.bgColor = USER_COLORS[3];"> <script type="text/javascript"> document.write(USER_COLORS[3]); </script> </span> </p> </body> </html>

Code Explanation As the page loads, an array called USER_COLORS is declared.


var USER_COLORS = new Array();

The next four lines populate the array with user-entered values.
USER_COLORS[0] USER_COLORS[1] USER_COLORS[2] USER_COLORS[3] = = = = window.prompt("Choose window.prompt("Choose window.prompt("Choose window.prompt("Choose a a a a color.", color.", color.", color.", ""); ""); ""); "");

The body of the page contains a paragraph with four span tags, the text of which is dynamically created with values from the USER_COLORS array.

Exercise: Working with Arrays


Duration: 15 to 25 minutes. In this exercise, you will practice working with arrays. 1. Open VariablesArraysOperators/Exercises/Arrays.html for editing. 2. Below the comment, declare a ROCK_STARS array and populate it with four values entered by the user. 3. Add functionality to the buttons, so that alerts pop up with values from the array when the buttons are clicked. 4. Test your solution in a browser.

Code Sample: VariablesArraysOperators/Exercises/Arrays.html


<html> <head> <title>JavaScript Arrays</title> <script type="text/javascript"> /* Declare a ROCK_STARS array and populate it with four values entered by the user. */ </script> </head> <body> <form> <input type="button" value="Favorite"/> <br/><br/> <input type="button" value="Next Favorite"/> <br/><br/> <input type="button" value="Next Favorite"/> <br/><br/> <input type="button" value="Next Favorite"/> </form> </body> </html>

Where is the solution?

Associative Arrays
Whereas regular (or enumerated) arrays are indexed numerically, associative arrays are indexed using names as keys. The advantage of this is that the keys can be meaningful,

which can make it easier to reference an element in an array. The example below illustrates how an associative array is used.

Code Sample: VariablesArraysOperators/Demos/AssociativeArrays.html


<html> <head> <title>JavaScript Arrays</title> <script type="text/javascript"> var BEATLES = []; BEATLES["singer1"] = "Paul"; BEATLES["singer2"] = "John"; BEATLES["guitarist"] = "George"; BEATLES["drummer"] = "Ringo"; </script> </head> <body> <p align="center"> <script type="text/javascript"> document.write(BEATLES["singer1"]); document.write(BEATLES["singer2"]); document.write(BEATLES["guitarist"]); document.write(BEATLES["drummer"]); </script> </p> </body> </html>

Array Properties and Methods


The tables below show some of the most useful array properties and methods. All of the examples assume an array called BEATLES that holds "Paul", "John", "George", and "Ringo".
var BEATLES = ["Paul", "John", "George", "Ringo"];

Array Properties Property Description Example length Holds the number of elements in an array. BEATLES.length // 4 Array Methods Property Description Returns a delimited list of the items join(delimiter) indexed with integers in the array. The default delimiter is a comma. Removes the last item in an array and pop() returns its value. shift() Removes the first item in an array and Example BEATLES.join(":") // Paul:John:George:Ringo BEATLES.pop() // Returns Ringo BEATLES.shift() // Returns

Array Methods Property Description returns its value. Returns a subarray from start to end. If slice(start, end) end is left out, it includes the remainder of the array. splice(start, Removes count items from start in the count) array and returns the resulting array.

Example Paul BEATLES.slice(1 ,2) //Returns [John, George] BEATLES.splice(1, 2) //Returns [Paul, Ringo]

JavaScript Operators
Arithmetic Operators Operator Description + Addition Subtraction * Multiplication / Division % Modulus (remainder) ++ Increment by one -Decrement by one Operator = Assignment += One step addition and assignment (a+=3 is the same as a=a+3) -= One step subtraction and assignment (a-=3 is the same as a=a-3) *= One step multiplication and assignment (a*=3 is the same as a=a*3) /= One step division and assignment (a/=3 is the same as a=a/3) %= One step modulus and assignment (a%=3 is the same as a=a%3) String Operators Operator Description + Concatenation (var greeting = "Hello " + firstname;) One step concatenation and assignment (var greeting = "Hello "; greeting += += firstname;) Ternary Operator Operator Description Conditional evaluation (var evenOrOdd = (number % 2 == 0) ? "even" : ?: "odd";) The following code sample shows these operators in use. Assignment Operators Description

Code Sample: VariablesArraysOperators/Demos/Operators.html


<html> <head> <title>JavaScript Operators</title> <script type="text/javascript"> var USER_NUM1 = window.prompt("Choose a number.", ""); alert("You chose " + USER_NUM1); var USER_NUM2 = window.prompt("Choose another number.", ""); alert("You chose " + USER_NUM2); var NUMS_ADDED = USER_NUM1 + Number(USER_NUM2); var NUMS_SUBTRACTED = USER_NUM1 - USER_NUM2; var NUMS_MULTIPLIED = USER_NUM1 * USER_NUM2; var NUMS_DIVIDED = USER_NUM1 / USER_NUM2; var NUMS_MODULUSED = USER_NUM1 % USER_NUM2; </script> </head> <body> <p style="text-align:center; font-size:large"> <script type="text/javascript"> document.write(USER_NUM1 + " + " + USER_NUM2 + " = "); document.write(NUMS_ADDED + "<br/>"); document.write(USER_NUM1 + " - " + USER_NUM2 + " = "); document.write(NUMS_SUBTRACTED + "<br/>"); document.write(USER_NUM1 + " * " + USER_NUM2 + " = "); document.write(NUMS_MULTIPLIED + "<br/>"); document.write(USER_NUM1 + " / " + USER_NUM2 + " = "); document.write(NUMS_DIVIDED + "<br/>"); document.write(USER_NUM1 + " % " + USER_NUM2 + " = "); document.write(NUMS_MODULUSED + "<br/>"); </script> </p> </body> </html>

Code Explanation The file above illustrates the use of the concatenation operator and several math operators. It also illustrates a potential problem with loosely-typed languages. This page is processed as follows: 1. The user is prompted for a number and the result is assigned to USER_NUM1. 2. An alert pops up telling the user what number she entered. The concatenation operator (+) is used to combine two strings: "You chose " and the number entered by the user. Note that all user-entered data is always treated as a string of text, even if the text consists of only digits. 3. The user is prompted for another number and the result is assigned to USER_NUM2. 4. Another alert pops up telling the user what number she entered. 5. Five variables are declared and assigned values.
6. 7. var NUMS_ADDED = USER_NUM1 + USER_NUM2; var NUMS_SUBTRACTED = USER_NUM1 - USER_NUM2;

8. 9.

var NUMS_MULTIPLIED = USER_NUM1 * USER_NUM2; var NUMS_DIVIDED = USER_NUM1 / USER_NUM2; var NUMS_MODULUSED = USER_NUM1 % USER_NUM2;

10. The values the variables contain are output to the browser.

So, 5 + 4 is 54! It is when 5 and 4 are strings, and, as stated earlier, all user-entered data is treated as a string. In the lesson on JavaScript Functions, you will learn how to convert a string to a number.

Exercise: Working with Operators


Duration: 15 to 25 minutes. In this exercise, you will practice working with JavaScript operators. 1. Open VariablesArraysOperators/Exercises/Operators.html for editing. 2. Add code to prompt the user for the number of CDs she owns of her favorite and second favorite rockstars'. 3. In the body, let the user know how many more of her favorite rockstar's CDs she has than of her second favorite rockstar's CDs. 4. Test your solution in a browser.

Code Sample: VariablesArraysOperators/Exercises/Operators.html


<html> <head> <title>JavaScript Operators</title> <script type="text/javascript"> var ROCK_STARS = []; ROCK_STARS[0] = prompt("Who is your favorite rock star?", "");

/* Ask the user how many of this rockstar's CDs she owns and store the result in a variable. */ ROCK_STARS[1] = prompt("And your next favorite rock star?", ""); /* Ask the user how many of this rockstar's CDs she owns and store the result in a variable. */ </script> </head> <body> <!-Let the user know how many more of her favorite rockstar's CDs she has than of her second favorite rockstar's CDs. --> </body> </html>

1. Open VariablesArraysOperators/Exercises/Operators-challenge.html for editing. 2. Modify it so that it outputs an unordered list as shown below.

Where is the solution?

Variables, Arrays and Operators Conclusion


In this lesson of the JavaScript tutorial, you have learned to work with JavaScript variables, arrays and operators. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

JavaScript Functions

In this lesson of the JavaScript tutorial, you will learn... 1. To work with some of JavaScript's built-in functions. 2. To create your own functions. 3. To return values from functions.

Built-in Functions
JavaScript has a number of built-in functions. We will examine some of them in this section.

Number(object)
The Number() function takes one argument: an object, which it attempts to convert to a number. If it cannot, it returns NaN, for "Not a Number."

Code Sample: JavaScriptFunctions/Demos/Number.html


<html> <head> <title>Number() Function</title> <script type="text/javascript"> var STR_NUM1 = "1"; var STR_NUM2 = "2"; var STR_SUM = STR_NUM1 + STR_NUM2; //returns 12 alert(STR_SUM); var INT_NUM1 = Number(STR_NUM1); var INT_NUM2 = Number(STR_NUM2); var INT_SUM = INT_NUM1 + INT_NUM2; //returns 3 alert(INT_SUM); </script> </head> <body> Nothing to show here. </body> </html>

Code Explanation Because STR_NUM1 and STR_NUM2 are both strings, the + operator concatenates them, resulting in "12".
var STR_NUM1 = "1"; var STR_NUM2 = "2"; var STR_SUM = STR_NUM1 + STR_NUM2; //returns 12 alert(STR_SUM);

After the Number() function has been used to convert the strings to numbers, the + operator performs addition, resulting in 3.
var INT_NUM1 = Number(STR_NUM1); var INT_NUM2 = Number(STR_NUM2); var INT_SUM = INT_NUM1 + INT_NUM2; //returns 3 alert(INT_SUM);

String(object)
The String() function takes one argument: an object, which it converts to a string.

Code Sample: JavaScriptFunctions/Demos/String.html


<html> <head> <title>String() Function</title> <script type="text/javascript"> var INT_NUM1 = 1; var INT_NUM2 = 2; var INT_SUM = INT_NUM1 + INT_NUM2; //returns 3 alert(INT_SUM); var STR_NUM1 = String(INT_NUM1); var STR_NUM2 = String(INT_NUM2); var STR_SUM = STR_NUM1 + STR_NUM2; //returns 12 alert(STR_SUM); </script> </head> <body> Nothing to show here. </body> </html>

Code Explanation Because INT_NUM1 and INT_NUM2 are both numbers, the + operator performs addition, resulting in 3.
var INT_NUM1 = 1; var INT_NUM2 = 2; var INT_SUM = INT_NUM1 + INT_NUM2; //returns 3 alert(INT_SUM);

After the String() function has been used to convert the numbers to string, the + operator performs concatenation, resulting in "12".
var STR_NUM1 = String(INT_NUM1); var STR_NUM2 = String(INT_NUM2); var STR_SUM = STR_NUM1 + STR_NUM2; //returns 12 alert(STR_SUM);

isNaN(object)
The isNaN() function takes one argument: an object. The function checks if the object is not a number (or cannot be converted to a number). It returns true if the object is not a number and false if it is a number.

Code Sample: JavaScriptFunctions/Demos/isNaN.html


<html> <head> <title>isNaN() Function</title> </head> <body> <table border="1" cellpadding="3" align="center"> <tr> <th>Function</th><th>Result</th> </tr> <script type="text/javascript"> document.write("<tr><td>isNaN(4)</td>"); document.write("<td>" + isNaN(4) + "</td></tr>"); document.write("<tr><td>isNaN(\"4\")</td>"); document.write("<td>" + isNaN("4") + "</td></tr>"); document.write("<tr><td>isNaN(0/0)</td>"); document.write("<td>" + isNaN(0/0) + "</td></tr>"); document.write("<tr><td>isNaN(\"hello\")</td>"); document.write("<td>" + isNaN("hello") + "</td></tr>"); var AGE_STR = "twelve"; document.write("<tr><td>isNaN(AGE_STR)</td>"); document.write("<td>" + isNaN(AGE_STR) + "</td></tr>"); var AGE_INT = 12; document.write("<tr><td>isNaN(AGE_INT)</td>"); document.write("<td>" + isNaN(AGE_INT) + "</td></tr>"); </script> </table> </body> </html>

Code Explanation

The output will look like this:

parseFloat() and parseInt()


The parseFloat() function takes one argument: a string. If the string begins with a number, the function reads through the string until it finds the end of the number, hacks off the remainder of the string, and returns the result. If the string does not begin with a number, the function returns NaN. The parseInt() function also takes one argument: a string. If the string begins with an integer, the function reads through the string until it finds the end of the integer, hacks off the remainder of the string, and returns the result. If the string does not begin with an integer, the function returns NaN.

Code Sample: JavaScriptFunctions/Demos/ParsingNumbers.html


<html> <head> <title>Parsing for Numbers</title> </head> <body> <table border="1" cellpadding="3" align="center"> <tr> <th>Function</th><th>Result</th> </tr> <script type="text/javascript"> var RACE = "26.2 miles";

document.write("<tr><td>parseFloat(RACE)</td>"); document.write("<td>" + parseFloat(RACE) + "</td></tr>"); document.write("<tr><td>parseInt(RACE)</td>"); document.write("<td>" + parseInt(RACE) + "</td></tr>"); RACE = "Marathon"; document.write("<tr><td>parseFloat(RACE)</td>"); document.write("<td>" + parseFloat(RACE) + "</td></tr>"); document.write("<tr><td>parseInt(RACE)</td>"); document.write("<td>" + parseInt(RACE) + "</td></tr>"); </script> </table> </body> </html>

Code Explanation

The output will look like this:

Built-in Functions vs. Methods


Methods and functions are similar in that they both make things happen. They are also syntactically similar. The major difference is that methods are tied to an object; whereas, functions are not. For example, alert() is a method of the window object; whereas parseInt() is a standalone function.

Exercise: Working with Built-in Functions


Duration: 15 to 25 minutes. In this exercise, you will practice working with JavaScript's built-in functions. 1. Open JavaScriptFunctions/Exercises/BuiltinFunctions.html for editing. 2. Modify the file so that it outputs the sum of the two numbers entered by the user.

Code Sample: JavaScriptFunctions/Exercises/BuiltinFunctions.html


<html> <head> <title>JavaScript Operators</title> <script type="text/javascript"> var USER_NUM1, USER_NUM2, NUMS_ADDED; USER_NUM1 = window.prompt("Choose a number.", ""); alert("You chose " + USER_NUM1); USER_NUM2 = window.prompt("Choose another number.", ""); alert("You chose " + USER_NUM2); NUMS_ADDED = USER_NUM1 + USER_NUM2; </script> </head> <body> <p style="text-align:center; font-size:large"> <script type="text/javascript"> document.write(USER_NUM1 + " + " + USER_NUM2 + " = "); document.write(NUMS_ADDED + "<br/>"); </script> </p> </body> </html>

Create a new HTML file that prompts the user for his name, the age at which he first worked on a computer, and his current age. After gathering this information, pop up an alert that tells the user how many years he's been working on a computer. The images

below show the steps:

Notice that the program is able to deal with numbers followed by strings (e.g, "12 years old"). Where is the solution?

User-defined Functions
Writing functions makes it possible to reuse code for common tasks. Functions can also be used to hide complex code. For example, an experienced developer can write a function for performing a complicated task. Other developers do not need to know how that function works; they only need to know how to call it.

Function Syntax
JavaScript functions generally appear in the head of the page or in external JavaScript files. A function is written using the function keyword followed by the name of the function. Syntax
function doSomething(){ //function statements go here }

As you can see, the body of the function is contained with in curly brackets ({}). The following example demonstrates the use of simple functions.

Code Sample: JavaScriptFunctions/Demos/SimpleFunctions.html

<html> <head> <title>JavaScript Simple Functions</title> <script type="text/javascript"> function changeBgRed(){ document.bgColor = "red"; } function changeBgWhite(){ document.bgColor = "white"; } </script> </head> <body> <p align="center"> <span onclick="changeBgRed();">Red</span> | <span onclick="changeBgWhite();">White</span> </p> </body> </html>

Passing Values to Functions


The functions above aren't very useful because they always do the same thing. Every time we wanted to add another color, we would have to write another function. Also, if we want to modify the behavior, we will have to do it in each function. The example below shows how to create a single function to handle changing the background color.

Code Sample: JavaScriptFunctions/Demos/PassingValues.html


<html> <head> <title>JavaScript Simple Functions</title> <script type="text/javascript"> function changeBg(color){ document.bgColor = color; } </script> </head> <body> <p align="center"> <span onclick="changeBg('red');">Red</span> | <span onclick="changeBg('white');">White</span> </p> </body> </html>

Code Explanation As you can see, when calling the changeBG() function, we pass a value (e.g, 'red'), which is assigned to the color variable. We can then refer to the color variable throughout the

function. Variables created in this way are called function arguments or parameters. A function can have any number of arguments, separated by commas.

A Note on Variable Scope


Variables created through function arguments or declared within a function with var are local to the function, meaning that they cannot be accessed outside of the function. Variables declared with var outside of a function and variables that are used without being declared are global, meaning that they can be used anywhere on the page.

Exercise: Writing a JavaScript Function


Duration: 15 to 25 minutes. In this exercise, you will modify a page called ColorMania.html, which will contain a form with four buttons. Each button will show the name of a color (e.g, red) and, when clicked, call a function that changes the background color. The buttons you will create will be of type button. For example, <input type="button" value="red" onclick="functionCall();"> 1. Open JavaScriptFunctions/Exercises/ColorMania.html for editing. 2. Write code to prompt the user for her name. 3. Write a function called changeBg() that changes the background color and then pops up an alert telling the user, by name, what the new background color is. 4. In the form, add four buttons that, when clicked, call the changeBg() function and pass it a color value.

The resulting page should look like this:

Code Sample: JavaScriptFunctions/Exercises/ColorMania.html


<html> <head> <title>Colormania</title> <script type="text/javascript"> //PROMPT USER FOR NAME /* Write a function called changeBg() that changes the background color and then pops up an alert telling the user, by name, what the new background color is. */ </script> </head> <body> <form style="text-align:center"> <!--ADD BUTTONS HERE--> </form> </body> </html>

Add another button called "custom" that, when clicked, prompts the user for a color, then changes the background color to the user-entered color and alerts the user to the change. Where is the solution?

Returning Values from Functions


The return keyword is used to return values from functions as the following example illustrates.

Code Sample: JavaScriptFunctions/Demos/ReturnValue.html


<html> <head> <title>Returning a Value</title> <script type="text/javascript"> function setBgColor(){ document.bgColor = prompt("Set Background Color:", ""); } function getBgColor(){ return document.bgColor; } </script> </head>

<body> <form> <input type="button" value="Set Background Color" onclick="setBgColor();"> <input type="button" value="Get Background Color" onclick="alert(getBgColor());"> </form> </body> </html>

Code Explanation When the user clicks on the "Get Background Color" button, an alert pops up with a value returned from the getBgColor() function. This is a very simple example. Generally, functions that return values are a bit more involved. We'll see many more functions that return values throughout the course.

JavaScript Functions Conclusion


In this lesson of the JavaScript tutorial, you have learned to work with JavaScript's builtin functions and to create functions of your own. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Built-In JavaScript Objects


In this lesson of the JavaScript tutorial, you will learn... 1. To work with the built-in String object. 2. To work with the built-in Math object. 3. To work with the built-in Date object.

JavaScript has some predefined, built-in objects that do not fit into the HTML DOM, meaning that they are not direct descendants of the window object.

String
In JavaScript, there are two types of string data types: primitive strings and String objects. String objects have many methods for manipulating and parsing strings of text. Because these methods are available to primitive strings as well, in practice, there is no need to differentiate between the two types of strings. Some common string properties and methods are shown below. In all the examples, the variable MY_STRING contains "Webucator". Common String Properties Property Description Read-only value containing the number of characters in length the string.

Example
MY_STRING.length //Returns 9

Common String Methods Method Description Example Returns the character MY_STRING.charAt(4) charAt(position) at the specified //Returns c position. Returns the Unicode character code of the MY_STRING.charCodeAt(4) charCodeAt(position) //Returns 99 character at the specified position. Returns the text representation of the specifies commafromCharCode(characterCode delimited character String.fromCharCode(169) s) //Returns codes. Used with String rather than a specific String object. Searches from MY_STRING.indexOf("cat"); startPosition for //Returns 4 substring. Returns indexOf(substring, the position at which MY_STRING.indexOf("cat", startPosition) the substring is 5); found. If substring is //Returns -1 not found, returns -1.
lastIndexOf(substring, endPosition)

Searches from the end of the string for

MY_STRING.lastIndexOf("cat" ); //Returns 4

Common String Methods Method Description Example substring until MY_STRING.lastIndexOf("cat" endPosition is , 5); reached. Returns the //Returns 4 position at which the substring is found. If substring is not found, returns -1. Returns the substring beginning at startPosition and ending with the MY_STRING.substring(4, 7); character before //Returns cat endPosition. substring(startPosition, endPosition) endPosition is MY_STRING.substring(4); optional. If it is //Returns cator excluded, the substring continues to the end of the string. Returns the substring of Length characters beginning at MY_STRING.substr(4, 3); startPosition. length //Returns cat substr(startPosition, is optional. If it is length) MY_STRING.substr(4); excluded, the substring continues //Returns cator to the end of the string. Same as slice(startPosition, MY_STRING.slice(4, 7); substring(startPositio //Returns cat endPosition) n, endPosition). positionFromEnd is a negative integer. Returns the the substring beginning slice(startPosition, MY_STRING.slice(4, -2); at startPosition and //Returns cat positionFromEnd) ending positionFromEnd characters from the end of the string. Returns an array by var s = "A,B,C,D"; split(delimiter) splitting a string on var a = s.split(","); document.write(a[2]); the specified

Method
toLowerCase() toUpperCase()

Common String Methods Description Example //Returns C delimiter. Returns the string in MY_STRING.toLowerCase() all lowercase letters. //Returns webucator Returns the string in MY_STRING.toUpperCase(); all uppercase letters. //Returns WEBUCATOR

You can see these examples in a browser by opening BuiltInObjects/Demos/StringPropertiesAndMethods.html.

Math
The Math object is a built-in static object. The Math object's properties and methods are accessed directly (e.g, Math.PI) and are used for performing complex math operations. Some common math properties and methods are shown below. Common Math Properties Description Example Pi ( ) Square root of 2.
Math.PI; //3.141592653589793 Math.SQRT2; //1.4142135623730951

Property
Math.PI Math.SQRT2

Method
Math.abs(number) Math.ceil(number) Math.floor(number) Math.max(numbers)

Common Math Methods Description Absolute value of number. number rounded up. number rounded down. Highest Number in numbers. Lowest Number in numbers. number to the power of power. Rounded number. Random number between 0 and 1.

Example
Math.abs(-12); //Returns 12 Math.ceil(5.4); //Returns 6 Math.floor(5.6); //Returns 5 Math.max(2, 5, 9, 3); //Returns 9 Math.min(2, 5, 9, 3); //Returns 2 Math.pow(2, 5); //Returns 32 Math.round(2.5); //Returns 3 Math.random(); //Returns random //number from 0 to 1

Math.min(numbers) Math.pow(number, power) Math.round(number) Math.random()

You can see these examples in a browser by opening BuiltInObjects/Demos/MathPropertiesAndMethods.html.

Method for Generating Random Integers


var var var var LOW = 1; HIGH = 10; RND1 = Math.random(); RND2 = Math.round(RND1 * (HIGH - LOW) + 1);

Date
The Date object has methods for manipulating dates and times. JavaScript stores dates as the number of milliseconds since January 1, 1970. The sample below shows the different methods of creating date objects, all of which involve passing arguments to the Date() constructor.

Code Sample: BuiltInObjects/Demos/DateObject.html


<html> <head> <title>Date Object</title> </head> <body> <h1>Date Object</h1> <h2>New Date object with current date and time</h2> <pre> //Syntax: new Date(); var NOW = new Date(); </pre> <b>Result:</b> <script type="text/javascript"> var NOW = new Date(); document.write(NOW); </script> <h2>New Date object with specific date and time</h2> <pre> //Syntax: new Date("month dd, yyyy hh:mm:ss); var RED_SOX_WINS = new Date("October 21, 2004 12:01:00"); </pre> <b>Result:</b> <script type="text/javascript"> var RED_SOX_WINS = new Date("October 21, 2004 12:01:00"); document.write(RED_SOX_WINS); </script> <pre> //Syntax: new Date(yyyy, mm, dd, hh, mm, ss, ms); RED_SOX_WINS = new Date(2004, 9, 21, 12, 01, 00, 00); </pre>

<b>Result:</b> <script type="text/javascript"> RED_SOX_WINS = new Date(2004, 9, 21, 12, 01, 00, 00); document.write(RED_SOX_WINS); </script> </body> </html>

Code Explanation This page is shown in a browser below.

A few things to note:


To create a Date object containing the current date and time, the Date() constructor takes no arguments. When passing the date as a string to the Date() constructor, the time portion is optional. If it is not included, it defaults to 00:00:00. Also, other date formats are acceptable (e.g, "10/21/2004" and "10-04-2004"). When passing date parts to the Date() constructor, dd, hh, mm, ss, and ms are all optional. The default of each is 0.

Months are numbered from 0 (January) to 11 (December). In the example above, 9 represents October.

Some common date methods are shown below. In all the examples, the variable RIGHT_NOW contains "Thu Apr 14 00:23:54:650 EDT 2005". Common Date Methods Description Example Returns the day of the RIGHT_NOW.getDate(); //Returns 14 month (1-31). Returns the day of the RIGHT_NOW.getDay(); week as a number (0-6, //Returns 4 0=Sunday, 6=Saturday). Returns the month as a RIGHT_NOW.getMonth(); number (0-11, 0=January, //Returns 3 11=December). Returns the four-digit year. //Returns 2005 Returns the hour (0-23). Returns the minute (0-59). Returns the second (0-59).
RIGHT_NOW.getFullYear(); RIGHT_NOW.getHours(); //Returns 0 RIGHT_NOW.getMinutes(); //Returns 23 RIGHT_NOW.getSeconds(); //Returns 54 RIGHT_NOW.getMilliseconds(); //Returns 650 RIGHT_NOW.getTime(); //Returns 1113452634650 RIGHT_NOW.getTimezoneOffset(); //Returns 240

Method
getDate()

getDay()

getMonth() getFullYear() getHours() getMinutes() getSeconds() getMilliseconds()

Returns the millisecond (0999). Returns the number of getTime() milliseconds since midnight January 1, 1970. Returns the time difference getTimezoneOffset() in minutes between the user's computer and GMT.
toLocaleString()

Returns the Date object as RIGHT_NOW.toLocaleString(); //Returns Thursday, April 14, a string. //2005 12:23:54 AM Returns the Date object as RIGHT_NOW.toGMTString(); //Returns Thu, 14 Apr 2005 a string in GMT timezone. //04:23:54 UTC

toGMTString()

You can see these examples in a browser by opening BuiltInObjects/Demos/DateMethods.html.

typeof Operator

The typeof operator is used to find out the type of a piece of data. The screenshot below shows what the typeof operator returns for different data types.

Some languages have functions that return the the month as a string. JavaScript doesn't have such a built-in function. The sample below shows a user-defined function that handles this and how the getMonth() method of a Date object can be used to get the month.

Code Sample: BuiltInObjects/Demos/MonthAsString.html


<html> <head> <title>Month As String</title> <script type="text/javascript"> function monthAsString(num){ var months = []; months[0] = "January"; months[1] = "February"; months[2] = "March"; months[3] = "April"; months[4] = "May"; months[5] = "June";

months[6] = "July"; months[7] = "August"; months[8] = "September"; months[9] = "October"; months[10] = "November"; months[11] = "December"; return months[num-1]; } function enterMonth(){ var userMonth = prompt("What month were you born?", ""); alert("You were born in " + monthAsString(userMonth) + "."); } function getCurrentMonth(){ var today = new Date(); alert(monthAsString(today.getMonth()+1)); } </script> </head> <body> <form> <input type="button" value="CHOOSE MONTH" onclick="enterMonth();"> <input type="button" value="GET CURRENT MONTH" onclick="getCurrentMonth();"> </form> </body> </html>

Exercise: Returning the Day of the Week as a String


Duration: 15 to 25 minutes. In this exercise, you will create a function that returns the day of the week as a string. 1. Open BuiltInObjects/Exercises/DateUDFs.html for editing. 2. Write a dayAsString() function that returns the day of the week as a string. 3. Write an enterDay() function that prompts the user for the day of the week and then alerts the string value of that day by calling the dayAsString() function. 4. Write a getCurrentDay() function that alerts today's actual day of the week according to the user's machine. 5. Add a "CHOOSE DAY" button that calls the enterDay() function. 6. Add a "GET CURRENT DAY" button that calls the getCurrentDay() function. 7. Test your solution in a browser. Where is the solution?

Built-In JavaScript Objects Conclusion

In this lesson of the JavaScript tutorial, you have learned to work with some of JavaScript's most useful built-in objects. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Conditionals and Loops


In this lesson of the JavaScript tutorial, you will learn... 1. 2. 3. 4. To write if - else if - else blocks. To write switch / case blocks. To return values from functions. To work with loops in JavaScript.

Conditionals
There are two types of conditionals in JavaScript. 1. if - else if - else 2. switch / case

if - else if - else Conditions


Syntax
if (conditions) { statements; } else if (conditions) { statements; } else { statements; }

Like with functions, each part of the if - else if - else block is contained within curly brackets ({}). There can be zero or more else if blocks. The else block is optional. Comparison Operators

Operator Description == Equals != Doesn't equal === Strictly equals !== Doesn't strictly equal > Is greater than < Is less than >= Is greater than or equal to <= Is less than or equal to Logical Operators Operator Description && and (a == b && c != d) || or (a == b || c != d) ! not !(a == b || c != d) The example below shows a function using and if - else if - else condition.

Code Sample: ConditionalsAndLoops/Demos/IfElseifElse.html


<html> <head> <title>JavaScript Conditionals Demo</title> <script type="text/javascript"> function checkAge(){ var age = prompt("Your age?", "") || ""; if (age >= 21) { alert("You can vote and drink!"); } else if (age >= 18) { alert("You can vote, but can't drink."); } else { alert("You cannot vote or drink."); } } </script> </head> <body style="text-align:center"> <h1>JavaScript if - else if - else Demo</h1> <h3>Age Check</h3> <form> <input type="button" value="Age Check" onclick="checkAge();"> </form> </body> </html>

Code Explanation

The display of the page is shown below.

When the user clicks on the Age Check button, the following prompt pops up.

After the user enters his age, an alert pops up. The text of the alert depends on the user's age. The three possibilities are shown below.

Compound Conditions
Compound conditions are conditions that check for multiple things. See the sample below.
if (age > 18 && isCitizen) { alert("You can vote!"); } if (age >= 16 && (isCitizen || hasGreenCard)) { alert("You can work in the United States"); }

Short-circuiting
JavaScript is lazy (or efficient) about processing compound conditions. As soon as it can determine the overall result of the compound condition, it stops looking at the remaining parts of the condition. This is useful for checking that a variable is of the right data type before you try to manipulate it. To illustrate, take a look at the following sample.

Code Sample: ConditionalsAndLoops/Demos/PasswordCheckBroken. html


<html> <head> <title>Password Check</title> <script type="text/javascript"> var USER_PASS = prompt("Password:", ""); //ESC here causes problems var PASSWORD = "xyz"; </script> </head>

<body> <script type="text/javascript"> if (USER_PASS.toLowerCase() == PASSWORD) { document.write("<h1>Welcome!</h1>"); } else { document.write("<h1>Bad Password!</h1>"); } </script> </body> </html>

Code Explanation Everything works fine as long as the user does what you expect. However, if the user clicks on the Cancel button when prompted for a password, the value null will be assigned to USER_PASS. Because null is not a string, it does not have the toLowerCase() method. So the following line will result in a JavaScript error.
if (USER_PASS.toLowerCase() == password)

This can be fixed by using the typeof() function to first check if USER_PASS is a string as shown in the sample below.

Code Sample: ConditionalsAndLoops/Demos/PasswordCheck.html


<html> <head> <title>Password Check</title> <script type="text/javascript"> var USER_PASS = prompt("Password:", "") || ""; var PASSWORD = "xyz"; </script> </head> <body> <script type="text/javascript"> if (typeof USER_PASS == "string" && USER_PASS.toLowerCase() == PASSWORD) { document.write("<h1>Welcome!</h1>"); } else { document.write("<h1>Bad Password!</h1>"); } </script> </body> </html>

Switch / Case
Syntax
switch (expression) {

case value : statements; case value : statements; default : statements; }

Like if - else if - else statements, switch/case statements are used to run different code at different times. Generally, switch/case statements run faster than if - else if - else statements, but they are limited to checking for equality. Each case is checked to see if the expression matches the value. Take a look at the following example.

Code Sample: ConditionalsAndLoops/Demos/SwitchWithoutBreak.ht ml


<html> <head> <title>Switch</title> <script type="text/javascript"> var QUANTITY = 1; switch (QUANTITY) { case 1 : alert("QUANTITY is 1"); case 2 : alert("QUANTITY is 2"); default : alert("QUANTITY is not 1 or 2"); } </script> </head> <body> Nothing to show here. </body> </html>

Code Explanation When you run this page in a browser, you'll see that all three alerts pop up, even though only the first case is a match. That's because if a match is found, none of the remaining cases is checked and all the remaining statements in the switch block are executed. To stop this process, you can insert a break statement, which will end the processing of the switch statement. The corrected code is shown in the example below.

Code Sample: ConditionalsAndLoops/Demos/SwitchWithBreak.html


<html> <head> <title>Switch</title> <script type="text/javascript"> var QUANTITY = 1; switch (QUANTITY) { case 1 : alert("QUANTITY is 1"); break; case 2 : alert("QUANTITY is 2"); break; default : alert("QUANTITY is not 1 or 2"); } </script> </head> <body> Nothing to show here. </body> </html>

The following example shows how a switch/case statement can be used to record the user's browser type.

Code Sample: ConditionalsAndLoops/Demos/BrowserSniffer.html


<html> <head> <title>Simple Browser Sniffer</title> <script type="text/javascript"> switch (navigator.appName) { case "Microsoft Internet Explorer" : alert("This is IE!"); break; case "Netscape" : alert("This is Mozilla!"); break; default : alert("This is something other than IE or Mozilla!"); } </script> </head> <body> Nothing to show here. </body> </html>

Code Explanation

The navigator object, which is a child of window, holds information about the user's browser. In this case we are looking at the appName property, which has a value of "Microsoft Internet Explorer" for Internet Explorer and "Netscape" for Mozilla-based browsers (e.g, Navigator, Firefox).

Exercise: Conditional Processing


Duration: 20 to 30 minutes. In this exercise, you will practice using conditional processing. 1. Open ConditionalsAndLoops/Exercises/Conditionals.html for editing. 2. Notice that there is an onload event handler that calls the greetUser() function. Create this function in the script block. 3. The function should do the following: 1. Prompt the user for his/her gender and last name and store the results in variables. 2. If the user enters a gender other than "Male" or "Female", prompt him/her to try again. 3. If the user leaves the last name blank, prompt him/her to try again. 4. If the user enters a number for the last name, tell him/her that a last name can't be a number and prompt him/her to try again. 5. After collecting the gender and last name: If the gender is valid, pop up an alert that greets the user appropriately (e.g, "Hello Ms. Smith!") If the gender is not valid, pop up an alert that reads something like "XYZ is not a gender!" 4. Test your solution in a browser. 1. Allow the user to enter his/her gender in any case. 2. If the user enters a last name that does not start with a capital letter, prompt him/her to try again. Where is the solution?

Loops
There are several types of loops in JavaScript.

while do...while for for...in

while Loop Syntax


Syntax
while (conditions) { statements; }

Something, usually a statement within the while block, must cause the condition to change so that it eventually becomes false and causes the loop to end. Otherwise, you get stuck in an infinite loop, which can bring down the browser.

do...while Loop Syntax


Syntax
do { statements; } while (conditions);

Again something, usually a statement within the do block, must cause the condition to change so that it eventually becomes false and causes the loop to end. Unlike with while loops, the statements in do...while loops will always execute at least one time because the conditions are not checked until the end of each iteration.

for Loop Syntax


Syntax
for (initialization; conditions; change) { statements; }

In for loops, the initialization, conditions, and change are all placed up front and separated by semi-colons. This makes it easy to remember to include a change statement that will eventually cause the loop to end. for loops are often used to iterate through arrays. The length property of an array can be used to check how many elements the array contains.

for...in Loop Syntax


Syntax
for (var item in array) { statements; }

for...in loops are specifically designed for looping through arrays. For reasons that will be better understood when we look at object augmentation, the above syntax has a slight flaw. If the Array class is changed, it is possible that the for...in loop includes more items than what you anticipated. To be on the safe side, we suggest that you use a more verbose syntax as seen below. Syntax
for (var item in array) if (array.hasOwnProperty(item)) { statements; }

The hasOwnProperty() call will ensure that the item is indeed an element that you added to the array, not something that was inherited because of object augmentation.

Code Sample: ConditionalsAndLoops/Demos/Loops.html


<html> <head> <title>JavaScript Loops</title> <script type="text/javascript"> var INDEX; var BEATLES = []; BEATLES["Guitar1"] = "John"; BEATLES["Bass"] = "Paul"; BEATLES["Guitar2"] = "George"; BEATLES["Drums"] = "Ringo"; var RAMONES = ["Joey", "Johnny", "Dee Dee", "Mark"]; </script> </head> <body> <h1>JavaScript Loops</h1> <h2>while Loop</h2> <script type="text/javascript"> INDEX = 0; while (INDEX < 5) { document.write(INDEX); INDEX++; } </script> <h2>do...while Loop</h2> <script type="text/javascript"> INDEX = 1; do { document.write(INDEX); INDEX++; } while (INDEX < 5); </script>

<h2>for Loop</h2> <script type="text/javascript"> for (var INDEX=0; INDEX<=5; INDEX++) { document.write(INDEX); } </script> <h2>for Loop with Arrays</h2> <ol> <script type="text/javascript"> for (var INDEX=0; INDEX<RAMONES.length; INDEX++) { document.write("<li>" + RAMONES[INDEX] + "</li>"); } </script> </ol> <h2>for...in Loop</h2> <ol> <script type="text/javascript"> for (var instrument in BEATLES) if (BEATLES.hasOwnProperty(instrument)) { document.write("<li>Playing " + instrument + " there is " + BEATLES[instrument] + "</li>"); } </script> </ol> </body> </html>

Code Explanation The sample above shows demos of all the aforementioned loops.

Exercise: Working with Loops


Duration: 20 to 30 minutes. In this exercise, you will practice working with loops. 1. Open ConditionalsAndLoops/Exercises/Loops.html for editing. You will see that this file is similar to the solution from the last exercise. 2. Declare an additional variable called greeting. 3. Create an array called presidents that contains the last names of four or more past presidents. 4. Currently, the user only gets two tries to enter a valid gender and lastName. Modify the code so that, in both cases, the user continues to get prompted until he enters valid data. 5. Change the switch block so that it assigns an appropriate value (e.g, "Hello Ms. Smith") to the greeting variable rather than popping up an alert. 6. After the switch block, write code that alerts the user by name if he has the same last name as a president. There is no need to alert those people who have nonpresidential names.

1. Modify the code so that the first prompt for gender reads "What gender are you: Male or Female?", but all subsequent prompts for gender read "You must enter 'Male' or 'Female'. Try again:". 2. Modify the code so that the first prompt for last name reads "Enter your last name:", but all subsequent prompts for last name read "Please enter a valid last name:". 3. If the user presses the Cancel button on a prompt dialog, it returns null. Test this. It very likely results in a JavaScript error. If so, fix the code so that no JavaScript error occurs. 4. For those people who do not have presidential names, pop up an alert that tells them their names are not presidential. Where is the solution?

Conditionals and Loops Conclusion


In this lesson of the JavaScript tutorial, you learned to work with JavaScript if-else if-else and switch/case conditionals and several types of loops. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

JavaScript Form Validation


In this lesson of the JavaScript tutorial, you will learn... 1. 2. 3. 4. 5. 6. 7. 8. To access data entered by users in forms. To validate text fields and passwords. To validate radio buttons. To validate checkboxes. To validate select menus. To validate textareas. To write clean, reusable validation functions. To catch focus, blur, and change events.

Accessing Form Data


All forms on a web page are stored in the document.forms[] array. The first form on a page is document.forms[0], the second form is document.forms[1], and so on. However, it is usually easier to give the forms names (with the name attribute) and refer to them that way. For example, a form named LoginForm can be referenced as document.LoginForm. The major advantage of naming forms is that the forms can be repositioned on the page without affecting the JavaScript.

Elements within a form are properties of that form and are referenced as follows: Syntax
document.FormName.ElementName

Text fields and passwords have a value property that holds the text value of the field. The following example shows how JavaScript can access user-entered text.

Code Sample: FormValidation/Demos/FormFields.html


<html> <head> <title>Form Fields</title> <script type="text/javascript"> function changeBg(){ var userName = document.forms[0].UserName.value; var bgColor = document.BgForm.BgColor.value; document.bgColor = bgColor; alert(userName + ", the background color is " + bgColor + "."); } </script> </head> <body> <h1>Change Background Color</h1> <form name="BgForm"> Your Name: <input type="text" name="UserName" size="10"><br/> Background Color: <input type="text" name="BgColor" size="10"><br/> <input type="button" value="Change Background" onclick="changeBg();"> </form> </body> </html>

Code Explanation Some things to notice: 1. When the user clicks on the "Change Background" button, the changeBg() function is called. 2. The values entered into the UserName and BgColor fields are stored in variables (userName and bgColor). 3. This form can be referenced as forms[0] or BgForm. The UserName field is referenced as document.forms[0].UserName.value and the BgColor field is referenced as document.BgForm.BgColor.value.

Exercise: Textfield to Textfield


Duration: 15 to 25 minutes.

In this exercise, you will write a function that bases the value of one text field on the value of another. 1. Open FormValidation/Exercises/TextfieldToTextField.html for editing. 2. Write a function called getMonth() that passes the month number entered by the user to the monthAsString() function in DateUDFs.js and writes the result in the MonthName field.

Code Sample: FormValidation/Exercises/TextfieldToTextField.html


<html> <head> <title>Textfield to Textfield</title> <script src="DateUDFs.js" type="text/javascript"></script> <script type="text/javascript"> /* Write a function called getMonth() that passes the month number entered by the user to the monthAsString() function in DateUDFs.js and writes the result in the MonthName field. */ </script> </head> <body> <h1>Month Check</h1> <form name="DateForm"> Month Number: <input type="text" name="MonthNumber" size="2"> <input type="button" value="Get Month" onclick="getMonth();"><br> Month Name: <input type="text" name="MonthName" size="10"> </form> </body> </html>

1. If the user enters a number less than 1 or greater than 12 or a non-number, have the function write "Bad Number" in the MonthName field. 2. If the user enters a decimal between 1 and 12 (inclusive), strip the decimal portion of the number. Where is the solution?

Basics of Form Validation


When the user clicks on a submit button, an event occurs that can be caught with the form tag's onsubmit event handler. Unless JavaScript is used to explicitly cancel the submit event, the form will be submitted. The return false; statement explicitly cancels the submit event. For example, the following form will never be submitted.

<form action="Process.html" onsubmit="return false;"> <!--Code for form fields would go here--> <input type="submit" value="Submit Form"> </form>

Of course, when validating a form, we only want the form not to submit if something is wrong. The trick is to return false if there is an error, but otherwise return true. So instead of returning false, we call a validation function, which will specify the result to return.
<form action="Process.html" onsubmit="return validate(this);">

The this Object


Notice that we pass the validate() function the this object. The this object refers to the current object - whatever object (or element) the this keyword appears in. In the case above, the this object refers to the form object. So the entire form object is passed to the validate() function. Let's take a look at a simple example.

Code Sample: FormValidation/Demos/Login.html


<html> <head> <title>Login</title> <script type="text/javascript"> function validate(form){ var userName = form.Username.value; var password = form.Password.value; if (userName.length === 0) { alert("You must enter a username."); return false; } if (password.length === 0) { alert("You must enter a password."); return false; } return true; } </script> </head> <body> <h1>Login Form</h1> <form method="post" action="Process.html" onsubmit="return validate(this);"> Username: <input type="text" name="Username" size="10"><br/> Password: <input type="password" name="Password" size="10"><br/> <input type="submit" value="Submit"> <input type="reset" value="Reset Form">

</p> </form> </body> </html>

Code Explanation 1. When the user submits the form, the onsubmit event handler captures the event and calls the validate() function, passing in the form object. 2. The validate() function stores the form object in the form variable. 3. The values entered into the Username and Password fields are stored in variables (userName and password). 4. An if condition is used to check if userName is an empty string. If it is, an alert pops up explaining the problem and the function returns false. The function stops processing and the form does not submit. 5. An if condition is used to check that password is an empty string. If it is, an alert pops up explaining the problem and the function returns false. The function stops processing and the form does not submit. 6. If neither if condition catches a problem, the function returns true and the form submits.

Cleaner Validation
There are a few improvements we can make on the last example. One problem is that the validation() function only checks for one problem at a time. That is, if it finds and error, it reports it immediately and does not check for additional errors. Why not just tell the user all the mistakes that need to be corrected, so he doesn't have to keep submitting the form to find each subsequent error? Another problem is that the code is not written in a way that makes it easily reusable. For example, checking for the length of user-entered values is a common thing to do, so it would be nice to have a ready-made function to handle this. These improvements are made in the example below.

Code Sample: FormValidation/Demos/Login2.html


<html> <head> <title>Login</title> <script type="text/javascript"> function validate(form){ var userName = form.Username.value; var password = form.Password.value; var errors = []; if (!checkLength(userName)) { errors[errors.length] = "You must enter a username.";

} if (!checkLength(password)) { errors[errors.length] = "You must enter a password."; } if (errors.length > 0) { reportErrors(errors); return false; } return true; } function checkLength(text, min, max){ min = min || 1; max = max || 10000; if (text.length < min || text.length > max) { return false; } return true; } function reportErrors(errors){ var msg = "There were some problems...\n"; var numError; for (var i = 0; i<errors.length; i++) { numError = i + 1; msg += "\n" + numError + ". " + errors[i]; } alert(msg); } </script> </head> <body> <h1>Login Form</h1> <form method="post" action="Process.html" onsubmit="return validate(this);"> Username: <input type="text" name="Username" size="10"><br/> Password: <input type="password" name="Password" size="10"><br/> <input type="submit" value="Submit"> <input type="reset" value="Reset Form"> </p> </form> </body> </html>

Code Explanation Some things to notice: 1. Two additional functions are created: checkLength() and reportErrors().

2. 3. 4.

5.

The checkLength() function takes three arguments, the text to examine, the required minimum length, and the required maximum length. If the minimum length and maximum length are not passed, defaults of 1 and 10000 are used. o The reportErrors() function takes one argument, an array holding the errors. It loops through the errors array creating an error message and then it pops up an alert with this message. The \n is an escape character for a newline. In the main validate() function, a new array, errors, is created to hold any errors that are found. userName and password are passed to checkLength() for validation. o If errors are found, they are appended to errors. If there are any errors in errors (i.e, if its length is greater than zero), then errors is passed to reportErrors(), which pops up an alert letting the user know where the errors are. The validate() function then returns false and the form is not submitted. If no errors are found, the validate() function returns true and the form is submitted.

By modularizing the code in this way, it makes it easy to add new validation functions. In the next examples we will move the reusable validation functions into a separate JavaScript file called FormValidation.js.

Exercise: Validating a Registration Form


Duration: 25 to 40 minutes. In this exercise, you will write code to validate a registration form. 1. Open FormValidation/Exercises/FormValidation.js for editing. o Create a function called compareValues() that takes two arguments: val1 and val2. The function should return: 0 if the two values are equal -1 if val1 is greater than val2 1 if val2 is greater than val1 o Create a function called checkEmail() that takes one argument: email. The function should return: false if email has fewer than 6 characters false if email does not contain an @ symbol false if email does not contain a period (.) true otherwise 2. Open FormValidation/Exercises/Register.html for editing. o Add code so that the functions in FormValidation.js are accessible from this page. o Create a validate() function that does the following: Checks that the FirstName, LastName, City, Country, UserName, and Password1 fields are filled out.

Checks that the middle initial is a single character. Checks that the state is exactly two characters. Checks that the email is a valid email address. Checks that the values entered into Password1 and Password2 are the same. If there are errors, passes reportErrors() the errors array and returns false. If there are no errors, returns true. 3. Test your solution in a browser. In FormValidation/Exercises/FormValidation.js, modify the checkEmail() function so that it also checks to see that the final period (.) is after the final @ symbol. The solution is included in FormValidation/Solutions/FormValidation.js. Where is the solution?

Validating Radio Buttons


Radio buttons that have the same name are grouped as arrays. Generally, the goal in validating a radio button array is to make sure that the user has checked one of the options. Individual radio buttons have the checked property, which is true if the button is checked and false if it is not. The example below shows a simple function for checking radio button arrays.

Code Sample: FormValidation/Demos/RadioArrays.html


<html> <head> <title>Radio Arrays</title> <script type="text/javascript"> function validate(form){ var errors = []; if ( !checkRadioArray(form.container) ) { errors[errors.length] = "You must choose a cup or cone."; } if (errors.length > 0) { reportErrors(errors); return false; } return true; } function checkRadioArray(radioButtons){ for (var i=0; i < radioButtons.length; i++) {

if (radioButtons[i].checked) { return true; } } return false; } function reportErrors(errors){ var msg = "There were some problems...\n"; var numError; for (var i = 0; i<errors.length; i++) { numError = i + 1; msg += "\n" + numError + ". " + errors[i]; } alert(msg); } </script> </head> <body> <h1>Ice Cream Form</h1> <form method="post" action="Process.html" onsubmit="return validate(this);"> <b>Cup or Cone?</b> <input type="radio" name="container" value="cup"> Cup <input type="radio" name="container" value="plaincone"> Plain cone <input type="radio" name="container" value="sugarcone"> Sugar cone <input type="radio" name="container" value="wafflecone"> Waffle cone <br/><br/> <input type="submit" value="Place Order"> </form> </body> </html>

Code Explanation The checkRadioArray() function takes a radio button array as an argument, loops through each radio button in the array, and returns true as soon as it finds one that is checked. Since it is only possible for one option to be checked, there is no reason to continue looking once a checked button has been found. If none of the buttons is checked, the function returns false.

Validating Checkboxes
Like radio buttons, checkboxes have the checked property, which is true if the button is checked and false if it is not. However, unlike radio buttons, checkboxes are not stored as arrays. The example below shows a simple function for checking to make sure a checkbox is checked.

Code Sample: FormValidation/Demos/CheckBoxes.html

<html> <head> <title>Checkboxes</title> <script type="text/javascript"> function validate(form){ var errors = []; if ( !checkCheckBox(form.terms) ) { errors[errors.length] = "You must agree to the terms."; } if (errors.length > 0) { reportErrors(errors); return false; } return true; } function checkCheckBox(cb){ return cb.checked; } function reportErrors(errors){ var msg = "There were some problems...\n"; var numError; for (var i = 0; i<errors.length; i++) { numError = i + 1; msg += "\n" + numError + ". " + errors[i]; } alert(msg); } </script> </head> <body> <h1>Ice Cream Form</h1> <form method="post" action="Process.html" onsubmit="return validate(this);"> <input type="checkbox" name="terms"> I understand that I'm really not going to get any ice cream. <br/><br/> <input type="submit" value="Place Order"> </form> </body> </html>

Validating Select Menus


Select menus contain an array of options. The selectedIndex property of a select menu contains the index of the option that is selected. Often the first option of a select menu is something meaningless like "Please choose an option..." The checkSelect() function in the example below makes sure that the first option is not selected.

Code Sample: FormValidation/Demos/SelectMenus.html


<html> <head> <title>Check Boxes</title> <script type="text/javascript"> function validate(form){ var errors = []; if ( !checkSelect(form.flavor) ) { errors[errors.length] = "You must choose a flavor."; } if (errors.length > 0) { reportErrors(errors); return false; } return true; } function checkSelect(select){ return (select.selectedIndex > 0); } function reportErrors(errors){ var msg = "There were some problems...\n"; var numError; for (var i = 0; i<errors.length; i++) { numError = i + 1; msg += "\n" + numError + ". " + errors[i]; } alert(msg); } </script> </head> <body> <h1>Ice Cream Form</h1> <form method="post" action="Process.html" onsubmit="return validate(this);"> <b>Flavor:</b> <select name="flavor"> <option value="0" selected></option> <option value="choc">Chocolate</option> <option value="straw">Strawberry</option> <option value="van">Vanilla</option> </select> <br/><br/> <input type="submit" value="Place Order"> </form> </body> </html>

Focus, Blur, and Change Events


Focus, blur and change events can be used to improve the user experience.

Focus and Blur


Focus and blur events are caught with the onfocus and onblur event handlers. These events have corresponding focus() and blur() methods. The example below shows 1. how to set focus on a field. 2. how to capture when a user leaves a field. 3. how to prevent focus on a field.

Code Sample: FormValidation/Demos/FocusAndBlur.html


<html> <head> <title>Focus and Blur</title> <script src="DateUDFs.js" type="text/javascript"></script> <script type="text/javascript"> function getMonth(){ var elemMonthNumber = document.DateForm.MonthNumber; var monthNumber = elemMonthNumber.value; var elemMonthName = document.DateForm.MonthName; var month = monthAsString(elemMonthNumber.value); elemMonthName.value = (monthNumber > 0 && monthNumber <=12) ? month : "Bad Number"; } </script> </head> <body onload="document.DateForm.MonthNumber.focus();"> <h1>Month Check</h1> <form name="DateForm"> Month Number: <input type="text" name="MonthNumber" size="2" onblur="getMonth();"> Month Name: <input type="text" name="MonthName" size="10" onfocus="this.blur();"> </form> </body> </html>

Code Explanation Things to notice: 1. When the document is loaded, the focus() method of the text field element is used to set focus on the MonthNumber element.

2. When focus leaves the MonthNumber field, the onblur event handler captures the event and calls the getMonth() function. 3. The onfocus event handler of the MonthName element triggers a call to the blur() method of this (the MonthName element itself) to prevent the user from focusing on the MonthName element.

Change
Change events are caught when the value of a text element changes or when the selected index of a select element changes. The example below shows how to capture a change event.

Code Sample: FormValidation/Demos/Change.html


<html> <head> <title>Change</title> <script src="DateUDFs.js" type="text/javascript"></script> <script type="text/javascript"> function getMonth(){ var elemMonthNumber = document.DateForm.MonthNumber; var i = elemMonthNumber.selectedIndex; var monthNumber = elemMonthNumber[i].value; var elemMonthName = document.DateForm.MonthName; var month = monthAsString(monthNumber); elemMonthName.value = (i === 0) ? "" : month; } </script> </head> <body onload="document.DateForm.MonthNumber.focus();"> <h1>Month Check</h1> <form name="DateForm"> Month Number: <select name="MonthNumber" onchange="getMonth();"> <option>--Choose--</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> <option value="6">6</option> <option value="7">7</option> <option value="8">8</option> <option value="9">9</option> <option value="10">10</option> <option value="11">11</option> <option value="12">12</option> </select><br> Month Name: <input type="text" name="MonthName" size="10" onfocus="this.blur();">

</form> </body> </html>

Code Explanation This is similar to the last example. The only major difference is that MonthNumber is a select menu instead of a text field and that the getMonth() function is called when a different option is selected.

Validating Textareas
Textareas can be validated the same way that text fields are by using the checkLength() function shown earlier. However, because textareas generally allow for many more characters, it's often difficult for the user to know if he's exceeded the limit. It could be helpful to let the user know if there's a problem as soon as focus leaves the textarea. The example below, which contains a more complete form for ordering ice cream, includes a function that alerts the user if there are too many characters in a textarea.

Code Sample: FormValidation/Demos/IceCreamForm.html


<html> <head> <title>Check Boxes</title> <script type="text/javascript"> function validate(form){ var errors = []; if ( !checkRadioArray(form.container) ) { errors[errors.length] = "You must choose a cup or cone."; } if ( !checkCheckBox(form.terms) ) { errors[errors.length] = "You must agree to the terms."; } if ( !checkSelect(form.flavor) ) { errors[errors.length] = "You must choose a flavor."; } if (errors.length > 0) { reportErrors(errors); return false; } return true; } function checkRadioArray(radioButtons){ for (var i=0; i < radioButtons.length; i++) {

if (radioButtons[i].checked) { return true; } } return false; } function checkCheckBox(cb){ return cb.checked; } function checkSelect(select){ return (select.selectedIndex > 0); } function checkLength(text, min, max){ min = min || 1; max = max || 10000; if (text.length < min || text.length > max) { return false; } return true; } function checkTextArea(textArea, max){ var numChars, chopped, message; if (!checkLength(textArea.value, 0, max)) { numChars = textArea.value.length; chopped = textArea.value.substr(0, max); message = 'You typed ' + numChars + ' characters.\n'; message += 'The limit is ' + max + '.'; message += 'Your entry will be shortened to:\n\n' + chopped; alert(message); textArea.value = chopped; } } function reportErrors(errors){ var msg = "There were some problems...\n"; var numError; for (var i = 0; i<errors.length; i++) { numError = i + 1; msg += "\n" + numError + ". " + errors[i]; } alert(msg); } </script> </head> <body> <h1>Ice Cream Form</h1> <form method="post" action="Process.html" onsubmit="return validate(this);"> <p><b>Cup or Cone?</b> <input type="radio" name="container" value="cup"> Cup <input type="radio" name="container" value="plaincone"> Plain cone <input type="radio" name="container" value="sugarcone"> Sugar cone <input type="radio" name="container" value="wafflecone"> Waffle cone

</p> <p> <b>Flavor:</b> <select name="flavor"> <option value="0" selected></option> <option value="choc">Chocolate</option> <option value="straw">Strawberry</option> <option value="van">Vanilla</option> </select> </p> <p> <b>Special Requests:</b><br> <textarea name="requests" cols="40" rows="6" wrap="virtual" onblur="checkTextArea(this, 100);"></textarea> </p> <p> <input type="checkbox" name="terms"> I understand that I'm really not going to get any ice cream. </p> <input type="submit" value="Place Order"> </form> </body> </html>

Exercise: Improving the Registration Form


Duration: 15 to 25 minutes. In this exercise, you will make some improvements to the registration form from the last exercise. 1. Open FormValidation/Exercises/FormValidation2.js in your editor. You will see that the functions discussed above have been added: checkRadioArray(), checkCheckBox(), checkSelect(), and checkTextArea(). 2. Open FormValidation/Exercises/Register2.html for editing. o Notice that the following changes have been made to the form: State has been changed from a textfield to a select menu. The first option is meaningless. The next 51 options are U.S. states. The rest of the options are Canadian provinces. Country has been changed to a radio array. A Comments field has been added. A Terms checkbox has been added. o Write code that: Checks that a country is selected. Checks that the country and state selection are in sync. Checks that the terms have been accepted. o Add an event handler to the Comments textarea that alerts the user if the comment is too long. 3. Test your solution in a browser.

Where is the solution?

JavaScript Form Validation Conclusion


In this lesson of the JavaScript tutorial, you have learned to capture form events, to reference form fields, and to write clean, reusable form validation scripts. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Working with Images


In this lesson of the JavaScript tutorial, you will learn... 1. 2. 3. 4. To create image rollovers. To create backward-compatible image rollovers. To preload images. To create a slide show.

Image Rollovers
Image rollovers are commonly used to create a more interesting user experience and to help highlight navigation points. When the user hovers the mouse over an image, the source of the image is modified, so that a different image appears. When the user hovers the mouse back off the image, the original source is restored. The code below shows how to create a simple rollover.

Code Sample: DynamicImages/Demos/SimpleRollover.html


<html> <head> <title>Simple Image Rollover</title> </head> <body> <div style="text-align:center;"> <h1>Simple Image Rollover</h1> <img name="MyImage" src="Images/Banner.jpg" border="0" onmouseover="this.src = 'Images/Hulk.jpg';" onmouseout="this.src = 'Images/Banner.jpg';"> <p>Who are you calling simple?</p> </div> </body> </html>

Code Explanation

The mouse-over event is captured with the img tag's onmouseover event handler. When this happens, the following JavaScript code is called.
this.src = 'Images/Hulk.jpg';

The this object refers to the current object - whatever object (or element) the this keyword appears in. In the case above, the this object refers to the img object, which has a property called src that holds the path to the image. The code above sets the src to "Images/Hulk.jpg". Likewise, the mouse-out event is captured with the img tag's onmouseout event handler. When this happens, the following JavaScript code is called.
this.src = 'Images/Banner.jpg';

This code sets the src to "Images/Banner.jpg," which is what it originally was.

Backward Compatibility
The code above should work fine in Firefox, Internet Explorer 4.0 and later, and Netscape 6 and later. However, in earlier versions of browsers, images could not capture mouse events. The workaround is to wrap the <img> tag in an <a> tag and to put the event handlers in the <a> tag as shown below.

Code Sample: DynamicImages/Demos/SimpleRolloverbackward.html


<html> <head> <title>Simple Image Rollover</title> </head> <body> <div style="text-align:center;"> <h1>Simple Image Rollover</h1> <a href="#" onmouseover="document.MyImage.src = 'Images/Hulk.jpg';" onmouseout="document.MyImage.src = 'Images/Banner.jpg';"> <img name="MyImage" src="Images/Banner.jpg" border="0"></a> <p>Who are you calling simple?</p> </div> </body> </html>

An Image Rollover Function

Image rollovers can be handled by a function as well. The two examples below show an image rollover function for modern browsers and a backward-compatible image rollover function.

Code Sample: DynamicImages/Demos/SimpleRolloverFunction.html


<html> <head> <title>Simple Image Rollover Function</title> <script type="text/javascript"> function imageRollover(img, imgSrc){ img.src = imgSrc; } </script> </head> <body> <div style="text-align:center;"> <h1>Simple Image Rollover Function</h1> <img name="Banner" src="Images/Banner.jpg" border="0" onmouseover="imageRollover(this, 'Images/Hulk.jpg');" onmouseout="imageRollover(this, 'Images/Banner.jpg');"> <img name="Bruce" src="Images/Bruce.jpg" border="0" onmouseover="imageRollover(this, 'Images/Batman.jpg');" onmouseout="imageRollover(this, 'Images/Bruce.jpg');"> <p>Who are you calling simple?</p> </div> </body> </html>

Code Sample: DynamicImages/Demos/SimpleRolloverFunctionbackward.html


<html> <head> <title>Simple Image Rollover Function</title> <script type="text/javascript"> function imageRollover(imgName, imgSrc){ if (document.images) { document.images[imgName].src = imgSrc; } } </script> </head> <body> <div style="text-align:center;"> <h1>Simple Image Rollover Function</h1> <a href="#" onmouseover="imageRollover('MyImage', 'Images/Hulk.jpg');" onmouseout="imageRollover('MyImage', 'Images/Banner.jpg');">

<img name="MyImage" src="Images/Banner.jpg" border="0"></a> <p>Who are you calling simple?</p> </div> </body> </html>

Code Explanation Why the check for document.images? Some early versions of browsers don't support the images array.

Preloading Images
When working with files on a local machine, image rollovers like the ones we have seen in previous examples work just fine. However, when the user first hovers over an image rollover image, the new image file has to be found and delivered to the page. If the new image is on a far-away server, this can take a few moments, causing an ugly pause in the rollover effect. This can be prevented by preloading images. Images can be preloaded by creating an Image object with JavaScript and assigning a value to the src of that Image. A sample is shown below.

Code Sample: DynamicImages/Demos/PreloadingImages.html


<html> <head> <title>Preloading Images</title> <script type="text/javascript"> var IMAGE_PATHS = []; IMAGE_PATHS[0] = "Images/Hulk.jpg"; IMAGE_PATHS[1] = "Images/Batman.jpg"; var IMAGE_CACHE = []; for (var i=0; i<IMAGE_PATHS.length; i++) { IMAGE_CACHE[i] = new Image(); IMAGE_CACHE[i].src = IMAGE_PATHS[i]; } function imageRollover(img, imgSrc) { img.src = imgSrc; } </script> </head> <body> <div style="text-align:center;"> <h1>Simple Image Rollover Function</h1> <img name="Banner" src="Images/Banner.jpg" border="0" onmouseover="imageRollover(this, 'Images/Hulk.jpg');"

onmouseout="imageRollover(this, 'Images/Banner.jpg');"> <img name="Bruce" src="Images/Bruce.jpg" border="0" onmouseover="imageRollover(this, 'Images/Batman.jpg');" onmouseout="imageRollover(this, 'Images/Bruce.jpg');"> <p>Who are you calling simple?</p> </div> </body> </html>

Code Explanation Notice that the code is not in a function. It starts working immediately as follows: 1. An array called IMAGE_PATHS is created to hold the paths to the images that need to be preloaded.
var IMAGE_PATHS = [];

2. An array element is added for each image to be be preloaded.


3. IMAGE_PATHS[0] = "Images/Hulk.jpg"; IMAGE_PATHS[1] = "Images/Batman.jpg";

4. An array called IMAGE_CACHE is created to hold the Image objects that will hold the preloaded images.
var IMAGE_CACHE = [];

5. A for loop is used to create an Image object and load in an image for each image path in IMAGE_PATHS.
6. 7. 8. } for (var i=0; i<IMAGE_PATHS.length; i++) { IMAGE_CACHE[i]=new Image(); IMAGE_CACHE[i].src=IMAGE_PATHS[i];

Exercise: Creating a Slide Show


Duration: 20 to 30 minutes. In this exercise, you will practice working with images by creating a slide show. 1. Open DynamicImages/Exercises/SlideShow.html for editing. 2. Write code to preload Images/Banner.jpg, Images/Hulk.jpg, Images/Bruce.jpg, and Images/Batman.jpg. The paths to the images should be stored in an array called IMAGE_PATHS. The Image objects should be stored in an array called IMAGE_CACHE. 3. Create a function called changeSlide() that takes one parameter: dir, and behaves as follows: o If dir equals 1, it changes the slide to the next image as stored in the IMAGE_PATHS array.

If dir equals -1, it changes the slide to the previous image as stored in the IMAGE_PATHS array. o If the user is viewing the last slide and clicks the Next button, the first slide should appear. o If the user is viewing the first slide and clicks the Previous button, the last slide should appear. 4. Test your solution in a browser. 1. Add a dropdown menu below the Previous and Next buttons that contains the names of the images without their extensions: "Banner", "Hulk", "Bruce" and "Batman". 2. When the user selects an image from the dropdown, have that image appear above. 3. When the user changes the image above using the Previous and Next buttons, have the dropdown menu keep in sync (i.e, show the name of the current image). The solution should look like the screenshot below.

Where is the solution?

Working with Images Conclusion


In this lesson of the JavaScript tutorial, you have learned how JavaScript can be used to manipulate HTML images to create image rollover effects and slide shows.

To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Regular Expressions
In this lesson of the JavaScript tutorial, you will learn... 1. To use regular expressions for advanced form validation. 2. To use regular expressions and backreferences to clean up form entries.

Getting Started
Regular expressions are used to do sophisticated pattern matching, which can often be helpful in form validation. For example, a regular expression can be used to check whether an email address entered into a form field is syntactically correct. JavaScript supports Perl-compatible regular expressions. There are two ways to create a regular expression in JavaScript: 1. Using literal syntax
var reExample = /pattern/;

2. Using the RegExp() constructor


var reExample = new RegExp("pattern");

Assuming you know the regular expression pattern you are going to use, there is no real difference between the two; however, if you don't know the pattern ahead of time (e.g, you're retrieving it from a form), it can be easier to use the RegExp() constructor.

JavaScript's Regular Expression Methods


The regular expression method in JavaScript has two main methods for testing strings: test() and exec().

The exec() Method

The exec() method takes one argument, a string, and checks whether that string contains one or more matches of the pattern specified by the regular expression. If one or more matches is found, the method returns a result array with the starting points of the matches. If no match is found, the method returns null.

The test() Method


The test() method also takes one argument, a string, and checks whether that string contains a match of the pattern specified by the regular expression. It returns true if it does contain a match and false if it does not. This method is very useful in form validation scripts. The code sample below shows how it can be used for checking a social security number. Don't worry about the syntax of the regular expression itself. We'll cover that shortly.

Code Sample: RegularExpressions/Demos/SsnChecker.html


<html> <head> <title>ssn Checker</title> <script type="text/javascript"> var RE_SSN = /^[0-9]{3}[\- ]?[0-9]{2}[\- ]?[0-9]{4}$/; function checkSsn(ssn){ if (RE_SSN.test(ssn)) { alert("VALID SSN"); } else { alert("INVALID SSN"); } } </script> </head> <body> <form onsubmit="return false;"> <input type="text" name="ssn" size="20"> <input type="button" value="Check" onclick="checkSsn(this.form.ssn.value);"> </form> </body> </html>

Code Explanation Let's examine the code more closely: 1. First, a variable containing a regular expression object for a social security number is declared.
var RE_SSN = /^[0-9]{3}[\- ]?[0-9]{2}[\- ]?[0-9]{4}$/;

2. Next, a function called checkSsn() is created. This function takes one argument: ssn, which is a string. The function then tests to see if the string matches the regular expression pattern by passing it to the regular expression object's test() method. If it does match, the function alerts "VALID SSN". Otherwise, it alerts "INVALID SSN".
3. 4. 5. 6. 7. 8. } function checkSsn(ssn){ if (RE_SSN.test(ssn)) { alert("VALID SSN"); } else { alert("INVALID SSN"); }

9. A form in the body of the page provides a text field for inserting a social security number and a button that passes the user-entered social security number to the checkSsn() function.
10. <form onsubmit="return false;"> 11. <input type="text" name="ssn" size="20"> 12. <input type="button" value="Check" 13. onclick="checkSsn(this.form.ssn.value);"> </form>

Flags
Flags appearing after the end slash modify how a regular expression works.

The i flag makes a regular expression case insensitive. For example, /aeiou/i matches all lowercase and uppercase vowels. The g flag specifies a global match, meaning that all matches of the specified pattern should be returned.

String Methods
There are several String methods that use regular expressions.

The search() Method


The search() method takes one argument: a regular expression. It returns the index of the first character of the substring matching the regular expression. If no match is found, the method returns -1.
"Webucator".search(/cat/); //returns 4

The split() Method


The split() method takes one argument: a regular expression. It uses the regular expression as a delimiter to split the string into an array of strings.

"Webucator".split(/[aeiou]/); /* returns an array with the following values: "W", "b", "c", "t", "r" */

The replace() Method


The replace() method takes two arguments: a regular expression and a string. It replaces the first regular expression match with the string. If the g flag is used in the regular expression, it replaces all matches with the string.
"Webucator".replace(/cat/, "dog"); //returns Webudogor "Webucator".replace(/[aeiou]/g, "x"); //returns Wxbxcxtxr

The match() Method


The match() method takes one argument: a regular expression. It returns each substring that matches the regular expression pattern.
"Webucator".match(/[aeiou]/g); /* returns an array with the following values: "e", "u", "a", "o" */

Regular Expression Syntax


A regular expression is a pattern that specifies a list of characters. In this section, we will look at how those characters are specified.

Start and End ( ^ $ )


A caret (^) at the beginning of a regular expression indicates that the string being searched must start with this pattern.

The pattern ^foo can be found in "food", but not in "barfood".

A dollar sign ($) at the end of a regular expression indicates that the string being searched must end with this pattern.

The pattern foo$ can be found in "curfoo", but not in "food".

Number of Occurrences ( ? + * {} )

The following symbols affect the number of occurrences of the preceding character: ?, +, *, and {}. A questionmark (?) indicates that the preceding character should appear zero or one times in the pattern.

The pattern foo? can be found in "food" and "fod", but not "faod".

A plus sign (+) indicates that the preceding character should appear one or more times in the pattern.

The pattern fo+ can be found in "fod", "food" and "foood", but not "fd".

A asterisk (*) indicates that the preceding character should appear zero or more times in the pattern.

The pattern fo*d can be found in "fd", "fod" and "food".

Curly brackets with one parameter ( {n} ) indicate that the preceding character should appear exactly n times in the pattern.

The pattern fo{3}d can be found in "foood" , but not "food" or "fooood".

Curly brackets with two parameters ( {n1,n2} ) indicate that the preceding character should appear between n1 and n2 times in the pattern.

The pattern fo{2,4}d can be found in "food","foood" and "fooood", but not "fod" or "foooood".

Curly brackets with one parameter and an empty second paramenter ( {n,} ) indicate that the preceding character should appear at least n times in the pattern.

The pattern fo{2,}d can be found in "food" and "foooood", but not "fod".

Common Characters ( . \d \D \w \W \s \S )
A period ( . ) represents any character except a newline.

The pattern fo.d can be found in "food", "foad", "fo9d", and "fo*d".

Backslash-d ( \d ) represents any digit. It is the equivalent of [0-9].

The pattern fo\dd can be found in "fo1d", "fo4d" and "fo0d", but not in "food" or "fodd".

Backslash-D ( \D ) represents any character except a digit. It is the equivalent of [^0-9].

The pattern fo\Dd can be found in "food" and "foad", but not in "fo4d".

Backslash-w ( \w ) represents any word character (letters, digits, and the underscore (_) ).

The pattern fo\wd can be found in "food", "fo_d" and "fo4d", but not in "fo*d".

Backslash-W ( \W ) represents any character except a word character.

The pattern fo\Wd can be found in "fo*d", "fo@d" and "fo.d", but not in "food".

Backslash-s ( \s) represents any whitespace character (e.g, space, tab, newline, etc.).

The pattern fo\sd can be found in "fo d", but not in "food".

Backslash-S ( \S ) represents any character except a whitespace character.

The pattern fo\Sd can be found in "fo*d", "food" and "fo4d", but not in "fo d".

Grouping ( [] )
Square brackets ( [] ) are used to group options.

The pattern f[aeiou]d can be found in "fad" and "fed", but not in "food", "faed" or "fd". The pattern f[aeiou]{2}d can be found in "faed" and "feod", but not in "fod", "fed" or "fd".

Negation ( ^ )
When used after the first character of the regular expression, the caret ( ^ ) is used for negation.

The pattern f[^aeiou]d can be found in "fqd" and "f4d", but not in "fad" or "fed".

Subpatterns ( () )
Parentheses ( () ) are used to capture subpatterns.

The pattern f(oo)?d can be found in "food" and "fd", but not in "fod".

Alternatives ( | )
The pipe ( | ) is used to create optional patterns.

The pattern foo$|^bar can be found in "foo" and "bar", but not "foobar".

Escape Character ( \ )
The backslash ( \ ) is used to escape special characters.

The pattern fo\.d can be found in "fo.d", but not in "food" or "fo4d".

Backreferences
Backreferences are special wildcards that refer back to a subpattern within a pattern. They can be used to make sure that two subpatterns match. The first subpattern in a pattern is referenced as \1, the second is referenced as \2, and so on. For example, the pattern ([bmpw])o\1 matches bob, mom, pop, and wow, but not "bop" or "pow". A more practical example has to do matching the delimiter in social security numbers. Examine the following regular expression.
^\d{3}([\- ]?)\d{2}([\- ]?)\d{4}$

Within the caret (^) and dollar sign ($), which are used to specify the beginning and end of the pattern, there are three sequences of digits, optionally separated by a hyphen or a space. This pattern will be matched in all of following strings (and more).

123-45-6789 123 45 6789 123456789 123-45 6789 123 45-6789 123-456789

The last three strings are not ideal, but they do match the pattern. Backreferences can be used to make sure that the second delimiter matches the first delimiter. The regular expression would look like this.
^\d{3}([\- ]?)\d{2}\1\d{4}$

The \1 refers back to the first subpattern. Only the first three strings listed above match this regular expression.

Form Validation with Regular Expressions


Regular expressions make it easy to create powerful form validation functions. Take a look at the following example.

Code Sample: RegularExpressions/Demos/Login.html


<html> <head> <title>Login</title> <script type="text/javascript"> var RE_EMAIL = /^(\w+[\-\.])*\w+@(\w+\.)+[A-Za-z]+$/; var RE_PASSWORD = /^[A-Za-z\d]{6,8}$/; function validate(form){ var email = form.Email.value; var password = form.Password.value; var errors = []; if (!RE_EMAIL.test(email)) { errors[errors.length] = "You must enter a valid email address."; } if (!RE_PASSWORD.test(password)) { errors[errors.length] = "You must enter a valid password."; } if (errors.length > 0) { reportErrors(errors); return false; } return true; } function reportErrors(errors){ var msg = "There were some problems...\n"; for (var i = 0; i<errors.length; i++) { var numError = i + 1; msg += "\n" + numError + ". " + errors[i]; } alert(msg); } </script> </head> <body> <h1>Login Form</h1> <form method="post" action="Process.html" onsubmit="return validate(this);">

Email: <input type="text" name="Email" size="25"><br/> Password: <input type="password" name="Password" size="10"><br/> *Password must be between 6 and 10 characters and can only contain letters and digits.<br/> <input type="submit" value="Submit"> <input type="reset" value="Reset Form"> </p> </form> </body> </html>

Code Explanation This code starts by defining regular expressions for an email address and a password. Let's break each one down.
var RE_EMAIL = /^(\w+\.)*\w+@(\w+\.)+[A-Za-z]+$/;

1. The caret (^) says to start at the beginning. This prevents the user from entering invalid characters at the beginning of the email address. 2. (\w+[\-\.])* allows for a sequence of word characters followed by a dot or a dash. The * indicates that the pattern can be repeated zero or more times. Successful patterns include "ndunn.", "ndunn-", "nat.s.", and "nat-s-". 3. \w+ allows for one or more word characters. 4. @ allows for a single @ symbol. 5. (\w+\.)+ allows for a sequence of word characters followed by a dot. The + indicates that the pattern can be repeated one or more times. This is the domain name without the last portion (e.g, without the "com" or "gov"). 6. [A-Za-z]+ allows for one or more letters. This is the "com" or "gov" portion of the email address. 7. The dollar sign ($) says to end here. This prevents the user from entering invalid characters at the end of the email address.
var RE_PASSWORD = /^[A-Za-z\d]{6,8}$/;

1. The caret (^) says to start at the beginning. This prevents the user from entering invalid characters at the beginning of the password. 2. [A-Za-z\d]{6,8} allows for a six- to eight-character sequence of letters and digits. 3. The dollar sign ($) says to end here. This prevents the user from entering invalid characters at the end of the password.

Exercise: Advanced Form Validation


Duration: 25 to 40 minutes. 1. Open RegularExpressions/Exercises/FormValidation.js for editing. o Write additional regular expressions to check for: 1. Proper Name

starts with capital letter followed by one or more letters or apostophes may be multiple words (e.g, "New York City") zero or one capital letters

2. Initial

3. State
two capital letters 4. US Postal Code five digits (e.g, "02138") possibly followed by a dash and four digits (e.g, "-1234") 5. Username between 6 and 15 letters or digits 2. Open RegularExpressions/Exercises/Register.html for editing. o Add validation to check the following fields: 1. first name 2. middle initial 3. last name 4. city 5. state 6. zip 7. username 3. Test your solution in a browser.

1. Add regular expressions to test Canadian and United Kingdom postal codes: o Canadian Postal Code - A letter followed by a digit, a letter, a space, a digit, a letter, and a digit (e.g, M1A 1A1) o United Kingdom Postal Code - One or two letters followed by a digit, an optional letter, a space, a digit, and two letters (e.g, WC1N 3XX) 2. Modify Register.html to check the postal code against these two new regular expressions as well as the regular expression for a US postal code. Where is the solution?

Cleaning Up Form Entries


It is sometimes nice to clean up user entries immediately after they are entered. This can be done using a combination of regular expressions and the replace() method of a string object.

The replace() Method Revisited


Earlier, we showed how the replace() method of a string object can be used to replace regular expression matches with a string. The replace() method can also be used with backreferences to replace a matched pattern with a new string made up of substrings from the pattern. The example below illustrates this.

Code Sample: RegularExpressions/Demos/SsnCleaner.html


<html> <head> <title>ssn Cleaner</title> <script type="text/javascript"> var RE_SSN = /^(\d{3})[\- ]?(\d{2})[\- ]?(\d{4})$/; function cleanSsn(ssn){ if (RE_SSN.test(ssn)) { var cleanedSsn = ssn.replace(RE_SSN, "$1-$2-$3"); return cleanedSsn; } else { alert("INVALID SSN"); return ssn; } } </script> </head> <body> <form onsubmit="return false;"> <input type="text" name="ssn" size="20"> <input type="button" value="Clean SSN" onclick="this.form.ssn.value = cleanSsn(this.form.ssn.value);"> </form> </body> </html>

Code Explanation The cleanSsn() function is used to "clean up" a social security number. The regular expression contained in RE_SSN, ^(\d{3})[\- ]?(\d{2})[\- ]?(\d{4})$, contains three subexpressions: (\d{3}), (\d{2}), and (\d{4}). Within the replace() method, these subexpressions can be referenced as $1, $2, and $3, respectively. When the user clicks on the "Clean SSN" button, the cleanSsn() function is called. This function first tests to see that the user-entered value is a valid social security number. If it is, it then cleans it up with the line of code below, which dash-delimits the three substrings matching the subexpressions.
var cleanedSsn = ssn.replace(RE_SSN, "$1-$2-$3");

It then returns the cleaned-up social security number.

Exercise: Cleaning Up Form Entries


Duration: 15 to 25 minutes. 1. Open RegularExpressions/Exercises/PhoneCleaner.html for editing.

2. Where the comment indicates, declare a variable called cleanedPhone and assign it a cleaned-up version of the user-entered phone number. The cleaned up version should fit the following format: (555) 555-1212 3. Test your solution in a browser. Some phone numbers are given as a combination of numbers and letters (e.g, 877WEBUCATE). As is the case with 877-WEBUCATE, such numbers often have an extra character just to make the word complete. 1. Add a function called convertPhone() that: o strips all characters that are not numbers or letters o converts all letters to numbers ABC -> 2 DEF -> 3 GHI -> 4 JKL -> 5 MNO -> 6 PRS -> 7 TUV -> 8 WXY -> 9 QZ -> 0 o passes the first 10 characters of the resulting string to the cleanPhone() function o returns the resulting string 2. Modify the form, so that it calls convertPhone() rather than cleanPhone(). 3. Test your solution in a browser. Where is the solution?

Regular Expressions Conclusion


In this lesson of the JavaScript tutorial, you have learned to work with regular expressions to validate and to clean up form entries. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Dynamic Forms
In this lesson of the JavaScript tutorial, you will learn... 1. To create jump menus.

2. To create interdependent select menus. 3. To create a JavaScript TIMER 4. To build a simple JavaScript testing tool.

Jump Menus
A jump menu is a select menu that contains a list of websites or pages to visit. There are two main types of jump menus. One jumps to the selected page as soon as the user makes a selection. The other jumps to a page only after the user has made a selection and clicked on a "Go" button. We'll look at the latter type first and then create the former type in an exercise.

Code Sample: DynamicForms/Demos/JumpMenus.html


<html> <head> <title>Jump Menus</title> <script type="text/javascript"> function jumpMenu(select){ var i = select.selectedIndex; var selection = select.options[i].value; var url; if (i === 0) { alert("Please select a state."); } else { url = "http://www.50states.com/" + selection + ".htm"; location.href = url; } } </script> </head> <body> <form> <select name="State"> <option value="0">--SELECT A STATE--</option> <option value="alabama">Alabama</option> <option value="illinois">Illinois</option> <option value="massachu">Massachusetts</option> <option value="montana">Montana</option> <option value="ncarolin">North Carolina</option> <option value="wvirgini">West Virginia</option> </select> <input type="button" value="GO" onclick="jumpMenu(this.form.State);"> </form> </body> </html>

Code Explanation

Viewed in a browser, the page looks like this:

The options[] Array


Select menus contain an array of options. Like with all JavaScript arrays, the first item has an index of 0. So, in this example, the first option, which reads "--SELECT A STATE--" is option 0.

The selectedIndex Property


Select menus have a selectedIndex property that holds the index of the currently selected option. For example, in the sample above, if North Carolina is selected, State.selectedIndex would hold 5. To see if the first option of a select menu is selected, check if selectedIndex is 0. Let's look at the code above in detail. 1. The "GO" button has an onclick event handler that, when clicked, passes the State select menu to the jumpMenu() function.
<input type="button" value="GO" onclick="jumpMenu(this.form.State);">

2. The jumpMenu() function does three things: 1. Creates the variable i that holds the selectedIndex property of the passedin select menu. 2. Creates the variable selection that holds the value of the selected option. 3. Checks to see if the first option is selected. If it is, the function alerts the user to select an option.

If it is not, the function creates a variable, url, to hold the destination page and then loads that page by changing the href property of the location object to url.

Disabling Form Elements


Form elements can be disabled by setting the element's disabled property to true. They can be re-enabled by setting the disabled property to false. Syntax
FormElement.disabled = true; FormElement.disabled = false;

The example below is a modified version of the jump menu that disables the "GO" button unless a state is selected.

Code Sample: DynamicForms/Demos/JumpMenus2.html


<html> <head> <title>Jump Menus</title> <script type="text/javascript"> function jumpMenu(select){ var i = select.selectedIndex; var selection = select.options[i].value; var url = "http://www.50states.com/" + selection + ".htm"; location.href = url; } function toggleButton(form){ if (form.State.selectedIndex === 0) { form.btnJump.disabled = true; } else { form.btnJump.disabled = false; } } </script> </head> <body onload="toggleButton(document.forms[0]);"> <form> <select name="State" onChange="toggleButton(this.form);"> <option value="0">--SELECT A STATE--</option> <option value="alabama">Alabama</option> <option value="illinois">Illinois</option> <option value="massachu">Massachusetts</option> <option value="montana">Montana</option> <option value="ncarolin">North Carolina</option> <option value="wvirgini">West Virginia</option> </select> <input type="button" name="btnJump" value="GO"

onclick="jumpMenu(this.form.State);"> </form> </body> </html>

Code Explanation Notice that the jumpMenu() function no longer checks if a state has been selected. It is only called when the user clicks the "Go" button, which is only enabled if a state is selected.

Exercise: Modifying the Jump Menu


Duration: 15 to 25 minutes. In this exercise, you will modify the jump menu from the demo so that it jumps to the selected page as soon as the user makes a selection. 1. Open DynamicForms/Exercises/JumpMenus.html for editing. 2. Add an onchange event handler to the select menu that calls jumpMenu() and passes in itself. 3. Modify the function so that it no longer alerts the user when the first option is selected. 4. Remove the "GO" button from the form. 5. Test the solution in a browser. The jumpMenu() function isn't reusable. Why not? Fix the function so that it is portable and then modify the form accordingly. Where is the solution?

Interdependent Select Menus


Interdependent select menus allow you to populate one select menu based on a choice made in another select menu. For example, a menu of cities might change based on the state that was selected in another menu, as shown in the following example.

Code Sample: DynamicForms/Demos/Interdependent.html


<html> <head> <title>Interdependent Select Menus</title> <script type="text/javascript"> var NEW_YORKERS = []; var CALIFORNIANS = [];

NEW_YORKERS[0] NEW_YORKERS[1] NEW_YORKERS[2] NEW_YORKERS[3] CALIFORNIANS[0] CALIFORNIANS[1] CALIFORNIANS[2] CALIFORNIANS[3]

= = = = = = = =

new new new new new new new new

Option("New York City", "NYC"); Option("Syracuse", "SYR"); Option("Albany", "ALB"); Option("Rochester", "ROC"); Option("Los Angeles", "LAN"); Option("San Diego", "SDI"); Option("San Francisco", "SFR"); Option("Oakland", "OAK");

function populateSub(mainSel, subSel){ var mainMenu = mainSel; var subMenu = subSel; var subMenuItems; subMenu.options.length = 0; switch (mainMenu.selectedIndex) { case 0: subMenuItems = NEW_YORKERS; break; case 1: subMenuItems = CALIFORNIANS; break; } for (var i = 0; i < subMenuItems.length; i++) { subMenu.options[i] = subMenuItems[i]; } } </script> </head> <body> <form name="Menus"> <select name="State" onchange="populateSub(this, this.form.City);"> <option value="NY">New York</option> <option value="CA">California</option> </select> <select name="City"> <option value="NYC">New York City</option> <option value="SYR">Syracuse</option> <option value="ALB">Albany</option> <option value="ROC">Rochester</option> </select> </form> </body> </html>

Code Explanation Let's look at the code above in detail. 1. As the page loads, two arrays (NEW_YORKERS and CALIFORNIANS) are created and then populated with Options using the Option() constructor. The Option() constructor takes four parameters: text, value, defaultSelected, and selected. Only text is required.

2. The body of the page contains a form with two select menus: State and City. When a state is selected, the onchange event handler triggers a call to the populateSub() function, passing in the State menu as mainSel and the City menu as subSel. 3. A few variables are created and then the City menu is emptied with the following code.
subMenu.options.length = 0;

4. A switch statement based on the option selected in the main menu is used to determine the array to look in for the submenu's options.
5. switch (mainMenu.selectedIndex) { 6. case 0: 7. arrSubMenu = NEW_YORKERS; 8. break; 9. case 1: 10. arrSubMenu = CALIFORNIANS; 11. break; }

12. A for loop is used to loop through the array populating the submenu with options.

Making the Code Modular


A problem with the code in DynamicForms/Demos/Interdependent.html is that it would need to be modified in several places to add or change options. This next example, though more complex, is much more modular, and hence, reusable.

Code Sample: DynamicForms/Demos/Interdependent2.html


<html> <head> <title>Interdependent Select Menus</title> <script type="text/javascript"> var MENU = []; MENU[0] = []; MENU[1] = []; MENU[0][0] MENU[0][1] MENU[0][2] MENU[0][3] MENU[0][4] MENU[1][0] MENU[1][1] MENU[1][2] MENU[1][3] MENU[1][4] = = = = = = = = = = new new new new new new new new new new Option("New York", "NY"); Option("New York City", "NYC"); Option("Syracuse", "SYR"); Option("Albany", "ALB"); Option("Rochester", "ROC"); Option("California", "CA"); Option("Los Angeles", "LAN"); Option("San Diego", "SDI"); Option("San Francisco", "SFR"); Option("Oakland", "OAK");

function populateMain(mainSel, subSel){ var mainMenu = mainSel; var subMenu = subSel; mainMenu.options.length = 0; for (var i = 0; i < MENU.length; i++) { mainMenu.options[i] = MENU[i][0]; } populateSub(mainMenu, subMenu); } function populateSub(mainSel, subSel){ var mainMenu = mainSel; var subMenu = subSel; var optMainMenu; subMenu.options.length = 1; optMainMenu = mainMenu.selectedIndex; for (var i = 1; i < MENU[optMainMenu].length; i++) { subMenu.options[i] = MENU[optMainMenu][i]; } } </script> </head> <body onload="populateMain(document.Menus.State, document.Menus.City);"> <form name="Menus"> <select name="State" onchange="populateSub(this, this.form.City);"></select> <select name="City"> <option value="0">--Please Choose--</option> </select> </form> </body> </html>

Code Explanation This example uses a two-dimensional array to hold the menus. The first item of each array holds the State options, which is used in the main menu. The rest of the items in each array hold the City options used to populate the submenu. The State select menu starts out empty and the City menu starts out with just a single "Please Choose" option. The two functions populateMain() and populateSub() are used to populate the two menus. Both functions are completely generic and reusable.

Exercise: Creating Interdependent Select Menus


Duration: 20 to 30 minutes. In this exercise, you will modify DynamicForms/Exercises/Interdependent.html, so that the second select menu is dependent on the first. 1. Open DynamicForms/Exercises/Interdependent.html for editing.

2. Notice that an external JavaScript file, Select.js, is included. This file is shown below. 3. An array, MENU, is created and populated with four internal arrays: MENU[0], MENU[1], MENU[2], and MENU[3]. 4. Populate the arrays so that: o The Bands select menu will be populated with "Beatles", "Rolling Stones", "Genesis", and "Eagles". o When "Beatles" is chosen from the Bands select menu, the Artists select menu contains: Paul McCartney with the value of "http://www.paulmccartney.com" John Lennon with the value of "http://www.johnlennon.it" George Harrison with the value of "http://www.georgeharrison.com" Ringo Starr with the value of "http://www.ringostarr.com" o When "Rolling Stones" is chosen, the Artists select menu contains: Mick Jagger with the value of "http://www.mickjagger.com" Keith Richards with the value of "http://www.keithrichards.com" Charlie Watts with the value of "http://www.rosebudus.com/watts" Bill Wyman with the value of "http://www.billwyman.com" o When "Genesis" is chosen, the Artists select menu contains: Phil Collins with the value of "http://www.philcollins.co.uk" Peter Gabriel with the value of "http://www.petergabriel.com" Mike Rutherford with the value of "http://www.mikemechanics.com" o When "Eagles" is chosen, the Artists select menu contains: Don Henley with the value of "http://www.donhenley.com" Joe Walsh with the value of "http://www.joewalsh.com" Glenn Frey with the value of "http://www.imdb.com/name/nm0004940" 5. Change the values of arg1 and arg2 in the calls to populateMain() and populateSub() in the event handlers in the HTML code so that the correct arguments are passed to these functions.

Code Sample: DynamicForms/Exercises/Select.js


function populateMain(mainSel, subSel){ var mainMenu = mainSel; var subMenu = subSel; mainMenu.options.length = 0; for (var i = 0; i < MENU.length; i++) { mainMenu.options[i] = MENU[i][0]; } populateSub(mainMenu, subMenu); } function populateSub(mainSel, subSel){ var mainMenu = mainSel;

var subMenu = subSel; var optMainMenu; subMenu.options.length = 1; optMainMenu = mainMenu.selectedIndex; for (var i = 1; i < MENU[optMainMenu].length; i++) { subMenu.options[i] = MENU[optMainMenu][i]; } } function jumpMenu(select){ var i = select.selectedIndex; var url = select.options[i].value; if (i > 0) { location.href = url; } }

Code Sample: DynamicForms/Exercises/Interdependent.html


<html> <head> <title>Interdependent Select Menus</title> <script type="text/javascript" src="Select.js"></script> <script type="text/javascript"> var MENU = []; MENU[0] = []; MENU[1] = []; MENU[2] = []; MENU[3] = []; /* Populate the arrays so that: -The Bands select menu will be populated with Beatles, Rolling Stones, Genesis, and Eagles -When Beatles is chosen, the Artists select menu contains: TEXT VALUE Paul McCartney http://www.paulmccartney.com John Lennon http://www.johnlennon.it George Harrison http://www.georgeharrison.com Ringo Starr http://www.ringostarr.com -When Rolling Stones is chosen, the Artists select menu contains: TEXT VALUE Mick Jagger http://www.mickjagger.com Keith Richards http://www.keithrichards.com Charlie Watts http://www.rosebudus.com/watts Bill Wyman http://www.billwyman.com -When Genesis is chosen, the Artists select menu contains: TEXT VALUE Phil Collins http://www.philcollins.co.uk Peter Gabriel http://www.petergabriel.com Mike Rutherford http://www.mikemechanics.com

-When Eagles is chosen, the Artists select menu contains: TEXT VALUE Don Henley http://www.donhenley.com Joe Walsh http://www.joewalsh.com Glenn Frey http://www.imdb.com/name/nm0004940 Change the values of arg1 and arg2 in the calls to populateMain() and populateSub() in the event handlers in the HTML code so that the correct arguments are passed to these functions. */ </script> </head> <body onload="populateMain(arg1, arg2);"> <form name="Menus"> Band: <select name="Bands" onchange="populateSub(arg1, arg2);"></select> Artist: <select name="Artists" onchange="jumpMenu(this);"> <option value="0">--Please Choose--</option> </select> </form> </body> </html>

Where is the solution?

Creating a JavaScript Timer


JavaScript timers can be used to create timed quizzes or events. The trick to a timer is to call a function recursively on a time increment.

The setTimeout() Method


The window object's setTimeout() method is used to execute a block of JavaScript code every n milliseconds. The syntax is shown below.
setTimeout("statements to execute", n);

Or
setTimeout(functionToCall, n);

The example below shows how the setTimeout() method can be used to create a timer.

Code Sample: DynamicForms/Demos/Timer.html


<html> <head> <title>JavaScript Timer</title> <script type="text/javascript">

var SECONDS_LEFT, TIMER, TIMES_UP; function init(){ document.Timer.btnStart.disabled = true; } function resetTimer(seconds){ SECONDS_LEFT = seconds; document.Timer.TimeLeft.value = SECONDS_LEFT; clearTimeout(TIMER); document.Timer.btnStart.disabled = false; document.Timer.btnReset.disabled = true; } function decrementTimer(){ TIMES_UP = false; document.Timer.TimeLeft.value = SECONDS_LEFT; document.Timer.btnStart.disabled = true; document.Timer.btnReset.disabled = false; SECONDS_LEFT--; if (SECONDS_LEFT >= 0) { TIMER = setTimeout(decrementTimer, 1000); } else { alert("Time's up!"); resetTimer(10); } } </script> </head> <body onload="init();"> <form name="Timer" onsubmit="return false;"> Timer: <input type="text" name="TimeLeft" size="2" style="text-align:center" onfocus="this.blur();"> seconds left<br> <input type="button" name="btnStart" value="Start" onclick="decrementTimer();"> <input type="button" name="btnReset" value="Reset" onclick="resetTimer(10);"> </form> </body> </html>

Code Explanation Let's look at the code above in detail. 1. As the page loads, three global variables are created: SECONDS_LEFT to hold the number of seconds left, TIMER - to hold the timer, and TIMES_UP - to flag if the timer has run out.
var SECONDS_LEFT, TIMER, TIMES_UP;

2. The body of the page contains a form with a text field holding the number of seconds left, a "Start" button and a "Reset" button.
3. <form name="Timer" onsubmit="return false;">

4. Timer: <input type="text" name="TimeLeft" size="2" 5. style="text-aliagn:center" onfocus="this.blur();"> 6. seconds left<br> 7. <input type="button" name="btnStart" 8. value="Start" onclick="decrementTimer();"> 9. <input type="button" name="btnReset" 10. value="Reset" onclick="resetTimer(10);"> </form>

11. The onload event handler of the body tag triggers the init() function, which disables the "Start" button.
12. function init(){ 13. document.Timer.btnStart.disabled = true; }

14. When the user clicks on the "Reset" button, the resetTimer() function is called. This function does the following: o Sets SECONDS_LEFT to the number passed into the function.
SECONDS_LEFT = seconds; o

Sets the value of the TimeLeft text field to SECONDS_LEFT.


document.Timer.TimeLeft.value = SECONDS_LEFT;

Clears the TIMER timer.


clearTimeout(TIMER);

Enables the "Start" button.


document.Timer.btnStart.disabled = false;

Disables the "Reset" button.


document.Timer.btnReset.disabled = true;

15. When the user clicks on the "Start" button, the decrementTimer() function is called. This function does the following: o Sets TIMES_UP to false.
TIMES_UP = false; o

Sets the value of the TimeLeft text field to SECONDS_LEFT.


document.Timer.TimeLeft.value = SECONDS_LEFT;

Disables the "Start" button.


document.Timer.btnStart.disabled = true;

Enables the "Reset" button.


document.Timer.btnReset.disabled = false;

Decrements SECONDS_LEFT by one.


SECONDS_LEFT--;

o o o o o o

Checks to see if SECONDS_LEFT is greater than or equal to zero.


if (SECONDS_LEFT >= 0) { TIMER = setTimeout(decrementTimer, 1000); } else { alert("Times up!"); resetTimer(10); }

If SECONDS_LEFT is greater than or equal to zero, setTimeout() is used to re-call decrementTimer() after 1000 milliseconds (1 second). This creates a timer object, which is assigned to TIMER. If SECONDS_LEFT is less than zero, an alert pops up notifying the user that time is up and resetTimer(), which clears the timer, is called.

Exercise: Adding a "Pause" Button to the Timer


Duration: 10 to 20 minutes. In this exercise, you will add a "Pause" button to the timer. 1. Open DynamicForms/Exercises/Timer.html for editing. 2. This page is the same as DynamicForms/Demos/Timer.html, except that it has a new "Pause" button that, when clicked, calls the pauseTimer() function. Your job is to create this pauseTimer() function and to modify the script so that the right

buttons are enabled at the right times. The diagram below shows the four different

phases. 3. Test your solution in a browser. Where is the solution?

A Sample Quiz Tool


The following example brings together the concepts learned in this lesson to create a quiz

tool. The quiz looks like this:

Code Sample: DynamicForms/Demos/MathQuiz.html

<html> <head> <title>Math Quiz</title> <script type="text/javascript" src="Select.js"></script> <script type="text/javascript"> var MENU = []; var SECONDS_LEFT, TIMER, TIMES_UP, TOTAL_TIME = 10; function MENU[0] MENU[1] MENU[2] MENU[3] init(){ = []; = []; = []; = []; = = = = = = = = = = = = = = = = = = = = new new new new new new new new new new new new new new new new new new new new Option("Addition"); Option("5 + 4", "9"); Option("9 + 3", "12"); Option("7 + 12", "19"); Option("13 + 24", "37"); Option("Subtraction"); Option("5 - 1", "4"); Option("12 - 4", "8"); Option("23 - 11", "12"); Option("57 - 19", "38"); Option("Multiplication"); Option("1 * 3", "3"); Option("4 * 8", "32"); Option("7 * 12", "84"); Option("13 * 17", "221"); Option("Division"); Option("4 / 1", "4"); Option("12 / 3", "4"); Option("21 / 7", "3"); Option("121 / 11", "11");

MENU[0][0] MENU[0][1] MENU[0][2] MENU[0][3] MENU[0][4] MENU[1][0] MENU[1][1] MENU[1][2] MENU[1][3] MENU[1][4] MENU[2][0] MENU[2][1] MENU[2][2] MENU[2][3] MENU[2][4] MENU[3][0] MENU[3][1] MENU[3][2] MENU[3][3] MENU[3][4]

populateMain(document.Quiz.Operator, document.Quiz.Question); resetTimer(TOTAL_TIME); document.Quiz.btnCheck.disabled = true; document.Quiz.Answer.disabled = true; } function resetTimer(seconds){ TIMES_UP = true; SECONDS_LEFT = seconds; document.Quiz.TimeLeft.value = SECONDS_LEFT; clearTimeout(TIMER); document.Quiz.Answer.value = ""; } function decrementTimer(){ TIMES_UP = false; document.Quiz.TimeLeft.value = SECONDS_LEFT; SECONDS_LEFT--;

if (SECONDS_LEFT >= 0) { TIMER = setTimeout(decrementTimer, 1000); } else { alert("Time's up! The answer is " + getAnswer() + "."); resetTimer(TOTAL_TIME); } } function checkAnswer(answer){ var correctAnswer = getAnswer(); if (answer === correctAnswer) { alert("Right! The answer is " + correctAnswer + "."); } else { alert("Sorry. The correct answer is " + correctAnswer + "."); } removeOption(); questionChange(); } function removeOption(){ var i = document.Quiz.Operator.selectedIndex; var j = document.Quiz.Question.selectedIndex; MENU[i].splice(j, 1); if (MENU[i].length == 1) { MENU.splice(i, 1); if (MENU.length === 0) { endQuiz(); } } populateMain(document.Quiz.Operator, document.Quiz.Question); resetTimer(TOTAL_TIME); } function questionChange(){ if (document.Quiz.Question.selectedIndex === 0) { document.Quiz.btnCheck.disabled = true; document.Quiz.Answer.disabled = true; resetTimer(TOTAL_TIME); } else { document.Quiz.btnCheck.disabled = false; document.Quiz.Answer.disabled = false; decrementTimer(); } } function endQuiz(){ resetTimer(TOTAL_TIME); alert("Thanks for playing! The quiz will now reload."); init(); } function getAnswer(){ var i = document.Quiz.Question.selectedIndex; var answer = document.Quiz.Question[i].value; return answer; }

</script> </head> <body onload="init();"> <form name="Quiz" onsubmit="return false;"> <table border="1" cellspacing="0" cellpadding="4" align="center"> <tr> <td>Category:</td> <td> <select name="Operator" onchange="populateSub(this, this.form.Question); resetTimer(TOTAL_TIME);"> </select> </td> </tr> <tr> <td>Question:</td> <td> <select name="Question" onchange="questionChange();"> <option value="0">--Please Choose--</option> </select> </td> </tr> <tr> <td>Answer:</td> <td> <input type="text" name="Answer" size="2"> <input type="button" name="btnCheck" value="Check Answer" onclick="checkAnswer(this.form.Answer.value);"> </td> </tr> <tr> <td>Timer:</td> <td><input type="text" name="TimeLeft" size="2" style="text-align:center" onfocus="this.blur();"> seconds left</td> </tr> </table> </form> </body> </html>

Code Explanation Here's how the quiz works: 1. The Question select menu is always populated with questions in the indicated category. 2. The "Check Answer" button and the Answer text field are only enabled when a question is selected. 3. The timer starts when a question is selected and stops when it runs out or when the "Check Answer" button is clicked. 4. When the "Check Answer" button is clicked, the user is alerted as to whether or not the answer is correct and the question is removed from the question menu.

5. When all questions in a category are gone, the category is removed from the category menu. 6. When all questions in all categories have been completed, the user is thanked for taking the quiz and the entire quiz is restarted. Spend some time reviewing this code. You shouldn't see anything new, except in the way the code is designed.

Dynamic Forms Conclusion


In this lesson of the JavaScript tutorial, you have learned to build interactive, dynamic forms. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

The User's Environment


In this lesson of the JavaScript tutorial, you will learn... 1. 2. 3. 4. To detect the user's operating system. To detect browser information. To determine if the user's browser supports a specific feature. To determine if Cookies are turned off.

Why Know the User's Environment?


Although browsers have come a long way in the past several years, they unfortunately do not all support the W3C specifications to the same degree. When writing sophisticated code that is intended to work across multiple browsers and platforms, it is often necessary to branch the code for the different environments. A very good way to manage this is to create a JavaScript file that stores many environment-related variables that hold flags indicating the type of browser or support for a specific object, property or method. The code below demonstrates how this file might be structured.

Code Sample: UserEnvironment/Demos/EnvVars.js


//Operating System var IS_WIN = (navigator.userAgent.indexOf("Win") >= 0);

var IS_MAC = (navigator.userAgent.indexOf("Mac") >= 0); var IS_UNIX = (navigator.userAgent.indexOf("X11") >= 0); //Browser Info var IS_IE = (navigator.appName == "Microsoft Internet Explorer"); var IS_FF = (navigator.userAgent.indexOf("Firefox") >= 0); var IS_NS = (navigator.vendor != "undefined" && navigator.vendor == "Netscape"); var IS_MOZ = (navigator.userAgent.indexOf("Mozilla/5") >= 0); var IS_NEW = ((IS_MOZ && parseInt(navigator.appVersion) >= 5) || (IS_IE && parseInt(navigator.appVersion) >= 4)); //Object Support var SUPPORTS_IMAGES = (typeof document.images != "undefined"); //Cookies var COOKIES_ON = navigator.cookieEnabled;

Examine UserEnvironment/Demos/UserEnvironment.html to see the properties of your environment.

Exercise: Checking if Cookies are Enabled


Duration: 10 to 20 minutes. In this exercise, you will modify a simple page so that it redirects a user to another page if cookies are not enabled. 1. Open UserEnvironment/Exercises/CookieCheck.html for editing. 2. Add JavaScript code so that the page checks to see if cookies are enabled. If they are, it should simply load. If they are not, it should redirect to CookieApology.html. Note that you can use settings from EnvVars.js. 3. Test your solution in a browser. You may have to temporarily disable cookies to see the effect of your code. Where is the solution?

The User's Environment Conclusion


In this brief lesson, you have learned to determine the settings and capabilities of a user's environment and to respond accordingly. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Quick JavaScript Recap


In this lesson of the JavaScript tutorial, you will learn... 1. 2. 3. 4. The basics of JavaScript in a context of a refresher. The fundamental data types of JavaScript That the DOM is not JavaScript What is the main object behind AJAX

This is just a refresher


The intent of this lesson is not to replace more comprehensive JavaScript classes or books. As a matter of fact, this lesson assumes you know your way around JavaScript but you may have forgotten one detail or two about the basics of the language. Here we will go quickly over the fundamental building blocks of JavaScript and try to clarify some concepts that sometimes can be a little blurry.

Primitive data types


JavaScript comes with a number of data types that we can use in our variables. Let's look at them.

Null
Null is a data type that has only one possible value, null, which is also a reserved word in the language. We use null to represent a value that we don't know or that is missing.
var name = "Homer"; var ssn = null;

In the above example we know what to put in the name variable but we don't know yet what to put in the ssn variable. Maybe we will know what to put in there later in our program, but maybe not.

Undefined
The Undefined type also has a single value, undefined, and it is similar to null but not exactly the same thing. JavaScript uses undefined as the default value for any variable that has not been initialized yet. Let's modify our previous example.

var name = "Homer"; var ssn;

Now the value of ssn is undefined because it is no longer initialized to null or anything else. The undefined type is used a lot when we want to detect if a global variable has been already declared. Which is kind of a code smell anyway, as we will see in an upcoming lesson.
//Check if we already have a start time if (START_TIME === undefined) { START_TIME = new Date(); }

Boolean
Boolean is a very common data type in every language. It has only two values, true and false, which are reserved words and, I hope, self-explanatory.
var enabled = true; var disabled = false;

Number
The Number data type can represent two types of numeric values, 32-bit integers or 64bit floating point numbers. Number values are created using number literals, which can be in a few different formats.
var age = 25; // simple, decimal, integer var price = 45.95; // floating point var permissions = 0775; // integer in octal, 509 in decimal // (note the leading zero) var flags = 0x1c; // integer in hexadecimal, 28 in decimal // (note the 0x prefix) var measurement = 5.397e-9; // floating point in // scientific notation

String
String is a very popular data type and they are used to represent text. We spend a lot of time manipulating strings in pretty much any programming language. We create strings using literal values enclosed in single or double quotation marks. These literal also support a few special encodings for common characters like new line, tab, and

the quotation marks themselves. This is similar to what happens with strings in many other programming languages.
var var var var name = 'Homer', lastName = "Simpson"; host = 'Conan O\'Brien'; path = 'c:\\temp\\dir\\myfile.txt'; tabDelimited = "COL1\tCOL2\tCOL3\nVAL1\tVAL2\tVAL3";

Every value in JavaScript can be converted to a string by using the toString() method, like var s = myValue.toString();.

Native Types
In addition to the primitive data types we just saw, JavaScript also provides a few other data types, which are implemented as objects.

Date
We can store date values using Date objects. The Date object stores the date and time information internally as the number of milliseconds since January 1st 1970. There aren't date literals in the language, so we have to explicitly create a Date object when we need one.
var rightNow = new Date(); // current date and time var holiday = new Date(2008, 6, 4); // 4th of July, note the // 0-based month number var birth = Date.parse('7/4/2008'); // 4th of July, format varies with browser // locale (avoid this)

There two important pitfalls in the above example: the month is a number from 0 to 11 when passed as a parameter and the parse-able string formats vary by browser implementation and by user locale, so we'd better off just avoid parsing altogether.

Array
How useful would be our programs if we couldn't organize the data in arrays or other collection structures? I guess not very much. Arrays are very powerful in JavaScript and they kind of blur the lines between arrays and custom objects. The Array object can be instantiated with both a constructor call or using literals. The array indices are not necessarily contiguous or numeric or even of the same data type.

var CITIES = new Array(); CITIES[0] = 'Albuquerque'; CITIES[9] = 'Tampa'; var TEAMS = [ 'Cubs', 'Yankees', 'Mariners' ]; var BANDS = [ ]; BANDS['rock'] = "Beatles, Rolling Stones, Pink Floyd"; BANDS['punk'] = "Sex Pistols, Ramones, Dead Kennedys"; BANDS[1992] = "Nirvana, Pearl Jam, Soundgarden, Metallica";

Object
The Object type serves as the base for all the objects in JavaScript, regardless of their data type or how they were created. The Object type is also used when we want to create custom objects. We can create new objects using a constructor call or a literal. We will cover this is greater detail in a future lesson.
var employee = new Object(); employee.name = 'Homer Simpson'; employee.badgeNumber = 35739; var boss = { }; //literal syntax, empty though boss.name = 'Montgomery Burns'; boss.badgeNumber = 1; employee.reportsTo = boss;

Regular Expressions
Regular Expressions is a syntax used to find occurrences of a string pattern inside a larger string. It has historically been more popular in Unix environments and in Perl programs, but it has gained some adoption in many other programming languages as well. Regular expressions is one of these technologies with a measurable learning curve but that can have a big payoff depending on the type of work you do. JavaScript implements regular expressions with RegExp objects. It also support the Perlsyle literals.
var text = "Webucator"; var pattern = new RegExp('cat', 'g'); var samePattern = /cat/g; //using the literal syntax alert( pattern.test( text ) );// shows 'true'

Functions
Functions in JavaScript are more than just static blocks of code. They are Function objects that we use just like any other data type value, e.g. we can pass functions to other functions, we can store a function in a variable, we can modify a function, etc.

We will have a lot to talk about functions in one of our lessons. For now let's just remember how we declare and call functions.
//declare the function function sayHowMuch(name, price, quantity) { var finalPrice = price * quantity; alert('The price for ' + quantity + ' ' + name + '(s) is $' + finalPrice); } //call the function with arguments sayHowMuch('ice cream cone', 1.99, 3); sayHowMuch('Movie ticket', 10.00, 5);

The DOM is not JavaScript


A common source of confusion is the relationship between the browser DOM (Document Object Model) and the JavaScript global objects. JavaScript being an interpreted language with a runtime execution engine, it needs a host environment to instantiate the engine and forward the JavaScript code to it. The browser is one of many hosts for JavaScript. Other hosts are Adobe Flash plugins (via ActionScript), desktop widgets (like Yahoo! Widgets, MS Gadgets, OS X Dashboard Widgets), Firefox browser add-ons, and even some kinds of electronic equipment. All that said, the browser was the originally intended host for JavaScript and by far the most common one.

The DOM
It's important to understand that the DOM is a standard that has nothing to do with JavaScript. It was created by the W3C to normalize the browser vendors' implementations of Dynamic HTML. The DOM is an API that enables programatic access to the HTML document structure, for reading or modification purposes. When we write code like document.getElementById('abc') we are using the DOM. With the DOM we can traverse our entire HTML document looking for specific HTML elements, which are called nodes, or even create new elements and append them pretty much anywhere we want inside the HTML document.

The window object


In browser scripts, the document object is actually a property of the window object, which is the default (or global) object of JavaScript in that environment. So typing

window.document.body is the same as typing document.body. The DOM starts at the document object. There are other things one may think are part of JavaScript when, in fact, they're browserspecific features, like the alert(), prompt(), setTimeout(), and open() functions. These are just methods of the window object, not part of JavaScript per se.

The XMLHttpRequest object


Another important object that we use a lot in JavaScript these days is the XMLHttpRequest object. This is the object that powers the AJAX functionality in a lot of web pages. This object is also not part of JavaScript. It can be used from JavaScript but it isn't part of the language. We won't cover AJAX here but it suffices to understand that this object allows our scripts to initiate a request to an URL and collect the server response without the need to reload the entire page.
function saveUser(name, age) { //the following line is actually a grossly simplified version // that is not supported in all browsers var ajax = new XMLHttpRequest(); ajax.open('POST', '/users/update', true); ajax.onreadystatechange = function () { if (ajax.readyState == 4) { alert('User ' + name + ' updated.'); } }; ajax.send( 'userName=' + name + '?userAge=' + age } saveUser('Joe Doe', 54); );

Quick JavaScript Recap Conclusion


We hope that after this short tour of JavaScript data types and the browser execution environment you will be able to absorb all the new concepts you are about to see in the remainder of the course. Because of the syntax similarities with C-style languages, it's often possible that we mixup what is available in JavaScript so this lesson can serve as a quick reminder or cheatsheet when needed.

To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Advanced Techniques
In this lesson of the JavaScript tutorial, you will learn... 1. 2. 3. 4. 5. To use the default operator. To pass a flexible number of arguments to a function. To pass a function as an argument to another function. To create anonymous functions. Other techniques related to functions.

Beyond The Basics


You can go very far in JavaScript using only the language basics that we have seen up to now but, to really unleash JavaScript's power, we need to get more familiar with a few programming techniques. These techniques will help your code become more expressive and more reusable at the same time. They are programming patterns that leverage some language features that may not be present in other programming languages. If you don't have experience with any other dynamically typed or functional language, the patterns you will see here may look very strange at first. But fear not, we will provide plenty of explanations and examples to make you feel comfortable enough to read and write JavaScript code that employ what you just learned.

Optional Function Arguments


When we declare a function in JavaScript, we normally include a list of the arguments the function expects.

Code Sample: AdvancedTechniques/Demos/sumAll1.html


---- Code Omitted ---function sumValues(val1, val2, val3) { return val1 + val2 + val3; } ---- Code Omitted ----

But this does not guarantee that our function will always be called with 3 arguments. It's perfectly valid for someone to call our function passing more than 3 or even less than 3 arguments.
var R1 = sumValues(3, 5, 6, 2, 7); var R2 = sumValues(12, 20);

Both calls will return surprising results (surprising from the caller's perspective.) In the first case, since we are not expecting more than 3 arguments, the extra values, 2 and 7, will simply be ignored. It's bad because the returned value is probably not what the calling code expected. It's even worse when we look at the second example. We are passing only 2 arguments. What happens to val3? It will have the value of undefined. This will cause the resulting sum to be NaN, which is clearly undesirable. Let's fix our function to deal with these types of situations.

Code Sample: AdvancedTechniques/Demos/sumAll2.html


---- Code Omitted ---function sumValues(val1, val2, val3) { if (val1 === undefined) { val1 = 0; } if (val2 === undefined) { val2 = 0; } if (val3 === undefined) { val3 = 0; } return val1 + val2 + val3; } var R1 = sumValues(3, 5, 6, 2, 7); var R2 = sumValues(12, 20); ---- Code Omitted ----

If we run our example again, we will see that we no longer get NaN for the second function call. Instead we get 32, which is probably what the calling code expected.

We now have a pretty robust function that adds 3 numbers but it still doesn't feel all that useful. Sooner or later we will need to add four or five numbers and we don't want to be updating our function to accept a val4 then a val5 parameters. That would be a less than desirable maintenance task. Fortunately JavaScript can help us with that too. Every function, when called, receives a hidden parameter called arguments, which is an array of all the arguments passed to the function. Back to our example, the first time we call sumValues, the arguments array will contain [3, 5, 6, 2, 7] and in the second call [12, 20]. What this means is that we can ignore the passed parameters altogether an deal only with the arguments array. Let's update our function once again.

Code Sample: AdvancedTechniques/Demos/sumAll3.html


---- Code Omitted ---function sumValues() { var sum = 0; for (var i = 0; i < arguments.length; i++) { sum += arguments[i]; } return sum; } var R1 = sumValues(3, 5, 6, 2, 7); var R2 = sumValues(12, 20); ---- Code Omitted ----

Note how we got rid of the parameter list and now we get the values directly from the arguments array. When we run our example now we see that the returned values are correct and precisely what was expected by the caller. We now have a function that accepts as many parameters as are thrown at it and will always return the sum of all those arguments.

Truthy and Falsy


JavaScript, as we already know, has a boolean data type, which has only two possible values: true or false. Boolean expressions also evaluate to a boolean value. But that's not the entire story. When used in a context that expects a boolean value, any JavaScript expression can be used. See below.

var NUMBER = 0; if (NUMBER) { alert('You should } NUMBER = 1; if (NUMBER) { alert('You should } var TEXT; if (TEXT) { alert('You should } TEXT = ""; if (TEXT) { alert('You should } TEXT = "hi"; if (TEXT) { alert('You should }

not see this');

be reading this');

not see this');

not see this');

be reading this');

In the example above we are using non-boolean expressions (NUMBER and TEXT) in the if statement and some of these expressions are being understood as true and some others false. What is happening here is Type Coercion. JavaScript does its best to convert the given expression into the desired type (boolean.) JavaScript resolves the following values to false.

false (of course) null undefined 0 (zero) NaN "" (empty string)

The above values are all referred to as falsy, which is a way of saying "Not the same as false but can be interpreted like such." Every other value, including the strings "0" and "false", will resolve to true and will be referred to as truthy. Again, "Not the same as true but can be interpreted as such." Type coercion is the reason we have the === (triple-equal or strictly equal) comparison operator in JavaScript. The regular equality operator == applies type coercion and sometimes your comparisons will not result as expected. Look at the following sample code.
var NUM = 0; if (NUM == "") {

alert('Hey, I did not expect to see this.'); } if (NUM === "") { alert('This will not be displayed.'); }

In the first if conditional is comparing two falsy values, and the type coercion will resolve both of them to false, causing the result of the comparison to be true, which is probably not the original intent of the code. To detect the type difference (string vs. number) we would need to use the triple equal operator, as shown in the second if statement.

Default Operator
The boolean operators && and || also use truthy and falsy to resolve each of the operands to a boolean value. From your previous experiences with other programming languages you may be led to believe that the result of a boolean operation is always true and false. This is not the case in JavaScript. In JavaScript, the result of a boolean operation is the value of the operand that determined the result of the operation. Let's clarify that with an example.
var A = 0, B = NaN, C = 1, D = "hi"; var RESULT = ( A || B || C || D ); alert("Result = " + RESULT); RESULT = ( D && C && B && A ); alert("Result = " + RESULT);

The first boolean expression ( A || B || C || D ) is evaluated from left to right until a conclusion is made. The || is the boolean OR operator, which only needs one of the operands to be true for the operation result in true. A is 0, which is falsy. The evaluation continues with the remaining operands because falsy didn't determine anything yet. When checking B, which is NaN and also falsy, we have the same situation - we need to continue evaluating. Then we check C, which is 1 and resolves to true. We no longer need to continue evaluating the remaining operands because we already know the expression will result true. But here's the catch. Instead of resulting strictly true, it will result in the truthy value that ended the evaluation, C in our case. The message displayed will be "Result = 1". As you might already expect, the && (boolean AND operator) works in a similar fashion, but with opposite conditions. The AND operator returns false as soon as it finds an operand that is false. If for the expression ( D && C && B && A ) we follow the same sequence we did for the OR operator we will see that, D and C are both truthy so we need

to keep going, then we get to B, which is falsy and causes the evaluation to stop and return B. The message displayed then is "Result = NaN". You may be reading all this and thinking how can this be of any use. It turns out that this behavior of returning the first conclusive value can be very handy when ensuring that a variable is initialized. Let's take another look at a function we saw back in the Form Validation lesson.
function checkLength(text, min, max){ min = min || 1; max = max || 10000; if (text.length < min || text.length > max) { return false; } return true; }

The first two lines in this function make sure that min and max always have a valid value. This allow the function to be called like checkValue("abc"). In this case the min and max parameters will both start with the undefined value. When we reach the line min = min || 1; we are simply assigning 1 to min, ensuring it overrides the undefined. Similarly we assign 1000 to max. If we had passed actual values for these parameters as in checkLength("abc", 2, 10) these values would be kept because they are truthy. With this usage of the || we are effectively providing default values for these two parameters. That's why this operator, in this context, is also called the Default Operator. The default operator replaces more verbose code like:
if (min === undefined) { min = 1; } // becomes simply min = min || 1; var contactInfo; if (email) { contactInfo = email; } else if (phone) { contactInfo = phone; } else if (streetAddress) { contactInfo = streetAddress; } // is greatly shortened to var contactInfo = email || phone || streetAddress;

Exercise: Applying defaults to function parameters


Duration: 15 to 25 minutes. Here we will revisit an earlier example and use the default operator to handle optional parameters. 1. Open AdvancedTechniques/Exercises/sumAll-defaults.html for editing. 2. Edit the existing sumAll() to use the default operator instead of the if blocks.

Code Sample: AdvancedTechniques/Exercises/sumAlldefaults.html


<html> <head> <title>Sum all numbers, default operator</title> <style type="text/css"> .wc_debug { background-color:#ffc; } </style> </head> <body> <h1>Sum all numbers, default operator</h1> <script type="text/javascript" src="../../Libraries/DebugHelp.js" ></script> <script type="text/javascript"> insertDebugPanel(); // this function uses the if blocks to // handle the optional parameters. // Change it to use the default operator instead. function sumValues(val1, val2, val3) { if (val1 === undefined) { val1 = 0; } if (val2 === undefined) { val2 = 0; } if (val3 === undefined) { val3 = 0; } return val1 + val2 + val3; } var R1 = sumValues(3, 5, 6, 2, 7);

var R2 = sumValues(12, 20); //print the results debugWrite(R1); debugWrite(R2); </script> </body> </html>

Where is the solution?

Functions Passed as Arguments


In JavaScript functions are first class data types. Functions aren't just an immutable block of code that can only be invoked. In JavaScript each function we declare becomes an object, with its own properties and methods, and can also be passed around like any other object. Let's see how we can use functions as parameters to other functions. Consider the following example.

Code Sample: AdvancedTechniques/Demos/functionarguments.html


---- Code Omitted ---var VALUES = [5, 2, 11, -7, 1]; function sum(a, b) { return a+b; } function multiply(a, b) { return a*b; } function combineAll(list, initialValue, operation) { var runningResult = initialValue; for (var i=0; i< list.length; i++) { runningResult = operation(runningResult, list[i]); } return runningResult; } var SUM = combineAll(VALUES, 0, sum); var PRODUCT = combineAll(VALUES, 1, multiply); ---- Code Omitted ----

You may be wondering what the following function call means: var SUM = combineAll(VALUES, sum);. In this statement we are passing the function sum as the second parameter of combineAll. We are not invoking sum yet, just passing a reference to it. Note that the open and close parenthesis aren't used after sum, that should serve as a tip off that this is not a function invocation. The line that ultimately invokes sum is runningResult = operation(initialValue, list[i]);, which received a reference to sum in the operation parameter. When operation is invoked, in reality, it is sum that is getting called, returning the sum of the two values passed in. This is a very important technique and the combineAll function is often called reduce. Take your time to review the code and run the example until you feel comfortable with it. We will be using this capability extensively in the remaining lessons.

Anonymous Functions
Going back to our previous example, the functions sum and multiply are only referred to once, in each call to combineAll. Furthermore, if we stick to that pattern, any new combination behavior that we desire, such as concatenate the values or compute the average value, will need a new function just to be passed to combineAll. That seems like too much overhead for such a simple thing. It would also not be very interesting to have all these functions that do such simple things scattered through out the code. Thankfully, we don't actually need to declare each of these functions. We don't even need to come up with names for them. JavaScript allow us to create functions on the spot, any time we need a function that will only be used at that spot. The syntax is rather compact. Syntax
function (arg1, arg2) { //function statements here }

Because the functions created this way don't have names, they are aptly called anonymous functions. Let's revisit our previous example and use anonymous functions to replace the single-use functions we declared.

Code Sample: AdvancedTechniques/Demos/anon-funcarguments.html


---- Code Omitted ----

var VALUES = [5, 2, 11, -7, 1]; function combineAll(list, initialValue, operation) { var runningResult = initialValue; for (var i=0; i< list.length; i++) { runningResult = operation(runningResult, list[i]); } return runningResult; } var SUM = combineAll(VALUES, 0, function (a, b) { return a+b; }); var PRODUCT = combineAll(VALUES, 1, function (a, b) { return a*b; }); ---- Code Omitted ----

The highlighted code represent the two anonymous functions, located where previously we had sum and multiply. This coding style can understandably be harder to read, but it also avoids all that jumping around to look up what that function that you are passing by name really does. The code of that function is right there, next to the code that is using it.

Inner Functions
Since functions in JavaScript are just one more type of object, we can create a function inside another function. These are called inner functions. The example below shows how to create and use a function inside another one.
function analyzeText(text) { var index = 0; function getNextCharacter() { if (index < index.legth) { return text.charAt(index); } return false; } var c = getNextCharacter(); while (c) { alert(index + ' ---> ' + c ); c = getNextCharacter(); } } analyzeText('abcdef');

The above example is not particularly useful. We will see more important uses of inner function when we look at private members. For the time being, just notice how getNextCharacter() has access to index and text, which are scoped to the analyzeText() function.

The eval() Function


The reason we are mentioning eval() in this lesson is to acknowledge its existence and to urge you not to use it. We will explain why, but first let's explain what it does. eval interprets a string containing JavaScript code. It can be a simple expression like "1 + 2" or a long and complex script, with functions and all. Here's one example that is not too different from what we can find in live sites on the web.
function getProperty(objectName, propertyName) { var expression = objectName + "." + propertyName; var propertyValue = eval(expression); return propertyValue; } var PROP = "title"; //assume this was given by the user alert(getProperty("document", PROP)); //shows the window title

This function creates a JavaScript expression by concatenating an object name, with a dot and a property name. Then it uses eval to evaluate that expression. As we can see eval is a powerful function, but it is also potentially dangerous and incredibly inefficient. It's dangerous because it's typically used to evaluate user entered input, which not always is a safe thing to do. It's inefficient because each call to eval starts a JavaScript compiler. The use of eval normally reveals lack of knowledge from the developer that wrote the script. For example, the sample that we just used is not necessary. Probably what happened was that the developer did not know about the [ ] accessor for properties. The same effect would be obtained with alert(window[PROP]);, with the advantage of not firing up a compiler just to retrieve the property value. Remember this, eval is evil. Avoid it as much as you can. If you think you need it, maybe it's because you did not learn yet about an alternative way in JavaScript.

Variable Scope
Variable scope defines which parts of your code have access to a variable that you define. This typically varies depending where you declare the variable.

Variables in JavaScript are either global or function scoped. When a variable is declared outside of any function body, then it will become a global variable, meaning that any portion of your script will have access to it.
var NAME = 'my global value'; function displayName() { alert(NAME); } alert(NAME); displayName();

The previous sample showed that the Name variable is visible inside the function displaName. There's a catch, though. If you forget to declare the variable using the var operator before using the variable, the variable will be created as a global variable even if you are declaring it inside a function or inside a for loop declaration.
function prepare() { TEST = 123; alert(typeof TEST); //let's forget the "var" in the "for" declaration for (abc=0; abc<5; abc++) { //... } } alert(typeof TEST); prepare(); alert(TEST); alert(abc);

Function Scope
Variables declared with var inside a function will be visible only inside that function. It doesn't matter if the variable was declared inside an if block or a for block. Once declared, the variable becomes visible throughout the remainder of the function. What that means is that JavaScript doesn't have block scope.

Advanced Techniques Conclusion


Hopefully, with what you learned in this lesson you will be able to write much more robust JavaScript code and start leveraging some of the flexibility JavaScript puts at your disposal to create really powerful code. If you still feel unsure of how these techniques work, re-read the lesson and look for alternative explanations on the web. Sometimes it helps reading a different phrasing of

the same topic. It's important that you can at least read this type of code fluently in order to understand some of what we will be looking at in the upcoming lessons. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Object Oriented Aspects of JavaScript


In this lesson of the JavaScript tutorial, you will learn... 1. 2. 3. 4. 5. That JavaScript is, in fact, object oriented. To create new objects in a variety of ways. To be careful with the this keyword. The difference between call() and apply(). To extend existing objects.

Yes, JavaScript is Object Oriented


If you are a web developer, chances are you work with an object oriented language on the server side, be it Java, C#, Ruby, VB, etc. You may also already understand the advantages of using an OO programing language over a purely procedural one. In this lesson and the next one we will provide quick recipes of how common OO techniques are implemented with JavaScript. We will also present some additional techniques that may not be available in other OO languages that you are familiar with. In this lesson we will cover most of the OO concepts as implemented in JavaScript. The screaming exception is Inheritance, which we we will defer to the next lesson.

Why Create JavaScript Objects?


Let's get back to our server code once again. If you are writing an application that reports data from a database, you are probably dealing with objects such as database connections, recordsets, error loggers, etc. You may also have custom objects that represent entities from your problem domain, like Order, Employee, Company, PriceQuote, etc. You need those objects because they encapsulate a lot of functionality that we can reuse over and over. It should not be surprising that soaring demand for Rich Internet Applications (RIA), the need for objects to assist with the UI logic also increases. What we are suggesting here is that it is much more appropriate to produce reusable JavaScript objects than vast collections of JavaScript functions. The problem with the

functions, as we know from procedural programming languages, is that they are either too complex to use (because they want to solve all the possible variants of the problem) or they need to be called in a specific sequence. Imagine that you have the common scenario of cascading drop down lists, one with a list of countries, the other with states or provinces. When the country changes you want to update the states list accordingly. If we were doing this in a procedural fashion, we would create a function to handle the change event of the countries list, in this function we would call another function, passing the country name to refresh the states list. This second function would receive the country name, retrieve the states list and update the states drop down.

Code Sample: ObjectOrientedJS/Demos/cascadingoriginal.html


---- Code Omitted ---function countryChanged() { var sel = document.getElementById("country"); var country = sel.options[sel.selectedIndex].value; updateStateList(country); } function updateStateList(country) { var states = STATES_PER_COUNTRY[ country ]; var list = document.getElementById("state"); list.options.length = 0; for (var i=0; i<states.length; i++) { list.options.add( new Option(states[i]) ); } } document.getElementById("country").onchange = countryChanged; ---- Code Omitted ----

A few days later we may need to implement a similar scheme, but this time with a Company/Employee pair of drop downs. Here we go writing all that code again. If we were using an OO approach, we could create an object called CascadingListPair that knows how to capture the change events of the first drop down, call some code to retrieve the list for the second drop down, and update the second dropdown.

Code Sample: ObjectOrientedJS/Demos/cascadingobjects.html


---- Code Omitted ----

var helper = new CascadingListPair( "country", "state", function (country) { return STATES_PER_COUNTRY[ country ]; }); ---- Code Omitted ----

Don't worry about how complex the above code looks right now. We will discuss what is going on during this lesson. For now just appreciate how much shorter this is and how much logic you did not have to write. Now if we were to create a cascading pair for Company/Employee, it would be so much simpler.
var helper2 = new CascadingListPair( "company", "employee", function (company) { return EMPLOYEES_PER_COMPANY[ company ]; });

Creating Simple Objects


Objects are the fundamental unit of code encapsulation and reuse in any OO language. It is incredibly easy to create objects in JavaScript. There's even more than one way to do so.

1 - Building objects
The first approach is to create an empty object and progressively add its properties and methods.

Code Sample: ObjectOrientedJS/Demos/buildingobjects.html


---- Code Omitted ---var GUITAR = { }; GUITAR.color = 'black'; GUITAR.strings = ['E', 'A', 'D', 'G', 'B', 'e']; GUITAR.tune = function (newStrings) { this.strings = newStrings; };

GUITAR.play = function (chord) { alert('Playing chord: ' + chord); }; GUITAR.print = function (price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); }; //using the object GUITAR.play('Dm7'); GUITAR.tune( ['D', 'A', 'D', 'G', 'B', 'e' ] ); debugWrite('this guitar is: ' + GUITAR.color); GUITAR.print(850, 'USD'); ---- Code Omitted ----

The above methodology isn't too hard to understand but it is certainly more work than we are used to in more popular programming languages. What we did here is quite simple. We just created the object and appended each property and method as desired.

2 - Declaring objects
JavaScript also has a literal notation for objects. The previous example could have been rewritten in literal notation as follows.

Code Sample: ObjectOrientedJS/Demos/declaringobjects.html


---- Code Omitted ---var GUITAR = { color: 'black', strings: ['E', 'A', 'D', 'G', 'B', 'e'], tune: function (newStrings) { this.strings = newStrings; }, play: function (chord) { alert('Playing chord: ' + chord); }, print: function (price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); } };

//using the object GUITAR.play('Dm7'); GUITAR.tune( ['D', 'A', 'D', 'G', 'B', 'e' ] ); debugWrite('this guitar is: ' + GUITAR.color); GUITAR.print(850, 'USD'); ---- Code Omitted ----

The syntax is easy to understand. It is a comma-delimited list of name: value pairs. Note that the method declaration is easy to be confused with a regular function declaration. Just remember that a function can be used as a value and that's what is happening here. You can also think of the methods as properties that contain a function as their values, if that helps you understand the notation.

JSON
JavaScript Object Notation, or JSON, is a subset of the literal notation that we just saw. JSON was first proposed by Douglas Crockford as a neutral way to represent and transport data, usually replacing XML. JSON, just like the literal notation, is also a list of name/value pairs. The main difference is that the values can only be a string, an Array, a Number, true, false, null, or another JSON object. The field names are also enclosed in double-quotes. Here's the GUITAR object represented in JSON. Note that we cannot represent the methods because JSON doesn't accept them. It makes sense because JSON is meant only for data interchange, where behaviors are irrelevant.
var GUITAR = { "color":"black", "strings":['E', 'A', 'D', 'G', 'B', 'e'] };

3 - Using factory functions


One important thing to notice in the previous two approaches is that we did not need to create a formal class to serve as the template of the GUITAR object. If we needed a second guitar object we would need to create it the same way we did for the first one. We could just encapsulate that logic in a function that can create a brand new guitar object on demand.

Code Sample: ObjectOrientedJS/Demos/factoryfunctions.html


---- Code Omitted ----

function createGuitar(color, strings) { var guitar = { }; guitar.color = color; guitar.strings = strings; guitar.tune = function (newStrings) { this.strings = newStrings; }; guitar.play = function (chord) { alert('Playing chord: ' + chord); }; guitar.print = function (price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); }; return guitar; } var GUITAR1 = createGuitar('black', ['E', 'A', 'D', 'G', 'B', 'e'] ); var GUITAR2 = createGuitar('maple', ['F', 'Bb', 'D#', 'G#', 'C', 'f'] ); ---- Code Omitted ----

4 - Constructors
There's a variation of the factory function methodology that may feel more natural to you. In JavaScript, when function is called preceded by the new operator, the function receives an implicit this argument that is a brand new object, ready to be assembled with properties and methods. Also, if we do not return anything explicitly, the new operator automatically returns this. Let's rework our last example into a constructor. A good convention is to start constructor functions with a capital letter, to differentiate from a regular function, signaling to the programmer that it needs to be called with the new operator.

Code Sample: ObjectOrientedJS/Demos/constructors.html


---- Code Omitted ---function Guitar(color, strings) { this.color = color; this.strings = strings; this.tune = function (newStrings) { this.strings = newStrings; };

this.play = function (chord) { alert('Playing chord: ' + chord); }; this.print = function (price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); }; } var GUITAR = new Guitar('black', ['E', 'A', 'D', 'G', 'B', 'e']); debugWrite('this guitar is: ' + GUITAR.color); GUITAR.play('Dm7'); GUITAR.tune( ['D', 'A', 'D', 'G', 'B', 'e' ] ); GUITAR.print(850, 'USD'); ---- Code Omitted ----

The biggest differences here are two. First we no longer need to create the new object because the new operator has already taken care of that and passed the new object under the this identifier. The other difference is that we will use this wherever we were using guitar before. We could have returned this but that, as we explained above, is no longer necessary.

Exercise: Creating objects to encapsulate behaviors


Duration: 30 to 60 minutes. In this exercise we will implement the ever popular Yellow Fade Technique, where we set the background color of an element to yellow and use timeouts to progressively fade the background back to white. The exercise starts with working code that is not object oriented and has some problems because of that. We will convert the existing code into an object type that has the same functionality and fixes the original shortcomings. 1. Open ObjectOrientedJS/Exercises/yellow-fade.html for editing. 2. Change the JavaScript code to get rid of the functions highLightElement(), increaseYellow(), and setYellowLevel(), moving all that functionality into a constructor function called HighLightEffect().

Code Sample: ObjectOrientedJS/Exercises/yellowfade.html


<html> <head> <title>Yellow Fade</title>

<style type="text/css"> .wc_debug { background-color:#ffc; } </style> <script type="text/javascript" src="../../Libraries/DebugHelp.js" ></script> </head> <body> <h1>Yellow Fade</h1> <p> Add items by name to your shopping cart. </p> <h2>Shopping cart</h2> <table id="shoppingCart"> <tr><th>Item</th><th>price</th><th></th></tr> <tr id="newItemRow" style="background-color:#ddd;"> <td colspan="3"><b>New item:</b></td> </tr> <tr> <td><input type="text" id="itemName" /></td> <td><input type="text" id="itemPrice" size="5" /></td> <td><input type="button" id="addItem" value="Add" onclick="addingItem();" /></td> </tr> </table> <script type="text/javascript"> function addingItem() { var name = document.getElementById('itemName').value; var price = document.getElementById('itemPrice').value; var newItemRow = document.getElementById('newItemRow'); var table = document.getElementById('shoppingCart'); var shoppingCart = table.getElementsByTagName("tbody")[0]; var row = document.createElement('tr'); addCellToRow(row, name); addCellToRow(row, price); var buttonCell = addCellToRow(row, ''); var button = document.createElement('input'); button.type = 'button'; button.value = 'remove'; button.onclick = removingItem; buttonCell.appendChild(button); shoppingCart.insertBefore(row, newItemRow); highLightElement(row); } function addCellToRow(row, cellText) { var cell = document.createElement('TD'); cell.innerHTML = cellText;

row.appendChild(cell); return cell; } function removingItem() { //'this' it the remove button var row = this.parentNode.parentNode; row.parentNode.removeChild(row); } /* All the above code can stay. The only thing we are interested in this exercise is to replace the functions below this comment with an object that does the same thing. Instead of doing this in addingItem(): highLightElement( row ); we want to write: new HighLightEffect( row ) challenge: -- Add extra properties and constructor parameters to the object to control the duration of the effect challenge 2: -- Add more properties to configure the start and end color of the effect */ var LEVEL = 128; var ELEMENT = null; var INTERVAL = 100; function highLightElement(element) { ELEMENT = element; LEVEL = 128; setYellowLevel(element, LEVEL); setTimeout(increaseYellow, INTERVAL); } function increaseYellow() { LEVEL += 10; if (LEVEL > 255) { LEVEL = 255; } setYellowLevel(ELEMENT, LEVEL); if (LEVEL < 255) { setTimeout(increaseYellow, INTERVAL); } } function setYellowLevel(element, level) { var hex = level.toString(16); element.style.backgroundColor = '#ffff' + hex; } </script>

<script type="text/javascript"> insertDebugPanel(); </script> </body> </html>

1. Change the HighLightEffect() function to take an extra parameter to configure the length of the effect in seconds. 2. Make the new parameter optional with a default value of 1 second. 3. Change the example to pass in 2 seconds when creating the object. 1. Change the HighLightEffect() function to take another two extra parameters to configure start and end colors of the effect. 2. Create a method that splits the RGB color components in a 3-element array with integer values. 3. In each timeout step, calculate the next value for each RGB component of the background color and set it. 4. Make these two extra parameters optional and default them to "#ffff99" and "#ffffff" respectively. 5. Change the background color of the shopping cart table to "#80ffff" and change the sample to use the start color of "#cc99cc" and end color the same as this new background. Where is the solution?

Memory usage
One thing is common in all the presented ways to create an object may go unnoticed. The play() and tune() methods being added to each objects are always recreated from scratch for each object instance. This will result in multiple copies of identical methods, which is clearly waste of memory. It may be a negligible waste for those tiny methods and in just a couple of object instances, but it's generally not a good practice. We will present the most adequate solution in the next lesson, but there's a middle-of-theroad alternative that we can use to address the duplication issue in the meantime. The idea is to create a single function for each of those methods and use them in the creation of the instance methods. The example below illustrates this.

Code Sample: ObjectOrientedJS/Demos/reusingfunctions.html

---- Code Omitted ---function tuneGuitar(newStrings) { this.strings = newStrings; } function playGuitar(chord) { alert('Playing chord: ' + chord); } function printInfo(price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); } function Guitar(color, strings) { this.color = color; this.strings = strings; this.tune = tuneGuitar; this.play = playGuitar; this.print = printInfo; } var GUITAR = new Guitar('black', ['E', 'A', 'D', 'G', 'B', 'e'] ); ---- Code Omitted ----

This reduces the number of identical functions created but it has a different cost. The code now looks more dispersed. The functions defined outside of the constructor method don't give any hint that they are used as methods of the Guitar objects. Although the object instantiation via constructors may look as if we are defining classes, we will learn in the next lesson that this is not really true.

The perils of this


You may look at the previous example and wonder: What if someone calls tuneGuitar() directly, instead of through a Guitar object? Or what happens if the Guitar constructor is called without the new operator? Well, that is trouble indeed. I JavaScript, most of the times, the this contains the object that owns the current method. One big exception are the constructor functions as we just saw. When we call a function directly like tuneGuitar( someArray ); there's no owning object for the stand alone tuneGuitar() function so this will be uninitialized. Or will it? As it turns out, there is a default value for this, which is a global object. That object holds all our global variables as its properties and global functions as its methods. When we are running JavaScript in a browser this global object is the window object.

Back to our problem, if we call tuneGuitar() directly, we will end up adding a strings property to the window object, which is the same as creating a global variable called strings. Even worse is when we call a constructor without the new operator. In this case we will create a series of global variables and functions. Another problem is that the variable that we are trying to assign the new object to will remain undefined:
var GUITAR1 = Guitar('black', ['E', 'A', 'D', 'G', 'B', 'e']); debugWrite(GUITAR1); // => undefined

The bottom line here is that we have to make sure that when we a function has a this identifier then that function is always called as an object's method or that we are providing the value for this explicitly, as we will see now.

Calling or Applying functions


In JavaScript functions are 1st class objects of type Function. As such they have properties and methods of their own. Two of the methods are apply() and call(). We can use apply() and call() to invoke the function as if they were methods of whatever object we want. This is done by effectively setting the desired value of this. Back once again to our Guitar objects and this time the printInfo() function.
function printInfo(price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); }

We already know that if we call this function directly, it will cause problems because this will be the window object. Since window doesn't have a strings property, the expression this.strings.length will fail. Here's how we can overcome this problem.
var GUITAR1 = new Guitar('black', ['E', 'A', 'D', 'G', 'B', 'e']); printInfo.call( GUITAR1, '349.99', 'USD'); // OR printInfo.apply( GUITAR1, ['349.99', 'USD']);

Both apply() and call() will produce the same result. The only difference is how the parameters are passed to the invoked function. apply() just expects all the parameters listed right after the target object. call() expects that the second parameter will be an array of the parameters to be passed to the function.

This difference can become useful when dealing with an unknown (or flexible) number of arguments.

Code Sample: ObjectOrientedJS/Demos/call-apply.html


---- Code Omitted ---var UTILS = { getMaxLength: function () { var max = 0; for (var i=0; i<arguments.length; i++) { if (arguments[i].length > max) { max = arguments[i].length; } } return max; }, pad: function (text, maxLength) { var result = text; for (var i=text.length; i<maxLength; i++) { result = ' ' + result; } return result; }, alignRight: function () { var maxLength = this.getMaxLength.apply(this, arguments); var result = []; for (var i=0; i<arguments.length; i++) { result.push( this.pad(arguments[i], maxLength) ); } return result; } }; var ALIGNED = UTILS.alignRight('Homer', 'Leonard', 'Montgomery', 'Abe'); ---- Code Omitted ----

Extending Existing Objects


When we showed how to create objects by building them, one thing that we did not mention but may go unnoticed is that we can add properties and methods to any object. It does not matter if the object was created by us or not. To illustrate this, let's find a DOM element in a page, which is clearly an object not created by us, and let's extend it by adding new members.

Code Sample: ObjectOrientedJS/Demos/extendingobjects.html


---- Code Omitted ---<div> <div id='userName'>Joe Doe</div> <input type="button" value="Prepare" onclick="extend('userName', '#ffff99');" /> <input type="button" value="Highlight" onclick="show('userName');" /> </div> <script type="text/javascript"> function extend(elementName, color) { var el = document.getElementById(elementName); el.highlightColor = color; el.highlight = function () { this.style.backgroundColor = this.highlightColor; }; } function show(elementName) { var el = document.getElementById(elementName); el.highlight(); } </script> ---- Code Omitted ----

In this example the first button calls extend() to extend the userName div element. To extend the element, it tries to find the element in the document, if the element is found then a new property called highlightColor is added with the chosen color value. A new method is then added with the name highlight, which will simply set the element's background color style to the value contained in highlightColor. The second button calls show(), which will once again find the element and call the brand new method highlight(). This causes the userName element to display a yellow background. This type of object modification is an important characteristic of Dynamically Typed programming languages like JavaScript.

Merging Objects
The technique of adding new members to an existing object is so common in JavaScript libraries that some of them formalize and encapsulate this operation. The code below is from the Prototype.js library and show the Object object itself being extended with a function that helps extending any object.

Object.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; };

With this new method we can make the process of augmenting an existing object look more like we are merging it with a second one. Prototype.js uses this method many times in its own source code as shown below.
Object.extend(String.prototype, { escapeHTML: function() { return this.replace(/&/g,'&amp;'). replace(/</g,'&lt;'). replace(/>/g,'&gt;'); }, unescapeHTML: function() { return this.replace(/&amp;/g,'&'). replace(/&lt;/g,'<'). replace(/&gt;/g,'>'); } });

Dynamic Languages
A dynamic language is one where the type of the objects is only loosely bound to the way it was created. Objects may be created through a constructor method and inherit from a base class or a prototype but that doesn't mean that the object is locked for alterations. Code can, at any time, add, remove or modify existing properties or methods of the objects. For that reason the base type of an object is less important in a dynamic language than it is in a statically typed language like Java or C#.

Duck Typing
A famous sentence reflects very well the importance given to base types in dynamic languages: "If it walks like a duck and quacks like a duck, I would call it a duck." The gist of this statement is that in order for your code to work with a given object, you don't need the object to derive from a particular base type or prototype. You only need it to honor the expected public interface. As an example, as long as the given object has a method called sort(), your code will be able to call that method to sort the contents of the object. It doesn't matter if the object is an array or a DOM tree or a custom hash-like object.

That sounds Dangerous

You may think that dynamic typing is a recipe for disaster but in practice that's not the case. There are two important factors that make dynamic languages very appealing.

Productivity
How many times when programming in statically typed languages we were in the situation where we had this class that was almost perfect but it lacked one important property or method? The usual route is to inherit a new class from that one and add the missing functionality. That works well in the beginning but it quickly leads to class explosion in the application. Before you notice you'll have dozens of classes that are minor improvements over other existing classes. Dynamic languages offer the capability of "fixing" the original classes on the spot and keep the code-base smaller and more manageable. That leads to greater programmer efficiency.

Unit Testing
Dynamic languages walk hand-in-hand with unit testing. We will take a closer look at unit testing in the next lesson but let's just say that automated unit testing will help detecting a bug at the moment it is introduced in the code. I wouldn't try tell you that unit testing is very popular in JavaScript, but in other dynamic languages like Ruby and Python it's common practice in enterprise-quality software. JavaScript is in a way still discovering the importance of unit testing.

Private members
Up to now all the properties and methods that we have been adding to our objects are accessible from any code that interacts with the object. In other object oriented programming languages it's often possible to define some of the properties and methods as private to the object itself. This private members are only accessible by code in the object itself but not from code that simply uses the object. JavaScript, at the current version, does not support private members as a language feature. But not all is lost. With a clever little trick we can create objects with private members.

Code Sample: ObjectOrientedJS/Demos/privatemembers.html


---- Code Omitted ---var Cycler = function () {

this.values = arguments; //private members var current = 0; var itemCount = arguments.length; var that = this; var moveIndex = function () { current = (current + 1) % itemCount; } //public members this.next = function () { moveIndex(); return that.values[current]; }; }; var WEEKDAYS = new Cycler('Mon', 'Tue', 'Wed', 'Thu', 'Fri'); ---- Code Omitted ----

The trick is related to JavaScript's variable scoping rules. Local variables inside the constructor function are visible inside the constructor, including in the functions defined inside the constructor, like moveIndex() and next(). Even moveIndex() is declared as a local variable.

Object Oriented Aspects of JavaScript Conclusion


In this lesson we started to see how JavaScript implements basic object creation. We also saw how flexible JavaScript is that we can even modify existing objects not created by us. We're only scratching the surface on the dynamism provided by this language. In the next lesson we will explore JavaScript objects even further, when we explain how inheritance is implemented using protype objects. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Prototype-Based Programming
In this lesson of the JavaScript tutorial, you will learn... 1. The differences between class-based and prototype-based programming

2. 3. 4. 5. 6.

To construct objects from other objects How JavaScript accomplishes inheritance To define constructor functions How JavaScript resolves object members The prototype object

Software developers that work with Object Oriented programming languages are familiar with the concept of classes. As it turns out, that's common but it's not the only way to accomplish object orientation and the JavaScript language designers chose not to use the most common one. In this lesson we will understand how JavaScript implements inheritance and how we can use it to build rich object hierarchies.

Class-Based Programming
If you have previous experience with Object Oriented languages such as Java, C++, C# or Visual Basic, chances are that you have employed class-based inheritance. You may have even concluded that was the only way to write object oriented software. In class-based inheritance, there's a clear distinction between the classes (or class objects) and the instances (or instance objects.) The classes define the behavior and structure of instance objects, which in turn simply contain instance data. The examples below illustrate a class being defined and then used in two different classbased programming languages.
//java or C# class Door{ public void open(){ //...code omitted } } Door frontDoor = new Door(); frontDoor.open(); 'Visual Basic Class Door Public Sub Open() '...code omitted End Sub End Class Dim frontDoor as new Door() frontDoor.Open()

Another important characteristic of class-based programs is how inheritance is implemented. Each class that you create has a base class (or super class) explicitly or

implicitly defined. Members of the base class will become available to the new class (the derived or inherited class.) Let's expand our examples a little bit to show class inheritance.
//java class SafeDoor extends Door{ public void unlock(string secretCombination){ //...code omitted } } SafeDoor safe = new SafeDoor(); safe.unlock("4-8-15-16-23-42"); safe.open(); 'Visual Basic Class SafeDoor Inherits Door Public Sub UnLock(ByVal secretCombination As String) '...code omitted End Sub End Class Dim safe as new SafeDoor() safe.UnLock("4-8-15-16-23-42") safe.Open()

Prototype Based Programming


Some languages choose to offer object oriented programming through mechanisms other than classes. One such mechanism is prototyping. JavaScript, Self, Lua, ActionScript, Agora, Cecil and many other languages are prototype-based.

Prototyping
Prototyping is a way to create objects by replicating another object (the so called prototype.) The new object may or may not have a link to the original object, depending on the language implementation. JavaScript maintains a link between the two as we will see shortly. In prototype-based languages there's usually an operator to effect the object creation by copying another object. Surprisingly JavaScript does not offer such operator, which is often consider a design flaw of the language. What we are looking for in such operator is a way to write the following code. Syntax
//attention, this is invalid syntax var BRAND_NEW_OBJ = object( EXISTING_OBJ );

Unfortunately, the object function above does not come with JavaScript. On the other hand, nothing stops us from creating our own implementation of that operator.
function object(original) { function F() {} F.prototype = original; return new F(); };

We will defer the explanation of the above function for a little later, after we explain some of the language features used in this code.

Prototypal Inheritance
Let's now take a look at prototypes in action and create an object hierarchy. Hopefully this will clarify how prototype objects relate to the new objects that derive from them. Consider the following simple implementation of an vehicle object.
var vehicle = { wheels: 0, color: 'white', make: 'ACME', model: 'Unknown', year: 1998 };

This will produce an object that we will illustrate in the following diagram. In the diagram the box represents the object and each property/value stored in the object pair is listed.

We can easily derive a more specialized car from the vehicle object with the help of the object function we mentioned above.
var car = object(vehicle); car.doors = 2;

The above code first creates the car object by linking to the existing vehicle object that is given to the object function. This link is represented by the arrow in the diagram below. After creating the car object, we add a new property called doors with the value of 2.

Note in the above diagram that car does not have a copy of all the properties from vehicle. Only the new doors property is stored in car. Will this work? Let's try it.
<html> <head> <title>Prototype-based inheritance</title> <script type="text/javascript"> function object(original) { function F() {} F.prototype = original; return new F(); }; var vehicle = { wheels: 0, color: 'white', make: 'ACME', model: 'Unknown', year: 1998 }; var car = object(vehicle); car.doors = 2; </script> </head> <body> The vehicle color is <script type="text/javascript">document.write(vehicle.color)</script> <br /> The vehicle has <script type="text/javascript">document.write(vehicle.wheels)</script> wheels <br /> The car has <script type="text/javascript">document.write(car.doors)</script> doors <br /> The car color is <script type="text/javascript">document.write(car.color)</script> <br />

</body> </html>

When we load this example we see that it all works as intended, but how exactly this all worked? The first three properties that we print are not hard to understand. They are standard properties just like we have seen in previous lessons. The interesting line is the one that prints car.color. We did not explicitly add a color property to the car object, so when JavaScript interpreter executes and tries to find that property in car it won't. But the interpreter doesn't stop there. It will check if the object has a link to the object that was used during the creation process (the prototype object.) In our case car does have a prototype so the interpreter proceeds to that object, vehicle, and tries to find a member named color there. It will then find the property and print white. The important thing to keep in mind here is that JavaScript knows about that arrow that we have in our diagram, and follows that arrow when something is not found in the object at hand. If the prototype object at the end of said arrow does not have the desired member, JavaScript will check if the prototype object has a prototype of its own and continue to do that recursively until the member is found or no more prototypes are available, in which case an error will be reported.

Overriding Properties
After closer inspection, we realize that our car has zero in its wheels property, which we don't agree with and want to change to 4. We decide to change our car creation a little bit.
var car = object(vehicle); car.doors = 2; car.wheels = 4;

Then we add one extra output to our example.


The car has <script type="text/javascript">document.write(car.wheels)</script>wheels <br />

This causes our diagram to change slightly, showing a new wheels property in the car object.

When we execute the updated example, we can see that indeed our car is listed with 4 wheels. This happens because, as we explained a few paragraphs before, the JavaScript interpreter only inspects the object's prototype when the desired member is not found in the object itself. This mechanism allows us to override the prototype's members. The prototype remains unchanged, we're just not reading the value from it anymore.

Constructor functions and prototypes


In the previous lesson we showed how we can use functions as object constructors. Because we did not talk about prototypes back there, we could not tell the entire truth. Every function has a prototype property that gets automatically assigned to the new object's (the one identified by this) prototype. By default the function prototype will be Object.prototype but we can change that. Let's rewrite one of the examples in the last lesson using a constructor function and its prototype.
//remember that we follow the convention // to start constructor functions with a capital letter function Guitar(color, strings) { this.color = color; this.strings = strings; } Guitar.prototype.tune = function (newStrings) { this.strings = newStrings; }; Guitar.prototype.play = function (chord) { alert('Playing chord: ' + chord); }; Guitar.prototype.print = function (price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); };

Now the Guitar objects will have a prototype property that no longer points to the default Object.prototype. Instead, it will point to Guitar.prototype and automatically inherit all the methods we just added to it.

Altering the prototype


The link between the object and its prototype is a live one. If we decide to alter the prototype values, the objects that inherited from that prototype automatically gain access to the prototype modifications. In the following example we will use the above Guitar constructor to create an object. After that we will add a new method to Guitar.prototype and we will see that the previously created object will have immediate access to this new method.
var GUITAR = new Guitar('Teak', ['E', 'A', 'D', 'G']); GUITAR.play('F#'); //shows 'Playing chord: F#' //augment the prototype Guitar.prototype.repeatChord = function (chord, times) { for (var i = 0; i < times; i++) { this.play(chord); } }; //try it out GUITAR.repeatChord('Em7', 3); //shows 'Playing chord: Em7' three times

Extending Standard JavaScript objects


Similarly to our custom objects, the standard objects in JavaScript also have prototypes. For example, every string inherits from String.prototype and every array inherits from Array.prototype. To demonstrate what we can do with this knowledge, let's add a trim() method to all our strings.

Code Sample: Prototype-BasedInheritance/Demos/string-trim.html


---- Code Omitted ---debugWrite(typeof String.prototype.trim); // shows 'undefined' String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g, ''); }; debugWrite(typeof String.prototype.trim); // shows 'function' var TEXT = ' extra padded text '; debugWrite('[' + TEXT.trim() + ']');//shows '[extra padded text]'

---- Code Omitted ----

Augmenting the prototypes of the native objects is a very powerful technique, taken to great lengths by popular libraries like Prototype.js, which we are about to discuss.

Prototype-Based Programming Conclusion


As we can see, JavaScript bears only trivial syntax similarities with many of the other mainstream programming languages. Under the surface, though, JavaScript is fundamentally different and many of the most important JavaScript idioms are arguably less intuitive. The important take away is that, although not as cleanly as in other languages, we can still write very solid OO code in JavaScript. That, combined with the great flexibility of the language, make it a surprisingly viable programming platform. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Error Handling and Debugging


In this lesson of the JavaScript tutorial, you will learn... 1. That errors can be trapped and dealt with 2. To use Firebug to trace and resolve errors No matter how careful you are, it always seems that errors find their way into your code. Sometimes they are runtime errors caused by unpredicted scenarios. Sometimes the errors are just incorrect behavior of your code, popularly known as bugs. Fortunately, we have tools to deal with either type of problem. In this lesson we will talk about detecting and handling errors and also about tracing down bugs and take them out of our applications.

Runtime Errors
Web browsers are such an hostile environment that it is almost guaranteed that we will constantly deal with runtime errors. Users provide invalid input in ways you didn't think

of. New browser versions change their behavior. An AJAX call fails for a number of reasons. Many times we can't prevent runtime errors from happening, but at least we can deal with them in a manner that makes the user experience less traumatic.

Completely unhandled errors


Look at this seemingly trivial code snippet.
function getInput() { var name = window.prompt('Type your name', ''); alert('Your name has ' + name.length + ' letters.'); }

It may not be obvious, but this code has a bug waiting to break free. If the user clicks Cancel or presses Esc the prompt() function will return null, which will cause the next line to fail with a null reference error. If you as a programmer don't take any step to deal with this error, it will simply be delivered directly to the end user, in the form of a utterly useless browser error message like the one below.

Depending on the user's browser or settings, the error message may be suppressed and only an inconspicuous icon shows up in the status bar. This can be worse than the error message, leaving the users thinking the application is unresponsive.

Globally handled errors


The window object has an event called onerror that is invoked whenever there's an unhandled error on the page.
window.onerror = function (message, url, lineNo) { alert( 'Error: ' + message + '\n Url: ' + url + '\n Line Number: ' + lineNo); return true; }

As you can see, the event will pass 3 arguments to the invoked function. The first one is the actual error message. The second one is the URL of the file containing the error

(useful if the error is in an external .js file.) The last argument is the line number in that file where the error happened. Returning true tells the browser that you have taken care of the problem. If you return false instead, the browser will proceed to treat the error as unhandled, showing the error message and the status bar icon. Here's the message box that we will be showing to the user.

Structured Error Handling


The best way to deal with errors is to detect them the closest possible to where they happen. This will increase the chances that we know what to do with the error. To that effect JavaScript implements structured error handling, via the try...catch...finally block, also present in many other languages. Syntax
try { statements; } catch (error) { statements; } finally { statements; }

The idea is simple. If anything goes wrong in the statements that are inside the try block's statements then the statements in the catch block will be executed and the error will be passed in the error variable. The finally block is optional and, if present, is always executed last, regardless if there was an error caught or not. Let's fix our example to catch that error.
function getInput(){ try { var name = window.prompt('Type your name', ''); alert('Your name has ' + name.length + ' letters.'); } catch (error) { alert('The error was: ' + error.name + '\n The error message was: ' + error.message); } finally { //do cleanup } }

The error object has two important properties: name and message. The message property contains the same error message that we have seen before. The name property contains

the kind of error that happened and we can use that to decide if we know what to do with that error. With that in place, if we reload the page and cancel out of the prompt, that's what we will see:

It's a good programming practice to only handle the error on the spot if you are certain of what it is and if you actually have a way to take care of it (other than just suppressing it altogether.) To better target our error handling code, we will change it to only handle errors named "TypeError", which is the error name that we have identified for this bug.
function getInput(){ try { var name = window.prompt('Type your name', ''); alert('Your name has ' + name.length + ' letters.'); } catch (error) { if (error.name == 'TypeError') { alert('Please try again.'); } else { throw error; } } finally { //do cleanup } }

Now if a different error happens, which is admittedly unlikely in this simple example, that error will not be handled. The throw statement will forward the error as if we never had this try...catch...finally block. It is said that the error will bubble up.

Throwing custom errors


We can use the throw statement to throw our own types of errors. The only recommendation is that our error object also has a name and message properties to be consistent in error handling.
throw { name: 'InvalidColorError', message: 'The given color is not a valid color value.' };

Debugging

One of the most important activities in software development is debugging. It can also be one of the most costly. That's why we need to do our best to reduce the amount of time spent in debugging. One way to reduce this time is to create automated unit tests, which we will see in the lesson Production Grade JavaScript. Another way is to use the best tools available and try to remove the pain associated with debugging. It used to be the case that debugging tools for JavaScript were archaic or close to non-existent. This situation has improved a lot and now we can confidently say we have feasible ways to debug JavaScript without resorting to horrendous tactics, such as sprinkling alert() calls across our code. We won't waste your time discussing all the existing tools for debugging. Instead we will focus on the tool that singlehandedly re-wrote the JavaScript debugging history.

Firebug
Firebug is an extension for the Mozilla Firefox browser. Once installed, Firebug will turn Firefox into almost an IDE for web development. Let's learn about Firebug's capabilities by debugging an issue in practice.

Exercise: The Background Highlighter


Duration: 30 to 45 minutes. In this exercise we are trying to understand why our BackgroundHighlighter object is not working as expected. The object is supposed to change the background color of an input field when it gets focus and revert it when it loses focus. For some reason the code is not working. Here is the code.

Code Sample: ErrorHandlingAndDebugging/Exercises/bgndchanger.html


<html> <head> <title>Background Highlighter</title> <style type="text/css"> .wc_debug { background-color:#ffc; }

</style> <script type="text/javascript" src="../../Libraries/DebugHelp.js" ></script> <script type="text/javascript"> //add the debud panel at the bottom of the page observeEvent(window, 'load', function () {insertDebugPanel();} ); </script> <script type="text/javascript"> var BackgroundHighlighter = function (field, color) { this.field = document.getElementById(field); this.color = color; this.field['onfocus'] = this.onGotFocus; this.field['onblur'] = this.onLostFocus; }; BackgroundHighlighter.prototype = { onLostFocus: function () { this.field.style.backgroundColor = ''; }, onGotFocus: function () { this.field.style.backgroundColor = this.color; } }; function onPageLoad() { //this function runs as soon as the page loads new BackgroundHighlighter('userName', '#ff9'); new BackgroundHighlighter('company', '#ff9'); } observeEvent(window, 'load', onPageLoad ); </script> </head> <body> <form action="#"> Name: <input type="text" name="userName" value="" id="userName"/> <br/> Company: <input type="text" name="company" value="" id="company"> </form> </body> </html>

1. Open the above file in Firefox. 2. Activate Firebug. Tools menu, Firebug, uncheck Disable Firebug (if checked) then Tools menu, Firebug, Open Firebug. Now if you click the Name textbox, Firebug will tell you that there's an error. See the picture below and note the error message in the status bar.

The Console tab in Firebug shows that the error message is "this.field has no properties". The error problem seems to be on line number 32. Expand the error message by clicking the "+" icon next to it. We will get one extra piece of information, the Call Stack, which in our case is simply one method call onGotFocus() as we can see in the image below.

When we click on onGotFocus() we will jump to the actual line of code in the Script tab. Let's place a breakpoint on that line by clicking on the gutter on the left margin, right to the left of the line number. Breakpoints are represented by a red circle on that margin.

Now let's click on the Name field again. Firebug kicks in an halts execution at the breakpoint we just set.

Looking at the Watch tab on the right, we can see that it is already tracking the value of this. And, to our surprise, this does not contain a reference to an instance of our BackgroundHighlighter. Instead it contains a reference to the input element. Remember when we said you should be careful when using the this keyword in our objects? That was back in The perils of this. Well, that is precisely the problem we are having right now. Our onGotFocus() method is being called as an event handler for the onfocus event of the input field, and that call is made with the input field being the value of this. Our problem is not on line 32 though. The problem is back a few lines before:
this.field['onfocus'] = this.onGotFocus; this.field['onblur'] = this.onLostFocus;

We cannot just pass a reference to one of our methods like that. We need to create some context that forces the this inside those methods to contain our object. This is not hard. Let's change those two lines to:
var that = this; this.field['onfocus'] = that.onGotFocus(); }; function() {

this.field['onblur'] = function() { that.onLostFocus(); };

Now save the page and refresh the browser. The page should work now. Where is the solution?

The Console tab


The console tab is an interactive interface between you and the executing code. It contains a prompt denoted by >>> where you can inspect objects and variables, change their values, create new ones, etc.

Inspect DOM elements


The HTML tab allows us to inspect the page's structure. As we click elements in the displayed tree, they are highlighted in the page right above. It is very useful to understand where in the page hierarchy a given element is.

Inspect CSS
The CSS tab allows you to see all the CSS rules that exist on the page, from the various files that may contain CSS, and you can change or disable any individual attribute. The effects of changing the attributes are immediately reflected on the page. This feature is great for tweaking the CSS rules of your page to fix CSS bugs.

All your scripts


Using the Scripts tab you can see all the scripts in the page and set breakpoints to assist during debugging.

Network traffic
One of the most interesting tabs is the Net tab. It shows all the requests made during the load and operation of the page.

This tab is especially useful during the debugging of AJAX calls, where after expanding one of the requests we can see all the HTTP traffic information for that particular request. The HTTP headers can become very useful when tracing a problem.

Firebug has many more features but you're better off playing with it and learning which ones become more useful to you. This tool is under active development, so make sure you check their site often to get newer versions as they become available.

Error Handling and Debugging Conclusion


Dealing with software errors in one of those skills that can always be polished more. Beyond any natural aptitude, the key to be effective in error handling is understanding the language support and the existing development tools. In case we didn't make it clear enough, we believe you should choose Firefox as your main development browser and install Firebug immediately. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Production Grade JavaScript


In this lesson of the JavaScript tutorial, you will learn...

1. 2. 3. 4. 5. 6.

To choose the correct way to deliver JavaScript code What Unobtrusive JavaScript is Techniques to reduce page load times To use existing tools for code documentation To adopt coding style conventions How to write and execute unit tests

As JavaScript becomes a larger part of your application, it's important that we treat it with the same care we treat our server side code. We will see how to implement proper documentation, unit tests, coding conventions, and a few other important aspects of any maintainable code base. The way we include JavaScript in our pages can affect the page performance and we will be comparing the various alternatives. We will also look at a host of utilities to help us along the way, including debuggers and code pre-processors.

JavaScript Delivery
As we have seen in the early lesson in our course, there are a few different ways of including JavaScript code in our pages. Let's quickly revisit them.

Embedded in the page source


We can put our code inside script tags in our HTML markup. The code within these tags are evaluated as they are encountered by the browser when loading the HTML, from top to bottom. Syntax
<script type="text/javascript"> var MY_VAR = 123; function doSomething() { //.. code here } </script>

In external files
The code can also be placed in separate files that are retrieved and loaded by the browser when a script tag with a src attribute is found. Syntax
<script type="text/javascript" src="library.js"></script>

Inline in HTML tags

Sometimes we also see some script in attributes of HTML tags. These are usually there to handle some event associated with the tag like a button's click. Syntax
<input type="button" value="Validate" onclick="if(!validInput()) { alert('Please fix input');" />

Choosing between these options


Traditionally, the choice of delivery mechanism has always revolved around code reuse and client caching. If we have scripts that are used in more than one page, then it is appropriate that we put these scripts in external files and add them to each page. There are two immediate benefits to this beyond the code reuse. First, the code will be maintained in a single place, the .js file. Avoiding code duplication is always a good practice. Second, browsers will cache the individual files, speeding up the page load process in subsequent visits. Lately, though, there has been a lot of emphasis being placed on what has been called Unobtrusive JavaScript. Read on.

Unobtrusive JavaScript
Current web development trends point to greater separation between content, style, and behavior. What that means is removing as much as possible scripts from the HTML markup and placing them exclusively in external files. This may sound impossible, especially for the event handling scripts, but it's actually quite feasible. Take a look at this "old school" event trickery.
<div id="editableTitle" onclick="enterEditMode(this);"> Click to Edit </div>

This click event code can be moved to an external file with a relatively straight forward equivalent.
//this code is in an external file window.load = function () { var element = document.getElementById('editableTitle'); element.onclick = function () { enterEditMode(element); }; };

If we were using a helper library like Prototype.js, the code could become a little more compact.
//this code is in an external file document.observe('dom:loaded', function () { $('editableTitle').observe('click', function () { enterEditMode(this); }); });

The detail here is that our script can only reference DOM elements (like that div) after they are rendered. The window object's load event is the appropriate place for that. There's a problem here. If we have more than one external file trying to implement unobtrusive JavaScript, it's possible that they hi-jack the window load event from each other. To avoid that we would recommend using a helper library (again, like Prototype) to assign concurrent event handlers. Our Prototype.js-based example above does just that. The clean separation of HTML and JavaScript makes us recommend that you use the unobtrusive JavaScript approach as much as possible. This practice tends to produce more organized and maintainable JavaScript.

Performance implications
The presence of JavaScript in our pages directly affects how the browser loads and renders the page. As soon as the browser finds a script tag in the HTML, it will stop rendering the HTML and will process that JavaScript, which may involve downloading an external .js file. Even if the file had been previously cached by the browser, it will still fetch it from the cache and then evaluate its contents. The pauses in the page rendering can become quite noticeable depending on how many script tags we have and where in the page they are located. This is compounded by the fact that many browsers only retrieve 2 files at most simultaneously.

Mitigation of performance issues


There are a few techniques to increase the efficiency of the page load process. Actually, what we are talking here is the perceived performance of the page load. Studies have shown that most of the time taken to load HTML pages is spent not retrieving the HTML file, but processing scripts and loading all the page's external references, like CSS, JavaScript, and image files. In a very large number of sites the time to retrieve and load the HTML text from the server is below 15% of the total time. Here we will look at techniques that apply to JavaScript but some will apply to other file types as well.

Move scripts to the bottom of the page

Since the browser stops rendering the page when scripts are found, it makes sense that we try to place scripts after most of the HTML has already been parsed and rendered. The recommendation here is that we move our script tags as further down in the page as we can afford to without breaking the scripts. If we are following unobtrusive JavaScript this should not be a problem at all. We will put all the script tags immediately before the closing </body> tag. This technique can be very easy to implement and it has a surprisingly effective result. It is a quick win that we should not overlook.

Compress the files


Another way to speed up the load process of our pages is to compress the files that the page retrieves. In terms of our external .js files, we should definitely remember to configure our web server to compress the files before transmitting them. When the browser requests a resource from the web server, it informs the web server about its de-compression capabilities. This is done by sending a HTTP header like the following. Syntax
Accept-encoding: gzip, deflate

If properly configured, as soon as the web server detects that header it will know it can return a compressed version of the requested content. When compressed content is served, the response will carry a HTTP header similar to the one below. Syntax
Content-Encoding: gzip

The vast majority of the existing browsers will accept compressed content and decompress it correctly. A surprising number of web sites do not compress scripts and that has a negative effect on their performance. Each web server is configured differently to enable the compression. We won't get into the details of how to do that but you should not have any trouble finding the appropriate instructions in your web server's documentation.

Minification of files
A similar technique is to minify our .js files. Minify was the term chosen to describe the process of removing irrelevant white space and comments from JavaScript.

Note that we are not suggesting that we should stop adding comments to our JavaScript code or that we remove all indentation at the same time. That would make our code incomprehensible even to its authors (ourselves.) The suggestion is that we use a utility to minify the code before it gets deployed on the server. We definitely want to have the commented and indented code in our development environment, where we typically do not experience the page load performance problems anyway.

JSMin
JSMin is one of the most popular tools to minify JavaScript code. On its web site you will find several versions written in different programming languages. That includes a version written in JavaScript itself, which we can try online at http://fmarcia.info/jsmin/test.html. If you prefer, you can download the .exe version of JSMin and run it from the command line like this. Syntax
jsmin.exe < myLibrary.js > myLibrary-minified.js

When we run JSMin on DebugHelp.js, which we have been using in many of our examples, we can see what it does to reduce the file size. Here's the original file.

Code Sample: ProductionGradeJS/Demos/DebugHelp.js


/** * @fileOverview * @name DebugHelp.js * @author Sergio Pereira sergio@sergiopereira.com */ /** * Sets up an event handler for the given element. * @param {DOM Element} target The element that will publish the event. * @param {string} eventName The name of the event, like 'click', 'load', 'mouseover'. * @param {function} observerFunction The function that will be invoked when the event happens. */ function observeEvent(target, eventName, observerFunction){ if (target.addEventListener) { target.addEventListener(eventName, observerFunction, false); } else if (target.attachEvent) { target.attachEvent('on' + eventName, observerFunction); } else { target["on" + eventName] = observerFunction; }

} /** The default id used for the debug div in case not explicitly set*/ var WC_DEFAULT_DEBUG_PANEL_ID = "wc_debug"; /** * Inserts a div element to print debug messages inside a given element or * at the bottom of the page. * @param {string} [containerID] The element to create the div in. * Defaults to the page body. * @param {string} [debugPanelID] The id of the created div. Defaults to 'wc_debug' */ function insertDebugPanel(containerID, debugPanelID){ debugPanelID = debugPanelID||WC_DEFAULT_DEBUG_PANEL_ID; var container = document.getElementById(containerID); container = container||document.body; var panel = document.createElement('DIV'); panel.id = debugPanelID; panel.className = 'wc_debug'; container.appendChild(panel); debugWrite('<h4 style=\"border-bottom:1px solid black;\">Output:</h4>', debugPanelID); } /** Appends a message to in the debug panel * @param {string} message The HTML text to be printed * @param {string} [debugPanelID] The id of the target debug panel, * in case there's more than one. Defaults to 'wc_debug'. */ function debugWrite(message, debugPanelID){ debugPanelID = debugPanelID||WC_DEFAULT_DEBUG_PANEL_ID; var panel = document.getElementById(debugPanelID); var msgObj = document.createElement('P'); msgObj.innerHTML = message; panel.appendChild(msgObj); }

After we minify it using the online tool or jsmin.exe we get the following file. Syntax
jsmin.exe < DebugHelp.js > DebugHelp-minified.js

Code Sample: ProductionGradeJS/Demos/DebugHelpminified.js


function observeEvent(target,eventName,observerFunction){if(target.addEventListe ner){target.addEventListener(eventName,observerFunction,false);}else if(target.attachEvent){target.attachEvent('on'+eventName,observerFuncti on);}else{target["on"+eventName]=observerFunction;}} var WC_DEFAULT_DEBUG_PANEL_ID="wc_debug";function insertDebugPanel(containerID,debugPanelID){debugPanelID=debugPanelID||W

C_DEFAULT_DEBUG_PANEL_ID;var container=document.getElementById(containerID);container=container||doc ument.body;var panel=document.createElement('DIV');panel.id=debugPanelID;panel.classNa me='wc_debug';container.appendChild(panel);debugWrite('<h4 style=\"border-bottom:1px solid black;\">Output:</h4>',debugPanelID);} function debugWrite(message,debugPanelID){debugPanelID=debugPanelID||WC_DEFAULT_ DEBUG_PANEL_ID;var panel=document.getElementById(debugPanelID);var msgObj=document.createElement('P');msgObj.innerHTML=message;panel.appen dChild(msgObj);}

The resulting file is clearly much smaller. Here's the comparison. Original Minified Lines 55 3 Size in bytes 2057 945 Relative size 100% 45.9% Note that the gains will vary according to the file contents. It's not rare to see the size shrink to below 30% in files with good amounts of comments. -

Obfuscation versus Minification


Some people may be uncomfortable with the fact that JavaScript code is delivered in clear text to the browser. For this reason obfuscators were created. An obfuscator will scan the source code, performs minification and renames a lot of the variables, functions, etc. By using very short and cryptic names for the renamed items, an obfuscator reduces the code size even more than a plain minifier. Obfuscation is not without its problems. As you can imagine, renaming things in your code can be very risky if that thing is being referenced outside the processed file. We are not going to recommend obfuscation because of the unreasonable potential it has for introducing bugs. A much better approach is to use minification combined with file compression. Compressed minified files compared to compressed obfuscated ones are almost of the same size. The intellectual property or security concerns that may prompt the use of an obfuscator aren't really addressed by this tool. Obfuscation will only stop the casual observer and only slow down a little bit anyone that is looking for vulnerabilities in your code.

Combine files
One simple way to speed up the page load is to reduce the number of external files referenced by the HTML. Since the browser downloads only a few files simultaneously,

if we minimize the number of downloads we will be contributing to the faster rendering of the page. Both JavaScript and CSS files are good candidates to be combined in one big file of each kind. If your page or site uses a big number of small .js files, consider combining all of them into a single larger file. Although pretty obvious, this is our number one recommendation for effectively use and deliver JavaScript in your pages. Do all you can to serve your .js files concatenated. Most server side technologies like ASP.NET and Ruby on Rails will provide you with an easy way to do this, even if you prefer to keep them separated during development.

More techniques
There are a few more techniques that you can apply to make your pages load faster. We would like to recommend that you take a look at YSlow, which is a Firefox extension that will try to identify performance problems in your pages and suggest some improvements.

Documenting JavaScript
If we plan to create our own JavaScript libraries that can be reused across projects and by more than one developer, it becomes important that we provide some level of documentation for them. Documentation is one of those things that quickly gets out of synch with the actual code unless these two things are kept close to each other. One popular way to do this is to include the source code documentation in comments embedded in the code. A tool is later run to extract all these comments and produce browsable documentation. Because the documentation is maintained within the code itself, it increases the chances that the documentation reflects the current code.

JsDoc Toolkit
Early on there was a tool called JSDoc that would do just that but it has one mild problem, it was written in Perl, what didn't help its popularization. Recently, a similar tool, that uses the same comment format, was written in JavaScript and invoked via Java. This tool is called JsDoc Toolkit and can be found at http://jsdoctoolkit.org That web site will provide you with all the documentation and samples. As you can imagine, you'll need to have some recent version of Java installed in your system, which you probably already do anyway.

Exercise: Using JsDoc Toolkit on DebugHelp.js

Duration: 20 to 30 minutes. To demonstrate this tool, let's generate the documentation for DebugHelp.js, which we have already seen in some of our examples. 1. Your class material includes the following file with JsDoc Toolkit: ClassFiles\ProductionGradeJS\Exercises\jsdoc-toolkit.zip 2. Extract all files from this .zip file into a directory named jsdoc-toolkit in that same directory (Exercises.) 3. The utility is run from the command line but it takes too many parameters. Let's create a file named buildDocs.bat so that we can simply pass a file or directory name to the tool. 4. Execute the above .bat file to generate the documentation for DebugHelp.js. 5. Open a command line and go to ClassFiles\ProductionGradeJS\Exercises 6. Execute buildDocs DebugHelp.js After running the command, you should see a new directory called docs under Exercises. Inside that directory, find and open the file index.html on your browser and you should see the generated documentation, just like the screen shot below.

Code Sample: ProductionGradeJS/Exercises/DebugHelp.js


/** * @fileOverview * @name DebugHelp.js * @author Sergio Pereira sergio@sergiopereira.com */ /** * Sets up an event handler for the given element. * @param {DOM Element} target The element that will publish the event. * @param {string} eventName The name of the event, like 'click', 'load', 'mouseover'.

* @param {function} observerFunction The function that will be invoked when the event happens. */ function observeEvent(target, eventName, observerFunction){ if (target.addEventListener) { target.addEventListener(eventName, observerFunction, false); } else if (target.attachEvent) { target.attachEvent('on' + eventName, observerFunction); } else { target["on" + eventName] = observerFunction; } } /** The default id used for the debug div in case not explicitly set*/ var WC_DEFAULT_DEBUG_PANEL_ID = "wc_debug"; /** * Inserts a div element to print debug messages inside a given element or * at the bottom of the page. * @param {string} [containerID] The element to create the div in. * Defaults to the page body. * @param {string} [debugPanelID] The id of the created div. Defaults to 'wc_debug' */ function insertDebugPanel(containerID, debugPanelID){ debugPanelID = debugPanelID||WC_DEFAULT_DEBUG_PANEL_ID; var container = document.getElementById(containerID); container = container||document.body; var panel = document.createElement('DIV'); panel.id = debugPanelID; panel.className = 'wc_debug'; container.appendChild(panel); debugWrite('<h4 style=\"border-bottom:1px solid black;\">Output:</h4>', debugPanelID); } /** Appends a message to in the debug panel * @param {string} message The HTML text to be printed * @param {string} [debugPanelID] The id of the target debug panel, * in case there's more than one. Defaults to 'wc_debug'. */ function debugWrite(message, debugPanelID){ debugPanelID = debugPanelID||WC_DEFAULT_DEBUG_PANEL_ID; var panel = document.getElementById(debugPanelID); var msgObj = document.createElement('P'); msgObj.innerHTML = message; panel.appendChild(msgObj); }

Where is the solution?

Coding Standards

It's common for many organizations to establish coding standards across the development team. It's also very common for developers to disagree and even ignore these standards. When writing JavaScript code, trust me, some standards are actually very much necessary. Because of its flexible nature and also because of some evil implementations of JavaScript by the browsers, it is way too easy to fall into traps and create bugs that are very hard to trace. Let's go through some of the most important JavaScript coding standards that we would like to encourage you to follow. Some are there for pure legibility and some are there for your own protection.

Avoid globals at all costs


Global variables are the hallmark of badly structured code. They might be acceptable when the use of JavaScript in the page is limited, but as the usage grows, global variables are dangerous and can produce unexpected behavior. Especially when we have more than one external .js file being used in the page, nothing prevents the code in one file from declaring a global variable or function with the same name as in another file. To remove this problem from our code we suggest, interestingly enough, that we create global namespacing objects. See the following example.
//in wc_library.js var COMPANY_NAME = 'Webucator'; function doSomething() { //... } //in amazon.js var COMPANY_NAME = 'Amazon.com'; function doSomething() { //... }

As we can see, if we include both files (wc_library.js and amazon.js) in the same page they will clobber each other and who knows what the consequences will be. See how namespacing objects would avoid that problem.
//in wc_library.js var WEBUCATOR = { companyName: 'Webucator' doSomething: function () { //... } };

//in amazon.js var AMAZON = { companyName: 'Amazon.com', doSomething: function () { //... } };

Now, instead of globals, we have methods and properties, locked inside well-organized objects. They are the only type of global objects we want to encourage.

Use === and !== instead of == and !=


As we have already seen in a previous lesson, the use of == and != can sometimes lead to unintended results because of automatic type coercion. To avoid this risk, use their longer cousins === and !==, they are well worth the extra character.

Avoid eval()
This is another problem we discussed previously. Again, just remember, eval() is evil.

Do not pass strings to setTimeout() or setInterval()


Passing strings to these functions is just another form of eval() in disguise. Instead of a string with JavaScript code, pass an actual function variable or an anonymous function.
//instead of this: setTimeout("alert('Time is up!');", 9000); //prefer this: setTimeout( function () {alert('Time is up');}, 9000);

Use the default operator ||


The use of the default operator is very common in high-quality JavaScript libraries and we should get used to employ it in our code as well.

Do not use assignments as expressions


Assignments used as expressions can be hard to understand and, worse, be confused with bugs. Do not use them like that. See how the following if statement looks like a bug where the developer forgot to use a == (or a ===).
if (flag = getFlag() ) { alert(flag); }

Use the ternary operator for value selection


The ternary operator is a good space saver. Use it instead of trivial if / else blocks.
//instead of this: var x; if (flag) { x = 'success'; } else { x = 'failure'; } //prefer this: var x = flag ? 'success' : 'failure';

Limit the amount of embedded JavaScript


Only use embedded JavaScript in the HTML when the JavaScript is only useful for the current page and session.

Constrain lines to 80 characters


Break lines right after operators or comma Indent the line after a line break with one extra tab

Use comments judiciously


Add meaningful comments Avoid obvious comments Prefer inline comments //

Declare all variables at the beginning of each function


Since JavaScript only has function scope, declaring variables inside other blocks, like if or for may give the false impression that the variable is scoped inside that block, which could lead to bugs.

Start constructor functions with a capital letter


Since constructor functions need to be called preceded by the new operator, we use its initial upper case character as a reminder that we are dealing with a constructor. This should help us remembering to use the new operator.

Use blocks all the time in control structures

Control structures like if, while or for can take either a single statement or a block of statements inside curly braces. We recommend that you always use a block, even if it has only one statement. The absence of the braces can hide bugs or make the code look different than its intent.

Naming your identifiers


Use only A-Za-z0-9_ for identifiers Don't start identifiers with _ Don't use $ or \ Start constructors with upper case Global variables in ALL_CAPS Start all other identifiers with lower case

Use whitespace wisely


Between any keyword and a (, like for ( ... ) No spaces between a function name and the parenthesis: execute(abc) Put a space after a comma Each ; in the control part of a for statement should be followed with a space.

Use {} instead of new Object()


Syntax
//instead of var APPOINTMENT = APPOINTMENT.title //prefer this: var APPOINTMENT = APPOINTMENT.title new Object(); = 'Team Meeting'; { }; = 'Team Meeting';

Use [] instead of new Array()


Syntax
//instead of var BOOKS = new Array(); BOOKS[0] = 'Ship It!'; //prefer this: var BOOKS = [ ]; BOOKS[0] = 'Ship It!';

Format of common code structures


format of the if statement

Syntax
if (condition) { statements; } else if (condition) { statements; } else { statements; }

format of the for statement


Syntax
for (initialization; condition; update) { statements; } for (variable in object) if (filter) { statements; }

format of the while statement


Syntax
while (condition) { statements; }

format of the do statement


Syntax
do { statements; } while (condition);

format of the switch statement


Syntax
switch (expression) { case expression: statements; break; //(or return or throw) default: statements; }

format of the try statement:


Syntax
try { statements; } catch (variable) {

statements; } finally { statements; }

Checking code with JSLint


Many of the coding conventions that we just discussed can be verified by a tool called JSLint. JSLint will analyze your JavaScript code and warn you of any unadvisable practices present in the code. It has quite a few options for you to ignore some of the rules if desired.

Unit Testing JavaScript


A growing number of developers are using automated unit testing frameworks to test their code. Some of these developers are practitioners of Test-Driven Development (TDD.) Adopters of TDD write the unit tests before they write the code. The idea is that this will force you to write code that is testable, which happens to also be better designed code in most cases. Unit testing is especially attractive to code written in dynamic languages like JavaScript. There's just too much flexibility in JavaScript and there isn't a compiler to give you a minimum level of confidence that your code is correct. You have to actually run the code after every change to be certain you didn't break anything. This is where unit testing frameworks are helpful. We will take a look at the most popular one, called JsUnit. If you have used any other testing framework of the x-Unit family, you should feel right at home in JsUnit. JsUnit comes with a series of functions that you can use in your unit testing scripts. The process typically involves creating a new HTML page to hold a group of your tests, add the required JsUnit scripts references, and write a few functions to perform the actual tests. These functions have to be named starting with "test" (there are ways around this). To execute the tests and see the results, we use the test runner and ask it to load the page with the tests. The test runner will find the "testXXXXXXX" functions and call one by one, collecting the results and reporting progress and results in a red or green progress bar. To demonstrate how this works, look at this test file, which tests a couple of methods of the FaderEffect object we saw in an earlier lesson. We included JsUnit in the class files for this lesson.

Code Sample: ProductionGradeJS/Demos/js-unit.html


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>Tests for Yellow Fade Effect</title> <script type="text/javascript" src="../jsunit/app/jsUnitCore.js" ></script> <script type="text/javascript" src="faderEffect.js" ></script> <script type="text/javascript" charset="utf-8"> function test_intToColorByte() { var el = document.getElementById('testElement'); var effect = new FaderEffect(el, 2, '#010203', '#0f0e0d'); assertEquals('Convert number lower than 16', '0a', effect.intToColorByte(10)); assertEquals('Convert number greater than 16', '20', effect.intToColorByte(32)); } function test_colorToArray() { var el = document.getElementById('testElement'); var effect = new FaderEffect(el, 2, '#010203', '#0f0e0d'); var array = effect.colorToArray('#04FF01') assertEquals('Convert RED element', 4, array[0]); assertEquals('Convert GREEN element', 255, array[1]); assertEquals('Convert BLUE element', 1, array[2]); } </script> </head> <body> <div id="testElement"></div> </body> </html>

Code Sample: ProductionGradeJS/Demos/faderEffect.js


function FaderEffect(element, duration, fromColor, toColor) { var that = this; this.element = element; this.interval = 100; this.duration = 1000 * (duration || 1); this.fromColor = fromColor || '#ffff99'; this.toColor = toColor || '#ffffff'; this.totalFrames = Math.round(this.duration / this.interval); this.intToColorByte = function (num) { var digits = num.toString(16); if (num < 16) { digits = '0' + digits; } return digits;

}; this.colorToArray = function (color) { return [ this.parseColorByte(color, 0), this.parseColorByte(color, 1), this.parseColorByte(color, 2) ]; } this.parseColorByte = function (color, whichByte) { return parseInt(color.substr(1 + whichByte*2, 2), 16); }; this.arrayToColor = function (arr) { return '#' + this.intToColorByte(arr[0]) + this.intToColorByte(arr[1]) + this.intToColorByte(arr[2]); }; this.fromRGB = this.colorToArray(this.fromColor); this.toRGB = this.colorToArray(this.toColor); this.frameNo = 0; this.updateBgColor = function () { if (that.frameNo < (that.totalFrames-1)) { var newRGB = that.calcNewColor(that.frameNo); that.element.style.backgroundColor = that.arrayToColor(newRGB); that.frameNo += 1; setTimeout(that.updateBgColor, that.interval); } else { that.element.style.backgroundColor = that.arrayToColor(that.toRGB); } }; this.calcNewColor = function () { var frac = (1.0 * this.frameNo)/this.totalFrames; return [ Math.floor((this.toRGB[0]-this.fromRGB[0])*frac + this.fromRGB[0]), Math.floor((this.toRGB[1]-this.fromRGB[1])*frac + this.fromRGB[1]), Math.floor((this.toRGB[2]-this.fromRGB[2])*frac + this.fromRGB[2]) ]; }; this.updateBgColor(); }

To use the test runner you will need to have Java installed in your machine. With that, navigate to ProductionGradeJS/jsunit/testRunner.html, select the file ProductionGradeJS/Demos/js-unit.html and click "Run". You should see something similar to the image below.

Unit testing, and TDD in special, are very large topics for which many books have been written. Our goal here is to simply show you that, unit testing is a very good practice for JavaScript code and that there's is support similar to other programming languages.

Production Grade JavaScript Conclusion


The days of JavaScript being written and maintained in sloppy fashion are gone. There aren't reasonable excuses to treat your JavaScript code with any less care than the server side code.

With the available tools and guidance you can produce very maintainable JavaScript and abandon the practice of blindly copying and pasting JavaScript (if you've been in web development for long enough, you know you've done it.) To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

The Prototype Library


In this lesson of the JavaScript tutorial, you will learn... 1. What is, who wrote, who uses, who maintains 2. The extensions to core JavaScript objects 3. How much more powerful strings can be 4. New ways to work with lists of items 5. Simulate class-based inheritance 6. The handy utility functions 7. Extensions to the DOM elements 8. How to do Ajax with Prototype 9. How to extend Prototype 10. Where to get more information Back in 2005, when the Web rediscovered JavaScript and the AJAX term was coined, a few JavaScript libraries emerged as the platform behind many of the new web sites collectively known as Web 2.0. One of the most popular libraries, arguably the most popular at the time, was Prototype. The Prototype library was originally written by Sam Stephenson when the company he worked for was creating some of the most well-known pioneer AJAX applications, like Basecamp and Campfire. This library is also what powers the client side scripts of applications built using Ruby on Rails. Today the library is still maintained by Sam Stephenson but now it has its own team of maintainers that continuously contribute new features, bug fixes, and tools to produce JavaScript with Prototype. Prototype is largely a framework type of library. It does not have any eye candy or UI widgets. On the other hand, because it is a good platform, there is a full blown ecosystem of other libraries built on top of Prototype to provide almost anything you need at the client side. During this lesson and the next, whenever we are referring to the Prototype library we will use the word with a capital P. We will use lower case prototype to refer to the prototype property that all JavaScript objects have.

Base Class Inheritance


We spent quite a bit of time explaining how the prototypal inheritance works in JavaScript and how it differs from the classical model. Future version of JavaScript are expected to support base class inheritance. Until then, Prototype comes with an implementation of the classical inheritance model with the intent of creating a more familiar environment for the developers that choose not to apply prototypal inheritance.

Creating classes
Prototype comes with an object named Class that we can use to create new classes and optionally specify their base classes.
var WEBUCATOR = { }; WEBUCATOR.Beverage = Class.create( { initialize: function (name) { this.name = name; }, describe: function () { return "I'm a beverage named " + this.name + "."; } }); var water = new WEBUCATOR.Beverage('Water'); alert(water.describe());

The object passed to Class.create will become the implementation of the class, copied to the new class' prototype object. The only special method is the one called initialize(); it will serve as the class' constructor.

Class inheritance
If the object passed to Class.create is another class object created with Class.create, that will be detected and this object (class) will become the base class for the new class. We can add new members or replace members in the base class by using a second argument containing an object. When we are replacing methods in the new class, we can define the first argument of the new method called $super, which will be a reference to the same method in the base class. We can call it if needed. The example below shows all that in action.

Code Sample: PrototypeJS/Demos/dollar-function.html


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title> Test Page </title>

<script src="../../Libraries/prototype.js" type="text/javascript"></script> <script src="../../Libraries/DebugHelp.js" type="text/javascript"></script> <script type="text/javascript"> function test() { var d = $('myDiv'); d.hide(); alert(d.innerHTML); d.show(); d.addClassName('active'); } //add the debud panel at the bottom of the page observeEvent(window, 'load', function () {insertDebugPanel();} ); </script> <style type="text/css" media="screen"> .active {background-color:#ff9;} </style> </head> <body> <div id="myDiv"> <p>This is a paragraph</p> </div> <div id="myOtherDiv"> <p>This is another paragraph</p> </div> <input type="button" value="Test $()" onclick="test();"/><br/> </body> </html>

This type of classical inheritance is used extensively in Prototype's source code, in case you are curious to inspect it (and we encourage you to do so.)

The utility functions


The library comes with many predefined objects and utility functions. The obvious goal of these functions is to save you a lot of repeated typing and idioms.

Using the $() function


The $() function is a handy shortcut to the all-too-frequent document.getElementById() function of the DOM. Like the DOM function, this one returns the element that has the id passed as an argument. Unlike the DOM function, though, this one goes further. The returned element object will be augmented with some extra methods. These extra methods simplify many tasks, like hiding/showing the element, getting its size, scrolling to the element, etc. You can get a list of the methods that are added to the returned element object in the reference for the Element.Methods object. Furthermore, if the element is a form it will also receive copies of the utility methods from Form.Methods and if the element is a form field (input, select,

or textarea) it will additionally receive copies of the utility methods from Form.Element.Methods.

Code Sample: PrototypeJS/Demos/dollar-function.html


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title> Test Page </title> <script src="../../Libraries/prototype.js" type="text/javascript"></script> <script src="../../Libraries/DebugHelp.js" type="text/javascript"></script> <script type="text/javascript"> function test() { var d = $('myDiv'); d.hide(); alert(d.innerHTML); d.show(); d.addClassName('active'); } //add the debud panel at the bottom of the page observeEvent(window, 'load', function () {insertDebugPanel();} ); </script> <style type="text/css" media="screen"> .active {background-color:#ff9;} </style> </head> <body> <div id="myDiv"> <p>This is a paragraph</p> </div> <div id="myOtherDiv"> <p>This is another paragraph</p> </div> <input type="button" value="Test $()" onclick="test();"/><br/> </body> </html>

Because many of the new methods added to the element return the element itself, you can chain the method calls to make more compact code:
//change the text, the CSS class, and make the element visible $('messageDiv').update('Your order was accepted.').addClassName('operationOK').show();

Another nice thing about this function is that you can pass either the id string or the element object itself, which makes this function very useful when creating other functions that can also take either form of argument.

Using the $$() function

The $$() function will help you a lot if you consistently separate CSS from the content wireframe. It parses one or more CSS filtering expressions, analogous to the ones used to define CSS rules, and returns the elements that match these filters. It's so easy to use it's ridiculous. Check this out.

Code Sample: PrototypeJS/Demos/double-dollarfunction.html


---- Code Omitted ---<script type="text/javascript"> function test$$() { /* in case CSS is not your forte, the expression below says 'find all the INPUT elements that are inside elements with class=field that are inside a DIV with id equal to loginForm.' */ var f = $$('div#loginForm .field input'); var s = ''; for (var i=0; i<f.length; i++) { s += f[i].value + '/'; } debugWrite(s); // shows: "joedoe1/secret/" //now passing more than one expression f = $$('div#loginForm .field input', 'div#loginForm .fieldName'); s = ''; for (var i=0; i<f.length; i++) { s += ( f[i].value ? f[i].value : f[i].innerHTML ) + '/'; } debugWrite(s); //shows: "joedoe1/secret/User name:/Password:/" } ---- Code Omitted ---<div id='loginForm'> <div class='field'> <span class='fieldName'>User name:</span> <input type='text' id='txtName' value='joedoe1'> </div> <div class='field'> <span class='fieldName'>Password:</span> <input type='password' id='txtPass' value='secret'> </div> <input type='submit' value='login'> </div> <input type="button" value='test $$()' onclick='test$$();'> ---- Code Omitted ----

A quick note on performance. The current implementation of the $$() function in prototype.js is not regarded as particularly efficient. If you plan on traversing deep and complex HTML documents using this function frequently, you may want to consider

other freely available implementations, possibly simply substituting the $$() function itself.

Using the $F() function


The $F() function is another welcome shortcut. It returns the value of any field input control, like text boxes or drop-down lists. The function can take as argument either the element id or the element object itself.

Code Sample: PrototypeJS/Demos/f-dollarfunction.html


---- Code Omitted ---<script type="text/javascript"> function test3() { debugWrite( $F('userName') ); } </script> <form> <input type="text" id="userName" value="Joe Doe"><br/> <input type="button" value="Test3" onclick="test3();"><br/> </form> </body> </html>

Using the $A() function


The $A() function converts the single argument it receives into an Array object. This function, combined with the extensions for the Array class, makes it easier to convert or copy any enumerable list into an Array object. One suggested use is to convert DOM NodeLists into regular arrays, which can be traversed more efficiently. See example below.
<script type="text/javascript"> function showOptions() { var someNodeList = $('lstEmployees').getElementsByTagName('option'); var nodes = $A(someNodeList); nodes.each( function (node) { alert(node.nodeName + ': ' + node.innerHTML); }); } </script> <select id="lstEmployees" size="10" > <option value="5">Buchanan, Steven</option> <option value="8">Callahan, Laura</option> <option value="1">Davolio, Nancy</option> </select>

<input type="button" value="Show the options" onclick="showOptions();" >

Using the $H() function


The $H() function converts objects into enumerable Hash objects that resemble associative arrays (a.k.a. hashes or hash tables.)
function testHash() { //let's create the object var a = { first: 10, second: 20, third: 30 }; //now transform it into a hash var h = $H(a); alert(h.toQueryString()); //displays: first=10&second=20&third=30 }

Using the $R() function


The $R() function is simply a short hand to writing new ObjectRange(lowerBound, upperBound, excludeBounds). Jump to the ObjectRange class documentation for a complete explanation of this class. In the meantime, let's take a look at a simple example that also shows the usage of iterators through the each method. More on that method will be found in the Enumerable object documentation.
<script type="text/javascript"> function demoDollar_R() { var range = $R(10, 20, false); range.each( function (value, index) { alert(value); }); } </script> <input type="button" value="Sample Count" onclick="demoDollar_R();" />

Using the Try.these() function


The Try.these() function makes it easy when you want to, ahem, try different function calls until one of them works. It takes a number of functions as arguments and calls them one by one, in sequence, until one of them works, returning the result of that successful function call.

In the example below, the function xmlNode.text works in some browsers, and xmlNode.textContent works in the other browsers. Using the Try.these() function we can return the one that works.
<script type="text/javascript"> function getXmlNodeValue(xmlNode) { return Try.these( function () {return xmlNode.text;}, function () {return xmlNode.textContent;} ); } </script>

Strings, reloaded
Strings are powerful objects. Prototype takes that power and elevates it by another level of magnitude.

String substitutions
When it comes string substitutions JavaScript already has the methods like String.Replace, which even works with regular expressions, but it's still not as flexible as the alternative introduced by Prototype. Meet the new String.gsub method. With this method you can not only find and replace a fixed string or a regular expression pattern, but you also have much more control over the replacement process. You can, for example, use a string template to instruct the method on how you would like the found elements to be transformed (rather than simply replaced.) The example below searches for words containing 't' and replaces the portion that comes after the 't' with 'tizzle'. In case the example is not very clear, the regular expression we chose has a capture group declaration: the \w+ enclosed in parenthesis. We can get the value captured by this group using #{1} in the replacement template string. In our example we are capturing what comes before the 't' and appending 'tizzle' to it. If we had more capture groups in the regular expression, we would get the values with #{2}, #{3}, and so on.
function talkLikeYouKnowSomething() { var s = 'prototype string extensions can help you'; var snoopdogfy = /\b(\w+)t\w+\b/; var snooptalk = s.gsub(snoopdogfy, '#{1}tizzle' ); alert(snooptalk); // shows: "prototizzle stizzle extizzle can help you" }

Let's not stop there. The substitution we have just made is not all that powerful because we are limited to pattern matching and substitutions. What if we could operate on the matches with custom logic to produce the desired substitution values? We can do that if we pass a function as the second argument to gsub(). The function will receive an array with the matched text (index 0) and any capture group values (index 1 to N.)
function scamCustomers() { var prices = 'book1 $12.5, magazine $5.50, pencil $1.20'; var priceFinder = /\$([0-9\.]+)/; var r = prices.gsub(priceFinder, jackUp); alert(r);//shows: "book1 $13.75, magazine $6.05, pencil $1.32" } function jackUp(matches) { //increases the prices by 10% var price = parseFloat(matches[1]); return '$' + Math.round(110 * price)/100; }

String templates
As you increase the amount of JavaScript code in your applications, increasingly you'll find yourself with collections of objects of the same type and that you need to list or present in a formatted way. It's not rare to find code in your applications that loops through a list of objects, building a string based on the object properties and some fixed formatting elements. Prototype comes with the Template class, which aims at helping you with exactly this type of scenarios. The example below shows how to format a list of items in a shopping cart in multiple HTML lines.
function printCart() { //creating a sample cart var cart = { }; cart.items = [ ]; //putting some sample items in the cart cart.items.push({product: 'Book 123', price: 24.50, quantity: 1}); cart.items.push({product: 'Set of Pens', price: 5.44, quantity: 3}); cart.items.push({product: 'Gift Card', price: 10.00, quantity: 4}); //here we create our template for formatting each item var itemFormat = new Template( 'You are ordering #{quantity} units ' + 'of #{product} at $#{price} each' ); var formatted = ''; for (var i=0; i<cart.items.length; i++) { var cartItem = cart.items[i];

formatted += itemFormat.evaluate(cartItem) + '<br/>\n'; } alert(formatted); /* SHOWS: You are ordering 1 units of Book 123 at $24.5 each<br/> You are ordering 3 units of Set of Pens at $5.44 each<br/> You are ordering 4 units of Gift Card at $10 each<br/> */ }

For a more complete list of new methods, see the String extensions reference.

DOM Manipulation
Another great strength of Prototype is it's repertoire of methods to assist with dynamically changing the HTML document structure. We've already sees that Prototype augments the DOM elements with a host of utility methods. Many of those methods are related to DOM traversing and manipulation. We can create, show, hide, alter elements in our HTML with a much simpler syntax than the one that comes as part of the DOM API. We can get a lot done with a few basic methods with suggestively enough names: insert(), show(), hide(), remove(), update(), and wrap(). We also can create elements from scratch using the new Element(tagName) syntax. All of these DOM operations are shown in the following example. Once again, note how we can chain these method calls to reduce the amount of code typed.

Code Sample: PrototypeJS/Demos/DOM.html


---- Code Omitted ---<h4>Creating new elements</h4> <script type="text/javascript" charset="utf-8"> function createElem() { var el = new Element('img', {src:'wc.png'}); $('box1').appendChild(el); } </script> <input type="button" name="btn1" value="Create img element" id="btn1" onclick="createElem();"> <div id="box1" class="" ></div> <h4>Insertions</h4> <script type="text/javascript" charset="utf-8"> function insertElTop() { var el = new Element('li').update( $F('newItem') ); $('list1').insert({top: el });

} function insertElBottom() { var el = new Element('li').update( $F('newItem') ); $('list1').insert({bottom: el }); } function insertElBefore() { var el = new Element('li').update( $F('newItem') ); $('list1').insert({before: el }); } function insertElAfter() { var el = new Element('li').update( $F('newItem') ); $('list1').insert({after: el }); } </script> <input type="text" name="newItem" value="Text here" id="newItem"/>&nbsp; <input type="button" value="Insert at TOP" id="btn2a" onclick="insertElTop();" /> <input type="button" value="Insert at BOTTOM" id="btn2b" onclick="insertElBottom();" /> <input type="button" value="Insert BEFORE" id="btn2c" onclick="insertElBefore();" /> <input type="button" value="Insert AFTER" id="btn2d" onclick="insertElAfter();" /> <div style="border:solid 1px black;"> <ol id="list1"> <li>Existing item #1</li> <li>Existing item #2</li> </ol> </div> <h4>Hide/Show</h4> <input type="button" value="Hide" onclick="$('box3').hide();" /> <input type="button" value="Show" onclick="$('box3').show();" /> <input type="button" value="Toggle" onclick="$('box3').toggle();" /> <div class="bigBox"><div id="box3" class="smallBox" />&nbsp;</div> <h4>Wrapping</h4> <script type="text/javascript" charset="utf-8"> function wrapInDiv() { $('img4').wrap('div', {'class':'lightBox'}); } </script> <input type="button" value="Wrap in Div" onclick="wrapInDiv();" /> <div style="width:330px;"> <img id="img4" src="wc.png" alt="webucator"/> <h4>Removing elements</h4> <script type="text/javascript" charset="utf-8"> function removeEl() { try { $('img5').remove();

} catch (ex) { alert('The element does not exist!!!!'); } } </script> <input type="button" value="Remove Image" onclick="removeEl();" /> <img id="img5" src="wc.png" alt="webucator"/> ---- Code Omitted ----

The Ajax object


The utility functions mentioned above are nice but, let's face it, they are not the most advanced type of thing, now are they? You could probably have done it yourself and you may even have similar functions in your own scripts. But those functions are just the tip of the iceberg. I'm sure that your interest in Prototype is driven mostly by its AJAX capabilities. So let's explain how the library makes your life easier when you need to perform AJAX logic. The Ajax object is a pre-defined object, created by the library to wrap and simplify the tricky code that is involved when writing AJAX functionality. This object contains a number of classes that provide encapsulated AJAX logic. Let's take a look at some of them.

Using the Ajax.Request class


If you are not using any helper library, you are probably writing a whole lot of code to create a XMLHttpRequest object and then track its progress asynchronously, then extract the response and process it. And consider yourself lucky if you do not have to support more than one type of browser. To assist with AJAX functionality, the library defines the Ajax.Request class. Let's say you have an application that can communicate with the server via the url http://yourserver/app/get_sales?empID=1234&year=1998, which returns an XML response like the following.
<?xml version="1.0" encoding="utf-8" ?> <ajax-response> <response type="object" id="productDetails"> <monthly-sales> <employee-sales> <employee-id>1234</employee-id> <year-month>1998-01</year-month> <sales>$8,115.36</sales> </employee-sales>

<employee-sales> <employee-id>1234</employee-id> <year-month>1998-02</year-month> <sales>$11,147.51</sales> </employee-sales> </monthly-sales> </response> </ajax-response>

Talking to the server to retrieve this XML is pretty simple using an Ajax.Request object. The sample below shows how it can be done.
<script type="text/javascript"> function searchSales() { var empID = $F('lstEmployees'); var y = $F('lstYears'); var url = 'http://yourserver/app/get_sales'; var pars = 'empID=' + empID + '&year=' + y; var myAjax = new Ajax.Request( url, { method: 'get', parameters: pars, onComplete: showResponse }); } function showResponse(response) { //put returned XML in the textarea $('result').value = response.responseText; } </script> <select id="lstEmployees" size="10" onchange="searchSales()"> <option value="5">Buchanan, Steven</option> <option value="8">Callahan, Laura</option> <option value="1">Davolio, Nancy</option> </select> <select id="lstYears" size="3" onchange="searchSales()"> <option selected="selected" value="1996">1996</option> <option value="1997">1997</option> <option value="1998">1998</option> </select> <br/><textarea id="result" cols=60 rows=10 ></textarea>

Can you see the second parameter passed to the constructor of the Ajax.Request object? The parameter {method: 'get', parameters: pars, onComplete: showResponse} represents an anonymous object in literal notation (a.k.a. JSON). What it means is that we are passing an object that has a property named method that contains the string 'get', another property named parameters that contains the querystring of the HTTP request, and an onComplete property/method containing the function showResponse.

There are a few other properties that you can define and populate in this object, like asynchronous, which can be true or false and determines if the AJAX call to the server will be made asynchronously (the default value is true.) This parameter defines the options for the AJAX call. In our sample, we are calling the url in the first argument via a HTTP GET command, passing the querystring contained in the variable pars, and the Ajax.Request object will call the showResponse function when it finishes retrieving the response. As you may know, the XMLHttpRequest reports progress during the HTTP call. This progress can inform four different stages: Loading, Loaded, Interactive, or Complete. You can make the Ajax.Request object call a custom function in any of these stages, the Complete being the most common one. To inform the function to the object, simply provide property/methods named onXXXXX in the request options, just like the onComplete from our example. The function you pass in will be called by the object with two arguments, the first one will be an Ajax.Response object (the response) and the second one will be the evaluated X-JSON response HTTP header (if one is present). You can then use the first argument to get the returned data and maybe check the status property, which will return the HTTP result code of the call. The X-JSON header is useful if you want to return some script or JSON-formatted data. The same JSON data is also available in the response's responseJSON property. Two other interesting options can be used to process the results. We can specify the onSuccess option as a function to be called when the AJAX call executes without errors and, conversely, the onFailure option can be a function to be called when a server error happens. Just like the onXXXXX option functions, these two will also be called passing the XHR that carried the AJAX call and the evaluated X-JSON header. Our sample did not process the XML response in any interesting way. We just dumped the XML in the textarea. A typical usage of the response would probably find the desired information inside the XML and update some page elements, or maybe even some sort of XSLT transformation to produce HTML in the page. There's also another form of event callback handling available. If you have code that should always be executed for a particular event, regardless of which AJAX call caused it to happen, then you can use the new Ajax.Responders object. Let's suppose you want to show some visual indication that an AJAX call is in progress, like a spinning icon or something of that nature. You can use two global event handlers to help you, one to show the icon when the first call starts and another one to hide the icon when the last one finishes. See example below.
<script type="text/javascript"> var myGlobalHandlers = { onCreate: function () { Element.show('systemWorking'); },

onComplete: function () { if(Ajax.activeRequestCount === 0){ Element.hide('systemWorking'); } } }; Ajax.Responders.register(myGlobalHandlers); </script> <div id='systemWorking'><img src='spinner.gif'>Loading...</div>

For more complete explanations, see the Ajax.Request reference and the options reference.

Exercise: AJAX Search


Duration: 30 to 45 minutes. We will use Prototype AJAX support to call a server-side search service and display the results without ever reloading our page. 1. 2. 3. 4. Create a method WEBUCATOR.doSearch(text) Call that method from the Go button's click Use the textbox's value as the search term (the argument to the method) Make the doSearch method perform an AJAX call to ../Common/search.php passing the search term as the "q" posted parameter. 5. Print the response returned form that method (you can use debugWrite or alert) Don't get too worried about the PHP file that we use. Just treat it as some generic URL that will find news items that contain a given word from a database. The search.php script will simply read the value posted in the q parameter and look into the cnn.xml file looking for items that contain that value in the description element. Once the page is ready, you should be able to type a word in the input field and click the Go! button to see all the stories that contain that word be listed.

Code Sample: PrototypeJS/Exercises/ajax-search.html


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title> Ajax Request </title> <script src="../../Libraries/prototype.js" type="text/javascript"></script> <script src="../../Libraries/DebugHelp.js" type="text/javascript"></script> </head> <body> <script type="text/javascript">

//add the debud panel at the bottom of the page Event.observe(window, 'load', function () {insertDebugPanel();} ); </script> <script type="text/javascript"> var WEBUCATOR = { }; /* The file ..\Common\cnn.xml contains an old copy of the xml from the URL: http://rss.cnn.com/rss/cnn_topstories.rss If you wish, you can get a newer version of the xml and update that file. 1 - Create a method WEBUCATOR.doSearch(text) 2 - Call that method from the Go button's click 3 - Use the textbox's value as the search term (the argument to the method) 4 - Make the doSearch method perform an AJAX call to ../Common/search.php passing the search term as the "q" posted parameter. 5 - Print the response returned form that method (you can use debugWrite or alert) */ WEBUCATOR.onload = function () { }; Event.observe(window, 'load', WEBUCATOR.onload ); </script> <form action="#"> <input type="text" name="searchTerm" value="" id="searchTerm"/> <input type="button" name="goButton" value="Go!" id="goButton"/> </form> </body> </html>

Code Sample: PrototypeJS/Common/search.php


<?php $doc = new DOMDocument(); $doc->load( 'cnn.xml' ); $searchTerm = $_POST['q']; $xpath = new Domxpath($doc); $newsItems = $xpath->query( "//item" ); foreach( $newsItems as $item ) { $desc = $item->getElementsByTagName( "description" ); $content = $desc->item(0)->nodeValue; $pos1 = stripos($content, $searchTerm);

if ($pos1 !== false) { $title = $item->getElementsByTagName( "title" )->item(0)->nodeValue; $link = $item->getElementsByTagName( "link" )->item(0)->nodeValue; echo "<li><strong><a href=\"$link\">$title</a></strong><br/>$content</li>"; } } ?>

Where is the solution?

Using the Ajax.Updater class


If you have a server endpoint that can return information already formatted in HTML, the library makes life even easier for you with the Ajax.Updater class. With it you just inform which element should be filled with the HTML returned from the AJAX call. An example speaks better than I can write.
<script type="text/javascript"> function getHTML() { var url = 'http://yourserver/app/getSomeHTML'; var pars = 'someParameter=ABC'; var myAjax = new Ajax.Updater( 'placeholder', url, { method: 'get', parameters: pars }); } </script> <input type="button" value="GetHtml" onclick="getHTML();"/> <div id="placeholder"></div>

As you can see, the code is very similar to the previous example, with the exclusion of the onComplete function and the element id being passed in the constructor. Let's change the code a little bit to illustrate how it is possible to handle server errors on the client. We will add more options to the call, specifying a function to capture error conditions. This is done using the onFailure option. We will also specify that the placeholder only gets populated in case of a successful operation. To achieve this we will change the first parameter from a simple element id to an object with two properties, success (to be used when everything goes OK) and failure (to be used when things go bad.) We will not be using the failure property in our example, just the reportError function in the onFailure option.
<script type="text/javascript">

function getHTML() { var url = 'http://yourserver/app/getSomeHTML'; var pars = 'someParameter=ABC'; var myAjax = new Ajax.Updater( {success: 'placeholder'}, url, { method: 'get', parameters: pars, onFailure: reportError }); } function reportError(response) { alert('Sorry. There was an error.'); } </script> <input type="button" value="GetHtml" onclick="getHTML();"/> <div id="placeholder"></div>

Exercise: AJAX Search


Duration: 30 to 45 minutes. We will use Prototype AJAX support to call a server-side search service and display the results without ever reloading our page. 1. 2. 3. 4. Create a method WEBUCATOR.doSearch(text) Call that method from the Go button's click Use the textbox's value as the search term (the argument to the method) Using the Ajax.pdater class, make the doSearch method perform an AJAX call to ../Common/search.php passing the search term as the "q" querystring parameter. 5. The returned HTML should automatically uplade the resultList element. The search.php script is the same from the previous exercise. You may have noticed that it returns a series of li elements. We will use these elements directly inside the resultList element.

Code Sample: PrototypeJS/Exercises/ajax-updater.html


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title> Ajax Updater </title> <script src="../../Libraries/prototype.js" type="text/javascript"></script> <script src="../../Libraries/DebugHelp.js" type="text/javascript"></script> <style type="text/css" media="screen">

#resultList { background-color:#ddd; width:500px; } #resultList li { background-color:#ddf; padding:4px; border:1px solid #33a; margin:4px; } </style> </head> <body> <script type="text/javascript"> //add the debud panel at the bottom of the page Event.observe(window, 'load', function () {insertDebugPanel();} ); </script> <script type="text/javascript"> var WEBUCATOR = { }; /* The file ..\Common\cnn.xml contains an old copy of the xml from the URL: http://rss.cnn.com/rss/cnn_topstories.rss If you wish, you can get a newer version of the xml and update that file. 1 - Create a method WEBUCATOR.doSearch(text) 2 - Call that method from the Go button's click 3 - Use the textbox's value as the search term (the argument to the method) 4 - Using the Ajax.pdater class, make the doSearch method perform an AJAX call to ../Common/search.php passing the search term as the "q" querystring parameter. 5 - The returned HTML should automatically uplade the resultList element */ WEBUCATOR.onload = function () { }; Event.observe(window, 'load', WEBUCATOR.onload ); </script> <form action="#"> <input type="text" name="searchTerm" value="" id="searchTerm"/> <input type="button" name="goButton" value="Go!" id="goButton" /> </form> <h2>Results:</h2> <ol id="resultList"> <li><strong>test:</strong><br/> test test test test test test test test test test

test test test test test test test test test test test test test test test test test test test test </li> <li><strong>test:</strong><br/> test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test </li> </ol> </body> </html>

Where is the solution? If your server logic returns JavaScript code along with HTML markup, the Ajax.Updater object can evaluate that JavaScript code. To get the object to treat the response as JavaScript, you simply add evalScripts: true; to the list of properties in the last argument of the object constructor. But there's a caveat. Those script blocks will not be added to the page's script. As the option name evalScripts suggests, the scripts will be evaluated. What's the difference, you may ask? Lets assume the requested URL returns something like this:
<script language="javascript" type="text/javascript"> function sayHi() { alert('Hi'); } </script> <input type="button" value="Click Me" onclick="sayHi();"/>

In case you've tried it before, you know it doesn't work. The reason is that the script block will be evaluated, and evaluating a script like the above will not create a function named sayHi. It will do nothing. To create this function we need to change our script to create the function. See below.
<script language="javascript" type="text/javascript"> sayHi = function () { alert('Hi'); }; </script> <input type="button" value="Click Me" onclick="sayHi();"/>

Note that in the previous example we did not use the var keyword to declare the variable. Doing so would have created a function object that would be local to the script block (at least in IE). Without the var keyword the function object is scoped to the window, which is our intent.

For more complete explanations, see the Ajax.Updater reference and the options reference.

What are all those "?" and squares?


So you went and wrote some quick test scripts to update your pages using the Ajax.Updater object and it all worked fine. Life was good until you ran your scripts against real data. All of a sudden the updated text was displayed with question marks or unprintable character symbols where the non-English characters should be. Your first suspect is Prototype, Of course, it seemed too easy to be true. But don't blame the library just yet. Ask yourself how much you really understand character encoding, code pages, and how the browser deals with it. If you have a positive answer then I bet you are on your way to fix the problem. If you are among the other 80% (another useless, imprecise author's estimate) of web developers that take character encoding for granted, keep reading. I won't pretend to be an authority on the topic, much less give you a complete explanation of how this is best handled. Instead you go straight to the solution that I use and provide hints on how this could be fixed in your own scenario. Simply put, the solution revolves around the following statement: Serve what the browser is expecting you to serve. If we are going to update the page with text that contains Unicode/UTF-8 characters then we better make the browser aware of that. Let's start with the simple case when you are just updating the page with text from a static HTML file that resides on your server. When you created that file, depending on which text editor you employed, it is very possible that the file was saved in ANSI (or better said, non-Unicode) format. This is the default for many text editors, especially source code editors, because the file size will be smaller and it's rather unusual to edit source code with Unicode characters in it. Suppose you have the following file named static-content.html on your server. You saved this file saved in ANSI format.
<div> Hi there, Jos. Yo no hablo espaol. </div>

Your main page updates itself using something like the snippet below.
<script type="text/javascript"> function updateWithFile() { var url = 'static-content.html'; var pars = ''; var myAjax = new Ajax.Updater( 'placeholder', url,

{method: 'get', parameters: pars}); } </script> <div id="placeholder">(this will be replaced)</div> <input id="btn" value="Test With Static File" onclick="updateWithFile();" type="button"/>

When you click the button the static file is retrieved but the non-English characters are replaced by question marks or some other symbol. The displayed text will look similar to "Hi there, Jos?. Yo no hablo espa?ol." or "Hi there, Jos?Yo no hablo espa?", depending on your browser. In this case, the solution is straightforward, simply save the static file in an appropriate format. Let's save it in UTF-8 and run the script again (any decent text editor will have an option in the Save As dialog.) You should now see the correct text (if not, your browser may have cached the old version, try using a different file name.) If the HTML that you are serving is not static, if it is being dynamically generated by some application framework (like ASP.NET, PHP, or even Perl,) make sure the code that generates this HTML is producing the text in the appropriate encoding and code page, and include in the HTTP response headers one header that informs this. Each platform has a different way to achieve this, but they are very similar. For example, in ASP.NET you can set this globally in your web.config file and the default configuration is good enough to avoid this problem in the first place. You should already have the following section in your web.config.
<globalization requestEncoding="utf-8" responseEncoding="utf-8" />

In classic ASP 3.0 you can fix this problem using the following code.
Response.CodePage = 65001 Response.CharSet = "utf-8"

In PHP the syntax to add the response header looks like this.
<?php header('Content-Type: text/html; charset=utf-8'); ?>

In any case, your ultimate goal is to have the following HTTP header sent with your response.
Content-Type: text/html; charset=utf-8

We used UTF-8 in our examples above, but if you need a different setting you can easily change.

Enumerating... Geez! Wow! Wahoo!

We are all familiar with for loops. You know, create yourself an array, populate it with elements of the same kind, create a loop control structure (for, foreach, while, repeat, etc,) access each element sequentially, by its numeric index, and do something with the element. When you come to think about it, almost every time you have an array in your code it means that you'll be using that array in a loop sooner or later. Wouldn't it be nice if the array objects had more functionality to deal with these iterations? Yes, it would, and many programming languages provide such functionality in their arrays or equivalent structures (like collections and lists.) Well, it turns out that Prototype gives us the Enumerable object, which implements a plethora of tricks for us to use when dealing with iterable data. The Prototype library goes one step further and extends the Array class with all the methods of Enumerable.

Loops, Ruby-style
In standard javascript, if you wanted to sequentially display the elements of an array, you could very well write something like this.
<script type="text/javascript"> function showList() { var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Maggie']; for (i=0; i<simpsons.length; i++) { alert(simpsons[i]); } } </script> <input type="button" value="Show List" onclick="showList();" />

With our new best friend, Prototype, we can rewrite this loop like this.
function showList() { var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Maggie']; simpsons.each( function (familyMember) { alert(familyMember); }); }

You are probably thinking "big freaking deal...just a weird syntax for the same old thing." Well, in the above example, yes, there's nothing too earth shattering going on. After all, there's not much to be changed in such a drop-dead-simple example. But keep reading, nonetheless. Before we move on. Do you see this function that is being passed as an argument to the each method? Let's start referring to it as an iterator function.

Your arrays on steroids

Like we mentioned above, it's very common for all the elements in your array to be of the same kind, with the same properties and methods. Let's see how we can take advantage of iterator functions with our new souped-up arrays. Here's how to find an element according to criteria.
<script type="text/javascript"> function findEmployeeById(emp_id) { var listBox = $('lstEmployees') var options = listBox.getElementsByTagName('option'); options = $A(options); var opt = options.find( function (employee) { return (employee.value === emp_id) ; }); alert(opt.innerHTML); //displays the employee name } </script> <select id="lstEmployees" size="10" > <option value="5">Buchanan, Steven</option> <option value="8">Callahan, Laura</option> <option value="1">Davolio, Nancy</option> </select> <input type="button" value="Find Laura" onclick="findEmployeeById(8);" />

Now let's kick it up another notch. See how we can filter out items in arrays, and then retrieve just a desired member from each element.
<script type="text/javascript"> function showLocalLinks(paragraph) { paragraph = $(paragraph); var links = $A(paragraph.getElementsByTagName('a')); //find links that do not start with 'http' var localLinks = links.findAll( function (link) { //we'll just assume for now that external links // do not have a '#' in their url return link.href.indexOf('#') >= 0; }); //now the link texts var texts = localLinks.pluck('innerHTML'); //get them in a single string var result = texts.inspect(); alert(result); } </script> <p id="someText"> This <a href="http://othersite.com/page.html">text</a> has a <a href="#localAnchor">lot</a> of <a href="#otherAnchor">links</a>. Some are

<a href="http://wherever.com/page.html">external</a> and some are <a href="#someAnchor">local</a> </p> <input type="button" value="Find Local Links" onclick="showLocalLinks('someText');"/>

It takes just a little bit of practice to get completely addicted to this syntax. Take a look at the Enumerable and Array references for all the available functions.

Handling Events
Events are a big part of most UI programming. On the web this part has been historically difficult because of the different ways browser makers chose to implement it. If you have written cross-browser event handling before, you are probably familiar with all the forks in you code to support Mozilla and IE. Prototype helps us by providing a uniform syntax for taking care of that. We mentioned unobtrusive JavaScript in a previous lesson and Prototype offers great support for that through its event subscription infrastructure.

Observing Events
The onXXXXXX properties of the DOM elements is a great source of contention for event handling code. Traditionally you would see scripts that, for example, run when the page loads by changing the body element's onload property.
window.onload = function(){alert('loaded');}; // or in the tag itsef: <body onload="alert('loaded');">

The obvious problem here is that if another script also needs to know about that load event, it's possible (and sadly often common) that this script would overwrite the original value of the onload property, causing the page to misbehave. The way Prototype addresses this is by using either the DOM standard for event subscription (when supported by the browser) or browser specific code (mostly IE) to add or remove listeners to these events. Prototype calls these listeners observers. Observers do not overwrite each other. They are just appended to a list of observers for each individual element event. The previous code would be written differently with Prototype.
Event.observe(window, 'load', function (evt) { alert('loaded'); } ); //to unsubscribe from the event later, the code is: Event.stopObserving(window, 'load');

To make it even easier, the event subscription mechanism is added to the DOM elements, simplifying the syntax even further.
$('myElement').observe('click', function (evt) { alert('myElement was clicked'); } );

DOM Loaded
To support unobtrusive JavaScript even more, Prototype extends the document object with an important event called dom:loaded. This event is fired when all the elements in the document are loaded in the DOM and ready to be programmed against. Maybe some images or CSS didn't finish loading yet, but the DOM is good to go. When the DOM is loaded is the right time to add the observers to the other elements.
document.observe('dom:loaded' function () { $('panelRight').observe('click', function (evt) { collapseRightPanel(); }); $('userNameField').observe('keypress', function (evt) { submitOnEnterKey(); }); });

Event Information
You may have noticed the evt parameter that is being passed for the observer functions in the previous examples. This parameter carries important information about the event itself. Things like what element was the source of the event, the mouse pointer coordinates and which mouse button was clicked. The complete list of properties and methods of this object can be found at the Event object reference.

Official Site
Before we start using the library reference below, it's important to note that Prototype is a project on the move and any reference material in print is bound to quickly become obsolete. To get the most up-to-date documentation of the library we should always consult Prototype's official site at http://www.prototypejs.org. If you like to always have the latest possible version of the library or if you're interested in tracking the library development as it happens, you can visit the project development site or even the current source code.

When you have questions and you need to ask for help, a good place to go is the mailing list. A lot of very knowledgeable Prototype developers hang out in this list and may be able to help you.

Reference for Prototype


Extensions to the JavaScript objects
One of the ways the Prototype library adds functionality is by extending the existing JavaScript objects. Extensions for the Object object Method Kind Arguments Description destination: any Provides a way to implement inheritance by extend(destination, static object, source: copying all properties and methods from source) any object source to destination. Returns a human-readable string representation of targetObj. It defaults to targetObj: any inspect(targetObj) static the return value of toString if the given object object does not define an inspect instance method. targetObj: any Returns an Array with the names of all the keys(targetObj) static object properties and methods of given object. targetObj: any Returns a Array with the values of all the values(targetObj) static object properties and methods of given object. targetObj: any clone(targetObj) static Returns a shallow copy of targetObj. object Extensions for the Number objects Method Kind Arguments Description Returns the hexadecimal representation of the number. Useful when converting toColorPart() instance (none) the RGB components of a color into its HTML/CSS representation. Returns the next number. This function succ() instance (none) is used in scenarios that involve iterations. iterator: a function Calls the iterator function repeatedly times(iterator) instance object conforming to passing the current index in the index Function(index) argument. The following sample will display alert message boxes from 0 to 9.

<script type="text/javascript"> function demoTimes() { var n = 10; n.times( function (index) { alert(index); }); /*************************** * you could have also used: * (10).times( .... ); ***************************/ } </script> <input type="button" value="Test Number.times()" onclick="demoTimes();"/>

Extensions for the Function objects Method Kind Arguments Description Returns an instance of the function object: the pre-bound to the bind(object [, arg1 [, arg2 object that function(=method) owner object. instance [...]]]) owns the The returned function will use the method same arguments as the original one (arg1, arg2, ... etc). Returns an instance of the function pre-bound to the object: the function(=method) owner object. bindAsEventListener(object object that The returned function will have the instance [, arg1 [, arg2 [...]]]) owns the current event object as its first method argument followed optionally any other arguments passed after the object argument. Let's see one of these extensions in action.
<input type="checkbox" id="myChk" value="1"/> Test? <script type="text/javascript"> //declaring the class var CheckboxWatcher = Class.create(); //defining the rest of the class implementation CheckboxWatcher.prototype = { initialize: function (chkBox, message) { this.chkBox = $(chkBox); this.message = message; //assigning our method to the event this.chkBox.onclick = this.showMessage.bindAsEventListener(this, ' from checkbox'); },

showMessage: function (evt, extraInfo) { alert(this.message + ' (' + evt.type + ')' + extraInfo); } }; var watcher = new CheckboxWatcher('myChk', 'Changed'); </script>

Method camelize()

capitalize() dasherize() escapeHTML() evalScripts() extractScripts()

gsub(pattern, replacement)

parseQuery() scan(pattern, replacement)

Extensions for the String objects Kind Arguments Description Converts a hyphen-delimited-string into a camelCaseString. This function instance (none) is useful when writing code that deals with style properties, for example. Converts the first character to upper instance (none) case. Replaces underscores '_' with dashes instance (none) '-'. Returns the string with any HTML instance (none) markup characters properly escaped Evaluates each <script /> block found instance (none) in the string. Returns an Array object containing instance (none) all the <script /> blocks found in the string. Returns a string that results from finding (or matching) the pattern string (or regular expression) in the current string and replacing it with the replacement string or the result of pattern: string or calling the replacement function regular expression passing an array with the strings that being searched. matched the pattern, including replacement: simple instance eventual regular expression string, template string, groupings. When the replacement is a or Function(strings[]) string, it can contain special to produce the templating tokens like #{n}, where n replacements. is the index of a grouping in the regular expession. #{0} will be replaced by the entire match, #{1} the first grouping, #{2} the second, and so on. instance (none) Same as toQueryParams(). pattern: string or Provides a way to iterate over instance regular expression matched patterns in the string and

Extensions for the String objects Method Kind Arguments Description being searched. operate on them. The pattern replacement: argument can be a string or a RegExp Function(strings[]) to but a RegExp is evidently more iterate over the useful. Similarly, the replacement matches. argument can be a string or a function but it probably only makes sense to pass in a function to be able to produce anything useful. Returns the string without any strip() instance (none) leading or trailing white spaces. Returns the string with any <script /> stripScripts() instance (none) blocks removed Returns the string with any HTML or stripTags() instance (none) XML tags removed pattern: string or regular expression being searched. replacement: string, or Very similar to gsub but only sub(pattern, Function(strings[]) to performs a limited number of replacement [, instance produce the replacements, specified by the count count]) replacements. count: parameter. number or replacements to perform - defaults to 1. Splits the string into an Array of its toArray() instance (none) characters. Splits a querystring into an toQueryParams() instance (none) associative Array indexed by parameter name (more like a hash). Used to produce a string of a known length: maximum maximum length. In case the string length of the resulting needs to be truncated to maintain the string. truncation: truncate(length [, maximum length, the text given in instance string used to replace truncation]) the truncation argument is used to the last characters of replace the last few characters. (e.g.: the resulting string var s='123456790'; defaults to '...' alert(s.truncate(5)); //displays '12...' ) Converts a CamelizedStringValue into a uderscore_formatted_string. underscore() instance (none) (e.g.: var s='Namespace::MyClass123'; alert(s.underscore()); //displays

Extensions for the String objects Method Kind Arguments Description 'namespace/my_class123' ). This function seems to be directly target at supporting Ruby on Rails functionality. unescapeHTML() instance (none) The reverse of escapeHTML()

Extensions for the Array objects


To start off, Array extends Enumerable, so all the handy methods defined in the Enumerable object are available. Besides that, the methods listed below are also implemented. Extensions for the Array objects Kind Arguments Description instance (none) Empties the array and returns itself. Returns the array without the elements that are null or undefined. instance (none) This method does not change the array itself Returns the first element of the instance (none) array. Returns a flat, one-dimensional version of the array. This flattening happens by finding each of the instance (none) array's elements that are also arrays and including their elements in the returned array, recursively. Returns the zero-based position of value: what you the given value if it is found in the instance are looking for. array. Returns -1 if value is not found. Overridden to return a nicely instance (none) formatted string representation of the array with its elements. Returns the last element of the instance (none) array. Returns the array in reverse applyToSelf: sequence. If no argument is given indicates if the instance or if the argument is true the array array itself should itself will be changed. Otherwise it also be reversed. will remain unchanged.

Method clear() compact()

first()

flatten()

indexOf(value)

inspect() last()

reverse([applyToSelf])

Extensions for the Array objects Method Kind Arguments Description Returns the first element and shift() instance (none) removes it from the array, reducing the array's length by 1. value1 ... valueN: Returns the array excluding the without(value1 [, value2 values to be elements that are included in the instance [, .. valueN]]) excluded if present list of arguments. This method does in the array. not change the array itself. Let's see some of these methods in action.
<script type="text/javascript"> var A = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; alert(A.inspect()); // "['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']" var B = A.without('e','f'); alert(B.inspect()); // "['a', 'b', 'c', 'd', 'g', 'h']" alert(A.inspect()); // did not change A: "['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']" A.push(null); A.push('x'); A.push(null); A.push('y'); alert(A.inspect()); // "['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', null, 'x', null, 'y']" A = A.compact(); alert(A.inspect()); // "['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'x', 'y']" var e = A.shift(); alert(e); // "a" alert(A.inspect()); // "['b', 'c', 'd', 'e', 'f', 'g', 'h', 'x', 'y']" alert(A.indexOf('c')); // 1 alert(A.first()); // 'b' alert(A.last()); // 'y' A.clear(); alert(A.inspect()); // "[]" A = ['a', 'b', 'c']; B = A.reverse(false); alert(B.inspect()); // "['c', 'b', 'a']" alert(A.inspect()); // A left untouched: "['a', 'b', 'c']" A.reverse(true); alert(A.inspect()); // "['c', 'b', 'a']" A = ['a', 'b', ['c1','c2','c3'] , 'd', ['e1','e2'] ]; B = A.flatten(); alert(B.inspect()); // "['a','b','c1','c2','c3','d','e1','e2']" alert(A.inspect()); // unchanged: "['a','b',['c1','c2','c3'],'d',['e1','e2']]" </script>

Extensions for the document DOM object Method Kind Arguments Description getElementsByClassName(className instance className: name of Returns all the

Extensions for the document DOM object Method Kind Arguments [, parentElement]) a CSS class associated with the elements, parentElement: object or id of the element that contains the elements being retrieved.

Description elements that are associated with the given CSS class name. If no parentElement id given, the entire document body will be searched.

Extensions for the Event object Property Type Description KEY_BACKSPACE Number 8: Constant. Code for the Backspace key. KEY_TAB Number 9: Constant. Code for the Tab key. KEY_RETURN Number 13: Constant. Code for the Return key. KEY_ESC Number 27: Constant. Code for the Esc key. KEY_LEFT Number 37: Constant. Code for the Left arrow key. KEY_UP Number 38: Constant. Code for the Up arrow key. KEY_RIGHT Number 39: Constant. Code for the Right arrow key. KEY_DOWN Number 40: Constant. Code for the Down arrow key. KEY_DELETE Number 46: Constant. Code for the Delete key. List of cached observers. Part of the internal observers: Array implementation details of the object. Method element(event) Kind Arguments event: an Event static object Description Returns element that originated the event. Traverses the DOM tree upwards, searching for the first element with the given tag name, starting from the element that originated the event. Returns true if the left mouse button was clicked. Returns true if the middle mouse button was clicked. Returns true if the right mouse button was clicked. Returns the x coordinate of the mouse pointer on the

event: an Event object, tagName: findElement(event, tagName) static name of the desired tag. isLeftClick(event) isMiddleClick(event) isRightClick(event) pointerX(event) event: an Event object event: an Event static object event: an Event static object event: an Event static object static

Method

Kind

Arguments event: an Event object event: an Event object

Description page. Returns the y coordinate of the mouse pointer on the page. Use this function to abort the default behavior of an event and to suspend its propagation.

pointerY(event)

static

stop(event)

static

element: object or id, name: event name observe(element, name, (like 'click', 'load', Adds an event handler static observer) etc), observer: function to an event. Function(evt) to handle the event. Removes an event handler element: object or id, from the event. If the name: event name observer isn't passed, it will stopObserving(element [, name (like 'click'), unregister all observers for static [, observer]]) observer: function the given event name. If the that is handling the event name isn't passed it event. will unregister all observers for the element. _observeAndCache(element, Private method, do not static name, observer, useCapture) worry about it. Private method, do not worry about it. Clears all unloadCache() static (none) cached observers from memory. Let's see how to use this object to add an event handler to the load event of the window object.
<script type="text/javascript"> Event.observe(window, 'load', page_loaded, false); function page_loaded(evt) { Event.observe('parent_node', 'click', item_clicked, false); } function item_clicked(evt) { var child = Event.element(evt); alert('The child node with id=' + child.id + ' was clicked'); Event.stop(evt); //avoid another call related to 'parent_node' itself } </script> ...

<div id="parent_node"> <div id="child1">First</div> <div id="child2">Second</div> <div id="child3">Third</div> </div>

New objects and classes defined by Prototype


Another way the library helps you is by providing many objects that implement both support for object oriented designs and common functionality in general.

The PeriodicalExecuter object


This object provides the logic for calling a given function repeatedly, at a given interval, using a timer. Arguments callback: a function that will be passed the PeriodcalExecuter [ctor](callback, constructor object itself as the only interval) argument, interval: number of seconds registerCallback() instance (none) stop() instance (none) Method Kind Description Creates one instance of this object that will call the function repeatedly. Resets the timer. Cancels the timer, avoiding the execution of the callback. This method is what will be called by the timer. It, in turn, will invoke the callback method passing the object itself.

onTimerEvent()

instance

(none)

Description The function to be called. objExecuter: the callback Function(objExecuter) PeriodcalExecuter making the call. A handle to the underlying timer object timer Timer responsible for repeatedly invoking the callback method frequency Number This is actually the interval in seconds currentlyExecuting Boolean Indicates if the callback is underway.

Property

Type

The Prototype object

The Prototype object does not have any important role, other than declaring the version of the library being used. Property Type Version String emptyFunction Function() Description The version of the library An empty function object A function object that just echoes back the given K Function(obj) parameter. ScriptFragment String A regular expression to identify scripts

The Enumerable object


The Enumerable object allows one to write more elegant code to iterate items in a listlike structure. Many other objects extend the Enumerable object to leverage its useful interface. Method Description Calls the given iterator function iterator: a function passing each element in the list instance object conforming to in the first argument and the Function(value, index) index of the element in the second argument This function is a way to test the entire collection of values using a given function. all will return true only if the iterator function iterator: a function returns a value that resolves to object conforming to true for all the elements. It will instance Function(value, index), return false otherwise. If no optional. iterator is given, then the test will be if the element itself resolves to true. You can simply read it as "check if all elements pass the test." This function is a way to test the entire collection of values using a given function. any will return iterator: a function true if the iterator function object conforming to instance returns a value that resolves to Function(value, index), true for at least one of the optional. elements. It will return false otherwise. If no iterator is given, then the test will be if the Kind Arguments

each(iterator)

all([iterator])

any([iterator])

Description element itself resolves to true. You can simply read it as "check if any element passes the test." Calls the iterator function for each element in the collection iterator: a function and returns each result in an collect(iterator) instance object conforming to Array, one result element for Function(value, index) each element in the collection, in the same sequence. Calls the iterator function for each element in the collection iterator: a function and returns the first element that detect(iterator) instance object conforming to caused the iterator function to Function(value, index) return true (or, more precisely, not-false.) If no element returns true, then detect returns null. entries() instance (none) Same as toArray(). iterator: a function find(iterator) instance object conforming to Same as detect(). Function(value, index) Calls the iterator function for each element in the collection iterator: a function and returns an Array with all the findAll(iterator) instance object conforming to elements that caused the iterator Function(value, index) function to return a value that resolves to true. This function is the opposite of reject(). Tests the string value of each element in the collection against pattern: a RegExp the pattern regular expression . object used to match The function will return an Array grep(pattern [, the elements, iterator: a containing all the elements that instance iterator]) function object matched the regular expression. conforming to If the iterator function is given, Function(value, index) then the Array will contain the result of calling the iterator with each element that was a match. Tries to find the given object in include(obj) instance obj: any object the collection. Returns true if the object is found, false otherwise. number: number of Returns the collection broken in inGroupsOf(number, instance items per group, groups containing as many items fillWith) fillWith: value used to as specified by the first

Method

Kind

Arguments

Method

Kind

Arguments fill empty spots

inject(initialValue, iterator)

initialValue: any object to be used as the initial value, iterator: a instance function object conforming to Function(accumulator, value, index)

methodName: name of the method that will be invoke(methodName called in each element, instance [, arg1 [, arg2 [...]]]) arg1..argN: arguments that will be passed in the method invocation. iterator: a function map(iterator) instance object conforming to Same as collect(). Function(value, index) Returns the element with the iterator: a function greatest value in the collection or max([iterator]) instance object conforming to the greatest result of calling the Function(value, index) iterator for each element in the collection, if an iterator is given. member(obj) instance obj: any object Same as include(). Returns the element with the iterator: a function lowest value in the collection or min([iterator]) instance object conforming to the lowest result of calling the Function(value, index) iterator for each element in the collection, if an iterator is given. iterator: a function Returns an Array containing two partition([iterator]) instance object conforming to other arrays. The first array will Function(value, index) contain all the elements that

Description argument. If the quantity of items in the initial collection is not divisible by the number in the first argument, the resulting empty items at the end of the last group will be filled with null or with the value of the second argument, if provided. Quick example: ['a','b','c','d'].inGroupsOf(3,'?') creates [ ['a','b','c'] , ['d','?','?'] ] Combines all the elements of the collection using the iterator function. The iterator is called passing the result of the previous iteration in the accumulator argument. The first iteration gets initialValue in the accumulator argument. The last result is the final return value. Calls the method specified by methodName in each element of the collection, passing any given arguments (arg1 to argN), and returns the results in an Array object.

Method

Kind

Arguments

Description caused the iterator function to return true and the second array will contain the remaining elements. If the iterator is not given, then the first array will contain the elements that resolve to true and the other array will contain the remaining elements.

propertyName name of Retrieves the value to the the property that will be property specified by read from each pluck(propertyName) instance propertyName in each element of element. This can also the collection and returns the contain the index of the results in an Array object. element Calls the iterator function for each element in the collection iterator: a function and returns an Array with all the reject(iterator) instance object conforming to elements that caused the iterator Function(value, index) function to return a value that resolves to false. This function is the opposite of findAll(). iterator: a function select(iterator) instance object conforming to Same as findAll(). Function(value, index) iterator: a function Returns an Array with all the sortBy(iterator) instance object conforming to elements sorted according to the Function(value, index) result the iterator function call. Returns an Array with all the toArray() instance (none) elements of the collection. Merges each given collection with the current collection. The merge operation returns a new array with the same number of collection1 .. elements as the current collection collectionN: and each element is an array zip(collection1[, enumerations that will (let's call them sub-arrays) of the collection2 [, ... instance be merged, transform: a elements with the same index collectionN function object from each of the merged [,transform]]]) conforming to collections. If the transform Function(value, index) function is given, then each subarray will be transformed by this function before being returned. Quick example: [1,2,3].zip([4,5,6],

Method

Kind

Arguments

Description [7,8,9]).inspect() returns "[ [1,4,7],[2,5,8],[3,6,9] ]"

The Hash object


The Hash object implements a hash structure, i.e. a collection of Key:Value pairs. Each item in a Hash object is an array with two elements: first the key then the value. Each item also has two properties: key and value, which are pretty self-explanatory. Method keys() values() Kind Arguments instance (none) instance (none) otherHash: merge(otherHash) instance Hash object toQueryString() instance (none) Description Returns an Array with the keys of all items. Returns an Array with the values of all items. Combines the hash with the other hash passed in and returns the new resulting hash. Returns all the items of the hash in a string formatted like a query string, e.g. 'key1=v1&key2=v2&key3=v3' Overridden to return a nicely formatted string representation of the hash with its key:value pairs.

inspect()

instance (none)

The ObjectRange class


Inherits from Enumerable Represents a range of values, with upper and lower bounds. Property Type Kind Description start (any) instance The lower bound of the range end (any) instance The upper bound of the range Determines if the boundaries themselves are part of the exclusive Boolean instance range. Description Creates one range object, start: the lower bound, spanning from start to end. It [ctor](start, end, end: the upper bound, is important to note that start constructor exclusive) exclusive: include the and end have to be objects of bounds in the range? the same type and they must have a succ() method. include(searchedValue) instance searchedValue: value Checks if the given value is Method Kind Arguments

Method

Kind

Arguments Description that we are looking for part of the range. Returns true or false.

The Class object


The Class object is used when declaring the other classes in the library. Using this object when declaring a class causes the to new class to support an initialize() method, which serves as the constructor. See the sample below.
//declaring the class var MySampleClass = Class.create(); //defining the rest of the class implementation MySampleClass.prototype = { initialize: function (message) { this.message = message; }, showMessage: function (ajaxResponse) { alert(this.message); } }; //now, let's instantiate and use one object var myTalker = new MySampleClass('hi there.'); myTalker.showMessage(); //displays alert

Method Kind Arguments Description create(*) instance (any) Defines a constructor for a new class

The Ajax object


This object serves as the root and namespace for many other classes that provide AJAX functionality. Property Type Kind Description activeRequestCount Number instance The number of AJAX requests in progress. Method Kind Arguments Description getTransport() instance (none) Returns a new XMLHttpRequest object

The Ajax.Responders object


Inherits from Enumerable

This object maintains a list of objects that will be called when Ajax-related events occur. You can use this object, for example, if you want to hook up a global exception handler for AJAX operations. Property Type Kind Description responders Array instance The list of objects registered for AJAX events notifications. Description The object passed in the responderToAdd argument should contain methods named like the AJAX events (e.g. onCreate, onComplete, responderToAdd: onException, etc.) register(responderToAdd) instance object with methods When the that will be called. corresponding event occurs all the registered objects that contain a method with the appropriate name will have that method called. The object passed in the responderToRemove: responderToRemove unregister(responderToRemove) instance object to be removed argument will be from the list. removed from the list of registered objects. Runs through the list of registered objects callback: name of the looking for the ones AJAX event being that have the method reported, request: the determined in the Ajax.Request object callback argument. responsible for the Then each of these dispatch(callback, request, event, transport: the methods is called instance transport, json) XMLHttpRequest passing the other 3 object that carried (or arguments. If the is carrying) the AJAX AJAX response call, json: the X-JSON contains a X-JSON header of the response HTTP header with (if present) some JSON content, then it will be evaluated and passed Method Kind Arguments

Method

Kind

Arguments

Description in the json argument. If the event is onException, the transport argument will have the exception instead and json will not be passed.

The Ajax.Base class


This class is used as the base class for most of the other classes defined in the Ajax object. Arguments options: AJAX setOptions(options) instance options responseIsSuccess() instance (none) responseIsFailure() instance (none) Method Kind Description Sets the desired options for the AJAX operation Returns true if the AJAX operation succeeded, false otherwise The opposite of responseIsSuccess().

The Ajax.Request class


Inherits from Ajax.Base Encapsulates AJAX operations Property Description List of possible events/statuses reported during an Events Array static AJAX operation. The list contains: 'Uninitialized', 'Loading', 'Loaded', 'Interactive', and 'Complete.' The XMLHttpRequest object that carries the AJAX transport XMLHttpRequest instance operation url String instance The URL targeted by the request. Method Description Creates one instance of this object that will call the url: the url to given url using the given be fetched, options. The onCreate event constructor options: will be raised during the AJAX constructor call. Important: options It is worth noting that the chosen url is subject to the Kind Arguments Type Kind

[ctor](url, options)

Method

Kind

evalJSON()

instance

evalResponse()

instance

header(name)

instance

onStateChange()

instance

request(url)

instance

respondToReadyState(readyState) instance

setRequestHeaders()

instance

Description browser's security settings. In many cases the browser will not fetch the url if it is not from the same host (domain) as the current page. You should ideally use only local urls to avoid having to configure or restrict the user's browser. (Thanks Clay). This method is typically not called externally. It is called internally to evaluate the (none) content of an eventual XJSON HTTP header present in the AJAX response. This method is typically not called externally. If the AJAX response has a Content-type header of (none) text/javascript then the response body will be evaluated and this method will be used. Retrieves the contents of any HTTP header of the name: HTTP AJAX response. Call this header name only after the AJAX call is completed. This method is typically not called externally. It is called (none) by the object itself when the AJAX call status changes. This method is typically not url: url for called externally. It is the AJAX already called during the call constructor call. This method is typically not readyState: called externally. It is called state number by the object itself when the (1 to 4) AJAX call status changes. This method is typically not (none) called externally. It is called

Arguments

Method

Kind

Arguments

Description by the object itself to assemble the HTTP header that will be sent during the HTTP request.

The options argument object


An important part of the AJAX operations is the options argument. There's no options class per se. Any object can be passed, as long as it has the expected properties. It is common to create anonymous objects just for the AJAX calls. Property asynchronous Boolean Type Default true Description Indicates if the AJAX call will be made asynchronously

contentType

String

encoding

String

encoding

Boolean

encoding

Boolean

method

String

'application/ x-wwwSets the Content-Type HTTP formheader of the Ajax request. urlencoded' The character encoding used in the body of a request (especially POST requests.) 'UTF-8' UTF-8 should be enough in most cases, but if you know what you're doing, you can use a different encoding. Apply eval() to the responseText if the request comes from the same origin true as the page. Use false to never eval or "force" to always eval regardless of origin. If the response is of content type application/json, applies eval() to the responseText and put the result in responseJSON. Only happens true if the request comes from the same origin as the page. Use false to never eval or "force" to always eval JSON regardless of origin. 'post' Method of the HTTP request

Property

Type

Default

parameters

String or Object

''

postBody

String

undefined

requestHeaders Array

undefined

onXXXXXXXX

Function( XMLHttpRequest, undefined Object)

Description The url-formatted list of values passed to the request (for example 'employee=john&month=11') or a hash-like object that represents the parameters (for example {employee:'john', month:11}.) Content passed to in the request's body in case of a HTTP POST List of HTTP headers to be passed with the request. This list must have an even number of items, any odd item is the name of a custom header, and the following even item is the string value of that header. Example:['my-header1', 'this is the value', 'my-otherheader', 'another value'] Custom function to be called when the respective event/status is reached during the AJAX call. There are several alternatives for the "XXXXXXXX" in this option, among the alternatives are the statuses in Ajax.Request.Events, and the HTTP status codes. Example var myOpts = {on403: notAllowed, onComplete: showResponse, onLoaded: registerLoaded};. The function used will receive one argument, containing the XMLHttpRequest object that is carrying the AJAX operation and another argument containing the evaluated X-JSON response HTTP header.

Property

Type

Default

onSuccess

Function( XMLHttpRequest, undefined Object)

onFailure

Function( XMLHttpRequest, undefined Object)

onException

Function(Ajax.Request, exception)

undefined

insertion

String

undefined

Description Custom function to be called when the AJAX call completes successfully. The function used will receive one argument, containing the XMLHttpRequest object that is carrying the AJAX operation and another argument containing the evaluated X-JSON response HTTP header. Custom function to be called when the AJAX call completes with error. The function used will receive one argument, containing the XMLHttpRequest object that is carrying the AJAX operation and another argument containing the evaluated X-JSON response HTTP header. Custom function to be called when an exceptional condition happens on the client side of the AJAX call, like an invalid response or invalid arguments. The function used will receive two arguments, containing the Ajax.Request object that wraps the AJAX operation and the exception object. A string that will determine how the new content will be inserted. Applies only to Ajax.Updater objects and if nothing is specified, the new content will completely replace the existing content. If a value s given, it will determine where the content will be inserted in relation to the container element. It can

Property

Type

evalScripts

Boolean

decay

Number

frequency

Number

Description be 'before', 'top', 'bottom', or 'after'. Determines if script blocks undefined, will be evaluated when the false response arrives. Applies only to Ajax.Updater objects. Determines the progressive slowdown in a Ajax.PeriodicalUpdater object refresh rate when the received response is the same as the last one. For example, if you use 2, after one of the undefined, refreshes produces the same 1 result as the previous one, the object will wait twice as much time for the next refresh. If it repeats again, the object will wait four times as much, and so on. Leave it undefined or use 1 to avoid the slowdown. Interval (not frequency) between refreshes, in undefined, seconds. Applies only to 2 Ajax.PeriodicalUpdater objects.

Default

The Ajax.Updater class


Inherits from Ajax.Request Used when the requested url returns HTML that you want to inject directly in a specific element of your page. You can also use this object when the url returns <script type="text/javascript"> blocks that will be evaluated upon arrival. Use the evalScripts option to work with scripts. Description This object contains two properties: containers.success will be containers Object instance used when the AJAX call succeeds, and containers.failure will be used otherwise. Method Kind Arguments Description [ctor](container, constructor container:this can be the id of Creates one instance of this Property Type Kind

Method url, options)

Kind

updateContent() instance

Arguments Description an element, the element object that will call the given object itself, or an object with url using the given options. two properties object.success element (or id) that will be used when the AJAX call succeeds, and object.failure element (or id) that will be used otherwise. url: the url to be fetched, options: AJAX options This method is typically not called externally. It is called by the object itself when the response is received. It will update the appropriate element with the HTML or (none) call the function passed in the insertion option. The function will be called with two arguments, the element to be updated and the response text.

The Ajax.PeriodicalUpdater class


Inherits from Ajax.Base This class repeatedly instantiates and uses an Ajax.Updater object to refresh an element on the page, or to perform any of the other tasks the Ajax.Updater can perform. Check the Ajax.Updater reference for more information. Description This value will be passed straight to the Ajax.Updater's container Object instance constructor. This value will be passed straight to the Ajax.Updater's url String instance constructor. Interval (not frequency) between refreshes, in seconds. Defaults to 2 seconds. This number will be multiplied frequency Number instance by the current decay when invoking theAjax.Updater object Keeps the current decay level applied when redecay Number instance executing the task updater Ajax.Updater instance The most recently used Ajax.Updater object Property Type Kind

Property timer

Type Object

Description The handle to the timer being used to notify the object instance when it is time for the next refresh. Kind Description

Kind

Method

[ctor](container, url, options)

Arguments container:this can be the id of an element, the element object itself, or an object with two properties object.success element (or id) constructor that will be used when the AJAX call succeeds, and object.failure element (or id) that will be used otherwise. url: the url to be fetched, options: AJAX options

Creates one instance of this object that will call the given url using the given options.

start()

instance

(none)

stop()

instance

(none)

updateComplete() instance

(none)

onTimerEvent()

instance

(none)

This method is typically not called externally. It is called by the object itself to start performing its periodical tasks. Causes the object to stop performing its periodical tasks. After stopping, the object will call the callback given in the onComplete option (if any.) This method is typically not called externally. It is called by the currently used Ajax.Updater after it completes the request. It is used to schedule the next refresh. This method is typically not called externally. It is called internally when it is time for the next update.

The Element object

This object provides some utility functions for manipulating elements in the DOM.

The Element.Methods object


This object provides some utility functions for manipulating elements in the DOM. You typically do not call these methods directly from the Element.Methods object, but rather from the Element object, which gets copies of all of them. All the methods defined here are automatically added to any element accessed using the $() function. When they are added to the elements, the first argument (always element) is removed because it's always the current element itself. So, in short, writing Element.show('myDiv'); is the same as writing $('myDiv').show(); Description Changes the position CSS element: element style to absolute but takes absolutize(element) instance object or id. care of not altering its current size and location on the page. element: element object or id, addClassName(element, Adds the given class name to instance className: className) the element's class names. name of a CSS class element: element object or id, Returns all the element's adjacent(element, [expression1 instance expressionX: siblings that match one of the [, expression2 [...]]]) CSS selection given CSS rules. rules Returns an Array with all the ancestors (parent nodes) of element: element the element. The array will ancestors(element) instance object or id start at the first direct parent element and go all the way to the html element. element: element object or id, ancestor: childOf(element, ancestor) instance Same as descendantOf. ancestor candidate element or id Returns an element: element Element.ClassNames object classNames(element) instance object or id representing the CSS class names associated with the Method Kind Arguments

Method

Kind

Arguments element: element object or id

cleanWhitespace(element)

instance

element: element object or id, source: element clonePosition(element, source [, to be cloned instance options]) options: object with settings for fine-tuning the clone operation.

cumulativeOffset(element)

instance element: object

cumulativeScrollOffset(element) instance element: object

Description given element. Removes any white space text node children of the element Copies the size and position of the source element into the current element. The options argument can be used to adjust this process. The available options are: setLeft, , setTop, setWidth, setHeight, which are booleans (true/false,) and offsetTop, offsetLeft, which are number of pixels. Returns an Array Error. This text should not be shown. Please email courseware@webucator.com to report it: Array with the total offsets of the element, including any of the element's ancestors offsets. The resulting array is similar to [total_left_offset, total_top_offset] Error. This text should not be shown. Please email courseware@webucator.com to report it: [total_left_offset, total_top_offset] Returns an Array Error. This text should not be shown. Please email courseware@webucator.com to report it: Array with the total offsets of the element, including any of the element's ancestors offsets. se this when the element is inside parent elements that scroll. The resulting array is similar to [total_left_offset, total_top_offset] Error. This

Method

Kind

Arguments

Description text should not be shown. Please email courseware@webucator.com to report it: [total_left_offset, total_top_offset] Returns a Boolean value indicating if the element is a descendant (child, grandchild, etc) of the given ancestor. Returns an Array with all the descendants (child nodes children, grandchildren, etc) of the element. Checks which of the element's descendants match the given rule and returns the first one or the Nth one given by the zero-based index argument. Lower index values mean closer to the element. Returns a Boolean value indicating if the element tag is empty (or has only whitespaces) Returns the dimensions of the element. The returned value is an object with two properties: height and width. Returns the offsetHeight of the element

element: element object or id, ancestor: descendantOf(element, ancestor) instance ancestor candidate element or id descendants(element) instance element: element object or id

down(element, expression, index)

element: element object or id, expression: a CSS selection instance rule, index: index of the returned descendant instance element: element object or id

empty(element)

getDimensions(element)

instance

element: element object or id

getHeight(element)

getStyle(element, cssProperty)

getWidth(element)

element: element object or id element: element object or id, cssProperty name of a CSS instance property (either format 'propname' or 'propName' works). instance element: element instance

Returns the value of the CSS property in the given element or null if not present.

Returns the offsetWidth of

Method

hasClassName(element, className)

hide(element) identify(element)

Arguments object or id element: element object or id, instance className: name of a CSS class element: element instance object or id instance element: element object or id

Kind

Description the element Returns true if the element has the given class name as one of its class names. Hides the element by setting its style.display to 'none'. Returns the element's id. If one does not exist, a new, unique one will be created. Returns a simplified string representation of the element's tag. It's useful for debugging and only contains the element's id and css class name, like <p id="result" class="hilite numeric"> Inserts new content before, after, at the top or at the bottom if the element. For example $(el).insert({top: '<li>new item</li>'}). It defaults to insertion at the bottom if the second argument is the content itself.

inspect(element)

instance

element: element object or id

insert(element, insertion)

element: element object or id instance insertion: object describing what to insert

element: element object or id element: element Changes the element's makePositioned(element) instance object or id style.position to 'relative' Verifies if the element is element: element matched by the given CSS object or id, selector rule. For example, a selector: string paragraph element with an id match(element, selector) instance or Selector of 'message' would be object of a CSS matched by the selector rule 'p#message'. This method returns true or false. element: element Checks which of the object or id, element's posterior sibilings next(element, expression, index) instance expression: a match the given rule and CSS selection returns the first one or the rule, index: Nth one given by the zeromakeClipping(element) instance

Method

nextSiblings(element)

observe(element, name, observer, useCapture)

previous(element, expression, index)

previousSiblings(element)

remove(element)

removeClassName(element, className)

Arguments Description index of the based index argument. Lower returned sibilng index values mean closer to the element. Returns an Array with all the siblings that come after the element: element element. The list is built by instance object or id recursively finding all the other elements via the nextSibling property. element: object or id, name: event name (like 'click', 'load', etc), observer: Function(evt) to handle the event, instance Shortcut for Event.observe. useCapture: if true, handles the event in the capture phase and if false in the bubbling phase. Checks which of the element: element element's previous sibilings object or id, match the given rule and expression: a returns the first one or the instance CSS selection Nth one given by the zerorule, index: based index argument. Lower index of the index values mean closer to returned sibilng the element. Returns an Array with all the siblings that come before the element: element element. The list is built by instance object or id recursively finding all the other elements via the previousSibling property. element: element Removes the element from instance object or id the document. element: element object or id, Removes the given class instance className: name from the element's name of a CSS class names. class

Kind

Method

replace(element, html)

scrollTo(element)

setStyle(element, cssPropertyHash)

select(element, expression1 [, expression2 [...]]) show(element) siblings(element)

stopObserving(element, name, observer, useCapture)

toggle(element) toggleClassName(element, className)

Description Replaces the entire html of the element with the given element: element html argument. If the given object or id, html contains <script instance html: html type="text/javascript"> content blocks they will not be included but they will be evaluated. element: element Scrolls the window to the instance object or id element position. element: element Sets the value of the CSS object or id, properties in the given cssPropertyHash instance element, according to the Hash object with values in the the styles to be cssPropertyHash argument. applied. element: element Finds all the child elements object or id. instance that match one of the given expressionN: CSS rules. CSS selectors element: element Shows the element by instance object or id resetting its style.display to ''. Returns an Array with all the element: element instance siblings that come before and object or id after the element. element: object or id, name: event name (like 'click', 'load', etc), observer: Function(evt) to handle the event, Shortcut for instance useCapture: if Event.stopObserving. true, handles the event in the capture phase and if false in the bubbling phase. element: element Toggles the visibility of the instance object or id element. element: element Toggles the presence of the instance object or id, given CSS class name in the

Kind

Arguments

Method

undoClipping(element) undoPositioned(element)

up(element, expression, index)

Arguments className: CSS class element: element instance object or id element: element instance object or id element: element object or id, expression: a CSS selection instance rule, index: index of the returned ancestor

Kind

Description element.

update(element, html)

visible(element)

Clears the element's style.position to '' Checks which of the element's ancestors match the given rule and returns the first one or the Nth one given by the zero-based index argument. Lower index values mean closer to the element. Replaces the inner html of the element with the given element: element html argument. If the given object or id, html contains <script instance html: html type="text/javascript"> content blocks they will not be included but they will be evaluated. Returns a Boolean value element: element instance indicating if the element is object or id visible.

You can add your own methods to the Element.Methods object and they will also be copied to the elements automatically. You only need to ensure that your methods also take the element as the first argument and (if possible) returns the element object. Here's an example of how to do that. I'll add three new utility methods to the elements.
//first a helper method var $CE = function (tagName, attributes, styles) { //short for create element var el = document.createElement(tagName); if (attributes) $H(attributes).each( function (pair) { eval("el." + pair.key + "='" + pair.value + "'"); }); if (styles) $H(styles).each( function (pair) { el.style[pair.key] = pair.value; }); return $(el); };

//adding he new methods Element.addMethods({ //removes any child noes from the element //example: <div id="myDiv"><b>hello</b></div> // $('myDiv').clearChildren(); // ==> <div id="myDiv"></div> clearChildren: function (element) { element = $(element); $A(element.childNodes).each( function (e) { e.parentNode.removeChild(e); }); return element; }, //method that creates a new element and appends to the current element // example: <div id="myDiv">Please</div> // $('myDiv').append('A',{href:'otherpage.html', className:'red'}).update('Continue...'); // ==> <div id="myDiv">Please<a href="otherpage.html" class="red">Continue...</a></div> append: function (element, tagName, attributes, styles) { element = $(element); var newEl = $CE(tagName, attributes, styles); element.appendChild(newEl); return newEl;//<-- this one returns the new element }, //appends a text node to the element // example: <div id="myDiv"><b>hello</b></div> // $('myDiv').appendText(', John'); // ==> <div id="myDiv"><b>hello</b>, John</div> appendText: function (element, text){ element = $(element); var t = document.createTextNode(text); element.appendChild(t); return element; } });

The Element.ClassNames class


Inherits from Enumerable Represents the collection of CSS class names associated with an element. Method [ctor](element) Arguments element: any constructor DOM element object or id instance Kind Description Creates an Element.ClassNames object representing the CSS class names of the given element. Includes the given CSS class name in className: a the list of class names associated with CSS class name the element. className: a Removes the given CSS class name

add(className)

remove(className) instance

Method

Kind

set(className)

instance

Arguments Description CSS class name from the list of class names associated with the element. Associates the element with the given className: a CSS class name, removing any other CSS class name class names from the element.

The Abstract object


This object serves as the root for other classes in the library. It does not have any properties or methods. The classes defined in this object are also treated as traditional abstract classes.

The Field object


This object provides some utility functions for working with input fields in forms. It's simply a shorthand to Form.Element.

The Form object


This object provides some utility functions for working with data entry forms and their input fields. Method serialize(form) Arguments Description form: form Returns a url-formatted list of field instance element object names and their values, like or id 'field1=v1&field2=v2&field3=v3' form: form Returns the first enabled field element instance element object in the form. or id form: form Returns an Array containing all the instance element object input fields in the form. or id form: form element object or id, Returns an Array containing all the typeName: the <input> elements in the form. instance type of the Optionally, the list can be filtered by input element, the type or name attributes of the name: the name elements. of the input element. instance form: form Disables all the input fields in the Kind

findFirstElement(form)

getElements(form)

getInputs(form [, typeName [, name]])

disable(form)

Arguments element object or id form: form enable(form) instance element object or id form: form focusFirstElement(form) instance element object or id form: form reset(form) instance element object or id

Method

Kind

Description form. Enables all the input fields in the form. Activates the first visible, enabled input field in the form. Resets the form. The same as calling the reset() method of the form object.

The Form.Element object


This object provides some utility functions for working with form elements. These functions have native DOM equivalents, but those don't return the element as the result of the function call, preventing the chaining of method calls. The methods below are not the only methods in this object at runtime. All the methods from Form.Element.Methods will also be added to this object. Arguments Description element: element object or id of a Moves the input focus to the focus(element) instance form field field. element: element object or id of a Selects the value entered in select(element) instance form field the element. Method Kind

The Form.Element.Methods object


This object provides some utility functions for working with form field elements, that will be copied over to any field element accessed via the functions $() and $$(). Similarly to the Element.Methods methods, when they are copied, the first argument is dropped and becomes the element itself. These methods are also copied to the Form.Element object so you don't typically use the methods from Form.Element.Methods directly, but rather via the Form.Element object for consistency. Arguments Description element: element Moves the input focus to the field and activate(element) instance object or id selects its contents. element: element clear(element) instance Empties the field. object or id Method Kind

Arguments element: element disable(element) instance object or id element: element enable(element) instance object or id element: element getValue(element) instance object or id element: element present(element) instance object or id element: element serialize(element) instance object or id

Method

Kind

Description Disables the field or button, so it cannot be changed or clicked. Enables the field of button for being changed or clicked. Returns the value of the element. Returns true if the field is not empty. Returns the element's name=value pair, like 'elementName=elementValue'

The Form.Element.Serializers object


This object provides some utility functions that are used internally in the library to assist extracting the current value of the form elements. Arguments Description element: object or id of a Returns an Array with the form element that has element's name and value, like inputSelector(element) instance the checked property, ['elementName', like a radio button or 'elementValue'] checkbox. element: object or id of a Returns an Array with the form element that has element's name and value, like textarea(element) instance the value property, like a ['elementName', textbox, button or 'elementValue'] password field. Returns an Array with the element's name and all selected element: object of a select(element) instance options' values or texts, like <select> element ['elementName', 'selOpt1 selOpt4 selOpt9'] Method Kind

The Abstract.TimedObserver class


This class is used as the base class for the other classes that will monitor one element until its value (or whatever property the derived class defines) changes. This class is used like an abstract class. Subclasses can be created to monitor things like the input value of an element, or one of the style properties, or number of rows in a table, or whatever else you may be interested in tracking changes to.

Arguments Description element: element object or id, frequency: interval in [ctor](element, Creates an object that will constructor seconds, callback: function frequency, callback) monitor the element. to be called when the element changes Derived classes have to implement this method to instance, getValue() (none) determine what is the abstract current value being monitored in the element. This method is typically not called externally. It is registerCallback() instance (none) called by the object itself to start monitoring the element. This method is typically not called externally. It is onTimerEvent() instance (none) called by the object itself periodically to check the element. Property Type element Object frequency Number Function(Object, callback String) lastValue String Description The element object that is being monitored. This is actually the interval in seconds between checks. The function to be called whenever the element changes. It will receive the element object and the new value. The last value verified in the element.

Method

Kind

The Form.Element.Observer class


Inherits from Abstract.TimedObserver Implementation of an Abstract.TimedObserver that monitors the value of form input elements. Use this class when you want to monitor an element that does not expose an event that reports the value changes. In that case you can use the Form.Element.EventObserver class instead. Method [ctor](element, frequency, callback) Arguments element: element object or id, frequency: constructor interval in seconds, callback: function to be called when the element Kind Description Inherited from Abstract.TimedObserver. Creates an object that will monitor the element's value property.

Method getValue()

Kind instance

Arguments changes (none)

Description Returns the element's value.

The Form.Observer class


Inherits from Abstract.TimedObserver Implementation of an Abstract.TimedObserver that monitors any changes to any data entry element's value in a form. Use this class when you want to monitor a form that contains a elements that do not expose an event that reports the value changes. In that case you can use the Form.EventObserver class instead. Method [ctor](form, frequency, callback) Arguments form: form object or id, frequency: interval in seconds, callback function constructor to be called when any data entry element in the form changes instance (none) Kind Description Inherited from Abstract.TimedObserver. Creates an object that will monitor the form for changes. Returns the serialization of all form's data.

getValue()

The Abstract.EventObserver class


This class is used as the base class for the other classes that execute a callback function whenever a value-changing event happens for an element. Multiple objects of type Abstract.EventObserver can be bound to the same element, without one wiping out the other. The callbacks will be executed in the order they are assigned to the element. The triggering event is onclick for radio buttons and checkboxes, and onchange for textboxes in general and listboxes/dropdowns. Method [ctor](element, callback) Arguments Description element: element object or id, callback: Creates an object that will constructor function to be called monitor the element. when the event happens instance, Derived classes have to (none) abstract implement this method to Kind

getValue()

Method

Kind

Arguments

registerCallback()

instance

(none)

registerFormCallbacks() instance

(none)

onElementEvent()

instance

(none)

Description determine what is the current value being monitored in the element. This method is typically not called externally. It is called by the object to bind itself to the element's event. This method is typically not called externally. It is called by the object to bind itself to the events of each data entry element in the form. This method is typically not called externally. It will be bound to the element's event.

Property Type element Object Function(Object, callback String) lastValue String

Description The element object that is being monitored. The function to be called whenever the element changes. It will receive the element object and the new value. The last value verified in the element.

The Form.Element.EventObserver class


Inherits from Abstract.EventObserver Implementation of an Abstract.EventObserver that executes a callback function to the appropriate event of the form data entry element to detect value changes in the element. If the element does not expose any event that reports changes, then you can use the Form.Element.Observer class instead. Arguments element: element object or id, callback: [ctor](element, constructor function to be called callback) when the event happens getValue() instance (none) Method Kind Description Inherited from Abstract.EventObserver. Creates an object that will monitor the element's value property. Returns the element's value

The Form.EventObserver class


Inherits from Abstract.EventObserver

Implementation of an Abstract.EventObserver that monitors any changes to any data entry element contained in a form, using the elements' events to detect when the value changes. If the form contains elements that do not expose any event that reports changes, then you can use the Form.Observer class instead. Arguments form: form object or id, callback: function to be [ctor](form, constructor called when any data entry callback) element in the form changes getValue() instance (none) Method Kind Description Inherited from Abstract.EventObserver. Creates an object that will monitor the form for changes. Returns the serialization of all form's data.

The Prototype Library Conclusion


Prototype is an incredibly powerful library that not only provides you handy little helpers but, more importantly, assists you to build your own libraries. It's solid class creation pattern and AJAX support can confidently be used as the backbone for all your client-side work. When combined with the great number of Prototype-based 3rd party libraries, Prototype can make your JavaScript coding very productive. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

script.aculo.us
In this lesson of the JavaScript tutorial, you will learn... 1. 2. 3. 4. 5. 6. 7. What is, who wrote, who uses, who maintains Where to get and how to make part of your site The visual effects The in-place editors How to create drag and drop elements The auto-completing input fields Where to get more information

script.aculo.us is the most popular add-on to Prototype that exists. It was developed and is maintained by Thomas Fuchs.

script.aculo.us is also bundled in Ruby on Rails and is extensively used on that platform. The number of developers already familiar with this library and it's user community make it a great choice for our projects. On its official site (http://script.aculo.us/) you will find the most current documentation and lists of additional resources like user-contributed extensions (new visual effects, transitions, etc.) and mailing lists. The most current source code for script.aculo.us can be found at http://github.com/madrobby/scriptaculous/tree/master.

Including script.aculo.us in the page


As any other JavaScript library, we include script.aculo.us in our page by means of a script tag referencing an external file. script.aculo.us is composed of multiple .js files so we better explain the proper way to load it. As we will see later, script.aculo.us is divided in several modules, each one implementing a specific functionality. We can load all the modules by adding only two library references as shown below. Syntax
<script type="text/javascript" src="scripts/prototype.js"></script> <script type="text/javascript" src="scripts/scriptaculous.js"></script>

We need a reference to Prototype because script.aculo.us was build completely dependent of Prototype (actually script.aculo.us, early on, was part of Prototype before spinning off.) By adding a reference to scriptaculous.js all the modules will be dynamically loaded. The modules are builder.js, effects.js, dragdrop.js, controls.js, slider.js, and sound.js. Loading all the modules is clearly an exaggeration and a disrespect with our users unless we are actually using each one of the modules, what is pretty unlikely. For that reason we can specify the modules we need explicitly when we reference scriptaculous.js. Syntax
<script type="text/javascript" src="scripts/prototype.js"></script> <script type="text/javascript" src="scripts/scriptaculous.js?effects,controls"></script>

The above format will make scriptaculous.js only load effects.js and controls.js. Although quite handy, we would like to suggest that you avoid this dynamic loading of modules altogether and be explicit about which modules you need. Just remember to load them in the same order that we listed a few paragraphs above. The previous example

would is rewritten below, which we believe will be clearer for another developer reading your code. Syntax
<script type="text/javascript" src="scripts/prototype.js"></script> <script type="text/javascript" src="scripts/effects.js"></script> <script type="text/javascript" src="scripts/controls.js"></script>

Effects
One of the main reasons developers use script.aculo.us is because of the visual effects that it implements. The visual effects can be very useful to call the user's attention to some important event that happened on the page. Effects can also be used to make visual changes in the page gradually, instead of an abrupt element insertion or relocation. The effects are part of the effects.js file and they need Prototype on the page as well. There are at least two ways to trigger the effects on a given element. Syntax
//using the constructor (don't forget the 'new'!) new Effect.EffectName('elementID' [, options]); //or via the extension added to the DOM elements $('elementId').visualEffect('effectName' [, options]);

Just like in many parts of Prototype, the options argument is a hash-like object that can provide non-mandatory adjustments for the effects. Effects are divided in two main groups, Core Effects and Combined Effects.

Core Effects
Think of the core effects as the simplest and more fundamental effects. Later we will be used to combine these effects to create richer effects. They are our building blocks. script.aculo.us Core Effects Effect Description Options This effect changes the opacity/transparency of the element, from: initial opacity (0.0 to 1.0), Effect.Opacity making it more or less translucid to: final opacity. progressively. By default is changes the opacity from 0.0 to 1.0. Moves the element to another point in x: X-coordinate movement in Effect.Move the page. The movement can be pixels, y: Y-coordinate specified in relative or absolute X and movement in pixels, mode:

script.aculo.us Core Effects Effect Description Options Y coordinates. coordinates mode, can be 'absolute' or 'relative' scaleX: resize on the X-axis, scaleY: resize on the Y-axis, Moves the element to another point in scaleContent: resizes the the page. The movement can be contents of the element as well, specified in relative or absolute X and Effect.Scale scaleFromCenter: resizes the Y coordinates. Sample usage: new element from its center instead Effect.Scale('elem', 150, of from the top-left corner, {scaleFromCenter: true}); scaleFrom: the starting point of the effect (a percentage value.) startcolor: background color to Remember when we implemented the start the effect, defaults to Yellow Fade Technique (YFT) a few #ffff99, endcolor: color that lessons ago? Well, scratch that and effect ends at, defaults to white, Effect.Highlight let's use this effect instead. It does the restorecolor: color that is used same thing but better and more after the effect finishes, defaults extensibly. to the element's background color. You can get a lot done with this effect alone. Simply put, it will change the element from its current appearance to what is defined by some form of CSS specification. Sample usage: new Effect.Morph Effect.Morph('elem', 'processedOrder', {duration: 1.5}); or new Effect.Morph('elem', {style: {backgroundColor: '#7f7', height: '120px'} }, {duration: 1.5});

Exercise: Simple Effect Usage


Duration: to 30 minutes. Let's see how easy it is to apply script.aculo.us effects. In this exercise we will use script.aculo.us to change the opacity of an element to 50%. 1. Create a method WEBUCATOR.setOpacity(element) 2. Call that method from the button's click passing box2 as the element. 3. The created method should use one of script.aculo.us effects to reduce the opacity of the given element to 50%.

Code Sample: Scriptaculous/Exercises/simpleeffect.html


---- Code Omitted ---<body> <h1>A Simple Effect</h1> <div id="box1">BOX 1</div> <div id="box2">BOX 2</div> <input type="button" value="Reduce Opacity" onclick="alert('not done yet!!!');" /> </body> ---- Code Omitted ----

Where is the solution?

Combined Effects
There's another core effect that we have not shown. It's more of an infrastructure effect. It's the Effect.Parallel. This effect is used to execute two or more effects simultaneously.
new Effect.Parallel( [ new Effect.Opacity('notice', { sync: true }), new Effect.Scale('notice', 100, { sync: true, scaleFrom: 50 }) ], { duration: 2 });

Note the sync option being passed to both inner effects. This is the clue for script.aculo.us to wait until all effects are created and execute them all at once. script.aculo.us comes with a series of effects that are built by pre-configuring or combining the core effects with Effect.Parallel. All the combined effects have shortcuts added to the DOM elements but can also be called like the core effects. Syntax
//we don't need the 'new' for combined effects //but if you use it, it doesn't cause any problem Effect.EffectName('element' [, options]); //or $('element').visualEffect('effectName' [, options]); //or $('element').effectName( [options] );

Effect Effect.ScrollTo

script.aculo.us Combined Effects Description Scrolls the page until the element is reached.

Options offset: vertical offset

Effect

script.aculo.us Combined Effects Description

Options after the element. Will be added to the scrolling distance.

Shakes the element from left to right a few times to call attention. Decreases and increases the opacity few times Effect.Pulsate to call attention. Effect.DropOut The element falls down and fades. Effect.Appear The element's opacity goes from 0.0 to 1.0. The element's opacity goes from 1.0 to 0.0, Effect.Fade then the element is hidden. The element's height is decreased until it Effect.BlindUp disappears. The element is hidden on completion. The element's height is increased from zero Effect.BlindDown until the element's original height. The element's height is decreased until it disappears. The contents need to be inside an Effect.SlideUp extra div inside the element and it will move up instead of shrinking. The element is hidden on completion. The element's height is increased from zero to the element's original height. The contents need Effect.SlideDown to be inside an extra div inside the element and it will move down instead of expanding. The element grows and fades. The element is Effect.Puff hidden on completion. The element shrinks towards the upper-left Effect.Squish corner. The element is hidden on completion. The element flickers quickly then shrinks like Effect.SwitchOff and old CRT TV set picture. The element is hidden on completion. The narrows vertically then horizontally until it Effect.Fold disappears. The element is hidden on completion. Effect.Shake

Effects pout-pourri
The following demo showcases the available effects and the code used to create them.

Code Sample: Scriptaculous/Demos/effects.html


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>effects</title> <style type="text/css" media="screen"> th {text-align:left; font-size:18px; background-color:#777; color:#fff;} .demo { display:none;} .demo-content {background-color:#FFB855; padding:10px;} .demo-content pre {background-color: #aaa;} #demoArea {height:400px; width:550px; background-color:#eee; } #demoList { margin:0; list-style-type: none; padding:0;} #demoList a { text-decoration:none;} </style> <script type="text/javascript" src="../../Libraries/prototype.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/effects.js"></script> <script type="text/javascript" charset="utf-8"> var WEBUCATOR = { currentDemo: '', loadDemo: function (demoName) { this.restorePreviousDemo(); //move demo content to demo area var demo = $(demoName); var content = demo.select('.demo-content')[0]; content.remove(); $('demoArea').insert(content); //make the code snippet in the demo // become the actual code this.importCodeSnippet(demoName, content); //if a demoName_pre() function exists, call it (this[demoName + '_pre'] || Prototype.emptyFunction).apply(this); //remember this demo name to restore it later this.currentDemo = demoName; }, importCodeSnippet: function (demoName, content) { if (typeof this[demoName] === 'undefined') { var code = content.select('.demoCode')[0]; var functionCode = code.innerHTML.unescapeHTML(); this[demoName] = new Function(functionCode); } }, restorePreviousDemo: function () { if (this.currentDemo !== '') { var prevDemo = $(this.currentDemo); var prevContent = $('demoArea').select('.demo-content')[0]; prevContent.remove(); prevDemo.insert(prevContent); }

}, doMainDemoAction: function () { this[this.currentDemo](); }, //cleanup code .... demoHighlight_pre: function () { $('theTime').update(''); }, demoOpacity_pre: function () { $('topBox').setOpacity(1); }, demoScale_pre: function () { $('smallBox').setStyle({ fontSize: '11px', width: '80px', height: '40px', left: '100px', top: '40px'}); $('smallBox').update('Scriptaculous'); }, demoMove_pre: function () { $('mover').setStyle({left: '25px', top: '25px'}); }, demoDropOut_pre: function () { $('droppingBox').show(); }, demoAppear_pre: function () { $('appearingBox').hide(); }, demoFade_pre: function () { $('fadingBox').show(); }, demoBlindUp_pre: function () { $('rollUpBox').show(); }, demoBlindDown_pre: function () { $('rollDownBox').hide(); }, demoSlideUp_pre: function () { $('slideUpBox').show(); }, demoSlideDown_pre: function () { $('slideDownBox').hide(); }, demoPuff_pre: function () {

$('puffingBox').show(); }, demoSwitchOff_pre: function () { $('tvBox').show(); }, demoSquish_pre: function () { $('squishable').show(); }, demoFold_pre: function () { $('napkin').show(); }, demoMorph_pre: function () { $('willMorph').setStyle( { left: '100px', width:'100px', color: '', backgroundColor: '#77E66C'}); }, temp: null } </script> </head> <body> <h1>Scriptaculous Effects Sampling</h1> <table border="0" cellspacing="5" cellpadding="5"> <thead><tr><th>Effect</th><th>Demo</th></tr></thead> <tbody> <tr valign="top"> <td> <ul id="demoList"> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoOpacity');">Effect.Opacity</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoMove');">Effect.Move</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoHighlight');">Effect.Highlight</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoScale');">Effect.Scale</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoMorph');">Effect.Morph</a>

</li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoScroll');">Effect.ScrollTo</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoDropOut');">Effect.DropOut</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoShake');">Effect.Shake</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoPulsate');">Effect.Pulsate</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoAppear');">Effect.Appear</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoFade');">Effect.Fade</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoBlindUp');">Effect.BlindUp</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoBlindDown');">Effect.BlindDown</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoSlideUp');">Effect.SlideUp</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoSlideDown');">Effect.SlideDown</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoPuff');">Effect.Puff</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoSwitchOff');">Effect.SwitchOff</a> </li> <li> <a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoSquish');">Effect.Squish</a> </li> <li>

<a href="javascript:void(0);" onclick="WEBUCATOR.loadDemo('demoFold');">Effect.Fold</a> </li> </ul> </td> <td> <div id="demoArea"></div> </td> </tr> </tbody> </table>

<div id="demoOpacity" class="demo"> <div class="demo-content"> <h3>Effect.Opacity</h3> <input type="button" value="Change opacity?" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px;">&nbsp; <div style="background-color:#7D85DA; position:absolute; left:5px; top:5px; width:100px; height:100px">&nbsp;</div> <div id="topBox" style="background-color:#77E66C; position:absolute; left:25px; top:25px; width:100px; height:100px">&nbsp;</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">new Effect.Opacity('topBox', {to: 0.50, duration: 2});</code></pre> <strong>Alternatives:</strong> <pre><code>$('topBox').visualEffect('opacity', {to: 0.50, duration: 2});</code></pre> </div> </div> <div id="demoMove" class="demo"> <div class="demo-content"> <h3>Effect.Move</h3> <input type="button" value="Move it" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="mover" style="background-color:#77E66C; position:absolute; left:25px; top:25px; width:100px; height:100px">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">new Effect.Move('mover', {x: 300, y: 75});</code></pre>

<strong>Alternatives:</strong> <pre><code>$('mover').visualEffect('move', {x: 300, y: 75});</code></pre> </div> </div> <div id="demoScroll" class="demo"> <div class="demo-content"> <h3>Effect.ScrollTo</h3> <input type="button" value="Go to the element" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="height:1200px; border:solid 1px #000;">&nbsp; </div> <div id="bottomBox" style="background-color:#77E66C; width:180px; height:40px; text-align:center; "> Scriptaculous (the element) </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">new Effect.ScrollTo('bottomBox');</code></pre> <strong>Alternatives:</strong> <pre><code>$('bottomBox').visualEffect('scrollTo');</code></pre> </div> </div> <div id="demoScale" class="demo"> <div class="demo-content"> <h3>Effect.Scale</h3> <input type="button" value="Resize it" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="smallBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">new Effect.Scale('smallBox', 200, {scaleFromCenter: true});</code></pre> <strong>Alternatives:</strong> <pre><code>$('smallBox').visualEffect('scale', 200, {scaleFromCenter: true});</code></pre> </div> </div> <div id="demoMorph" class="demo"> <div class="demo-content"> <h3>Effect.Morph</h3> <input type="button" value="Morph it" onclick="WEBUCATOR.doMainDemoAction();"/>

<div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="willMorph" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:100px; height:40px; text-align:center;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">new Effect.Morph('willMorph', { style: { backgroundColor:'#ff0000', color: '#ffffff', width: '220px', left: '250px' } });</code></pre> <strong>Alternatives:</strong> <pre><code>$('willMorph').visualEffect('morph', { style: { backgroundColor:'#ff0000', color: '#ffffff', width: '220px', left: '250px' } }); //or $('willMorph').morph( { backgroundColor:'#ff0000', fontColor: '#ffffff', width: '220px' });</code></pre> </div> </div> <div id="demoHighlight" class="demo"> <div class="demo-content"> <h3>Effect.Highlight</h3> <input type="button" value="What time is it?" onclick="WEBUCATOR.doMainDemoAction();"/> <strong>The time is: </strong> <span id="theTime" ></span> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">var time = new Date(); $('theTime').update(time.toString()); new Effect.Highlight('theTime', { duration: 2, endcolor:'#FFB855' } );</code></pre> <strong>Alternatives:</strong> <pre><code>$('theTime').visualEffect('highlight', {to: 0.50, duration: 2}); //or $('theTime').highlight({to: 0.50, duration: 2});</code></pre> </div>

</div> <div id="demoDropOut" class="demo"> <div class="demo-content"> <h3>Effect.DropOut</h3> <input type="button" value="Wave goodbye to it" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="droppingBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.DropOut('droppingBox');</code></pre> <strong>Alternatives:</strong> <pre><code>$('droppingBox').visualEffect('dropOut'); //or $('droppngBox').dropOut();</code></pre> </div> </div> <div id="demoShake" class="demo"> <div class="demo-content"> <h3>Effect.Shake</h3> <input type="button" value="Hey, look at me" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="willShake" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.Shake('willShake');</code></pre> <strong>Alternatives:</strong> <pre><code>$('willShake').visualEffect('shake'); //or $('willSahke').shake();</code></pre> </div> </div> <div id="demoPulsate" class="demo"> <div class="demo-content"> <h3>Effect.Pulsate</h3> <input type="button" value="Hey, I'm here" onclick="WEBUCATOR.doMainDemoAction();"/>

<div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="willPulsate" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.Pulsate('willPulsate');</code></pre> <strong>Alternatives:</strong> <pre><code>$('willPulsate').visualEffect('pulsate'); //or $('willPulsate').pulsate();</code></pre> </div> </div> <div id="demoAppear" class="demo"> <div class="demo-content"> <h3>Effect.Appear</h3> <input type="button" value="Show it" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="appearingBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px; display:none">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.Appear('appearingBox');</code></pre> <strong>Alternatives:</strong> <pre><code>$('appearingBox').visualEffect('appear'); //or $('appearingBox').appear();</code></pre> </div> </div> <div id="demoFade" class="demo"> <div class="demo-content"> <h3>Effect.Fade</h3> <input type="button" value="Vanish" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="fadingBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px;">Scriptaculous</div> </div>

<br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.Fade('fadingBox');</code></pre> <strong>Alternatives:</strong> <pre><code>$('fadingBox').visualEffect('fade'); //or $('fadingBox').fade();</code></pre> </div> </div> <div id="demoBlindUp" class="demo"> <div class="demo-content"> <h3>Effect.BlindUp</h3> <input type="button" value="Roll it up" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="rollUpBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.BlindUp('rollUpBox');</code></pre> <strong>Alternatives:</strong> <pre><code>$('rollUpBox').visualEffect('blindUp'); //or $('rollUpBox').blindUp();</code></pre> </div> </div> <div id="demoBlindDown" class="demo"> <div class="demo-content"> <h3>Effect.BlindDown</h3> <input type="button" value="Roll it down" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="rollDownBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px; display:none;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.BlindDown('rollDownBox');</code></pre> <strong>Alternatives:</strong> <pre><code>$('rollDownBox').visualEffect('blindDown');

//or $('rollDownBox').blindDown();</code></pre> </div> </div> <div id="demoSlideUp" class="demo"> <div class="demo-content"> <h3>Effect.SlideUp</h3> <input type="button" value="Push it up" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="slideUpBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px;"><div>Scriptaculous</div></div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.SlideUp('slideUpBox');</code></pre> <strong>Alternatives:</strong> <pre><code>$('slideUpBox').visualEffect('slideUp'); //or $('slideUpBox').slideUp();</code></pre> </div> </div> <div id="demoSlideDown" class="demo"> <div class="demo-content"> <h3>Effect.SlideDown</h3> <input type="button" value="Push it down" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="slideDownBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px; display:none;"><div>Scriptaculous</div></div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.SlideDown('slideDownBox');</code></pre> <strong>Alternatives:</strong> <pre><code>$('slideDownBox').visualEffect('slideDown'); //or $('slideDownBox').slideDown();</code></pre> </div> </div> <div id="demoPuff" class="demo">

<div class="demo-content"> <h3>Effect.Puff</h3> <input type="button" value="Make it go up in smoke" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="puffingBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:120px; height:40px; text-align:center;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.Puff('puffingBox', {duration: .5});</code></pre> <strong>Alternatives:</strong> <pre><code>$('puffingBox').visualEffect('puff', {duration: .5}); //or $('puffingBox').puff({duration: .5});</code></pre> </div> </div> <div id="demoSwitchOff" class="demo"> <div class="demo-content"> <h3>Effect.SwitchOff</h3> <input type="button" value="Turn it Off" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="tvBox" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:120px; height:40px; text-align:center; font-size:11px;">Scriptaculous TV</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.SwitchOff('tvBox');</code></pre> <strong>Alternatives:</strong> <pre><code>$('tvBox').visualEffect('switchOff'); //or $('tvBox').switchOff();</code></pre> </div> </div> <div id="demoSquish" class="demo"> <div class="demo-content"> <h3>Effect.Squish</h3> <input type="button" value="Squish it" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp;

<div id="squishable" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">Effect.Squish('squishable');</code></pre> <strong>Alternatives:</strong> <pre><code>$('squishable').visualEffect('squish'); //or $('squishable').squish();</code></pre> </div> </div> <div id="demoFold" class="demo"> <div class="demo-content"> <h3>Effect.Fold</h3> <input type="button" value="Fold it" onclick="WEBUCATOR.doMainDemoAction();"/> <div style="position:relative; height:200px; border:solid 1px #000;">&nbsp; <div id="napkin" style="background-color:#77E66C; position:absolute; left:100px; top:40px; width:80px; height:40px; textalign:center; font-size:11px;">Scriptaculous</div> </div> <br/><br/> <strong>Code:</strong> <pre><code class="demoCode">new Effect.Fold('napkin');</code></pre> <strong>Alternatives:</strong> <pre><code>$('napkin').visualEffect('fold'); //or $('napkin').fold();</code></pre> </div> </div> </body> </html>

Common effect options


All effects share a few options that can be used to gain more control of the effect execution. Option delay Description Number o seconds before the effect starts. Defaults to 0.0.

Description Used to trigger effects in succession. It defaults to 'parallel' but can also be queue 'front', 'end', and 'with-last' duration Length of the effect in seconds. Defaults to 1.0. Number of frames per second. Defaults to 100 but 25 is good enough for fps the human eye and avoids unnecessary CPU usage. sync Use true when running effects in parallel. from Starting point of the effect. A number between 0.0 and 1.0. to Ending point of the effect. A number between 0.0 and 1.0. beforeStart Callback function that is invoked just before the effect starts. beforeSetup Callback function that is invoked just before the first frame is calculated. Callback function that is invoked right after the first frame is calculated afterSetup and before it is rendered. beforeUpdate Callback function that is invoked before each frame is rendered. afterUpdate Callback function that is invoked after each frame is rendered. Callback function that is invoked before the effect runs its finalization beforeFinish logic. afterFinish Callback function that is invoked after the effect runs its finalization logic. Specifies the pace of the effect progression. It defaults to Effect.Transitions.sinoidal but is ca also be Effect.Transitions.spring, Effect.Transitions.linear, Effect.Transitions.flicker, Effect.Transitions.wobble, Effect.Transitions.pulse, transition Effect.Transitions.none, Effect.Transitions.full, and Effect.Transitions.reverse. It can also be customized by passing a function that receives a number between 0.0 and 1.0 and returns another number in the same range.

Option

Dragging, Sorting, and Dropping


Drag and drop is a common user interface metaphor in desktop applications. It's not as prevalent in web applications, though. Nevertheless, this is something that can greatly help users in some situations. The dragging and dropping cycle involves a number of decisions. Among them: what to drag, what to grab it by, where to drop it, what to do after it is dropped. script.aculo.us helps with each of these problems. It's very easy to make an element draggable. See the example below.

Code Sample: Scriptaculous/Demos/drag-simple.html


---- Code Omitted ----

<script type="text/javascript" src="../../Libraries/prototype.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/effects.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/dragdrop.js"></script> <script type="text/javascript" charset="utf-8"> document.observe('dom:loaded', function () { //it's easy to make an element draggable: new Draggable('theImage'); } ); </script> ---- Code Omitted ---<h1>This image is draggable</h1> <img id="theImage" src="../Common/olympics.png" alt="olympics" /> ---- Code Omitted ----

All we needed to make the img element draggable was to instantiate a new Draggable object associated with that element. Syntax
new Draggable('theImage');

That was easy indeed, but that's not where the story ends. Just like with the Effect objects, we also have a number of options that we can pass in the second argument of the constructor. Syntax
new Draggable(element, { /* options here */ });

Option constraint delay

endeffect

ghosting

handle

Description Specifies if the drag will be limited. Defaults to false but can also be 'vertical' or 'horizontal'. Amount of time (in seconds) to initiate the dragging after the mouse button is pressed. This can be useful to avoid dragging when the user just wanted to click a button or link. Defaults to zero. Effect that will be applied to the element when the dragging ends. The default is to change the opacity to 100% in .2 seconds. This is a function that takes the dragged element as the argument. Determines if a clone element will be created during the drag, leaving the original element in its original place until a drop occurs. Default is false. Use this when we want to define one child element in the draggable element to serve as the handle for dragging the bigger element. It defaults to false but can also be a DOM element, an id, or a CSS class name.

Description Function that will be called during dragging when the mouse is moved onDrag and the element is about to be have its position changed. The function will receive the Draggable object as the first parameter. Function that will be called when the dragging ends. The function will onEnd receive the Draggable object as the first parameter. Function that will be called when the dragging starts. The function will onStart receive the Draggable object as the first parameter. When working with Droppables this will indicate if the element should revert go back to the original position if it's not dropped in a Droppable. Defaults to false. This creates the effect that will be used when reverting the drag. revertEffect Defaults to an Effect.Move but can be replaced with a function that takes the element, the dragged X, and the dragged Y in pixels. The scroll container for the drag. When defined, the container will scroll scroll when the dragged element nears its edges. It can be the window object or any other element that has scrolling enabled. Number of pixels from the edges of the container that trigger the scrollSensitivity scrolling. A number representing the pixels per second that the container will scroll when the element is dragged near the edges. Defaults to 15. This is scrollSpeed actually the minimum speed because the speed will be greater when the closer to the border the drag is. Defines a grid that will cause the dragging to snap to a grid. It can be a number, which will be the number of pixels between each snapping snap position in the grid. It can also be a 2-element array, containing the number of pixel in the X and Y direction respectively. Effect that will be applied to the element when the dragging starts. The starteffect default is to change the opacity to 50% in .2 seconds. This is a function that takes the dragged element as the argument. Number that will be used as the element's z-index while being dragged zindex to ensure it is above the other elements. Default is 1000. The next example illustrates how the use of dragging and effects to create a simple puzzle game.

Option

Code Sample: Scriptaculous/Demos/shuffle.html


---- Code Omitted ---var WEBUCATOR = { image: { width: 600.0, height:380.0 }, tiles: [],

createGrid: function () { var columns = 4, rows = 3; var cellWidth = this.image.width/columns; var cellHeight = this.image.height/rows; var left, top, cell, tile; for (var row = 0; row < rows; row++) { for (var col = 0; col < columns; col++) { left = col * cellWidth; top = row * cellHeight; tile = this.createTile(left, top, cellWidth, cellHeight); tile.originalPosition = {x: left, y: top}; $('grid').insert(tile); this.tiles.push(tile); } } this.makeTilesDraggable(); }, createTile: function (left, top, width, height) { var tile = new Element('div', {'class':'cell'}); tile.setStyle({ width: width + 'px', height: height + 'px', left: left + 'px', top: top + 'px', backgroundPosition: '-' + left + 'px -' + top + 'px' }); return tile; }, makeTilesDraggable: function () { this.tiles.each( function (tile) { new Draggable(tile); }); }, shuffleGrid: function () { this.tiles.each( function (tile) { var left = Math.floor(Math.random() * WEBUCATOR.image.width); var top = Math.floor(Math.random() * WEBUCATOR.image.height); tile.visualEffect('move', {x: left, y: top, mode:'absolute'}); }); }, restoreGrid: function () { this.tiles.each( function (tile) { tile.visualEffect('move', Object.extend( tile.originalPosition, {mode:'absolute'} ) ); }); }

}; document.observe('dom:loaded', function () { WEBUCATOR.createGrid();} ); ---- Code Omitted ----

Exercise: Dragging with options


Duration: 15 to 30 minutes. We will create a Draggable that is a little bit more than the default. 1. Find the place where you are suppose to add your code (see comments.) 2. Create a Draggable that will enable dragging of both the image and the title bar together. 3. Check that it works. 4. Change the code to allow dragging only by the title bar. 5. Check that it still works. 6. Add code to make the dragging carry only a copy of the picture. 7. Check that it still works. 8. Add code to force the dragging to happen in chunks of 30 pixels horizontally and 50 vertically.

Code Sample: Scriptaculous/Exercises/draghandle.html


---- Code Omitted ---<script type="text/javascript" src="../../Libraries/prototype.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/effects.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/dragdrop.js"></script> <script type="text/javascript" charset="utf-8"> document.observe('dom:loaded', function () { //Write code here to make the // picture below draggable by the red title bar // also make the drag adhere to a 30 by 50 grid (X by Y) // change the dragging behavior to leave the original picture // behind and use a copy until it gets finally dropped }); </script> ---- Code Omitted ---<h1>This image is draggable by the title bar</h1> <div id="picture" class="draggablePicture"> <div class="titlebar">Olympics</div>

<div><img id="theImage" src="../Common/olympics.png" alt="olympics" /></div> </div> ---- Code Omitted ----

Where is the solution? The Draggable object has a number of members as listed below. Member Description destroy() A method that will remove the dragging capabilities of the element. dragging A Boolean value indicating if the element is currently being dragged. The DOM element that is associated with this object. In other words, that's the element element that can be dragged. If specified as an option, this will contain a reference to the DOM element that handle will be used as the handle for dragging the main element. The options set for this Drggable. This includes both the options specified at the options constructor call and all the default options that were not overridden.

Droppables
Many times dragging items around is not all that useful unless you have a designated place to drop the items. The Drop part of Drag and Drop is handled by the Droppable object. We create dropping zones in our page by making adding an element to the Droppables list. Syntax
Droppables.add(element [, options]);

The available options for the Draggables are listed below. Description Name of a CSS class that will restrict which elements can be dropped in this accept drop zone. Only draggables with that class will be successfully dropped. Instead of a CSS class we can pass an element in this option so only containment draggables that are children of this element will be accepted. Name of a CSS class that will be added to the drop zone element while an hoverclass element in dragged over it. Takes a function that will be called when an element is dropped. The onDrop function will receive three arguments: the element being dropped, the drop zone element, and the event information. Option

Exercise: Dragging then dropping


Duration: 30 to 45 minutes. We will create two dropping zones in a page, where we can drop book cover images dragged from another part of the page. 1. 2. 3. 4. 5. Find the place where you are suppose to add your code (see comments.) Write code to make all the existing book covers draggable Check that it works. Make the green and yellow areas drop zones for the books When a book is dropped we want to create a duplicate image of the book in the drop zone. 6. Make the drop zones show a border when the books are dragged over them.

Code Sample: Scriptaculous/Exercises/simpledrop.html


---- Code Omitted ---<script type="text/javascript" charset="utf-8"> document.observe('dom:loaded', function () { /* here we need to make the books draggable and make the DIVs with ids alreadyRead and willRead drop zones we will create a copy of the book images in the drop zone when a book is dropped there */ }); var WEBUCATOR = { /* here we will create a function to handle the drop event for both drop zones */ }; </script> ---- Code Omitted ---<body> <h1>Books that I must read</h1> <table> <tr> <th>Available<br/>books</th> <th>Books<br/>I've read</th> <th>Books I<br/>should read</th> </tr> <tr> <td> <div id="available"> <img id="book1" src="../Common/poeaa.jpg" alt="Poeaa" class="book"/>

<img id="book2" src="../Common/ddd.jpg" alt="ddd" class="book"/> <img id="book3" src="../Common/wewlc.jpg" alt="wewlc" class="book"/> <img id="book4" src="../Common/tpp.jpg" alt="pragprog" class="book"/> </div> </td> <td><div id="alreadyRead"></div></td> <td><div id="willRead"></div></td> </tr> </table> </body> ---- Code Omitted ----

Where is the solution?

Sortables
It's very common to use dragging to reorder items in a list. That's a much better user interface design than using "move up" or "move down" buttons. For that reason script.aculo.us offers that behavior pre-packaged in the Sortable object. To make items sortable we just need to make the parent element sortable, like we can see below.

Code Sample: Scriptaculous/Demos/sort-books.html


---- Code Omitted ---<script type="text/javascript" charset="utf-8"> document.observe('dom:loaded', function () { Sortable.create('books'); }); </script> ---- Code Omitted ---<ul id="books"> <li> <div class="book"> <img src="../Common/poeaa.jpg" alt="Poeaa" align="left"/> <h2>Patterns of Enterprise Application Architecture</h2> <h3>Martin Fowler</h3> </div> </li> <li> <div class="book"> <img src="../Common/ddd.jpg" alt="ddd" align="left"/> <h2>Domain-Driven Design</h2> <h3>Eric Evans</h3> </div> </li> <li> <div class="book"> <img src="../Common/wewlc.jpg" alt="wewlc" align="left"/>

<h2>Working Effectively With Legacy Code</h2> <h3>Michael Feathers</h3> </div> </li> <li> <div class="book"> <img src="../Common/tpp.jpg" alt="pragprog" align="left"/> <h2>The Pragmatic Programmer</h2> <h3>Andy Hunt and Dave Thomas</h3> </div> </li> </ul> ---- Code Omitted ----

When creating a Sortable object we can also pass some options in the second argument of Sortable.create(). One such option is the onUpdate, which takes a function that will receive the parent element as the argument. To be able to use the onUpdate callback we need to add an id attribute to each li element in our list. The ids need to be in the format xxxxxx_value, where the value part will be extracted and considered the value of the item. The Sortable gives us the current sequence via the Sortable.serialize(listElementID) method. This returns a string formated like listElementID[]=value1&listElementID[]=value3&listElementID[]=value2. Although this format looks a little funky, it's due to script.aculo.us strong bond with Ruby on Rails and it's not too hard to live with in non-Ruby on Rails platforms. Our next exercise will show how to use that in PHP.

Exercise: AJAX Sorting


Duration: 30 to 45 minutes. In this exercise we will use a Sortable to reorder a list of items and submit each order update to the server using AJAX. 1. 2. 3. 4. 5. Add the necessary references to external .js files to use Sortable. Find the place where you are suppose to add your code (see comments.) Make the list of books sortable upon page load. Check that it works. Add code to the WEBUCATOR object that will be called whenever the order changes. 6. Verify that the code gets called after updates (hint: the li elements will need id attributes.) 7. Change the code to perform an Ajax.Update to the URL ../Common/savesequence.php passing the serialized sortable as a form field named seq. The updater will direct its result to the div element with id result.

Code Sample: Scriptaculous/Exercises/sort-save.html


---- Code Omitted ---<script type="text/javascript" charset="utf-8"> var WEBUCATOR = { //we will add code here to handle events and start ajax calls }; document.observe('dom:loaded', function () { //We will add code here to create the sortable }); </script> ---- Code Omitted ---<div id="result" >&nbsp;</div> <ul id="books"> <li> <div class="book"> <img src="../Common/poeaa.jpg" alt="Poeaa" align="left"/> <h2>Patterns of Enterprise Application Architecture</h2> <h3>Martin Fowler</h3> </div> </li> <li> <div class="book"> <img src="../Common/ddd.jpg" alt="ddd" align="left"/> <h2>Domain-Driven Design</h2> <h3>Eric Evans</h3> </div> </li> <li> <div class="book"> <img src="../Common/wewlc.jpg" alt="wewlc" align="left"/> <h2>Working Effectively With Legacy Code</h2> <h3>Michael Feathers</h3> </div> </li> <li> <div class="book"> <img src="../Common/tpp.jpg" alt="pragprog" align="left"/> <h2>The Pragmatic Programmer</h2> <h3>Andy Hunt and Dave Thomas</h3> </div> </li> </ul> ---- Code Omitted ----

Code Sample: Scriptaculous/Common/savesequence.php


<?php

parse_str($_POST['seq']); $received = ""; for($i = 0; $i < count($books); $i++) { //here we would for example // save the posted sequence to the database //In our sample we will just echo back the posted values $index = $i + 1; $received = $received . " [$index: $books[$i]]"; } echo $received ?>

Where is the solution?

Controls
script.aculo.us contains a few classes to assist creating user interface elements commonly known as widgets. The approach script.aculo.us takes is to attach objects of those classes to existing HTML elements and enhance them with added behaviors. In order to use the controls in script.aculo.us we need to include the controls.js file, which will need effects.js and prototype.js Syntax
<script type="text/javascript" src="prototype.js"></script> <script type="text/javascript" src="effects.js"></script> <script type="text/javascript" src="controls.js"></script>

Autocompleter
The auto-completion functionality was popularized by Google Suggest, where a regular input text field shows a list of possible search terms before you're done typing the entire term. This is now a more or less common feature in many sites.

What happens behind the scenes is that the web page is issuing AJAX requests to the server, passing whatever you have typed so far, getting a list of possible words or phrases that you might be planning to enter, and displaying the list. Then you can just select one from the list instead of finishing typing it off. script.aculo.us comes with two flavors of autocompletion controls. One is fed by an AJAX call just like described above. The other one (referred to as local) gets its list from an array already in the page. The two controls support a lot of the same functionality, differing obviously in the source of the suggested items and the options that are related to the AJAX request itself.

Here's how we create an Autocompleter. First an example of a local one. Syntax


var states = ['Alabama', 'Alaska', ... ,'Wisconsin', 'Wyoming']; Autocompleter.Local(fieldElement, popUpElement, states);

Now an AJAX-based one. Syntax


new Ajax.Autocompleter(fieldElement, popUpElement, '/url/to/getSuggestions' [, options]);

In both cases fieldElement is the element or id of an input text field where the typing will occur and popUpElement is the element or id of a div that will be used as the pop-up list that will be displayed under the text field. Below is a working example of a local Autocompleter that looks up an array with the list of the U.S. states.

Code Sample: Scriptaculous/Demos/suggest-local.html


---- Code Omitted ---<script type="text/javascript" src="../../Libraries/prototype.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/effects.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/controls.js"></script> <script type="text/javascript" charset="utf-8"> var WEBUCATOR = { usStates: [ 'Alabama' , 'Alaska' , 'Arizona' , ---- Code Omitted ---'Wisconsin' , 'Wyoming' ] }; document.observe('dom:loaded', function () { new Autocompleter.Local('userState', 'suggestPopUp', WEBUCATOR.usStates); } ); </script> ---- Code Omitted ---What State do you live in? <input type="text" id="userState" /> <div id="suggestPopUp" class="suggestions"/> ---- Code Omitted ----

As you can see there isn't a lot of code to look at. Surprisingly enough the majority of the work that we have to do is related to CSS rules to make the pop-up look nice. In this example we removed the bullet points from each li element in the pop-up and highlighted the currently selected item by adding a different background color to the item with the class name selected (script.aculo.us takes care of putting that class name in the correct item for us.) The same example can be converted to an AJAX version provided we write the server side code. This particular example isn't actually a good candidate for being AJAX-fied because it draws results from a relatively short and fixed list, but let's do it anyway for illustrative purposes.

Code Sample: Scriptaculous/Demos/suggest-ajax.html


---- Code Omitted ---<script type="text/javascript" src="../../Libraries/prototype.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/effects.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/controls.js"></script> <script type="text/javascript" charset="utf-8"> document.observe('dom:loaded', function () { new Ajax.Autocompleter('userState', 'suggestPopUp', '../Common/lookup-state.php'); }); </script> ---- Code Omitted ---What State do you live in? <input type="text" id="userState" name="state" /> ---- Code Omitted ----

Code Sample: Scriptaculous/Common/lookup-state.php


<?php $states = array( "Alabama","Alaska" ,"Arizona","Arkansas" ,"California" , "Colorado" ,"Connecticut","Delaware" ,"Florida","Georgia", "Hawaii" ,"Idaho","Illinois" ,"Indiana","Iowa" , "Kansas" , "Kentucky" ,"Louisiana","Maine","Maryland" , "Massachusetts","Michigan" ,"Minnesota","Mississippi", "Missouri" ,"Montana","Nebraska" ,"Nevada" , "New Hampshire","New Jersey" ,"New Mexico" ,"New York" , "North Carolina" ,"North Dakota" ,"Ohio" ,"Oklahoma" , "Oregon" ,"Pennsylvania" ,"Rhode Island" ,"South Carolina" , "South Dakota" ,"Tennessee","Texas","Utah" ,"Vermont","Virginia" , "Washington" ,"Washington D.C.","West Virginia"

); $term = $_POST["state"]; echo "<ul>"; $returns = 0; foreach($states as $st) { $pos1 = stripos($st, $term); if ($pos1 !== false) { //we will build: <li>bla bla bla <strong>TERM HERE</strong> bla bla</li> $text = substr($st, 0, $pos1); $text = $text . "<strong>" . substr($st, $pos1, strlen($term)) . "</strong>"; $text = $text . substr($st, $pos1 + strlen($term)); echo "<li>$text</li>"; $returns+=1; } if($returns == 10){ break; } } echo "</ul>"; ?>

The lookup-state.php receives the value entered by the user in a POSTed form field with the same name as the input field's name attribute (not the id, watch out for this possible tripwire.) With the value in hands, the server script is responsible for figuring out what the returned suggestions should be and return them in ul/il elements. In the above example we highlight the typed text in the result list. The Autocompleter takes a number of options. Three are shared by both the local and AJAX versions. Description If this is set to true and there's only one value being suggested, then that value autoSelect is automatically selected. This option defaults to false. This is a misnomer. This option specifies the number of seconds between frequency refreshing the suggestions. Defaults to 0.4. Number of characters that need to be typed before the first suggestions list is minChars produced. Defaults to 1. The Autocompleter.Local adds its own specific options. Option Description Option

Description Defaults to false and determines if the suggestions should include any items fullSearch that contain the typed text anywhere, not just beginnings of words. Determines if character casing is disregarded in the comparisons. Defaults ignoreCase to true. When set to true the search algorithm will look for suggestions that begin partialSearch with the typed text. The default value is false where it matches any suggestion that has a word that begins with the typed text. Minimum number of typed characters that cause a partial search to be partialChars performed. Defaults to 2. Method that performs the actual work. The default value is the algorithm that obeys all the above options. In the rare event that you need something selector completely different (like searching in a XML document,) you can pass this option with a function that receives the autocompleter instance, builds and returns the ul/li DOM structure. As expected, the Ajax.Autocompleter also has a number of options that control how it works. Description If you need to customize the creation of the submitted value and parameter name, this option lets you pass a function that will receive a reference to the callback input element and the typed text and returns the querystring-formatted 'param=value'. Id or reference to a DOM element that will automatically be displayed and indicator hidden during the AJAX calls. This is usually a reference to an img element with some animated glyph that lets the user know some work is being done. Just like the original Ajax.Request, this lets us change the HTTP method method from the default POST to GET. Fixed set of parameters to always be send to the AJAX URL. These parameters parameters are encoded in URL querystring format, like 'par1=abc&par2=xyz'. The submitted parameter name is the same as the name attribute of the input paramName field. This options lets you change that to whatever name you desire. Class name of an element inside the suggested li that will provide the text select for the selection. This is useful when the suggested items are richer than just text-only li items. String containing a separator character or an array of separators to be used when the text field is expected to collect more than one suggestions, like a tokens To: field in an email form, where you can choose more than one recipient for the message. Option

Option

Exercise: AJAX Autocompleter with options

Duration: 30 to 45 minutes. Let's use Ajax.Autocompleter objects to create a very interesting user interface for finding books. 1. Add the necessary references to external .js files to use the Ajax.Autocompleter. 2. Find the place where you are suppose to add your code (see comments.) 3. Add code to the method WEBUCATOR.createAutocompleter() to take a number as an argument and create an Ajax.Autocompleter for the input field named 'bookN'. 4. The autocompleter needs to call the URL ../Common/lookup-book.php. 5. lookup-book.php expects the desired text in a parameter named bookTitle. 6. Add code to the document load event to create the 3 autocompleters. 7. Run the code and verify that the suggestPopUp gets populated with the books list. 8. The book selection is still broken. Let's fix that by collecting only the text inside the element with a CSS class name of book_title. 9. Check that this last change works. 10. To add a little more of flair, let's make an animated progress icon show to the side of each input field when the AJAX call is in progress. 11. Use the image file ../Common/ajax-loader.gif. 12. Modify WEBUCATOR.createAutocompleter() to create and insert the img element with this icon right after each text box, initially hidden.

Code Sample: Scriptaculous/Exercises/importantbooks.html


---- Code Omitted ---<script type="text/javascript" charset="utf-8"> var WEBUCATOR = { createAutocompleter: function (/* ??? */) { //change this function to create the autocompleter // with the necessary options } }; document.observe('dom:loaded', function () { //add code here to create each of the 3 // autocompleters using the WEBUCATOR.createAutocompleter() method }); </script> ---- Code Omitted ---<body> <h1>Name 3 important programming books:</h1> 1 - <input type="text" id="book1" name="book1" /> <br /> 2 - <input type="text" id="book2" name="book2" /> <br /> 3 - <input type="text" id="book3" name="book3" /> <br /> <div id="suggestPopUp" class="suggestions"/> </body> ---- Code Omitted ----

Code Sample: Scriptaculous/Common/lookup-book.php


<?php $books = array( array('poeaa', 'Patterns of Enterprise Application Architecture', 'Martin Fowler'), array('ddd', 'Domain-Driven Design', 'Eric Evans'), array('wewlc', 'Working Effectively With Legacy Code', 'Michael Feathers'), array('tpp', 'The Pragmatic Programmer', 'Andy Hunt and Dave Thomas') ); $term = $_POST["bookTitle"]; //simulate lengthy task //sleep(1); echo "<ul>"; foreach($books as $b) { $pos1 = stripos($b[1], $term); $pos2 = stripos($b[2], $term); if ($pos1 !== false || $pos2 !== false) { echo "<li><div class=\"book\">"; echo "<img src=\"../Common/$b[0].jpg\" alt=\"$b[1]\" align=\"left\" style=\"width:50px;height:70px\"/>"; echo "<h2 class=\"book_title\">$b[1]</h2>"; echo "<h3>$b[2]</h3>"; echo "</div></li>"; } } echo "</ul>"; ?>

Where is the solution?

InPlaceEditor
The InPlaceEditor adds behavior to static text elements so that they, upon being clicked, become input text fields and a pair or buttons or links to accept or cancel the edit operation. Accepting the input causes an AJAX call to happen so the value can be persisted immediately. There are two flavors of in-place editing in script.aculo.us, Ajax.InPlaceEditor and Ajax.InPlaceCollectionEditor. The former makes the static text become a text box. The

latter uses a drop down list instead, limiting the possible resulting value to one of the list items. Creating either type of editor can be very simple but there are also a large number of options, which we will discuss shortly. First let's see how the minimal usage looks like.

Code Sample: Scriptaculous/Demos/inplace-simple.html


---- Code Omitted ---<script type="text/javascript" src="../../Libraries/prototype.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/effects.js"></script> <script type="text/javascript" src="../../Libraries/scriptaculous/controls.js"></script> <script type="text/javascript" charset="utf-8"> document.observe('dom:loaded', function () { new Ajax.InPlaceEditor('userName', '../Common/echo.php'); new Ajax.InPlaceCollectionEditor('rate', '../Common/echo.php', {collection: ['Excellent', 'Good', 'Fair', 'Unsatisfactory', 'Horrible']}); } ); </script> ---- Code Omitted ---<p> <span class="fieldLabel">Your Name:</span> <br/> <span id="userName" class="field">[your name here]</span> </p> <p> <span class="fieldLabel">How would you rate our service:</span> <br/> <span id="rate" class="field">Fair</span> </p> ---- Code Omitted ----

The above code uses the following helper PHP script named echo.php.

Code Sample: Scriptaculous/Common/echo.php


<?php //this script simply returns a posted value. By default the // name of the posted item is "value" but it can be changed // via the "key" query string parameter: echo.php?key=otherField $key = $_GET["key"]; if ($key === null) { $key = "value"; }

echo $_POST[$key]; ?>

This very basic example gives us the behavior displayed in the images below. Before clicking After clicking

The default operation of these controls is to accept the value when the Return key is pressed or the ok button is clicked. The control will abort the update if Esc is pressed or the cancel link is clicked. When the value is accepted an AJAX call to the given URL is made, posting the entered text in the value form parameter and the id of the element in the editorId parameter. The server is expected to return the final value of the operation, i.e. the HTML text that will become the new static text. It usually is simply the exact same value that was posted, but we will see that there are options to enable richer operations. One important thing to note is that the control is capable of multi-line edits. For that to happen the element's content must have line break characters (\r: carriage returns or \n: line feeds) or that the rows option is greater than 1. When that happens, the editor will be a textarea field instead of a simple input text field. As you can see, there's a lot of functionality in the control even with the default settings, but there's a lot that can be customized just by specifying a few options. Option ajaxOptions autoRows callback Description These are the options that are passed to the Ajax.Updater. Defaults to { evalScripts: true }. If the rows option is 1 and the editor is multi-line, then this value will be used instead. Defaults to 3. Function that is called to format the values posted in the AJAX call. It will be passed the form element created around the editor and the value entered in the editor. Specifies what type of element will be created to cancel the operation. The values can be 'button', 'link', or false. The last one avoids any cancel button or link. Text that is shown in a tooltip when the text is not in edit mode. Array of items used to create the drop down list in the Ajax.InPlaceCollectionEditor. Number of columns of the created input text field. Defaults to 40. Reference or id of an element used to put the text in edit mode.

cancelControl clickToEditText collection cols externalControl

Description The text itself remains capable of entering edit mode upon being clicked. Specifies if the input field will be activated or just receive focus fieldPostCreation after being created. Possible values are 'focus' or 'activate', which is the default (it selects the text.) CSS class name applied to the form element that is created to formClassName house the edit field. Defaults to inplaceeditor-form. The id of the created form. Defaults to ELEMENT_IDformId inplaceeditor, where ELEMENT_ID is the id of the original text element. CSS class that is applied to the text element when the mouse is hoverClassName over it. Specifies that the we expect the server to return the final HTML content of the element. It defaults to true. We use false when we htmlResponse want to process the server response and then determine the element's final content. This option works with the onComplete option. When using the loadTextURL option, this is the text that will be loadingText displayed while the text of the edit field is being fetched. Defaults to 'Loading...'. URL that will be called to provide the text content of the edit field. This can be useful when the text we want to edit is not loadTextURL HTML and we want the user to type in a syntax like Textile, Markdown, Wikitext, etc. Specifies what type of element will be created to accept the okControl operation. The values can be 'button', 'link', or false. The last one avoids any ok button or link. Called when the AJAX request ends. It gets passed the onComplete Ajax.Response and the element. Called when the control enters edit mode. It gets passed the onEnterEditMode control instance. Called when the mouse hovers over the element. It gets passed the onEnterHover control instance. The default value causes the element to be highlighted. Called when there's an AJAX error. It gets passed the control and onFailure the Ajax.Response. By default a simple alert box is displayed. This is called to give you a chance to tweak the form when it's onFormCustomization created. It is passed the control and the form element. Called when the control leaves edit mode. It gets passed the onLeaveEditMode control instance. Called when the mouse hovers over the element. It gets passed the onLeaveHover control instance. The default value causes an Effect.Highlight to

Option

Option

Description

be applied to the element. If the default parameter name of value doesn't work for you, you paramName can choose a different one with this option. Number of rows in the textarea input field that is created for rows multi-line edits. CSS class name that is applied to the saving text when it is being savingClassName displayed. savingText Text that is displayed while the AJAX call is in progress. size The desired size of the single-line editor field. No default value. Determines if HTML tags will be removed from the AJAX stripLoadedTextTags response text. Defaults to false. When true causes the control to accept the value when it looses submitOnBlur focus. The default is false.

script.aculo.us Conclusion
We can get a lot done with the effects and UI widgets that come with script.aculo.us. Learning when to use the effects is what will make the difference between a smooth user experience and that annoying website that insists in getting in your way with unnecessary tricks. The best part of script.aculo.us is the framework mentality that guided it's design. The library is full of extensions points that let you customize as little or as much as you need and still leverage reusable functionality. To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Potrebbero piacerti anche