JavaScript in 24 Hours Book
JavaScript in 24 Hours Book
Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
JavaScript® 24-Hour Trainer
Jeremy McPeak
JavaScript® 24-Hour Trainer
Published by
Wiley Publishing, Inc.
10475 Crosspoint Boulevard
Indianapolis, IN 46256
www.wiley.com
Copyright © 2011 Wiley Publishing, Inc.
ISBN: 978-0-470-64783-7
10 9 8 7 6 5 4 3 2 1
No part of this publication may be reproduced, stored in a retrieval system or transmitted in any form or by any means,
electronic, mechanical, photocopying, recording, scanning or otherwise, except as permitted under Sections 107 or 108 of
the 1976 United States Copyright Act, without either the prior written permission of the Publisher, or authorization through
payment of the appropriate per-copy fee to the Copyright Clearance Center, 222 Rosewood Drive, Danvers, MA 01923,
(978) 750-8400, fax (978) 646-8600. Requests to the Publisher for permission should be addressed to the Permissions
Department, John Wiley & Sons, Inc., 111 River Street, Hoboken, NJ 07030, (201) 748-6011, fax (201) 748-6008, or
online at http://www.wiley.com/go/permissions.
Limit of Liability/Disclaimer of Warranty: The publisher and the author make no representations or warranties with respect
to the accuracy or completeness of the contents of this work and specifically disclaim all warranties, including without
limitation warranties of fitness for a particular purpose. No warranty may be created or extended by sales or promotional
materials. The advice and strategies contained herein may not be suitable for every situation. This work is sold with the
understanding that the publisher is not engaged in rendering legal, accounting, or other professional services. If professional
assistance is required, the services of a competent professional person should be sought. Neither the publisher nor the author
shall be liable for damages arising herefrom. The fact that an organization or Web site is referred to in this work as a citation
and/or a potential source of further information does not mean that the author or the publisher endorses the information the
organization or Web site may provide or recommendations it may make. Further, readers should be aware that Internet Web
sites listed in this work may have changed or disappeared between when this work was written and when it is read.
For general information on our other products and services please contact our Customer Care Department within the
United States at (877) 762-2974, outside the United States at (317) 572-3993 or fax (317) 572-4002.
Wiley also publishes its books in a variety of electronic formats. Some content that appears in print may not be available
in electronic books.
Trademarks: Wiley and the Wiley logo are trademarks or registered trademarks of John Wiley & Sons, Inc. and/or its
affiliates, in the United States and other countries, and may not be used without written permission. JavaScript is a
registered trademark of Oracle America, Inc. All other trademarks are the property of their respective owners. Wiley
Publishing, Inc. is not associated with any product or vendor mentioned in this book.
This book is dedicated to my wife, Starla, and my
sons, Hayden and Evan. Thank you for your love,
support, and (especially) patience during the writing
of this book. This book is also dedicated to my
grandparents, Virginia and Don. Thank you for your
love and support.
Credits
Jeremy McPeak is a self-taught programmer who began his career by tinkering with websites in 1998.
He is the coauthor of Professional Ajax, Second Edition (Wiley, 2007) and Beginning JavaScript,
Fourth Edition (Wiley, 2009). He also contributes to Nettuts+ (http://net.tutsplus.com), providing
articles and video tutorials for ASP.NET. He is currently employed by an oil and gas company building
in-house conventional and web applications. Jeremy can be contacted via the p2p forums or through
his website at http://www.wdonline.com.
John Peloquin is a front-end engineer with experience ranging across JavaScript applications of
all sizes. John earned his bachelor of arts degree in mathematics from the University of California
at Berkeley and is currently a lead developer for a startup in the healthcare industry. When he is not
coding or collecting errata, John is often found engaged in mathematics, philosophy, or juggling.
Acknowledgments
First and foremost, I want to thank God for the blessings he has bestowed upon me. Also, a huge
thank-you goes to my wife for putting up with my late nights and sleep-deprived (grouchy) attitude.
Writing and producing a book requires a lot of people, and I know I cannot name everyone who has
had a hand in this project. But a very big thank-you goes to Scott Meyers for making this book hap-
pen, and to Ed Conner for putting up with me while getting this book into its final, printed form.
Thank you, John Peloquin, for providing your input and ensuring the text I wrote is accurate and
meaningful. Thanks, Ginny Munroe, for stepping in for Ed in the latter stages of the project, and to
Rosemarie Graham for organizing everything.
Lastly, thank you Nicholas C. Zakas, author of Professional JavaScript, Second Edition (Wiley, 2009)
and coauthor of Professional Ajax, Second Edition (Wiley, 2007), for getting me into this business.
Contents
Preface xix
Variables 9
Primitive Data Types in JavaScript 12
Determining a Variable’s Data Type 21
Keywords and Reserved Words 22
Try It 22
Lesson Requirements 22
Step-by-Step 23
Chapter 3: Functions 25
Function Declarations 26
Functions as Values 29
Passing Functions to Other Functions 30
Returning Functions 31
Try It 32
Lesson Requirements 32
Step-by-Step 32
Conditional Statements 35
Comparison Operators 35
Logical Operators 38
Assigning the Results of Conditions 41
CONTENTS
Chapter 5: Loops 49
Loop Basics 49
The for Loop 50
The while Loop 52
The do-while Loop 52
Try It 53
Lesson Requirements 53
Step-by-Step 54
Chapter 6: Scope 57
Global Scope 57
Functional Scope 58
No Block-Level Scopes 60
Variable Declarations 61
Identifier Lookup 61
Try It 62
Lesson Requirements 62
Step-by-Step 63
x
CONTENTS
xii
CONTENTS
xiii
CONTENTS
xiv
CONTENTS
Try It 269
Lesson Requirements 269
Step-by-Step 270
xv
CONTENTS
xvi
CONTENTS
Try It 382
Lesson Requirements 383
Step-by-Step 383
xvii
CONTENTS
Index 417
xviii
Preface
JavaScript is the most popular programming language in the world. It is used on all major
Internet sites, and even those not so important. Whether you make a purchase from an online retailer,
manage your funds using your bank’s website, visit a news site to get caught up on current events,
or simply read a blog written by one of the millions of bloggers worldwide, you have experienced
JavaScript in some way.
Despite this popularity, there has always been some form of confusion regarding JavaScript. Some
claim, because of its name, that it’s a light version of Java, a programming language originally devel-
oped by Sun and now owned by Oracle. This is not the case at all. While JavaScript’s syntax is similar
to Java’s, JavaScript is its own language used primarily to program the web pages you visit daily using
a web browser. Its purpose is to provide interactivity for the user — making the web page or web
application feel like a conventional application running on your computer.
JavaScript is a simple, yet complex, object-oriented language. While getting started with JavaScript
takes little time, it can take years to master the language. And even then, masters of the language
still find themselves learning something new about it.
This book aims to teach you JavaScript, to put you on the right path so that you can use the language
to add interactivity to your web pages and applications. Don’t worry if you don’t know how to pro-
gram. This book teaches you JavaScript from the very beginning — showing you the concepts behind
the basics of all programming languages. You’ll find that JavaScript can be a gateway to the world of
programming. It was for your author.
looking at some of JavaScript’s built-in objects, and you’ll learn to write your own objects to repre-
sent complex data.
Building on that base, you’ll learn how to use JavaScript to program the browser by creating and
manipulating new windows and scripting frames. Then you’ll learn how to interact with the HTML
elements in a page by adding them, removing them, and modifying their styles. You’ll learn how to
react to a user’s action by using events, and the different ways to do this for different browsers. You’ll
continue a study of the browser and page by learning how to animate elements, script HTML forms,
and leverage Ajax to send and receive data from the server.
You’ll then turn your studies toward errors, debugging, and best practices. You’ll learn how to iden-
tify and avoid common mistakes, as well as how to handle errors that are caused by stimuli outside
your control. For those tough-to-find errors, you’ll learn how to debug your code with Firebug.
After that you’ll learn some conventions that professional JavaScript developers follow, as well as
techniques for writing efficient and maintainable JavaScript.
Most lessons contain a tutorial called “Try It,” and each tutorial applies the concepts of the lesson
in an example. You can also watch the DVD to see your author complete the tutorial, as well as
offer commentary on the subject matter.
xx
PREFACE
After you’ve finished reading the book and watching the DVD, you can visit Wrox’s p2p forums,
where your author offers support.
Conventions
To help you get the most from the text and keep track of what’s happening, this book uses a number
of conventions.
Boxes like this one hold important, not-to-be forgotten information that is
directly relevant to the surrounding text.
Notes, tips, hints, tricks, and asides to the current discussion are offset and
placed in italics like this.
References like this one point you to the DVD to watch the instructional video
that accompanies a given lesson.
xxi
PREFACE
Because many books have similar titles, you may find it easiest to search by
ISBN; this book’s ISBN is 978-0-470-64783-7.
Once you download the code, just decompress it with your favorite compression tool. Alternatively, you
can go to the main Wrox code download page at www.wrox.com/dynamic/books/download.aspx to
see the code available for this book and all other Wrox books.
Errata
Every effort is made to ensure that there are no errors in the text or in the code. However, no one is
perfect, and mistakes do occur. If you find an error in this book or any Wrox book for that matter,
such as a spelling mistake or faulty piece of code, your feedback is appreciated. By sending in errata,
you can save a reader hours of frustration, and at the same time, you can help your author and
Wrox provide even higher-quality information.
To find the errata page for this book, go to www.wrox.com and locate the title using the Search box
or one of the title lists. Then, on the Book Search Results page, click the Errata link. On this page,
you can view all errata that have been submitted for this book and posted by Wrox editors.
If you don’t spot “your” error on the Errata page, click the Errata Form link and complete the form
to send us the error you have found. We’ll check the information and, if appropriate, post a message
to the book’s errata page and fix the problem in subsequent editions of the book.
xxii
PREFACE
p2p.wrox.com
For author and peer discussion, join the P2P forums at p2p.wrox.com. The forums are a web-based
system you can use to post messages relating to Wrox books and related technologies and interact
with other readers and technology users. The forums offer a subscription feature to e‑mail you topics
of interest of your choosing when new posts are made to the forums. Wrox authors and editors,
other industry experts, and your fellow readers are present on these forums.
At http://p2p.wrox.com you will find a number of different forums that will help you not only as
you read this book, but also as you develop your own applications. To join the forums just follow
these steps:
1. Go to p2p.wrox.com and click the Register link.
2. Read the terms of use and click Agree.
3. Complete the required information for joining as well as any optional information you wish
to provide and click Submit.
4. You will receive an e‑mail with information describing how to verify your account and com-
plete the joining process.
You can read messages in the forums without joining P2P, but in order to post
your own messages you must join.
Once you join you can post new messages and respond to messages other users post. You can read
messages at any time on the Web. If you would like to have new messages from a particular forum
e‑mailed to you, click the Subscribe to This Forum icon by the forum name in the forum listing.
For more information about how to use the Wrox P2P, be sure to read the P2P FAQs for answers to
questions about how the forum software works as well as many common questions specific to P2P
and Wrox books. To read the FAQs, click the FAQ link on any P2P page.
xxiii
Introduction to JavaScript
▶▶LESSON 3: Functions
▶▶LESSON 5: Loops
▶▶LESSON 6: Scope
There are two ways to use <script/> elements: either by embedding JavaScript code directly into
the page, in which case it is referred to as inline code, or by using the src attribute to refer to that
external file.
To add inline code to a web page, place the code between the opening and closing tags, as the follow-
ing code shows:
<script type="text/javascript">
function inlineScript() {
var message = "Hello, World!";
alert(message);
}
inlineScript();
</script>
Don’t worry about the code just yet — you’ll learn what this code does in due time. Even though
browsers automatically assume that code within a <script/> element is JavaScript, it is considered
good practice to include the type attribute. The browser stops loading the rest of the page when it
encounters a <script/> element; it loads the code and interprets it before processing the rest of the
HTML page.
It’s important to note that inline code blocks, as shown in the previous code sample, should be included
in a CDATA block in XHTML documents. Failing to do so does not currently result in an error in the
browser. In fact, all major browsers still load the JavaScript and execute it normally. However, there is
no guarantee that future browsers will behave as today’s browsers do. So, if you use XHTML, future-
proof your inline script by encapsulating it in a CDATA section, like this:
<script type="text/javascript">
<![CDATA[ ... ]]
function inlineScript() {
var message = "Hello, World!";
alert(message);
}
inlineScript();
]]>
</script>
Adding an external JavaScript file to the page incorporates the src attribute. It’s possible to cut the
JavaScript code from the previous example, without the <script> tags, and paste it into another
file. Then, by using the src attribute, the page can reference the external JavaScript file like this:
<script type="text/javascript" src="sample.js"></script>
This <script/> element references an external JavaScript file called sample.js. Using the .js
extension is a convention nearly all JavaScript developers use. It does not matter how the file is
named — the browser will download it and interpret the file’s contents as JavaScript code.
Just as with inline code, the browser halts processing of the HTML page when it encounters the
<script> tag, but it also has to download the file before it begins interpreting it. After the browser
loads the JavaScript from the external file, it resumes processing the HTML page.
The <script/> Element ❘ 5
Choosing whether to use inline or external scripts is ultimately up to you. However, the majority of
developers use external JavaScript files for the following reasons:
➤➤ External scripts are much easier to maintain than inline scripts, especially if you use the
same code in several pages. Imagine having to edit multiple HTML pages to make identical
changes to blocks of inline code. That would certainly be a time-consuming task. With an
external file, you simply make changes to one file.
➤➤ External scripts are cached by the browser, just as images and style sheets are. The browser down-
loads the script file once and uses it for subsequent page requests as long as the script file hasn’t
changed. This grants a certain performance gain in that the browser doesn’t have to download the
same code over and over again, whereas it does if the code is embedded in the HTML page.
How and where <script/> elements are placed in the page do matter — they are loaded and inter-
preted in the order in which they’re placed in the page.
Tag Placement
The <script/> element is one of the few elements that can go almost anywhere in an HTML page:
it can go within the <head/> element or the <body/> element. The more traditional place is within
the <head/> element, along with the <style/> and <link/> elements. The following HTML
demonstrates this:
<html>
<head>
<title>Sample Page</title>
<script type="text/javascript" src="sample.js"></script>
</head>
<body>
</body>
</html>
While this keeps resources for the HTML page in one place within the document, remember that
the browser halts all processing of the HTML document once it encounters a <script/> element, in
order to download and load the script that it references. So placing <script/> elements at the top of
the document has the side effect of delaying the browser from loading the page’s content. To users,
this is a performance issue, as they see a blank web page while the browser downloads and loads the
JavaScript.
Because of this, it’s becoming a best practice to place all <script> tags just before the closing
<body> tag, like this:
<html>
<head>
<title>Sample Page</title>
</head>
<body>
This way, the browser loads and renders the entire page before loading JavaScript code. Even though
the net download time is the same regardless of the <script> tags’ placement, the user sees their
requested web page sooner and perceives a faster web page.
Try It
In this lesson, you add an external JavaScript file to a web page.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a folder somewhere in your file system called JS24Hour, and create a subfolder called Lesson01.
Store the files you create in this lesson in the Lesson01 folder you created.
Step-by-Step
Add an external JavaScript file to a web page by following these steps:
1. Open your text editor and type the following JavaScript code:
function inlineScript() {
var message = "Hello, World!";
alert(message);
}
inlineScript();
<body>
4. Save this file as lesson01_sample01.html, and make sure you save it in the same location
as lesson01_sample.js.
5. Open the HTML file in your web browser by double-clicking the file. You should see an alert
box saying “Hello, World!” when the browser loads the page.
Please select Lesson 1 on the DVD to view the video that accompanies this les-
son. You can also download the sample code for all lessons from the book’s
website at www.wrox.com.
2
Variables and JavaScript Syntax
In the midst of teaching you how and where you add script to your pages, the previous lesson
introduced the following simple script:
function inlineScript() {
var message = "Hello, World!";
alert(message);
}
inlineScript();
To fully understand what this code does, it’s important to know the basics of the JavaScript
language. The core of any language is its syntax — a set of rules that dictate how certain
pieces of the language fit together. Think of a spoken language and how difficult it would be
to communicate with others if that language did not have a set of rules that defined how words
are arranged into complete and understandable sentences. The same is applicable to program-
ming languages. In the case of JavaScript, the browser wouldn’t know what to do with your
code if it weren’t for the rules set forth by the language specifications. Syntax defines how very
small pieces of code fit together in order to perform some kind of work.
This lesson focuses on the syntax of one line of code in the listing at the top of the page — the
variable declaration:
var message = "Hello, World!";
In this lesson, you learn about variables and the syntax required to make them.
Variables
Developers write programs to make working with data easier. Large amounts of data are
typically stored outside the program, via the file system or a database, but many times that
data is brought into the application and stored in the computer’s memory. Because of this, all
10 ❘ Lesson 2 Variables and JavaScript Syntax
programming languages need some way to access the data stored in memory in order to perform
work on it.
Variables are parts of a programming language that grant programmers the ability to store and
retrieve data stored in memory. You can think of a variable as a box — the box is empty when you
create the variable, and the box is full when you assign that variable to hold data.
Some languages are very strict as to what type of data you can put in a particular box. These types
of languages are called strongly-typed languages. For example, some languages have boxes for num-
bers and numbers only — trying to put pieces of text in a box for numbers results in an error.
JavaScript is not one of these strongly-typed languages. Instead, it is a loosely-typed language — it
sees a variable as a box that can hold any type of data at any given time. That box may grow or
shrink to accommodate the data you put into it, but it does not discriminate between what types of
data can fit in the box. This means that a variable can contain a text value at one time, and a num-
ber value at another.
Defining a variable in JavaScript requires the use of a variable declaration statement. A statement
is a small piece of code that performs some kind of action. Any given program is made up of one or
more statements in a specific sequence. The following code shows a variable declaration statement:
var myVariable;
Most JavaScript statements end with a semicolon, as shown in this code. But it is also valid to omit
them, as shown in the following code:
var myVariable
However, omitting the semicolon is considered a bad programming practice and is strongly discour-
aged. This code is considered bad code.
Variable declarations begin with the var keyword, a word reserved by the language for a specific
use. The var keyword is used specifically for declaring variables. Following the var keyword is the
variable’s identifier, or name. Identifiers are made up of one or more characters, and they must fol-
low these rules:
➤➤ An identifier’s first character must be a letter, an underscore, or a dollar sign. Many JavaScript
libraries make extensive use of the dollar sign. So, it is suggested that you avoid using it.
➤➤ All other characters can be letters, whole decimal numbers (0–9), underscores, or dollar signs.
➤➤ An identifier cannot be the same as a keyword or reserved word (more on these later).
It is important to note the casing of each letter in the variable’s identifier. JavaScript is a case-
sensitive language: It distinguishes between uppercase and lowercase letters. The previous identifier,
Variables ❘ 11
myVariable, is not the same as myvariable, Myvariable, mYVariable, or any such variation. It’s
important to always be aware of an identifier’s case, as failing to do so is a very common cause of
errors.
The myVariable identifier uses what’s called camel-case. This means that the first letter of the identi-
fier is lowercase, and each word in the identifier starts with an uppercase letter. Camel-casing isn’t
a requirement of JavaScript, but most of JavaScript’s built-in identifiers use camel-case. As a pro-
grammer, it’s a good idea to follow the conventions of the language you’re using. In the case of
JavaScript, that means following the camel-casing guidelines when naming your own identifiers.
The variable declaration statement in the previous code doesn’t contain any data. It has been
declared, but it has not been initialized, or assigned a value. To assign data to a variable, use the
equals sign (=), also known as the assignment operator, to assign a value to the variable. An operator
is a symbol or keyword that takes one or more operands as input and performs an action on those
operands.
var myVariable;
myVariable = "some text";
This code declares and initializes myVariable in two separate statements. The first statement is a
declaration statement (declaring the variable with the var keyword), and the second statement is an
initialization statement (giving the variable an initial value). Notice there is no var keyword used
in the initialization line. The var keyword is used only for declaring variables. Once a variable is
declared, it is no longer necessary to use var for that variable.
The operands in an assignment operation, as shown in the initialization statement in the previous
example, are on both sides of the assignment operator (the equals sign). The operand on the right is
an expression or value that is assigned to the operand on the left (the variable).
Using two statements, as in the previous example, is sometimes necessary, as the data for a variable
may not exist at the time the variable is declared. However, a variable can be declared and initial-
ized in one statement. The following code shows this:
var myVariable = "some text";
Using one statement is preferred, as it is easier to read and results in a smaller file size.
A variable’s value can be overwritten with another value at any time. Using the myVariable from
the previous example, the following code overwrites the "some text" value with that of a number:
myVariable = 100;
The variable now contains the number 100. There is no limit to the number of times a variable can
be overwritten.
A variable can also be assigned the value contained in another variable. The following line creates a
new variable called myVariable2, and it is initialized with the value contained within myVariable:
var myVariable2 = myVariable;
So now the myVariable2 variable contains a value of 100. Even though these two variables con-
tain the same value, and it may look as if they are linked, they are, in fact, independent from one
12 ❘ Lesson 2 Variables and JavaScript Syntax
another. In other words, modifying the value contained in myVariable has no effect on the data in
myVariable2. This variable independence comes from the fact that numbers are a primitive data
type in JavaScript.
JavaScript’s primitive data types hold only one value at a time and are immutable. This means that
once a primitive value is created, it cannot be changed. Instead, the variable’s original value must
be destroyed and replaced with a new value. A primitive variable directly contains the value it is
assigned. Assigning a variable the value of a primitive data type copies the value of the second vari-
able, and that copy is assigned to the first variable.
Consider the following code, which recreates the myVariable and myVariable2 variables:
var myVariable = 5,
myVariable2 = myVariable; // copies the number and assigns copy
Before I discuss this code, you undoubtedly noticed some new syntax at the end of lines two, three,
and four. These are comments. Comments are human-readable annotations in code. They are ignored
by JavaScript and are meant to provide information to anyone reading the code. The types of com-
ments presented in the previous code are single-line comments. They begin with two forward slashes
(//), and anything after the slashes is considered a comment. Again, comments are completely ignored
by JavaScript, but they give the reader insight about particular lines of code.
Something else you might have noticed is a comma separating the initialization of the myVariable
and myVariable2 variables, along with the omission of the var keyword in the second line. The first
two lines of this code initialize two variables in a single statement. These multiple variable initializa-
tion statements begin with the var keyword, and each subsequent variable initialization is separated
with a comma. You can initialize as many variables as you want within a single statement. Just start
the statement with var, separate each initialization with a comma, and end the statement with a
semicolon.
Variables ❘ 13
It does not matter if you initialize variables in one or multiple statements. It’s a
personal preference. For the sake of clarity, this book opts to use multiple state-
ments to initialize variables.
The first line of code initializes the myVariable variable as containing a value of 5. This value is of
the Number data type. The second line of code initializes the myVariable2 variable by copying the
value contained within myVariable and assigning that copy to myVariable2. The third line of code
reassigns a new value of 6 to myVariable — destroying its original value of 5. But myVariable2 still
contains a value of 5, since it was assigned a copy of myVariable’s original value. Thus, both vari-
ables are separate and independent of each other.
Every time a variable containing a value of a primitive data type is used in code, a copy of that value is
made and used in its place. The only way to modify a variable’s value is to assign the variable a new value.
Numbers
JavaScript is different from most other languages in that it does not distinguish between different
types of numbers, such as integer and decimal numbers. Instead, all numbers are represented by the
Number type, which consists solely of floating-point numbers, rational numbers with a very large
value range.
In most JavaScript applications, numbers are used as numeric literals. This means that numbers
appear directly in the code. The examples up to this point have used whole number, or integer, liter-
als, like the following code:
var myNumber = 5;
But you can also use a numeric literal to assign a number with decimals, like this:
myNumber = 3.14;
Numbers are primarily used to perform mathematical calculations with arithmetic operators.
Arithmetic Operators
This code creates a variable called myNumber. Its value is the result of a mathematic operation add-
ing 2 and 2 together; thus, the value assigned to the myNumber variable is 4.
Mathematical operations are not limited to using numeric literals; variables containing a numeric
value can be used in any mathematical expression. The following code uses the myNumber variable
created earlier in another mathematical calculation:
myNumber = myNumber - 4;
Remember that assignment operations always go from right to left. So the expression on the right
side of the equals sign (myNumber - 4) is evaluated, and the resulting value is assigned to the variable
on the left side of the equals sign. Since myNumber’s value is 4, this code is essentially the same as the
following:
myNumber = 4 - 4;
Arithmetic operations are not limited to one operation per statement; a statement can contain multiple
operations, like the following code:
myNumber = 1 + 2 * 3 – 4 / 2;
Executing this statement results in the myNumber variable’s containing a value of 5. JavaScript’s arith-
metic operators have a precedence order similar to the “My Dear Aunt Sally” order in mathematics.
Multiplication and division operations have a higher precedence than addition and subtraction, so
they execute first. The previous code could be rewritten to the following code after the multiplication
and division operations have been performed:
myNumber = 1 + 6 - 2;
Also as in mathematics, you can use parentheses to give a higher precedence to certain operations.
The following code adds some parentheses to the previous code sample:
myNumber = (1 + 2) * 3 - 4 / 2;
By placing parentheses around the 1 + 2 operation, you give it a higher precedence than multiplica-
tion or division, so it is executed first, followed by the multiplication and division operations, and
finally by the subtraction operation. Because the 1 + 2 operation is executed first, the result value
has changed from 5 to 7.
Operator precedence determines the outcome of mathematical operations. So it’s important to keep
operator precedence in mind when performing them.
The final operator, the modulus (or remainder) operator, is represented by a percent sign (%). It performs
a division operation on the two operands and returns the remainder of the quotient. The following code
demonstrates this:
var remainder = 3 % 2; // returns 1
var remainder2 = 2 % 2; // returns 0
Variables ❘ 15
The modulus operator is very handy when you’re testing whether a numeric value is even or odd. If
the result of a modulus operation by 2 is 0, the number is even. If it is any other number, the number
it’s odd. Look at the following code:
var isEven = 4 % 2; // 0 - so even
var isOdd = 1 % 2; // 1 - so odd
Any operation involving a NaN value always results in NaN. This has the side effect of possibly ruin-
ing mathematical operations that contain multiple steps.
Strings
Strings are text values — sequences of zero or more characters. Strings are delimited by double-
quote (") or single-quote characters ('). The following code shows two valid strings:
var str1 = "Hello, World";
var str2 = 'Hello, World (again)';
A string beginning with a double quote must end with a double quote; likewise, a string beginning
with a single quote must end with a single quote. For example, the following code is not correct and
results in an error because the beginning and ending quotes don’t match:
var str3 = 'Hello, World (yes again!)"; // invalid string literal
It is, however, perfectly fine for a string delimited with double quotes to contain single quotes, like
this:
var str4 = "Hello, Jeremy's World";
Similarly, strings delimited with single quotes can contain double quotes:
var str5 = 'Jeremy said, "Hello, World!"';
Unlike other languages, JavaScript does not discriminate between the type of quotes used to delimit
strings. Using single quotes does not change how JavaScript treats the string compared to using double
quotes. They’re all the same, so which type of quote you use to delimit strings is completely up to you.
The examples in this book primarily use double quotes.
A string delimited with single quotes cannot use a single quote inside the string. The following code
is incorrect and will generate an error:
var str6 = 'Hello, Jeremy's World'; // error
16 ❘ Lesson 2 Variables and JavaScript Syntax
This is incorrect because JavaScript thinks that the string ends after the y in 'Hello, Jeremy'. It treats
the second single quote as the end of the string and doesn’t know what to do with the text s World';.
The same is true for double-quote characters in a string delimited with double quotes, like this:
var str7 = "Jeremy said, "Hello, World!""; // error
JavaScript thinks the string in this statement is "Jeremy said, ", and it again doesn’t know what to
do with the remainder of the statement: Hello, World!"";.
In addition to quotation marks, JavaScript has many other special characters that cannot be typed
within a string. They can, however, be represented with escape sequences. Think of an escape
sequence as an HTML entity: It is a set of characters that represents a particular keystroke when
rendered.
For example, HTML entities start with an ampersand (&) and end with a semicolon. The "
HTML entity looks funky in the HTML markup, but it renders as a double quotation mark in the
browser. Similarly, the \" escape sequence in JavaScript looks funky in the code, but it is interpreted
as a double quotation mark when it occurs in a JavaScript string.
As an example, the following code revisits the str7 variable declaration and assigns it a string value
containing escape sequences:
var str7 = "Jeremy said, \"Hello, World!\""; // valid string
Escape sequences in JavaScript begin with a backslash, and are followed by one or more characters
to denote the special character the escape sequence represents. The following table lists the more
commonly used escape sequences:
Escape Sequences
\n New line
\t Tab
\' Single quote (used when the string is delimited with single quotes)
\" Double quote (used when the string is delimited with double quotes)
\\ Backslash
Escape sequences are used when you want to store such a character in a literal string.
Modifying Strings
Like the Number data type, strings are immutable. A string value cannot change once it has been
created. Modifying a string value involves destroying the original string value and replacing it with
a different value.
Variables ❘ 17
Say a variable called str1 contains a string value of "This is a string.". The following code
demonstrates this:
var str1 = "This is a string.";
The string value assigned to str1 cannot technically be altered; however, a concatenation, or join-
ing, operation can use the original value contained within str1 and join it with another string value
to create a new string value.
Concatenating two or more strings requires the use of the concatenation operator: the plus sign (+).
The following code concatenates the original string value in str1 with another string value to mod-
ify the data contained within str1:
str1 = str1 + " Hello, World!";
The value now contained within str1 is "This is a string. Hello, World!".
Focus on the code that appears to the right of the assignment operator (the equals sign). This expres-
sion is a concatenation operation. The first operand retrieves a copy of str1’s value (remember that
values are not used directly; they are copied). The second operand is a string literal, and the con-
catenation operation joins the two values into one value — a value that is then assigned to the str1
variable.
Note the space before Hello in the string literal. If the space had been left out, the concatenated
string would be "This is a string.Hello World!". JavaScript will not format strings automati-
cally for you; you must do that yourself.
The same principle can be applied to multiple variables. Look at this code:
var firstString = "This is a string.";
var greetingString = "Hello,";
var name = "Jeremy";
var finalString = firstString + " " + greetingString + " " + name + "!";
This code initializes four strings. The first three are simple strings, and the fourth concatenates the
first three into a greeting. Notice that this time, the first three string values do not contain spaces to
help with formatting. Instead, the concatenation operation in the fourth line of code inserts spaces
as needed. So the value assigned to finalString is this: "This is a string. Hello, Jeremy!".
This code declares and initializes a variable called possiblyConfusing. The expression on the right
side of the equals sign consists of a string value, a + operator, and a number. JavaScript is smart enough
to know that the string "This is the number " cannot be used in a mathematical operation.
18 ❘ Lesson 2 Variables and JavaScript Syntax
Therefore, JavaScript converts the number 1 to a string and concatenates the two operands together.
The value contained within the variable is "This is the number 1".
When a string is involved in an operation consisting of the + sign, as in the previous line of code, all
non-string values to the right of the string are converted to string values. The operand to the left of
the operator maintains its original value, but the end result is a string value
Keep that in mind as you look at the following code and determine the result of the operation:
var probablyConfusing = 1 + 2 + "45";
In this code, the expression to the right of the = operator consists of three operands. The first two are
number values, and the third is a string. Since the two numbers are to the left of the string, they are
treated as numbers, and the + operator performs an addition operation. So this code can be simplified
to this:
var probablyConfusing = 3 + "45";
Now JavaScript sees a number value and a string value and treats the + operator as a concatenation
operator. The final value that is assigned to the probablyConfusing variable is "345". Even though
the string looks like a number, it is still a string. Any operation involving the + operator and a string
results in a concatenation operation.
A string can be converted to a number using any other arithmetic operator. Look at the following
example:
var probablyConfusing = "1" + 2 - 3;
In this code, the string "1" is concatenated with the numeric value 2. So this code can be simplified
like this:
var probablyConfusing = "12" - 3;
The subtraction operation, however, is not a valid string operator. So JavaScript converts the string to
a numeric value and performs the operation. The result of this code is 9, since "12" is converted to 12.
This executes the parseInt() function. However, it returns a value of NaN because it does not have
any data to work with. Some functions, like parseInt(), accept data in the form of arguments, a
set of special variables used by the function. For example, the parseInt() function accepts a string
value to convert to a number, like this:
var num = parseInt("45");
Variables ❘ 19
This code converts the string "45" to the number 45 and stores it in the num variable. The parseInt()
function returns an integer (a whole number) value. If a string containing a decimal number is passed
to the parseInt() function, it drops the decimal and the numbers after it and returns the whole
number. It does not round up. The following code demonstrates this:
num = parseInt("1.8392"); // returns 1
In this code, a string containing a decimal number is passed to the function. The result is the whole
number: 1.
It is sometimes desirable to keep the numbers after the decimal. In cases such as these, use the
parseFloat() function. An example is shown here:
var floatingNum = parseFloat("1.8392"); // returns 1.8392
In this code, the same string is passed to the parseFloat() function, and the result is the exact
number passed to the function.
The parseInt() and parseFloat() functions parse the strings passed to them. They work by start-
ing at the beginning of the string and look at each character in the string. The function determines
if the character is a valid number. If it is, the function uses that numeric value to start building the
number to return and looks at the next character. If the character isn’t a number, JavaScript assumes
the number has ended and stops parsing the string. This means that these two functions can parse
strings that also contain alphabetical characters. The following code shows this:
var num = parseInt("1234abcd"); // returns 1234
var decNum = parseFloat("1.234abcd"); // returns 1.234
var num2 = parseInt("1234abcd5678"); // returns 1234
This code demonstrates how parseInt() and parseFloat() parse strings that contain alphabetical
characters. In all three lines, the parsing functions begin parsing the strings with the first character,
and they stop parsing when they encounter a non-numeric character. The third line demonstrates
this better, since the string passed to parseInt() contains several numeric characters separated by a
group of alphabetical characters.
The parseInt() function accepts an optional second argument called a radix. The radix determines
what numeral system the string passed in the first argument is converted to. For example, we humans
use a base 10 numeral system, but computers use a base 2 (binary) system. The binary number 100 is
actually the base 10 number 4. When calling parseInt(), it’s important to specify the radix; other-
wise, JavaScript may interpret the string as a number of a different numeral system. So, if you want
to convert a string to a number that we humans use, specify the radix of 10, like this:
var parsedNumber = parseInt("1043", 10); // 1043
Boolean
All computer systems use a binary system at their most basic level. The CPU and other processors
are made up of transistors that are either turned on or turned off. The hard drive stores data by
magnetically positively or negatively charging a tiny piece of the platter.
20 ❘ Lesson 2 Variables and JavaScript Syntax
A bit is the simplest unit in a computer system. It can have only one of two states: It can either be
turned on (1) or it can be turned off (0). In programming, the Boolean data type is typically the
most basic data type, as it exactly represents the state of a bit. Boolean values are either true or
false (on or off, respectively).
JavaScript has two Boolean literals: true and false. These values are different from the numeric
equivalents of 1 and 0 even though they can be used to mean the same thing. You assign Boolean
values like this:
var trueVar = true;
var falseVar = false;
Keep JavaScript’s case-sensitivity in mind here. The true and false literal values are not the same
as True and False, respectively.
The String data type follows along the same lines as numbers. An empty string ("") is a falsy value,
whereas anything other than an empty string is a truthy value. The following lines of code show
some examples:
truthyVar = " ";
truthyVar2 = "This is a string.";
falseyVar = "";
JavaScript has two more primitive data types, and they both serve as falsy values.
The first line declares the myVariable variable by using the var keyword followed by the variable’s
name. No value is assigned to it. Therefore, its value is undefined until another value is assigned to it.
The Null data type comes into play when you are dealing with objects. Later lessons will delve into
objects, but for now, think of an object as a thing that contains multiple types of data. An empty object
is said to be null. The value of null is best used to assign a value to a variable that will later hold
an object. The following line of code assigns null to a variable:
var obj = null;
Don’t worry about the Null data type right now. Later lessons will revisit null in the context of dis-
cussing objects, where it’ll make much more sense.
These are fairly self-explanatory, except for null. Remember that null refers to an empty object.
Therefore, its data type is considered to be an object.
Look at the following code for an example:
var myVariable = 3.14;
var variableType = typeof myVariable; // "number"
The first line of code creates the myVariable variable and initializes it with a number value of 3.14. The
second line uses the typeof operator against the myVariable operand to get the variable’s data type.
In this case, the typeof operator returns "number".
22 ❘ Lesson 2 Variables and JavaScript Syntax
JavaScript also has a set of words that are reserved words. These are words that are not currently
used in the language, but are reserved for future use. Following is the complete list of reserved words:
Try It
In this lesson, you learn about variables, primitive data types, and some of JavaScript’s syntax.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
Try It ❘ 23
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson02 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson02 folder.
Step-by-Step
Create and initialize three variables. The first variable should contain a numeric value. The sec-
ond variable’s initialization should use the value of the first variable in an addition operation. The
third variable should contain a string followed by the numeric value in the second variable. Use the
alert() function to display the string value in the third variable.
1. Open your text editor, type the following code, and save the file as lesson02_sample01.js.
var myVariable = 3;
var myVariable2 = myVariable + 2;
var myVariable3 = "The value is: " + myVariable2;
alert(myVariable3); // alert the value
2. Open another instance of your text editor, type the following HTML, and save it as
lesson02_sample01.htm:
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<script type="text/javascript" src="lesson02_sample01.js"></script>
</body>
</html>
3. Open the HTML file in your browser. You will see an alert box displaying a value of
"The value is: 5".
Please select Lesson 2 on the DVD to view the video that accompanies this les-
son. You can also download the sample code for all lessons from the book’s
website at www.wrox.com.
3
Functions
Variables are certainly an important part of any programming language; developers could not
create efficient and meaningful applications without them. However, a language without any
means of organizing code can lead to inefficient applications. In fact, some early programming
languages, such as those used for punch cards (and languages based on them like RPG II), lacked
organizational constructs, which resulted in many lines of repeated code.
Consider an application that performs the same series of operations on multiple variables. The
following shows an example of such an application:
var valueOne = 100;
valueOne = valueOne + 150;
valueOne = valueOne / 2;
valueOne = "The value is: " + valueOne; // results in "The value is: 125"
var valueThree = 2;
valueThree = valueThree + 150;
valueThree = valueThree / 2;
valueThree = "The value is: " + valueThree; // results in "The value is: 76"
This is code duplication. The same operations are duplicated three times for three variables.
Imagine having to repeat these same operations over and over again for as many variables as
your program needs. Also imagine needing to change the addition operation (+ 150) to a sub-
traction operation (- 150). Not only would you have to make three changes with this code, but
you’d also need to make the same change to any other variable that used the same pattern of
statements. It’s time-consuming and makes updates more prone to error.
26 ❘ Lesson 3 Functions
Believe it or not, many older programming languages require this type of programming — typing
statement after statement as many times as needed to perform the same set of operations for as
many variables as needed. Not only is it time-consuming to create, but it’s a nightmare to maintain
when updates are required. This is the primary reason most programming languages today have
functions.
Functions are arguably the most important part of any programming language. They encapsulate
one or more statements to perform a certain task, and you can call, or execute, them whenever you
want. But in JavaScript functions are exceptionally important because they have first-class status. In
programming, first-class means that functions can be created on-the-fly, they can be stored in variables,
and they can be passed as arguments to other functions. In other words, functions are a type of
value in JavaScript, and this is a powerful feature that makes JavaScript one of the best languages
to program with.
You’ve already been introduced to a few functions. The parseInt() and parseFloat() functions
convert string values to numeric values. You’ve also seen a function called alert(), which displays
information in an alert box in the browser. You know how to call functions and pass arguments to
them, so now it’s time to learn how to write your own functions.
Function Declarations
Functions are declared in function declaration statements by means of the function keyword. Unlike
the statements seen in Lesson 2 and at the beginning of this lesson, function declaration statements do
not end with a semicolon. The following code shows the syntax of a function declaration statement:
function functionName(parameter1, parameter2, etc) {
// statements here
}
Function declaration statements begin with the function keyword, followed by a space and the
function’s identifier. The identifier should be unique throughout the entire HTML page, but at the
same time it should be meaningful. For example, there’s no guesswork as to what a function called
sum() does; anyone reading the code can instantly recognize that the function adds two or more
numbers together. Contrast that with a function named function4392(). Not even the person who
wrote a function with that name would be able to remember what the function does. Readability is
very important in writing code — especially when you’re revisiting the code to make updates.
Function identifiers follow the same rules for identifiers outlined in Lesson 2:
➤➤ The first character must be a letter, underscore, or dollar sign.
➤➤ All other characters can be letters, numbers, underscores, or dollar signs.
➤➤ An identifier cannot be the same as a keyword or reserved word.
After the identifier is a list of parameters separated by commas that are encapsulated within a pair
of parentheses. Parameters are special variables that a function may need in order to properly do
its work.
Function Declarations ❘ 27
You may hear and read the terms parameter and argument used interchangeably,
but there is a distinction between them. The term parameter is more appropriately
used to refer to an identifier between the parentheses in a function declaration.
For example: “The function has three parameters.” The term argument is more
appropriately used to refer to a value passed to a function: “Pass the value 3 as an
argument to the function.”
Parameters are optional. Functions do not have to have them, but on the other hand, a function
can accept multiple arguments. Regardless of the amount of parameters a function has, the opening
and closing parentheses are required in the declaration. The following example shows the difference
between a zero-parameter function and a function accepting two parameters:
function noArgumentFunction() {
The first function does not define any parameters due to the lack of any identifiers inside the paren-
theses. The second function, however, lists two parameters, and you can see how they are used
within the function’s body — just like variables.
After the parameter list is an opening curly brace. This marks the beginning of the function’s body,
and a closing curly brace marks the end. When called, the function executes the statements within
its body. Any statements outside the function’s body are not executed by the function.
The function’s body, or any amount of code occurring within a pair of curly
braces, is sometimes referred to as a block of code.
Let’s apply this information by modifying the code at the beginning of this lesson, and write a func-
tion to make the code easier to type and maintain. This function is called augmentValue(), and it
accepts only one argument, called originalValue:
function augmentValue(originalValue) {
var augmentedValue = originalValue;
augmentedValue = augmentedValue + 150;
augmentedValue = augmentedValue / 2;
augmentedValue = "The value is: " + augmentedValue;
return augmentedValue;
}
28 ❘ Lesson 3 Functions
Compare the statements inside the function to the statements at the beginning of this lesson. The
original value is stored within a variable and modified by the addition of 150, then divided in half,
and finally converted into a string. With the exception of the last statement (the return statement),
all statements in this function perform the same exact operations as the code listed at the beginning
of this lesson. The only difference is the names of the variables.
The last line of the function uses the return keyword to exit the function and return the value stored
within augmentedValue to the code that called the function. To use this function, simply call it by
using its name, and pass a number value as an argument to the function (as with the parseInt() and
parseFloat() functions in Lesson 2). The following code does just that:
var valueOne = augmentValue(100); // results in "The value is: 125"
var valueTwo = augmentValue(50); // results in "The value is: 100"
var valueThree = augmentValue(2); // results in "The value is: 76"
When the function executes, the parameter originalValue contains the value passed to the func-
tion as an argument. So in the first line of this code, originalValue contains the value 100. When
augmentValue() is called on the second line, originalValue contains 50, and in the final call to
augmentValue(), originalValue is 2. The various operations are performed on each value, and the
result is returned to the valueOne, valueTwo, and valueThree variables, respectively.
So isn’t that easier? If you simply organize repetitive code into a function, the entire code is much
easier to type, and maintenance becomes much simpler. Again, imagine that instead of adding 150
to the original value, you need to subtract 150 from it. In the old code you would have to make at
least three changes and hope you don’t forget to change the code anywhere else it’s used. If you use a
function, however, making that change is simple and easy because you have to make only one change
to the function, and that one change is effective every time the function is called.
The use of functions is an example of code reuse as opposed to the code duplication seen at the
beginning of this lesson. Instead of duplicating the same operations over and over again, you simply
reuse one piece of code (the function) to get the same results. It makes application development and
maintenance much easier.
It is not a requirement for functions to return a value; a function should return a value only if it needs
to do so. In the case of augmentValue(), the function was written to replace code that resulted in a
string value’s being stored in a variable.
It’s also important to note that functions immediately exit when they reach a return statement.
Any statements following a return statement never execute. The following example rewrites the
augmentValue() function to demonstrate this:
function augmentValue(originalValue) {
var augmentedValue = originalValue;
return augmentedValue;
The bolded lines never execute in this version of augmentValue() because the function exits before
those statements can execute. So, in effect, this function doesn’t augment the original value at all; it
simply returns the same value that is passed to the function.
There are a few circumstances where code might execute after a return state-
ment, but do not worry about that right now.
You can also use the return statement without specifying a value to return. Developers use this
technique to exit a function early if it does not need to return a value.
Functions as Values
Using the function declaration statement isn’t the only way to create a function; you can create an
anonymous function. Anonymous functions, as the name suggests, don’t have a name, and they are
quite useful despite that fact.
Anonymous functions look very much like the function statements of the previous section. The only
difference is the lack of an identifier, as the following example shows:
function(parmOne, parmTwo) {
// statements here
}
On its own, this is actually invalid code because the interpreter expects a named function declaration,
but what makes functions powerful in JavaScript is that they themselves are a type of value. This means
that a function can be assigned to a variable, and doing so gives the function the name of the variable.
Assigning a function to a variable is called using a function expression statement. Such a statement is a
cross between the variable initialization statement and the function declaration statement. The follow-
ing code rewrites the augmentValue() function using a function expression:
var augmentValue = function(originalValue) {
var augmentedValue = originalValue;
augmentedValue = augmentedValue + 150;
augmentedValue = augmentedValue / 2;
augmentedValue = "The value is: " + augmentedValue;
return augmentedValue;
};
This code creates an anonymous function, complete with a parameter and its statements, and assigns
it to the augmentValue variable. Notice the semicolon after the function’s closing curly brace. Even
though this statement involves the creation of a function, it is still an assignment operation, and as
such should end with a semicolon.
You execute this function exactly like any other function, as follows:
var valueOne = augmentValue(100);
30 ❘ Lesson 3 Functions
Other than how the function is created and named, there is virtually no difference between declared
functions and functions assigned to variables. You execute them the same way, they execute the
body of code the same way, and they exit the same way.
This code creates a new function called sum(). It accepts two arguments and returns their sum. The
last line of this code creates a variable called sumCopy, and it is assigned the sum() function. Pay
careful attention to this assignment, and notice the identifier sum is used without the trailing set of
parentheses. When you omit the parentheses after the name of a function, you use the function as
an actual value.
So what this code does is create the sum() function, and then assign the actual sum() function to
the sumCopy variable; sumCopy is now a function allowing you to use it exactly as you would sum(),
like this:
var sumOne = sum(1, 2); // 3
var sumTwo = sumCopy(1, 2); // 3
The variable name sumCopy used in this example is actually misleading because it’s not really a copy
of sum() — it is sum() with a different identifier. Great, so you have a function with two different
names! But why? Well, take a look at the following code:
function sum(paramOne, paramTwo) {
return paramOne + paramTwo;
}
This code creates three functions: sum(), multiply(), and calculate(). The first two functions
are self-explanatory, but the third, calculate(), isn’t so obvious. It accepts three arguments: two
numeric values and a function value (calculator). In the body of calculate(), the function assigned
to calculator is called and the two numeric values are passed to the function contained within
calculator.
When the calculate() function is called in the final two lines of code, the sum() and multiply()
functions are passed as the third argument. By passing sum() in the next-to-last line, the calculate()
function calls sum(), passing it the numeric values of 1 and 2, and returns the result of sum() to the
sumResult variable. In the last line multiply() is passed to calculate(). So calculate() calls
multiply() and returns the result of calling multiply(1, 2).
This code uses the same philosophy as the code using sum() and multiply(), but an anonymous
function is passed as the third argument to calculate(). This anonymous function subtracts the
two values, resulting in a value of -1. So, you don’t have to declare or name a function before you
pass it as an argument to another function — it’s usually sufficient to simply pass an anonymous
function.
Code such as this is highly modular and flexible. Anytime you want to perform a mathematical
operation on two numeric values, you can write a function to do it and pass it to the calculate()
function to do the work for you. Granted, this particular example isn’t all that useful, as you can
directly call the sum() and multiply() functions without needing to call calculate(). But this
technique of passing a function to another function is invaluable starting in Lesson 16, when you
learn about events.
Returning Functions
You can also return a function from a function as you would any other value. For example:
function someFunction() {
return function() {
alert("Hello!");
};
}
This code creates a function called someFunction(), and it returns an anonymous function to what-
ever called someFunction(). You can see this in the last two lines of this code. A variable called foo
is assigned the return value of someFunction(), essentially making foo a function. Then, the foo()
function is executed, alerting the text Hello! As in the previous section, the ability to return a func-
tion may not seem overly helpful. As you progress through the book, and especially in Lessons 20
and 23, you’ll see many uses for returning a function from another function.
32 ❘ Lesson 3 Functions
Try It
In this lesson, you learn about functions, how to write them, and how they are treated as values in
JavaScript.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson03 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson03 folder.
Step-by-Step
Write a function with two parameters: a first and last name. Have the function concatenate a greet-
ing message using the two arguments, and return the greeting to the caller.
1. Open your text editor and type the following code. Save the file as lesson03_example01.js.
function getGreeting(firstName, lastName) {
var greeting = "Hello, " + firstName + " " + lastName + "!";
return greeting;
}
This code creates a function called getGreeting(). It accepts a first name and a last name
as arguments, and it uses those values to create a greeting message that is stored in the
greeting variable. Then the value contained within greeting is returned to the caller.
2. Now call this function and alert the message by adding the following bolded code:
function getGreeting(firstName, lastName) {
var greeting = "Hello, " + firstName + " " + lastName + "!";
return greeting;
Try It ❘ 33
alert(message);
Save it as lesson03_example01.htm.
5. Open the HTML file in your browser. You will see an alert box displaying the message
Hello, John Doe!
Please select Lesson 3 on the DVD to view the video that accompanies this les-
son. You can also download the sample code for all lessons from the book’s
website at www.wrox.com.
4
Making Decisions
Programming is much more than variables, functions, and calculations. If programming were
as simple as those three things, computers wouldn’t be anything more than giant calculators,
and chances are good you wouldn’t be reading this book.
What sets computers apart from other electronic calculation devices is their capability to simu-
late intelligence. Granted, it’s the programmer who tells the computer how to behave and how
and when to make decisions, but it’s the computer that decides what action to take based on a
set of criteria.
Making decisions is an important part of programming. At the core of decision making are
conditions, and the program makes a decision based on whether or not a particular condition
is met. There are a few control structures that use conditions to determine how code executes,
but first let’s look at how to write conditional statements.
Conditional Statements
In JavaScript, a conditional statement consists of an operation comparing two or more values to
result in a Boolean value together with statements that may run, depending on the result. Recall
from Lesson 2 that Boolean values consist of either true or false. A condition is said to have
been met if the conditional statement results in true; otherwise, a value of false defines
the condition as unmet. In order to get a true or false value, you typically use JavaScript’s
comparison operators.
Comparison Operators
Lesson 2 introduced several operators: the assignment operator to give a variable a value, the
string concatenation operator to join two or more strings together, the typeof operator to
determine a variable’s data type, and mathematical operators to perform arithmetic opera-
tions. Similarly, JavaScript provides many operators to test conditions. These operators are
36 ❘ Lesson 4 Making Decisions
binary operators, meaning they have a left side (the left operand) and a right side (the right oper-
and). A comparison operator looks at both operands, compares them, and returns a true or false
value after performing its test.
Let’s introduce you to the less-than comparison operator. Like its mathematical counterpart’s, its
symbol is a less-than sign (<). This operator determines if the left operand is less than the right oper-
and. The following code demonstrates the use of this operator:
5 < 6;
This reads as “Is 5 less than 6?” which of course is true. However, reversing the operands so that 6
is on the left and 5 is on the right (6 < 5) results in false.
JavaScript has many comparison operators, as the following table shows:
Comparison Operators
Most of these operators are self-explanatory; the less-than, greater-than, less-than-or-equal-to, and
greater-than-or-equal-to operators work the same way as their mathematical counterparts (with
numbers, that is). However, the difference between the equality and identity operators, and between
the inequality and nonidentity operators, isn’t immediately obvious.
JavaScript’s equality operator is lenient when comparing both operands; it coerces or converts the
operands to the same type and then compares the converted values. Consider the following code:
"6" == 6; // true
"6" != 6; // false
The first line checks if a string value of "6" is equal to a numeric value of 6. Even though they look
different, JavaScript says they are equal. The second line performs an inequality check: It determines
if the string "6" is not the same as the number 6. Once again, looks can be deceiving. Even though
Conditional Statements ❘ 37
the code compares a string with a number, JavaScript returns false because it converts both values
and then compares them.
This is considered wrong in many programming circles, but thankfully this is where the identity and
nonidentity operators come into play. These operators do not convert the operands to the same type.
Instead, these operators leave the operands alone, and compare both operands’ types and values. The
following code performs the previous comparison, but instead uses the identical and not-identical
operators:
"6" === 6; // false
"6" !== 6; // true
This is a more desirable result, and the use of the identity and nonidentity operators is therefore the
recommended practice.
Operator Precedence
Like arithmetic operators, comparison operators have an order of precedence. The equality/inequality
and identity/nonidentity operators have the lowest precedence, and the other comparison operators
have the same higher level of precedence. This means that the less-than, greater-than, less-than-or-
equal-to, and greater-than-or-equal-to operators perform their tests before the equality and identity
operators perform theirs. Look at the following code for an example:
5 < 6 === true; // true
In this condition the less-than comparison is performed first since it has a higher precedence than
the identical operator. The result of the less-than comparison is the Boolean value of true, which
is then compared with the Boolean literal true. This condition can essentially be simplified as
true === true. So the overall result of the condition is true.
Remember that the greater-than comparison has precedence over the identity comparison, so the
6 >= 6 comparison is made first. The number 6 is greater than or equal to 6, and the result of the
comparison is true. With that conclusion, the overall condition can be simplified to false !== true,
which of course is true, because false does not exactly equal true
Now look at a condition example involving some arithmetic operators:
3 * 4 > 11 + 2; // false
Comparison operators have a lower precedence than arithmetic operators. This means the arithmetic
operations are performed before the comparison operation. After the arithmetic is performed, the
condition is simplified to 12 > 13, which is false.
The previous examples have been relatively simple, and knowing the operator precedence rules cer-
tainly helps in deciphering each conditional statement and its outcome. However, using parentheses
can make the conditional statements much clearer, as well as ensure that you don’t get unexpected
results. Look at the following lines:
38 ❘ Lesson 4 Making Decisions
These three lines are rewrites of the three previous examples of conditional statements in this section.
Adding parentheses ensures that precedence is clear, and that’s important when you (or someone else)
are maintaining code. As a general rule, use parentheses.
Finally, a note of caution about comparison operators: Using more than one comparison operator in
a condition can cause unexpected results. Consider the following condition as an example and deter-
mine its outcome:
5 > 4 > 3;
If you said false, you are correct. At a glance, it looks like the condition tests whether 4 is less than
5 and greater than 3. In normal mathematical notation, that is correct, but that’s not the case in
JavaScript.
Let’s break this down. First, the comparison operators in this condition are all > operators and have
the same precedence. Therefore, this condition is evaluated from left to right. The first comparison
checks if 5 is greater than 4. Remember that comparisons result in a true or false value. So the result
of the 5 > 4 comparison is true, and the overall condition can be simplified to true > 3. This second
comparison is obviously false, as true is not greater than 3, and that makes the final result of the
conditional statement false.
But what if you want to perform a check such as: “Is 4 less than 5 and greater than 3?” This type
of check has to be split into two conditions, such as 5 > 4 and 4 > 3. These two conditions are then
combined into a single expression with a different set of operators called logical operators.
Logical Operators
Logical operators combine multiple conditions into one expression. JavaScript has three logical
operators, and the following table lists them and their symbols:
Logical Operators
It’s important to note that the AND and OR operators use two ampersands (&) and pipes ("), respec-
tively. Using just one gives you strange results, as a single & and a single " are JavaScript’s bitwise
operators — operators that work on individual bits.
Like comparison and arithmetic operators, the logical AND and OR operators operate on a left
operand and a right operand. The primary difference between logical and comparison operators is
Conditional Statements ❘ 39
that the logical operators operate on Boolean values, so both the left and the right values must eval-
uate to either true or false. Typically, one or both operands is a conditional statement. The logical
NOT operator is a unary operator, but it too operates on a Boolean value.
The logical AND and OR operators have the lowest precedence. All other operations (mathematical
and conditional) are performed before the logical AND and OR operators perform their jobs. The
logical NOT operator, on the other hand, has a higher precedence than all other mathematical and
conditional operators. Let’s look at these operators in more detail, starting with logical AND.
Logical AND
The logical AND operator (&&) behaves very much like the English word and. For example, “Is 4 less
than 5 and greater than 3?” There are two conditions in this statement. The left condition is “Is
4 less than 5?” and the right condition is “Is 4 greater than 3?” If both conditions result in true,
then the entire statement must be true. But if one or both conditions results in false, then the entire
statement cannot be true, and thus is false. Let’s translate this into a conditional expression. The
following conditional expression is the code equivalent of “Is 4 less than 5 and greater than 3?”
5 > 4 && 4 > 3;
With the logical AND operator, both the left and the right operand must evaluate to true in order
for the entire expression to result in true. This code results in true because 5 is greater than 4, and
4 is greater than 3. But look at this next expression and determine its final outcome:
5 > 4 && 3 < 2;
If you said false, you are correct. The left operand, the 5 > 4 condition, results in true. The right
operand, the 3 < 2 condition, results in false. Therefore, the result of the entire expression is false,
because both operands are not true.
The following table is a truth table that outlines all possible outcomes of the logical AND operator:
JavaScript doesn’t want to do any more work than is absolutely necessary; it exhibits a behavior
called short-circuiting. This means that the right operand is evaluated only if the left operand does
not determine the outcome of the logical operation.
Looking at the truth table, you can see that the logical AND operation returns true if, and only if,
both operands are true. If the left operand is true, JavaScript has to evaluate the right operand in
order to determine whether the logical operation returns true or false. If the left operand is false,
40 ❘ Lesson 4 Making Decisions
JavaScript automatically knows that the logical AND operation cannot return true, so it ceases the
evaluation of the expression and returns false.
Logical OR
Like the logical AND operator, the logical OR operator ("") behaves somewhat like its English
counterpart, the word or. In English, the word “or” is often implicitly exclusive. For example, the
question “Do you want a cookie or a cupcake?” implies you can pick only one of those choices.
Logical OR, however, is implicitly inclusive. Consider the following question: “Is 4 less than 5 or
less than 3?” This phrase has two conditions. The one on the left is “Is 4 less than 5?” and the con-
dition on the right is “Is 4 less than 3?” Like this question, a logical OR operation looks at both
conditional operands and returns true if one or more of them is true. The following expression is
the JavaScript equivalent of this “or” question:
5 > 4 "" 4 < 3;
The first operand, 5 > 4, evaluates to true, but the second operand, 4 < 3, does not. Even so, this
expression returns true because at least one of the operands is true.
Remember that a logical AND operation returns true only if both operands are true; all other outcomes
are false. With the logical OR operation, the outcome is true unless both operands are false.
To make it a bit clearer, here is the truth table for the logical OR operator:
Like the logical AND operator, the logical OR operator can short-circuit. If the left operand evalu-
ates to true, JavaScript doesn’t bother to evaluate the right operand and returns true. If the left
operand evaluates to false, however, JavaScript will check the right operand to determine the OR
operation’s outcome.
Logical NOT
The logical NOT operator (!) is a little different from AND and OR. One difference, as mentioned
earlier, is that it operates on only one operand — the one to the right of the operator. Another dif-
ference is that it reverses the Boolean value returned by that operand. This is a useful behavior, as
you’ll see when covering the if statement. Keeping those two things in mind, look at the following
example of the logical NOT operator:
!(4 < 3);
Executing Code Based on Conditions ❘ 41
If you were to read this expression in your native language, it would read “4 is not less than 3.” By
reading it that way, you of course know the expression is true. However, it’s important to know how
to determine the outcome of the statement without translating it into spoken language.
First evaluate the 4 < 3 condition. Its result is false, so the simplified version of this expression is
!false. The logical NOT operator has the behavior of reversing whatever Boolean value its operand
contains. In this case, it reverses false to true.
Following is the NOT operator’s truth table:
true false
false true
It’s quite simple, really: just declare a variable, use the assignment operator, and put the conditional
expression to the right of the equals sign.
The if Statement
You’ll find yourself using the if statement in almost every program you write; it works very much
as it does in spoken language. For example, “If it is cold outside, Sally will put on her coat.” This
sentence says that Sally will put on her coat (an action) if it’s cold outside (a condition). Translating
this to JavaScript could look something like this:
if (weatherType === "cold") {
putCoatOn();
}
42 ❘ Lesson 4 Making Decisions
This code assumes a variable of type string called weatherType. It shows the syntax of the if state-
ment. Notice the lack of semicolons, except for the statement within the code block. An if statement
is made up of the if keyword followed by a condition surrounded by parentheses. In the case of this
code, the condition is a string comparison comparing the value of weatherType to the value “cold”.
If this condition results in true, the code within the code block executes. If it’s false, the code block
is skipped and JavaScript begins executing the next line of code after the code block.
What if you want to do the reverse: to check if it isn’t cold outside and perform an action based
on that outcome? There are a few different actions you could take. For one, you could use the
!== operator to see if the weatherType variable is not “cold”; or you could use the logical NOT
operator, like this:
if (!(weatherType === “cold”)) {
// do something for warm weather
}
This is where the logical NOT can be useful. This code uses it to reverse the result of the compari-
son operator. If the comparison’s result is false, the logical NOT operator changes it to true and
executes the warm-weather code. If the comparison’s result is true (meaning cold weather), then the
NOT alters the value to false, thus skipping the warm-weather code.
To execute code for cold or warm weather, you could write these two if statements sequentially,
like this:
if (weatherType === “cold”) {
putCoatOn();
}
But JavaScript provides a cleaner way: the else statement. Like the if statement, the else state-
ment translates very well into spoken language: “If it’s cold outside, Sally will put on her coat, or
else she’ll wear clothes for warm weather.” The following code demonstrates the use of the else
statement:
if (weatherType === “cold”) {
putCoatOn();
} else {
// do something for warm weather
}
As you can see here, the else statement is added after the closing curly brace of the if code block,
and it too has its own block of code. The else statement cannot be used on its own; it must be used
in conjunction with an if statement.
The example, so far, of how Sally prepares for the weather is all well and good if warm and cold are
the only weather conditions to plan for, but it’s quite evident that other weather patterns exist in this
world. It could be raining or windy. The point is, a simple if … else statement block isn’t enough
for this example; it needs to take several other weather patterns into consideration.
Executing Code Based on Conditions ❘ 43
JavaScript is accommodating by allowing you to use an if statement immediately after an else The
else if statement is used in conjunction with the if statement, but before the final else statement.
It is used like an else statement while providing the ability to test another condition. So, in other
words, it looks exactly like an if statement, except that it features the word else before if. The
following example includes more weather possibilities for Sally:
if (weatherType === "cold") {
putCoatOn();
} else if (weatherType === "rainy") {
getUmbrella();
} else if (weatherType === "windy") {
putOnJacket();
} else {
// do something for warm weather
}
This code includes new checks for rainy and windy weather, and you can easily add more else if
statements to check for other weather conditions. With if … else if … else you can turn a dumb
calculator-type application into a thinking program, able to handle different types of situations. It’s
very useful for checking for various conditions.
In this example, each condition checked the value of one variable, the weatherType variable. In this
case there’s a more efficient means to achieve the same results: the switch statement.
The last example in the previous section, the if … else if … else example, can be rewritten with
a switch statement, as follows:
switch (weatherType) {
case "cold":
putCoatOn();
break;
case "rainy":
getUmbrella();
break;
case "windy":
putOnJacket();
break;
default:
// do something for warm weather
44 ❘ Lesson 4 Making Decisions
break;
}
The switch statement’s test expression follows the switch keyword and is contained within paren-
theses. This code uses the weatherType variable as the test expression, but you can use any valid
expression inside the parentheses.
Next are the case statements. The case statements do the condition checking by using the value in
the case statement and checking if it is equal to the switch statement’s test expression. For example,
the first case statement in this example can be translated as if (weatherType == "cold"). Case
statements end with a colon (:) and each case statement is paired with a break statement. The code
in between the case statement and the break statement is executed if the test expression matches
the case statement’s condition.
The break keyword is used to break out of the switch statement when a case is matched and the
code within the case is executed. It is possible to omit the break statement: Doing so causes code
execution to drop to the next case statement. If this happens, the case’s conditional value is ignored,
and JavaScript executes all code until it reaches either a break statement or the end of the switch
code block. The following example demonstrates this:
switch (true) {
case true:
alert("Hello");
case false:
alert("World");
}
Running this code results in an alert window displaying the text "Hello", because the first case
statement matches the text expression (both values are true). After the user clicks OK to exit the
alert window, code execution drops to the next case statement. JavaScript doesn’t perform a check
to see if false is equal to the test expression, but instead simply executes the code alert("World").
This results in another alert window displaying the text "World".
As a general rule of thumb, use the break keyword at the end of each case statement’s block of code
to break out of the switch statement. Otherwise you might experience some unexpected results.
The final piece of the switch statement is the special case statement called default. The default
case does not use the case keyword, and is the code that executes when no matching case could be
found. It is optional, as the previous example shows. If it is omitted, however, keep in mind that no
code will execute if no matching case is found. It’s generally a good idea to include a default case
unless you are absolutely certain that you do not need it.
The case expressions in this section use literal values to determine the code that executes when a
match is found.
if (4 < 5) {
someVariable = "4 is less than 5.";
} else {
someVariable = "4 isn't less than 5.";
}
There is nothing technically wrong with this code. You initialize a variable called someVariable
with a value of null, and then you assign a string value to someVariable dependent upon the out-
come of the if statement’s condition. It does, however, require six lines of code to essentially assign
a value to someVariable. Thankfully, there is a shorter way of performing this same exact process,
and it uses the ternary operator to do it. The ternary operator’s syntax looks like this:
(<condition>) ? <return value if true> : <return value if false>;
To convert the previous if … else block to use the ternary operator, the code would look like the
following:
var someVariable = (4 < 5) ? "4 is less than 5." : "4 isn't less than 5.";
The ternary operator is handy in situations like this where you want to assign or return a value
based on a condition. It is, however, limited to only returning a single value; you cannot execute
multiple statements in a ternary operation like you can with if … else.
Try It
In this lesson, you learn about conditional statements and how to use them to make decisions with
the if and switch statements, as well as the ternary operator.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson04 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson04 folder.
46 ❘ Lesson 4 Making Decisions
Step-by-Step
Control code execution by using a condition to determine whether a number is less than three and
greater than zero.
1. Open your text editor, type the following code, and save the file as lesson04_sample01.js.
var valueToTest = 2;
2. Open another instance of your text editor, type the following HTML, and save it as
lesson04_sample01.htm:
<html>
<head>
<title>Playing with if...else</title>
</head>
<body>
<script type="text/javascript" src="lesson04_sample01.js"></script>
</body>
</html>
3. Open the HTML file in your browser. You will see an alert window displaying the text
"The value is less than 3 and greater than 0".
4. Change the value of the valueToTest variable to any number greater than three, save the
JavaScript file, and reload the page to see a different message.
In this next exercise, your script will be more dynamic and require user input via the prompt()
function. You have not seen this function yet, but it is very simple.
1. Open your text editor, type the following code, and save the file as lesson04_sample02.js.
var weatherType = prompt("What is it like outside?", "");
switch (weatherType) {
case "sunny":
alert("It's sunny outside! Go out and play!");
break;
case "rainy":
alert("It's rainy outside! Stay inside!");
break;
case "cloudy":
alert("It's cloudy outside. Stay in or go out.");
break;
case "windy":
alert("It's windy outside! Carry a jacket!");
break;
case "cold":
Try It ❘ 47
2. Open another instance of your text editor, type the following HTML, and save it as
lesson04_sample02.htm:
<html>
<head>
<title>Weather Program</title>
</head>
<body>
<script type="text/javascript" src="lesson04_sample02.js"></script>
</body>
</html>
3. Open the HTML file in your browser. A window will prompt you to answer a question.
Answer the question and see how your program responds.
To get the sample database files, you can download Lesson 4 from the book’s website at www.wrox.com.
Please select Lesson 4 on the DVD to view the video that accompanies this
lesson.
5
Loops
In Lesson 3, you learned how functions can greatly clean up your code by encapsulating
repetitive operations and calculations to use throughout the application’s code. There are
times, however, when a function isn’t the right tool for handling repetitive code.
For example, you want a particular line of code repeated 10 times — no more and no less.
You can, of course, write that same line of code 10 times, but in doing so you condemn yourself
to a maintenance nightmare in which you may have to make 10 changes to 10 lines of code.
Functions don’t solve the problem either. Although the code might be a little less complex, you
still have to type (or copy and paste) 10 identical lines of code.
For this reason, programming languages have a concept called looping. In programming, looping
means to repeat one or more lines of code while a particular condition is true. This functionality
is useful, and you’ll see loops used throughout the book. But for now, let’s take a look at some
loops.
Loop Basics
There are several different types of loops, but they all perform the same primary function of
repeating code. They also share some similar features. For example, all loops repeat a code
block based on the results of a condition. As long as the condition evaluates to true, the loop
iterates, or repeats its code, and it immediately ends when the condition evaluates to false.
Loops can be grouped into two categories: pre-test and post-test. A pre-test loop tests its
condition at the beginning of every iteration of the loop — even the first iteration. It’s entirely
possible for a pre-test loop to not execute its block of code at all. If the condition fails on the
first iteration, the loop doesn’t execute any code.
A post-test loop tests its condition at the end of each iteration. This means that a post-test
loop’s code block is executed at least once. After the code block executes, JavaScript evaluates
the loop’s conditional logic and determines whether the loop continues or exits.
50 ❘ Lesson 5 Loops
Loops, while useful, can sometimes cause trouble when you’re developing an application. Remember
that loops repeat their code over and over again until their provided condition returns false. It is
very easy to miss a flaw in the condition’s logic and cause the loop to never end. This type of error is
referred to as an infinite loop, and it is very common when applications are being developed. Always
double-check the loop condition’s logic; it can save you a lot of debugging time, or time spent locat-
ing and fixing errors.
It is also possible to break out of a loop early by using the break statement introduced with the switch
statement in Lesson 4, and you can skip iterations by using a continue statement.
Let’s now look at the different types of loops starting with the for loop.
Focus on the logic inside the parentheses. The initialization, condition, and increment statement are
separated by semicolons. The loop counter (the loopCounter variable, but it can be any variable) is
initialized with a value of 0. Computer scientists begin counting at 0 instead of 1 mainly because of
the binary system that all computers are based upon. This is counter to what we humans have done
for centuries; so it does take a while to get used to.
Counters do not have to start at 0; they can start at any integer value.
After the counter initialization is the condition. As long as the condition results in true, the loop
iterates and repeats the code within the code block. If false, the loop immediately exits. The for
loop is a pre-test loop; therefore, it can execute zero or more times. In this example the loop iterates
as long as loopCount is less than 10. The loop ends once loopCount equals 10 or more.
Following the condition is the increment portion of the loop’s logic. The code here increments the
loop counter by one at the end of each iteration of the loop. In this example you see a new operator,
The for Loop ❘ 51
the increment operator (++). The increment operator is a unary operator that adds one to the value con-
tained in the variable. The code loopCounter++ is equivalent to loopCounter = loopCounter + 1.
The increment operator is a special unary operator in that it can either precede or follow the variable
to be incremented: ++loopCounter and loopCounter++ are both valid statements. Where the incre-
ment operator is placed determines how the value is incremented. If it is placed before the variable
(++loopCounter), the value contained within loopCounter is incremented by one, and the new value
is returned to the caller. Consider the following code as an example:
var x = 1;
var y = ++x; // returns 2
Here a variable called x is initialized with a value of 1. In the second line the increment operator pre-
cedes the x variable. The value in x is incremented, and the new value of x is assigned to the y variable.
So y contains a value of 2. Contrast that example with what happens when the ++ operator follows the
variable, as shown here:
var x = 1;
var y = x++; // returns 1, but x is now 2
As in the previous example, this code initializes the x variable with a value of 1. In the second line,
the current value of x (which is 1) is assigned to y, and then x is incremented by one to 2. So y con-
tains 1, while x contains 2.
It’s important to understand the difference between ++loopCounter and loopCounter++, as unex-
pected results may occur if the increment operator is placed incorrectly.
It’s important to note that an increment or decrement operator does not have to be used in the
increment portion of the for loop. You can increment the loop counter however you want to —
with addition, subtraction, multiplication, or division. Look at the following code as an
example:
for (var loopCounter = 0; loopCounter < 10; loopCounter = loopCounter * 2) {
// code to repeat here
}
Here loopCounter is increased by being multiplied by 2 every time the loop iterates. But there’s a
flaw in the logic of this particular implementation. The loopCounter variable is initialized as 0. When
loopCounter is incremented, 0 is multiplied by 2, resulting in 0. So loopCounter always has a value of
0, which is less than 10, and that means the loop runs forever.
52 ❘ Lesson 5 Loops
The for loop is especially useful when you know exactly how many times to execute the same block
of code, but sometimes you just need a loop that iterates as long as a condition is true. Enter the
while loop.
The while loop is a pre-test loop, so JavaScript tests the condition before the loop iterates. The
loop’s syntax looks like this:
while (condition) {
// statements
}
It begins with the while keyword followed by conditional logic in parentheses and then the block of
code that the loop repeats. Using the while loop can be as simple as the following example:
var counter = 0;
This code initializes the counter variable with a value of 0. To determine if the loop should execute
code, the value of counter (currently 0) is compared to the number 5. If the comparison results
in true, then the code within the loop’s code block executes. An alert window displays the value of
counter, and counter is incremented via the ++ operator. As long as counter is less than 5, the loop
executes. Otherwise the loop exits and JavaScript begins executing the next line of code after the loop.
With this loop, the code within the do block executes at least once, and then the condition is evaluated
to determine if the loop iterates or exits. Notice the semicolon at the end of the while statement. This
is the only time a semicolon is used to terminate a loop statement.
Look at this example of an implementation of the do-while loop:
var counter = 0;
do {
alert(counter++);
} while (counter < 5);
This code results in the same outcome as the example for the while loop. First the counter variable
is initialized with a value of 0. Then the code within the do code block is executed; it uses an alert
window to display the value of counter and increments the variable in the same statement. Then the
condition tests if the loop should continue or exit.
Try It
In this lesson you learn about looping with the for, while, and do-while loops. You will write an
HTML page and a JavaScript file for each loop.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson05 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson05 folder.
54 ❘ Lesson 5 Loops
Step-by-Step
Use a for loop and keep a running total of the even numbers from 0 to 20. In other words, add each
even number together to get a total. Use the continue statement to achieve this. Use alert() to dis-
play the grand total when the loop exits.
1. Open your text editor and type the following code. Use the name i for the counter variable,
and the name total for the variable that keeps track of the total.
var total = 0;
alert(total);
2. Add the bold line in the following code to the file. This line calculates total:
var total = 0;
alert(total);
3. This code adds the counter to total in each iteration of the loop. You want only the even
numbers, so use the continue statement to skip an iteration when i is odd. Add the follow-
ing bold lines of code:
var total = 0;
total = total + i;
}
alert(total);
This code uses the modulus operator to determine whether i is even or odd. Remember,
modulus operations return 0 if i is even. Because 0 is a false value, the continue statement
is not executed. If i is odd, however, the modulus operation returns a nonzero number — a
truth value. So, when that’s the case, the continue statement executes and causes the loop
to skip the current iteration.
4. Save the file as lesson05_sample01.js.
Try It ❘ 55
5. Open another instance of your text editor, type the following HTML, and save it as
lesson05_sample01.htm.
<html>
<head>
<title>Playing with for loops</title>
</head>
<body>
<script type="text/javascript" src="lesson05_sample01.js"></script>
</body>
</html>
6. Open the HTML file in your browser. An alert window will display the value 90.
Write a simple application using the while loop. The application should prompt the user to enter his
or her name. The loop will use an alert window to display a greeting to the user, as well as a counter
value, and it will continue to loop until the user types exit. The counter variable’s name is i, and
the variable to hold the user’s name is userName.
1. Open your text editor and type the following code:
var i = 0;
var userName = prompt("Please enter your name. Type exit to exit", "");
2. Add the following bold lines to the file. The first line uses an alert window to display the
message to the user. This line concatenates several string literals with the value the user
typed, as well as incrementing the i variable by one. The increment operator is used before
the variable to display a number to the user starting with 1. The second line prompts the user
to enter his or her name again.
var i = 0;
var userName = prompt("Please enter your name. Type exit to exit", "");
<body>
<script type="text/javascript" src="lesson05_sample02.js"></script>
</body>
</html>
5. Open the HTML file in your browser and enter a few names. Then, type exit to exit the loop.
do {
var userName = prompt("Please enter your name. Type exit to exit", "");
alert("Hello, " + userName + ". You've run this " + ++i + " times.");
} while (userName !== "exit");
2. Open another instance of your text editor, type the following HTML, and save it as
lesson05_sample03.htm.
<html>
<head>
<title>Playing with do-while loops</title>
</head>
<body>
<script type="text/javascript" src="lesson05_sample03.js"></script>
</body>
</html>
3. Open the HTML file in your browser and enter a few names. Then type exit to exit the loop.
Notice that the alert window displays a greeting for exit. You can fix this behavior by using
an if statement to determine if exit was typed. Add the bold lines from the following listing,
save the JavaScript file, and reload the web page.
var i = 0;
do {
var userName = prompt("Please enter your name. Type exit to exit", "");
if (userName !== "exit") {
alert("Hello, " + userName + ". You've run this " + ++i + " times.");
}
} while (userName !== "exit");
Please select Lesson 5 on the DVD to view the video that accompanies this les-
son. You can also download the sample code for all lessons from the book’s
website at www.wrox.com.
6
Scope
In the Try It section of Lesson 5, you used a variable called i to count a for loop’s iterations.
Most programmers use the i identifier for that exact reason; it’s used everywhere and all the
time to count loop iterations. So if multiple developers were working on different pieces of the
same application, wouldn’t all these uses of i conflict with each other?
Thanks to scope, the answer is no. Scope is an important concept in programming, particu-
larly in JavaScript. It’s important to understand scope because it determines what variables a
function can see and access.
Global Scope
Variables and functions defined outside of a function are created in the global scope. JavaScript’s
global scope is the topmost level of scope. It persists throughout the entire web page, and any
variables and functions created in the global scope are accessible anywhere in the page.
Consider the following code:
var globalVar = "This is a global variable.";
function globalFunction() {
globalVar = "This changes the value of globalVar";
}
globalFunction();
In this example, a variable called globalVar is created outside of any function; thus, it is a
global variable and can be accessed anywhere in the page. Because the globalVar variable is
a global variable, the globalFunction() function, which is also defined in the global scope,
can access the variable and modify it. Since globalVar is global, its new value can be seen and
accessed anywhere in the page; modifying a global variable’s value makes the new value visible
and usable everywhere.
58 ❘ Lesson 6 Scope
Global variables and functions persist throughout the page, and they remain relevant to the page
until the user navigates to a different one. Contrast that with functional scope, in which scope is
limited only to the function.
Functional Scope
Think of scope as shown in Figure 6-1.
Figure 6-1
The shaded area outside the circles is the global scope. All variables and functions defined in this area
have access to the other variables and functions in the same space. The circles represent functions:
Some are globally defined while others are defined inside other functions. The boundary line of each
circle is a barrier preventing access to the variables and functions defined within the circle from items
outside the circle. However, a circle is contained within either another circle or the global space, and
the items defined within a circle have complete access to the items defined outside the circle.
This figure shows the primary characteristic of scope in JavaScript. Items in a particular circle of
scope have access to the items inside their containing circle, as well as items outside the circle. They
cannot, however, penetrate the boundary into another circle — even one contained within itself.
The variables and functions defined within a function are said to have functional, or local, scope —
they are local to their containing functions. They have access to global variables and functions, as well
as to other variables and functions defined within the same function, but they cannot be accessed from
outside the function in which they are defined. This can be shown in the following example:
var globalVar = "This is a global variable.";
function globalFunction() {
Functional Scope ❘ 59
globalFunction();
This example uses the global variable and function from the previous section, but adds a variable
initialization within the function. This new variable is called localVar, a local variable to the glo-
balFunction() function.
The second-to-last line of this example uses an alert window to display the value contained within
localVar. The only problem here is that localVar cannot be accessed outside of the globalFunction()
function because it is a local variable of that function — it is only accessible to code within the func-
tion. What this line actually does is try to find a global variable called localVar. Since it doesn’t exist,
an error occurs and code execution stops. Neither alert window displays.
When the function exits, all its local variables are destroyed. But notice that within the function, the
globalVar variable was changed to contain the value contained within localVar. While the local
variable localVar was destroyed, its value was copied to globalVar before the function exited.
The same behavior is seen with function parameters. The function sees them as local variables, and
while all code within the context of the function can access those parameters, code outside the func-
tion cannot. Look at the following example:
var globalVar;
function globalFunction(paramOne) {
globalVar = paramOne;
}
globalFunction("This is a parameter");
This code recreates the globalFunction() function by modifying it to accept a parameter. After the
function declaration is the statement to execute globalFunction() by passing a string value to the
function. The next-to-last line attempts to access a variable called paramOne, but such a variable does
not exist within the global scope. So once again, an error occurs and code execution stops before an
alert window pops up.
Let’s make things a little more interesting. Look at the following code:
var globalVar = "This is a global variable";
function globalFunction(paramOne) {
var localVar = "This is a local variable";
function innerFunction() {
60 ❘ Lesson 6 Scope
innerFunction();
alert(innerVar); // error; execution stops
}
globalFunction("This is a parameter");
This code yet again recreates the globalFunction() function. It accepts a parameter as in the previ-
ous example, but the body has drastically changed. A function called innerFunction() is declared
inside globalFunction()’s body. This function is created within the scope of globalFunction(), so
it has access to all other variables and functions within globalFunction(), as well as those within
the global scope. In other words, it can directly access the paramOne and localVar variables of the
outer globalFunction() function and the globalVar variable in the global context.
The innerFunction() function has its own local scope. While it can access the items in the
outer function and global scopes, the outer scopes cannot access the innerVar variable defined
within innerFunction(). Also, the innerFunction() function cannot be accessed outside of
globalFunction().
No Block-Level Scopes
The idea of scope can be confusing, especially when you’re dealing with functions within a func-
tion within a function. Many other languages include a level of scope for every block of code —
that is, a list of statements inside a pair of curly braces ({}). This means that if statements, loops,
and switch statements have their own level of scope in other languages. This is not the case with
JavaScript. Consider the following code:
if (true) {
var hundred = 100;
}
alert(hundred);
In other languages the hundred variable would be destroyed after the if statement was executed,
and so the last line would result in an error. JavaScript, on the other hand, adds the declared vari-
able to the scope in which it was created (the global scope in this case, making it usable anywhere in
the application).
The function code block is one of the few code blocks with its own scope. Most other code blocks,
such as if … else, loops, and switches, share the same scope as their container.
Identifier Lookup ❘ 61
Variable Declarations
Throughout this book, variable declarations and initializations have started with the var keyword.
It is, in fact, possible to create a variable without using the var keyword, but doing so has the side
effect of creating a global variable. Look at the following example:
function someFunction() {
someVariable = "This creates a global variable";
}
someFunction();
alert(someVariable);
This code creates a function called someFunction(). Its only statement assigns a string value
to a variable called someVariable. When this statement executes, the JavaScript engine
looks for the someVariable variable to assign it a new value. The engine can’t find it, and
so creates it in the global scope. When the last line of this code executes, it displays the text
"This creates a global variable".
Even though this may seem beneficial, omitting the var keyword can lead to unexpected errors.
It is strongly recommended that you always use a var keyword to declare variables.
This behavior is caused, in part, by how JavaScript looks up identifiers.
Identifier Lookup
When you access a variable or a function in your
code, the JavaScript engine begins to look up the
item’s identifier, starting in the current level of
scope. If it cannot find a match in scope, it begins
to look in the next level of scope — traveling up to
every level of scope until it either finds a match or
doesn’t find one in the global scope. For example,
consider the following code:
var globalVar = "Hello, Global Scope!";
function someFunction() {
alert(globalVar);
}
function overrideVar() {
62 ❘ Lesson 6 Scope
overrideVar();
alert(aNumber); // 100
This code creates a global variable called aNumber; it has a value of 100. Next the code creates a
function called overrideVar(). Inside this function, another variable called aNumber is initialized
with a value of 101. The second line of the function uses the aNumber variable by passing it to the
alert() function. JavaScript performs a lookup for the aNumber identifier, finds one in the current
scope, and immediately stops searching for the identifier. Therefore the local aNumber variable is
used in the call to alert(), so the alert window displays 101.
The local aNumber variable does not modify the global variable of the same name in any way, but
any line of code inside overrideVar() uses the local aNumber variable instead of the global vari-
able. The JavaScript engine simply finds the identifier in the local scope, and uses it because the
engine found that variable’s identifier first.
Outside the function, the global aNumber variable is intact. The aNumber variable inside the
overrideVar() function is limited to the scope of that function, so the final line of code in this
example calls the alert() function, passes the global aNumber variable, and displays the value 100
in the alert window.
Try It
In this lesson, you learn about JavaScript’s scope and how it can affect your application. You will
write a function to determine the area of triangles and rectangles.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson06 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson06 folder.
Try It ❘ 63
Step-by-Step
1. Open your text editor and type the following code:
function shapeArea(base, height, shape) {
var area = 0;
function triangle() {
area = (base * .5) * height;
}
function rectangle() {
area = base * height;
}
switch (shape) {
case "triangle":
triangle();
break;
case "rectangle":
rectangle();
break;
case "square":
rectangle();
break;
}
return area;
}
This code creates a function called shapeArea(). It accepts three arguments: a number rep-
resenting the shape’s base, a number representing the shape’s height, and a string containing
the type of shape of which to compute the area. Inside the function, a local variable named
area is initialized with the value of 0.
Next are two inner functions: triangle() and rectangle(). They access the values passed
to the base and height parameters of shapeArea(). Because of local scope, they have access
to those values.
Then a switch statement switches through three possible values for the shape parameter:
triangle, rectangle, and square. The appropriate inner function is called based on
shape’s value. Finally, area is returned to the caller.
</body>
</html>
4. Open the HTML file in your browser. An alert window will display the value 4.
To get the sample code files, you can download Lesson 6 from the book’s website at www.wrox.com.
Please select Lesson 6 on the DVD to view the video that accompanies this
lesson.
7
Objects and Built-In Types
You live in an object-based world. Stop and think about the objects you come in contact with
pretty much every second of every day. Whether you’re driving to work or school, sitting at
a table eating dinner, sleeping in a bed, or even walking in a field — you’re interacting with
objects. You don’t even think about most of the objects you come in contact with every day.
Interaction with objects is natural to you.
Before the 1990s, programmers primarily used procedural programming languages. These
languages were based on the steps a program takes to reach the desired goal — very much like
the examples thus far in this book. It wasn’t until the 1990s that mainstream developers began
to shift their thinking to involve objects.
Objects are a very important part of JavaScript: In fact, just about everything in JavaScript is an
object. An understanding of what objects are and how to use them is required in order to use the
language to its full potential. First let’s talk a little more about what objects are and why they’re
useful.
In programming, objects are things that can have properties, or special variables, that contain data
about them. They also have methods, which are similar to functions, which perform some type of
action — usually on the data stored in properties. Just as the properties and methods of an automo-
bile object define an automobile, the properties and methods of an object in programming define
that object. Object-oriented programming is a type of programming intended to mimic the real
world by using objects to organize and work with data — complex data, as opposed to the simple
primitive data discussed in Lesson 2.
JavaScript has many object data types. Before looking into them, however, first look at how to use
objects.
This is a variable-initialization statement that creates a String object containing the data
"Hello, World!" and assigns it to the message variable.
Even though you can create a String value, or a value of any of the other primitive
types, by calling its constructor, it is recommended that you use literal notation
to create such values. You can still access the properties and methods of those
data types.
Once an object is created, it is then possible to use the properties and methods (collectively known
as members) of that data type. Accessing a data type’s members involves the use of the dot (.) opera-
tor. Use this operator between the object’s identifier and the member’s identifier, like this:
var numberOfCharacters = message.length; // 13
Built-In Data Types ❘ 67
This statement uses the String data type’s length property, which returns the amount of characters
and whitespace in the string. The property is accessed by means of the object’s identifier (message in
this code), followed by the dot operator, and then the property’s identifier (length).
Properties are very much like variables attached to an object. Some properties are read-only — meaning
they can return data, but they cannot be assigned a new value. The String data type’s length property
is a read-only property. Because of this, the following line is incorrect:
message.length = 10; // wrong; length is read-only
Accessing methods follows the same principle: use the object’s identifier, followed by a dot, and then
the method’s identifier. The following code calls one of the String data type’s methods:
var lowerCaseMessage = message.toLowerCase(); // "hello, world!"
Just as properties are like variables, methods are like functions. The primary difference between
a method and a function is that methods perform some kind of work on or for the object. The
toLowerCase() method is an example of this. It performs work using the String object’s value by
copying and converting all alphabetical characters to lowercase and returning the new value to the
caller, the lowerCaseMessage variable in this code.
Now that you have an idea of how to use objects, let’s start looking at JavaScript’s built-in data
types.
Future lessons will introduce you to the Object and Function data types.
Array
Expanding on the automobile example at the beginning of this lesson, an automobile has a set of
gears that determine how the vehicle moves. Putting an automobile into reverse moves it backward,
whereas another gear moves the vehicle forward. Storing all possible gear values in an object’s prop-
erty could be problematic, as properties and variables typically contain only one piece of data at a
time. The following code provides an example:
var gear = 1;
68 ❘ Lesson 7 Objects and Built-In Types
There are times, such as in the gear situation, when it’s desirable to store multiple values in one vari-
able or property. This is where arrays are helpful. The simplest definition of an array is an ordered
collection of values.
Arrays are created in one of two ways. The first way uses the Array data type’s constructor, like this:
var myArray = new Array(); // create a new array
The Array() constructor can accept a set of parameters containing the data to store in an array. The
following line of code creates an array and adds possible values for an automobile’s gears:
var gears = new Array(1, 2, 3);
This array is said to have three elements, or items. There is a much shorter and preferred method of
creating arrays, however. By using array literal notation, you can create the same arrays with fewer
keystrokes:
var myArray = [];
var gears = [ 1, 2, 3 ];
Array literal notation involves the use of square brackets, as shown in this code. The use of square
brackets is a result of how you access each individual element in the array: index notation. You first
use the array’s identifier, followed by an opening square bracket, followed by the element’s index,
and then the closing square bracket.
Elements in an array are stored in a numerical sequence starting with zero. Remember from Lesson 5
when examples showed loops starting at zero? This is why. The first element in an array has an index
of 0. The second element has an index of 1, the third element has an index of 2, and so on. You use
an element’s index number to access it in the array. The code to retrieve the third element in the
gears array looks like this:
gears[2]; // 3
The code to retrieve the first element in the gears array looks like this:
gears[0]; // 1
It’s OK if this is confusing to you, as it usually is for beginning developers. As humans, we typically
start counting with the number one. Computers don’t — they start counting at zero. JavaScript, as
well as many other languages, follows the computer’s pattern and starts counting at zero too. This
means that counting to 10 actually involves 11 numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, and 10. This is an
important concept to understand, as this counting method is used throughout the language.
Each element in an array is very much like a variable; you can get data as shown in the previous
examples or you can modify existing elements by assigning them new data. The following code
changes the first element in the array:
gears[0] = 2; // changed from 1 to 2
gears[0] = 1; // changed back to 1
Built-In Data Types ❘ 69
It’s exactly like using normal variables, except you use index notation to access an element in the array.
The gears array has three elements, and this information is retrieved by means of the Array type’s
length property. It behaves much like the String type’s length property, except that instead of
counting characters, it counts the number of elements in the array. The following code shows an
example of the length property:
var length = gears.length; // 3
The length property is especially useful when you’re using a loop to iterate over an array. Instead of
programming a specific end point for the loop, the length property can ensure that the loop iterates
over each element and ends after the last element. The following code shows this:
for (var i = 0; i < gears.length; i++) {
alert(gears[i]);
}
This code is the basis for looping through an array. The initialization portion of the for loop initial-
izes a variable called i with a value of 0. The loop’s condition tells the loop to repeat itself as long as
i is less than the length of the gears array. It will iterate three times, and the value of i for each itera-
tion is: 0, 1, 2. On the fourth iteration, the condition determines if i, which contains a value of 3, is
less than the length of the gears array, which also is 3. Because 3 is not less than 3, the loop exits.
The length property always returns the number of elements in the array. This means that adding a
new element to the gears array will cause gears.length to return 4. One way to add a new element
to an array is to use index notation. The following line adds a new element to represent the gear of
reverse:
gears[3] = -1;
This code adds a new element in an index position of 3, which is the fourth position in the array.
While hard-coding the index position works in some cases, such as this example, there are times when
you will not know how many elements are in an array. In times like these you can use the length
property to add the new element at the next index position. For more flexible code, the previous line
of code could be rewritten like this:
gears[gears.length] = -1;
In this example, the length property returns a value of 3, which is also the index number needed
to add a new element at the end of the array. There is an easier way to achieve the same results,
however, and it requires the use of one of the Array type’s methods: the push() method,
shown here:
gears.push(-1);
The push() method accepts one argument: the value to add to the array. Now that a new element
has been added to the array, the length property returns 4 since there are now four elements in the
array: 1, 2, 3, and -1.
70 ❘ Lesson 7 Objects and Built-In Types
As well as the push() method, the Array data type exposes a few other useful methods, as shown in
the following table:
Method Description
concat(array1, array2, ...) Joins two or more arrays into a single array.
join(separator) Converts the array into a string. Each element is sepa-
rated by the specified separator.
pop() Removes the last element of the array and returns it to
the caller.
push(element) Adds a new element to the end of the array. Returns the
new length.
reverse() Reverses the order of the elements in the array.
sort() Sorts the elements in the array.
The next few sections show you how to use some of these methods.
Joining Arrays
If you want to join multiple arrays together, you can use the concat() method. It does not alter the
original arrays; instead, it creates a new array, populates it with the elements of all the specified arrays,
and returns it to the caller. Let’s depart from the automobile example and look at a family, as shown
in this example:
var parents = ["John", "Jane" ];
var kids = [ "Jason", "Joe", "Jordan" ];
var pets = [ "Jackson", "Jimmy" ];
var family = parents.concat(kids, pets);
The first three lines of this code create three arrays, all representing portions of a family — the
names of the parents, children, and pets. The final line of this code calls the concat() method on
the parents array and passes the kids and pets arrays to the method. The newly created array,
family, contains all the elements of the other arrays. This means family contains the names: John,
Jane, Jason, Joe, Jordan, Jackson, and Jimmy.
The order in which the concat() method is called determines the order of the elements in the
new array. In this example concat() is called on the parents array, so the elements in this array
are the first elements in family. The kids array is the first argument passed to the method, so
its elements can be found after John and Jane in family. The pets array is the last argument
passed to concat(), so its elements appear at the end of family. Changing how concat() is
called changes the order in which the provided arrays are joined together. For example, if the
Built-In Data Types ❘ 71
final line is var family = pets.concat(parents, kids), the elements of family would be in
this order: Jackson, Jimmy, John, Jane, Jason, Joe, and Jordan.
This code passes a comma followed by a whitespace character to the join() method called on the
family array. This results in familyString, containing the value "John, Jane, Jason, Joe,
Jordan, Jackson, Jimmy". The separator passed to the method separates the element’s values in
the string.
As mentioned earlier, it’s more efficient to use the join() method to concatenate array elements
than it is to use the + operator to concatenate strings. For this reason you will see developers use
something like the following code to generate strings:
var stringParts = [
"John's family is: ",
family.join(", ")
];
This code first creates an array called stringParts. Its first element is a string value, and its sec-
ond element is the result of converting the family array. The final result of this code is the string
"John's family is: John, Jane, Jason, Joe, Jordan, Jackon, Jimmy".
Like the concat() method, the join() method does not alter the original array.
Sorting Elements
Arrays are typically used for organizing similar pieces of data. Therefore, it might be desirable to
sort that data in alphabetical order if the elements are string values or in numerical order if the data
consists of numbers.
The sort() method makes sorting easy: It sorts the elements of the array in alphabetical order. The
following example uses the family array yet again:
family.sort();
Unlike the other methods before it, the sort() method alters the original array The elements
contained within family are now in the following order: Jackson, Jane, Jason, Jimmy, Joe, John,
and Jordan.
72 ❘ Lesson 7 Objects and Built-In Types
The sort() method is case-sensitive: Words starting with an uppercase letter come before words
starting with a lowercase letter. So Jackson comes before jackson as the result of a sort in
JavaScript.
The sort() method sorts the elements in ascending order: There is not a method or a parameter to
sort in descending order. However, there is a method, called reverse(), that can reverse the order
of an ascending-ordered array.
Reversing Elements
The reverse() method alters the array by reversing the order of the array’s elements. To get an idea
of how this works, let’s look at the kids array from an earlier section:
var kids = [ "Jason", "Joe", "Jordan" ];
kids.reverse();
0 Jason
1 Joe
2 Jordan
This table shows each element in the array and its index position. Calling the reverse() method
reorders the elements in the array in reverse order, and the elements receive new index positions. The
following table shows the array after calling reverse():
Index Element Value
0 Jordan
1 Joe
2 Jason
By combining the use of sort() and reverse() methods, you can essentially sort an array in
descending order, as in the following code:
family.sort().reverse();
Remember that many of the Array object methods return an array; sort() is one such method. Because
of this, it’s possible to chain method calls to perform multiple processes in a single statement. The call
to sort() returns the sorted version of the family array, but instead of the result’s being assigned to a
variable, the return value is used to call the reverse() method. This is just one technique experienced
developers use to write more efficient code.
Built-In Data Types ❘ 73
Date
Every computer system needs to maintain date and time functionality. Many of today’s operating sys-
tems rely on date and time to provide essential information and services. Similarly, every programming
language needs some way to get the current date and time so that programmers can write meaningful
and usable applications.
JavaScript uses the Date data type to handle this functionality. It provides access to information such
as the current date and time, as well as any other date and time, so calculations can be performed.
JavaScript’s Date object uses the UNIX timestamp; in fact, it stores date information in this for-
mat. The UNIX timestamp is the number of milliseconds since the UNIX Epoch, January 1, 1970
at 12:00 a.m. GMT. For example, the UNIX timestamp for January 1, 2010 at 12:00 a.m. GMT is
1264982400000. The date and time contained within a Date object are based on your computer’s
date and time.
There are four ways to create a Date object. The first is the simplest, and it returns the current date
and time. Simply call the constructor without passing any arguments to it, like this:
var date = new Date(); // current date and time
The second way to create a Date object is to pass a year, month, day, hours, minutes, seconds, and
milliseconds to the Date constructor:
var date = new Date(2010, 0, 1, 0, 0, 0, 0); // January 1, 2010 12:00AM
var date2 = new Date(2010, 0, 1); // January 1, 2010 12:00AM
Most parameters are optional; omitting them causes 0 to be passed in their place. An important
note to make here is that the month argument is passed as a zero-based number. So January is 0,
February is 1, March is 2, and so on. If you are specifying hours, they must be passed in 24-hour
format: 0 is midnight, 13 is 1:00 p.m., and 23 is 11:00 p.m.
You can also create Date objects by passing a string value, like this:
var date = new Date("January 1, 2010");
var date2 = new Date("Jan 1 2010");
var date3 = new Date("1 January 2010");
There are a variety of string variations you can pass to the constructor. If in doubt, try it out. Browsers,
however, behave differently in creating a Date object this way. For example, Firefox does not support
"01-01-2010" as a valid date format. So make sure to test Date object creation in multiple browsers
when passing a string to the constructor.
The final way to create a Date object is to pass it a UNIX timestamp. This is perhaps the least-
used method of Date object creation. The following line creates a Date object by passing a UNIX
timestamp:
var copy = new Date(date.getTime()); // January 1, 2010 12:00AM
74 ❘ Lesson 7 Objects and Built-In Types
The Date object’s getTime() method returns the UNIX timestamp of the Date object it was called
on. So, this code makes a copy of the date Date object from the previous example by calling its
getTime() method, passing it to the Date constructor, and assigning the new Date object to the
copy variable.
JavaScript stores dates and times in UNIX timestamps. This isn’t useful, so the Date data type
exposes some methods that allow you to get more meaningful information from a Date object. The
following table lists the more useful methods involving dates.
Date-Related Methods
Method Description
Unfortunately, the Date object does not return the name of the month or the day of the week; devel-
opers have to provide that functionality in their code. With arrays, however, it’s a simple exercise.
Look at this code:
var daysOfWeek = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday" ];
The result of running this code depends on the day on which it is executed. If it were run on January
1, 2010, the alert window would display the text, "Today is Friday." Because array indexes are
zero-based, as is the getDay() method of the Date object, you can use the value returned by getDay()
Number ❘ 75
as the index for the correct day of the week. The same principle can be applied to months, which
you’ll see in the Try It section later.
Just as JavaScript’s Date data type has methods for working with dates, it also exposes methods to
work with the time portion of a Date object. The following table lists these time-based methods:
Method Description
getHours() Returns the hour in 24-hour format. Possible values are 0 through
23.
Number
Lesson 2 thoroughly covered the Number data type. Number objects do not have any properties,
but they do have some methods. The most useful, and perhaps the most used, Number method
is the toFixed() method. This method formats the number to a fixed amount of decimal places,
like this:
var num = 1.2345;
var fixedNum = num.toFixed(2); // 1.23
It accepts one argument specifying the amount of decimal places to include in the new number. It
does not modify the original number; it copies it, formats the copy, and returns the copy to the caller.
The toFixed() method rounds the nearest decimal place up or down. The previous example looked
at the decimal place after the hundredths and determined that it should be rounded down. The next
example uses the toFixed() method to round up:
var num = 1.54321;
var fixedNum = num.toFixed(0); // 2
76 ❘ Lesson 7 Objects and Built-In Types
This code chops all decimal places off the number. The toFixed() number looks at the first decimal
place to determine whether to round the number up or down. Since it is greater than or equal to five,
toFixed() rounds the number up to 2.
The toFixed() method can chop off only zero to 20 decimal places.
String
The beginning of this lesson introduced you to two members of the String data type: the length
property and the toLowerCase() method. The length property is String’s only property, but unlike
the Number type, the String type has a multitude of useful methods. They don’t alter the original
string in any way. Instead, they make a copy, perform their operation on the copy, and return the
copy to their caller.
The following table lists several useful String methods that will be covered in this section:
Method Description
Method Description
alert(welcomeString.indexOf("Jeremy")); // 7
alert(welcomeString.lastIndexOf("Jeremy")); // 40
alert(welcomeString.indexOf("JavaScript")); // -1
The indexOf() method begins searching at the beginning of the string for the specified substring.
It then starts searching the string and doesn’t stop until it either finds a match or reaches the end
of the string. When it finds a match, it immediately returns the index of the match. In this code,
indexOf("Jeremy") returns 7 because the substring was found starting at the seventh character
position.
The lastIndexOf() method begins searching at the end of the string as opposed to the beginning,
and it doesn’t stop until it either finds a match or reaches the beginning of the string. When it finds a
match of the specified substring, it, too, immediately returns the index position of the match. In this
example, lastIndexOf() finds a match that starts at an index position of 40.
If indexOf() or lastIndexOf() does not find a match, it returns a value of -1.
Extracting Substrings
There are times when extracting pieces of a string is beneficial, and JavaScript’s String data type
allows this with the substr() and substring() methods. Both of these methods provide the same
end result, but they do so based on different parameter values.
The substr() method accepts two parameters. The first is required, and it’s the index at which to
start the extraction. The second parameter is optional. If specified, it determines how many charac-
ters after the starting index to extract, including the starting character. If the second parameter is
omitted, the substr() method extracts all characters after the starting index. The following code
uses the substr() method to extract a portion of a string:
var message = "JavaScript 24-Hour Trainer";
var subString = message.substr(11, 7); // 24-Hour
78 ❘ Lesson 7 Objects and Built-In Types
This code uses the substr() method to extract the 24-Hour portion of the string contained within
the message variable. The 2 in 24-Hour is the twelfth character in the string — making the starting
index 11 (again, remember that indexes start at 0) — and the phrase is 7 characters long. Passing these
two values to the substr() method extracts the phrase 24-Hour and assigns it to the subString vari-
able. The string contained within message is not altered.
The substring() method can provide the same results, but it does so with slightly different data.
It still requires a starting index, and the substring() method behaves like substr() if the second
parameter is omitted. But if it is specified, it is the index at which to stop the extraction. An impor-
tant, and often forgotten, behavior of the substring() method is that the extraction includes the
characters up to the ending index; it does not include the character at the ending index. The follow-
ing code rewrites the previous example by using the substring() method:
var message = "JavaScript 24-Hour Trainer";
var substring = message.substring(11, 18); // 24-Hour
Use the method that makes the most sense to you. The substr() method is obviously the better
choice if you know the exact length of the desired substring. The substring() method is the better
choice when you don’t know how long the desired substring is, and can be a powerful tool combined
with the indexOf() and lastIndexOf() methods.
Converting to an Array
In many ways strings are closely related to arrays. In fact, many programming languages see a string
as an array of characters. Unfortunately, JavaScript is not one of these languages. While String objects
have a length property like array objects, you cannot use index notation to access individual char-
acters as you can with elements in an array. Instead, String objects have a charAt() method. Pass an
index to this method and you can retrieve, but not edit, an individual character at the specified index.
It is possible, however, to convert a string to an array by using the split() method: it splits a string
into an array of substrings. The method knows where to split the string according to the argument
passed to it. If you supply an empty string as an argument, this method creates an array in which
each individual character is an element. The following code demonstrates this:
var message = "Hello, World!";
var messageArray = message.split(""); // converts to array
Once the string is in array form, you can access the individual characters using index notation. This
method is also quite useful when you have a string containing a list of items delimited by a consis-
tent delimiting character, such as a comma. Look at this code:
var fruitsString = "orange,apple,banana";
var fruitsArray = fruitsString.split(","); // creates array with 3 elements
This code creates an array by splitting the fruitsString string at each comma. The resulting array
contains three elements: orange, apple, and banana.
Replacing Text
The replace() method accepts two arguments: the string to replace, and the string to replace it
with. This method searches the string starting at the beginning and looks for the first occurrence of
Try It ❘ 79
the specified substring. It does not replace all occurrences of the specified substring — only the first,
as shown in the following code:
var message = "Today is Monday. Yesterday was Monday.";
var newMessage = message.replace("Monday", "Tuesday");
alert(newMessage); // Today is Tuesday. Yesterday was Monday.
This code first creates a string value that contains an error. It says that today is Monday and yester-
day was Monday. To fix this error, the second line of code uses the replace() method to change
"Monday" to "Tuesday" and returns the new string to the newMessage variable. When the alert win-
dow displays the new message, it reads, "Today is Tuesday. Yesterday was Monday."
As with all the other String methods discussed in this lesson, the original string (message in this
case) is not altered.
Try It
In this lesson, you learn how to use objects and some of JavaScript’s built-in object types. Here, you
apply some of the knowledge and concepts you gained from previous sections .
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson07 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson07 folder.
Step-by-Step
Write an application that prompts the user to enter his or her name. Display the name and today’s
date in an alert window. Use a function to get the name of the month, and use another function to
abbreviate it to three characters.
1. Open your text editor and type the following function.
80 ❘ Lesson 7 Objects and Built-In Types
function getMonthName(index) {
var months = [ "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" ];
return months[index];
}
This getMonthName() function creates an array containing the names of the month, and
returns the element at the provided index.
2. Now add the following bolded function:
function getMonthName(index) {
var months = [ "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" ];
return months[index];
}
function abbrName(text) {
return text.substr(0, 3);
}
This new function, abbrName(), uses the substr() method to select the first three charac-
ters of the provided text and returns it to the caller.
3. Add the following bolded code:
function getMonthName(index) {
var months = [ "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" ];
return months[index];
}
function abbrName(text) {
return text.substr(0, 3);
}
The first line of this code creates a Date object containing today’s date. This Date object will
be used to display date information to the user.
Try It ❘ 81
The second statement creates an array called messageParts. This array’s purpose is to con-
tain elements that will be concatenated into the message to display to the user. The first ele-
ment is a simple string. The second element calls the prompt() function to prompt the user
to enter his or her name. The fourth element gets the current month from the date object
and passes it to the getMonthName() function. The return value of getMonthName() is then
passed to the abbrName() function. So the fourth element in the array is a string containing
the first three characters of the current month.
4. Alert the user to the message by adding the following bolded code:
function getMonthName(index) {
var months = [ "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" ];
return months[index];
}
function abbrName(text) {
return text.substr(0, 3);
}
alert(messageParts.join(""));
This line calls the join() method, passing it an empty string to concatenate the elements
without a noticeable separator.
5. Save the file as lesson07_sample01.js.
6. Open another instance of your text editor, type the following HTML, and save it as
lesson07_sample01.htm.
<html>
<head>
<title>Lesson 7: Objects</title>
</head>
<body>
<script type="text/javascript" src="lesson07_sample01.js"></script>
</body>
</html>
7. Open the HTML file in your browser. An alert window will display the name provided to
the prompt window in a message containing today’s date.
8
Custom Objects
The built-in objects discussed in Lesson 7 go a long way in making applications easier to write
and maintain, but they stop short of allowing developers to tailor objects to their personal needs
and those of their applications. For example, what if it were your job to write an application that
emulates a two-dimensional Cartesian coordinate plane? How would the Array, Date, Number,
and String data types help you achieve that goal? It could be done, but it would take a lot of
time and effort to write a proper application.
The fact is that JavaScript or any object-oriented programming language for that matter wouldn’t
be useful without the capability to create objects other than the built-in objects provided by
the language. Being able to build your own objects and data types makes it possible to write
flexible and maintainable applications.
At the heart of JavaScript’s object-oriented design is the Object data type. It’s the basis of all
objects and reference types in JavaScript, and you’ll look at how to use it in this lesson.
This code calls the Object() constructor using the new keyword and assigns it to the
coordinates variable. As with the Array data type, there is a much simpler (and preferred)
way to create an object, by using object literal notation:
var coordinates = {};
Object literal notation uses curly braces as opposed to the square braces used to denote an
array literal.
84 ❘ Lesson 8 Custom Objects
A plain object isn’t usable. There’s not much you can do with one, but JavaScript is a dynamic language.
You can add properties to objects on-the-fly. All you need to do is assign a value to a property, like this:
var coordinates = {};
coordinates.x = 0;
coordinates.y = 0;
It’s possible to take this code a step further and define the object and its properties in one statement.
Doing so produces something like the following code:
var coordinates = {
x : 0,
y : 0
};
This statement starts like any other assignment statement. The var keyword denotes a variable dec-
laration or initialization, followed by the variable’s identifier (coordinates in this case), and then
the assignment operator. Next is an opening curly brace denoting an object literal, but then things
start looking different.
Inside the curly braces is a list of property definitions separated by commas. Property definitions
inside an object literal consist of the property’s name, a colon (:), and then the property’s value.
Each definition is separated from the next by a comma; otherwise JavaScript wouldn’t know where
one property definition ends and another begins. In this code two properties are defined for the
coordinates object. They are x and y, and this code assigns them both a value of 0.
After an object and its properties are defined, you can use them as you would any other object.
Look at the following code:
var text = "X is " + coordinates.x + " and Y is " + coordinates.y;
alert(text);
The x and y properties of the coordinates object are concatenated with other string values. The
final result is assigned to the text variable, and the second statement displays the contents of text
to the user. This functionality would work well as a method, so let’s add a method that performs
this action to the coordinates object.
Method definitions follow the same principles as property definitions; the only difference is that the
value to the right of the colon is a function. The following bolded code adds a method definition to
the coordinates object:
var coordinates = {
x : 0,
y : 0,
sayCoordinates : function() {
var text = "X is " + this.x + " and Y is " + this.y;
alert(text);
}
};
First, notice that a comma was added to the end of y’s definition. Since it is no longer the last mem-
ber in the definition list, it needs a comma to separate it from the definition of sayCoordinates().
The Object Data Type ❘ 85
The code within the new method’s body is very similar to the code in the previous example. But
there’s one change: A new keyword, called this, is used where the coordinates identifier was pre-
viously used. The this keyword is a special variable used inside a function; it points to the object
on which the function is invoked as a method. The sayCoordinates() method is a member of
coordinates. As such, it executes within the context of the coordinates object. Because of this,
the this variable points to the coordinates object. The context in which this is used can easily be
translated into spoken language. A statement like this.x = 0; translates to “Set the x property of
this object to 0.” When you see this, think “this object.”
Outputting Objects
Custom objects have many uses in JavaScript development, but they’re primarily used for data
exchange. To continue the Cartesian plane example, a single point in 2D space must have two pieces
of information: the x, or horizontal, information, and the y, or vertical, information. In order for
the application to know where the current point is in 2D space, a function called getCurrentPoint()
needs to return the x and y coordinates for the current point. JavaScript does not have a data type
that represents coordinate data, but as you’ve seen in this lesson, a custom object can do it easily.
The following code shows an example of what such a function might look like:
function getCurrentPoint() {
// set initial values for current coordinates
var currentX = 0;
var currentY = 0;
return {
x : currentX,
y : currentY
};
}
This function first initializes two local variables called currentX and currentY with a value of 0.
Then, as stated by the comments, some code determines the x and y data and assigns the appropri-
ate values to the currentX and currentY variables. Finally, the function returns an object, created
with object literal notation, with two properties, x and y, that contain the values needed for the cur-
rent point. Notice that this object has no identifier, and is not assigned to any local variable within
the function. This code demonstrates how you can return any type of data without specifically cre-
ating and assigning it to a variable first.
To use this function, you might write code similar to this:
var point = getCurrentPoint();
alert(point.x);
alert(point.y);
86 ❘ Lesson 8 Custom Objects
The first line calls the getCurrentPoint() function and assigns the resulting value to the point
variable, which is now an object. The next two lines of code show the point object in use by access-
ing the x and y properties.
Objects as Input
Just as returning an object to represent complex data is beneficial, so too is accepting objects as
arguments in functions. Your fictional Cartesian plane application needs to draw a dot at a specific
point on the graph, and a function called drawPoint() provides this ability for the application. The
function might look like the following:
function drawPoint(x, y) {
// do something with x
// do something with y
}
Here, a traditional function accepts two arguments, and it performs some type of work with those
arguments to draw a point on the screen. There’s nothing wrong with this function, but it could fit
better in an object-oriented environment by accepting an object containing coordinate data as an
argument:
function drawPoint(point) {
// do something with point.x
// do something with point.y
}
By means of a simple change, this function fits very well into an object-based environment. It’s much
easier to call since it now accepts one argument containing complex data.
This Point constructor accepts two arguments, x and y coordinates. It accepts individual pieces of
data, as opposed to the single object shown at the end of the previous section. This is a personal
choice: The philosophy behind it is that individual pieces of data are combined to construct a singu-
lar complex piece of data. It would be perfectly fine for the constructor function to accept a custom
object containing x and y properties.
Inside the constructor function two properties, x and y, are assigned the values passed to the con-
structor function. Notice the use of the this variable. Remember that this refers to the current
object, and the current object, in this case, is the object being created by the calling of the Point()
constructor function with the new keyword. So the first two statements in the constructor create
x and y properties by assigning their values. If this were omitted and var were used in its place, x
and y would simply be variables.
After creating the x and y properties, the constructor function creates a method called getDistance().
This method accepts a Point object as an argument, and it uses the Math object to work out the
Euclidean distance formula to determine the distance between the point represented by the current
object and the Point object passed to the method.
To create a Point object, simply call the constructor by using the new keyword, and pass it some
data, like this:
var point1 = new Point(0, 0);
var point2 = new Point(1, 1);
With more than one Point object created, you can determine the distance between the two points
by calling the getDistance() method on one of the objects and passing the other as an argument,
like this:
var distance = point1.getDistance(point2);
When calling a constructor you create, it is very, very important to remember to include the new
keyword. Not doing so results in near-catastrophic circumstances. Custom constructor functions
use the special this variable to create properties and methods for the object the constructor func-
tion is making. If the new keyword is omitted, the this variable points to the global object.
Because of this, the constructor function creates the properties and methods defined within it on the
global object. Not only is the global object polluted with properties and methods it doesn’t need, but
a new object is not created. There is no warning or error given by JavaScript; it simply executes the
code. The only clue you have is that your code will not work as expected. So always, always remem-
ber to use the new keyword when calling a constructor function.
88 ❘ Lesson 8 Custom Objects
Try It
In this lesson, you learn how to create your own objects and use them in your code.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson08 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson08 folder.
Step-by-Step
Write a Person data type that has two properties: firstName and lastName. It should also have a
method called getFullName() to concatenate the two properties together and return the result of
that concatenation, and a method to greet other Person objects.
1. Open your text editor and type the following function:
function Person(firstName, lastName) {
This is the basis of the constructor function for your Person data type. The function has
two parameters: firstName and lastName.
2. Now add the following bolded code:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
This code adds the firstName and lastName properties to the data type. Their values come
from the values passed to the constructor.
Try It ❘ 89
This code adds the getFullName() method. It concatenates the firstName and lastName
properties and returns the result of that operation. The result of the concatenation is not
stored in a variable; it is directly returned to the caller.
4. Now it’s time to add the greet() method. It’s bolded in the following code:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.getFullName = function() {
return this.firstName + " " + this.lastName;
};
this.greet = function(person) {
alert("Hello, " + person.getFullName());
};
}
The greet() method accepts a Person object as an argument, and it calls the getFullName()
method on that Person object to get the person’s name and use it in a greeting message. The
message is displayed in an alert window.
5. Once again, add the bolded code that follows. Feel free to pass your own name to one of the
constructor calls.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.getFullName = function() {
return this.firstName + " " + this.lastName;
};
this.greet = function(person) {
alert("Hello, " + person.getFullName());
};
}
person1.greet(person2);
7. Open another instance of your text editor, type the following HTML, and save it as
lesson08_sample01.htm:
<html>
<head>
<title>Lesson 8: Custom Objects</title>
</head>
<body>
<script type="text/javascript" src="lesson08_sample01.js"></script>
</body>
</html>
8. Open the HTML file in your browser. An alert window displays the result of calling
person.getFullName(): a string containing the values passed to the Person() constructor.
Please select Lesson 8 on the DVD to view the video that accompanies this
lesson. You can also download the sample code for all lessons from the book’s
website at www.wrox.com.
9
Prototypes and Inheritance
(The Function Object)
The latter half of Lesson 8 taught you how to create your own data types by creating a construc-
tor function in conjunction with the this variable to create properties and methods. We’ll pick
up with the Point data type; its code is repeated here for your convenience:
function Point(x, y) {
this.x = x;
this.y = y;
this.getDistance = function(point) {
var x = Math.pow(point.x - this.x, 2);
var y = Math.pow(point.y - this.y, 2);
This implementation has a slight flaw. When calling the constructor with the new keyword,
JavaScript creates a Point object, and in doing so it has to recreate everything within the con-
structor every time it’s called. That is, the x and y properties, and a new Function object to serve
as the getDistance() method. In JavaScript, functions are objects, too — complete with their
own properties and methods.
In an object-oriented environment, creating a new object should also create its properties.
Properties, and more importantly their values, are specific to each instance of a data type, and
they should rarely be shared with all other instances of a particular data type. For example, a
Point object’s x and y properties contain values that are independent from another Point object’s
x and y properties. Changing one Point’s x property should not affect another Point object’s x
property. So a Point’s x and y properties should be created when the Point object is created.
A Point object’s methods, however, are another matter. From a developer’s standpoint, you
want a Point object to behave like every other Point object in an application. A change made to
a Point object’s behavior should be reflected in all other Point objects; otherwise you could not
depend on any object to behave how you need it to behave.
92 ❘ Lesson 9 Prototypes and Inheritance (The Function Object)
In JavaScript, an instance of the Function data type is created every time a function is defined or
declared. That means that with every call to the Point() constructor, JavaScript creates a new func-
tion instance for the getDistance() method. JavaScript shouldn’t have to create new behavior with
every new object that is created; it should use an already existing behavior and share that behavior
with all objects of the same type. This is achieved by means of the prototype property.
Point.prototype.getDistance = function(point) {
var x = Math.pow(point.x - this.x, 2);
var y = Math.pow(point.y - this.y, 2);
Here, the getDistance() method is moved out of the constructor and added to the
Point.prototype object. This is advantageous for two reasons:
➤➤ Only one Function object is created and assigned to getDistance. Calling the Point()
constructor now does not create a new Function object for getDistance().
➤➤ The getDistance() method is shared among all Point objects. Changes made to the method
are seen by all Point objects.
Even though this method is now created on the prototype, you still access the method from an
object in the same way, as shown in this code:
var point1 = new Point(0, 0);
var point2 = new Point(1, 1);
point1.getDistance(point2);
Using the prototype Object ❘ 93
This works because of the way JavaScript looks up identifiers on objects. When accessing this
getDistance() method, JavaScript first performs a search for the getDistance identifier on the
object itself — looking for an identifier with the same name. If JavaScript finds a match, it returns
the match’s value (remember: Functions can be values). However, if it does not find a match, JavaScript
searches for the getDistance identifier in the data type’s prototype object and returns either the
value if a match is found or undefined if one could not be found.
Because of this behavior, it’s possible to override methods defined in the prototype object. To do
this, simply assign a value to the getDistance identifier, like this:
var point1 = new Point(0, 0);
var point2 = new Point(1, 1);
point1.getDistance = function(point) {
alert("This method no longer works.");
};
Look at the bold portion of this code. Here, a new function object is created and assigned
to the getDistance member on the object. You might think that this code alters the
getDistance() method on the prototype, but it doesn’t. The Point.prototype.getDistance()
method can only be modified by directly accessing it through the prototype object (that is
Point.prototype.getDistance = new value). This code actually creates a new getDistance()
method on the point1 object itself, essentially overriding the getDistance() method defined on
the prototype.
So now when the getDistance() method on point1 is accessed, JavaScript searches for the
getDistance identifier, finds a match on the point1 object, and returns the newer function. The
original getDistance() method on the prototype object is still intact, and the following code
demonstrates this:
point2.getDistance(point1); // returns 2
Here, the code calls the point2.getDistance() method. The point2 object does not actually have
a getDistance member, so JavaScript pulls the value from Point.prototype.getDistance — the
function that performs the distance calculation.
Be wary of overriding methods in this manner. The whole idea behind using the prototype object
is that members, usually methods, don’t have to be created for each object. Besides, overriding a
method like this can cause undesired results.
Person.prototype.getFullName = function() {
return this.firstName + " " + this.lastName;
};
It’s a simple data type. It has properties for the person’s first and last name, as well as a method to
concatenate the two into the person’s full name. A lot of work is already done with this data type.
Wouldn’t it be great if you could use it without altering it (otherwise, your coworker’s code may
break), while still adding the functionality you need? Inheritance allows you to do so.
There are a number of inheritance patterns in JavaScript, each with its pros and
cons. This book teaches you just one pattern.
The first step is to create a new data type that will inherit from the Person data type. This new type
is called Employee, and the constructor function has three parameters: the employee’s first and last
name, and his or her position.
function Employee(firstName, lastName, position) {
this.position = position;
}
This constructor function creates a position property and assigns it the value passed to the argu-
ment of the same name, but nothing is done with the firstName and lastName parameters. As it
Inheriting Members from Other Objects ❘ 95
stands right now, Employee objects have only one property. So Employee needs some way of gaining
the firstName and lastName properties of the Person type. This is where the call() method of
Function objects comes into play.
The call() method allows you to call a function as if it was a method of a specific object. In other
words, it calls a method with a specified value for the this variable. The idea is probably easier to
understand with some code. The following bolded code adds a new line to the Employee() constructor
function:
function Employee(firstName, lastName, position) {
Person.call(this, firstName, lastName);
this.position = position;
}
So what happens here is that the value of this (the Employee object being created) is passed to the
call() method, which gives the this variable in Person() the value of the Employee object being
created. So, essentially, Person() executes as if it were Employee().
The other arguments passed to the call() method reflect the parameters of the Person() function:
firstName and lastName. The result of this code is that firstName and lastName properties are
added to Employee objects when they are created. So, Employee has now inherited Person’s properties,
but it still needs to inherit the getFullName() method.
If getFullName() were defined inside the constructor, inheritance would be complete and you’d
be done. But the method is defined in Person’s prototype, so you’ll use something called prototype
chaining to inherit Person’s prototype. The process of prototype chaining is a simple one: Simply cre-
ate a new instance of the base-type and assign it to the sub-type’s prototype, as in this bolded code:
function Employee(firstName, lastName, position) {
Person.call(this, firstName, lastName);
this.position = position;
}
Prototype chaining gets its name because it adds another prototype to the search list, much as one
would add a new link to a chain. Now when you create an Employee object and call the g etFullName()
method, JavaScript first looks at the object for a match. It does not find a match, so it looks at
Employee’s prototype and doesn’t find a match there either. So it looks at Person’s prototype, since
that is a new “link” in the “chain” of prototypes, finds a match, and executes it. Of course, the more
prototypes you add to the chain, the more JavaScript has to search for items in prototypes. So it’s a
good idea to keep prototype chains relatively small.
You can now create an Employee object and use the firstName, lastName, and position properties,
as well as call the getFullName() method. The following code demonstrates this:
var employee = new Employee("Jeremy", "McPeak", "Author");
alert(employee.firstName); // Jeremy
alert(employee.lastName); // McPeak
alert(employee.getFullName()); // Jeremy McPeak
96 ❘ Lesson 9 Prototypes and Inheritance (The Function Object)
Try It
In this lesson, you learn how to use prototypes to make your code more efficient by creating fewer
objects. You also learn about the Function’s call() method, and how to chain prototypes to make
inheriting a data type’s prototype possible. In this section you learn how to override a base-type’s
method in a sub-type.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson09 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson09 folder.
Step-by-Step
The getFullName() method needs to include the employee’s position. Follow these steps to override
Person’s getFullName() method to provide that functionality.
1. Open your text editor and type the following function:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.getFullName = function() {
return this.firstName + " " + this.lastName;
};
};
3. The Person data type’s getFullName() method has functionality that we want to use. To save
time recoding that portion of the method, use the call() method to call Person’s getFullName()
method and assign the result to a variable. The new code is bold in the following:
// Person omitted for printing
This code calls the Person.prototype.getFullName() method in the context of the current
object (the Employee object). The call() method has to be used here because getFullName()
for Employee objects now has a new value (the method you’re creating right now).
4. Append the employee’s position to the string in fullName and return it:
function Employee(firstName, lastName, position) {
Person.call(this, firstName, lastName);
this.position = position;
}
alert(employee.getFullName());
8. Open the HTML file in your browser. An alert window displays the result of calling
employee.getFullName(): a string containing the values passed to the Employee()
constructor.
To get the sample code files you can download Lesson 9 from the book’s website at www.wrox.com.
Please select Lesson 9 on the DVD to view the video that accompanies this
lesson.
Programming the Browser
This concept can be taken a step further with variables and functions. When you create a variable or
function in the global scope, you actually create a property or method of the window object. Look at
this code:
var someVariable = "Some value"; // is same as window.someVariable = "Some value";
function showSomeVariable() {
alert(window.someVariable); // explicitly use window
}
Here a variable called someVariable is created and given a string value. Next
a function called showSomeVariable() is declared; its body contains a call
to the alert() method and passes the window.someVariable property to
it. The final line of this code explicitly uses the window object to execute
the showSomeVariable() method. The result of this code can be seen in
Figure 10-1
Figure 10-1.
All global variables and functions are members of the window object; thus, you can access them by
explicitly using the window object. However, it is conventional to omit the explicit use of window
unless there is a reason to use it. You’d want to use it, for example, to deal with scope when a local
variable within a function has the same name as a global variable. Consider the following code:
var someVariable = "Global value";
function showSomeVariable() {
var someVariable = "Local value";
In this code a global variable called someVariable is initialized with a string value. Next the
showSomeVariable() function is declared, and its first statement is the initialization of a variable
called someVariable — the same name as the global variable. Inside the function, the someVariable
identifier refers to the local variable. In order to access the global variable with the same name, you
must prepend it with window; otherwise, JavaScript retrieves the local variable.
The general rule of thumb is to omit window unless you absolutely need to explicitly specify the
global object.
Showing and Using Dialog Boxes ❘ 103
This code creates a Person data type and correctly calls the constructor for that data type. A new
Person object is created and assigned to the person variable. There is nothing wrong with this code.
Compare it to the following code, which omits the new keyword:
var person2 = Person("Jeremy", "McPeak");
Here a statement calls the Person() function without prefixing it with new. JavaScript sees the call
as being like any other function call and executes it. Because Person("Jeremy", "McPeak") is the
same as window.Person("Jeremy", "McPeak"), the this variable is bound to the window object;
therefore, the code within the function creates two global variables, firstName and lastName, and
assigns them a value of whatever was passed to the function. Also, since Person() does not return a
value, the person2 variable contains a value of undefined. So not only has this code added unnec-
essary variables to the global scope, but code depending on person2 breaks because person2 is
undefined instead of a Person object.
Again, it is very, very important to always use the new keyword when calling a constructor function.
The code that generates the alert box in this figure is as follows:
alert("Hello, Alert box!");
Use the alert() method to show this type of dialog. Its only parameter is the data to display to the
user. If you want to get a user’s input with a dialog box, use the prompt() method.
The prompt() method accepts two arguments. The first is the message to display to the user. The
second is optional and determines the default text entered in the textbox.
Clicking OK causes the prompt method to return the text typed into the textbox to the caller. In the
case of this code, the value of the name variable depends on what text is in the textbox when the user
clicks OK. If the user clicks Cancel, the prompt() method returns null. Therefore, when relying on
the prompt() method to get data from the user, make sure the data returned is not null,
like this:
if (name != null) {
// do something with data
}
The prompt() method is an obtrusive means of getting data from the user. From a user-interface
perspective, form elements within a web page are a better means of acquiring text-based user input.
Not only can form elements take on the look and feel of your web application, but you can acquire
multiple pieces of data without having to display an ugly pop-up window for user input.
There is, however, a dialog box that gets input from the user that is ideal in some situations.
You can show the confirm dialog box by calling the confirm() method. It accepts an argument con-
taining a message to display to the user, and it returns a Boolean value based on the button the user
presses. The following code uses the confirm() method in an if statement:
if (confirm("Is it sunny today?")) {
alert("Let's go outside");
} else {
alert("Let's stay inside");
}
Here the reload() method is called on the location object. Remember that window is implied, so
this code actually translates to window.location.reload(true). A value of true is passed to this
method, so it will always retrieve the page from the webserver.
106 ❘ Lesson 10 The window Object
This code assigns a new URL to the href property, and the browser automatically navigates to the
page (Google’s search page in this case). You can, however, simplify this code by omitting the href
property:
location = "http://www.google.com";
This code performs the same function as the previous example, and it’s actually the preferred
method of navigating to a different page.
Try It
In this lesson you learn about the window object and its global nature. You also learn about
the dialog boxes it provides you with, as well as the ability to reload the page and navigate to
another page.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson10 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson10 folder.
Try It ❘ 107
Step-by-Step
Write a simple application that either navigates to a different page or reloads the current page, based
on the user’s input.
1. Open your text editor and type the following code:
if (confirm("Do you want to go to Google?")) {
location = "http://www.google.com";
}
This code shows a confirm dialog box asking the user if he or she wants to go to another
web page. If the user clicks OK, the browser will go to Google’s website.
2. Now add an else block to reload the page. It is bolded in the following code:
if (confirm("Do you want to go to Google?")) {
location = "http://www.google.com";
} else {
location.reload(true);
}
5. Open the HTML file in your browser. A dialog box will ask whether or not you want to go
to Google. Click OK to go there, or Cancel to reload the current page.
To get the sample code files you can download Lesson 10 from the book’s website at www.wrox.com.
Please select Lesson 10 on the DVD to view the video that accompanies this
lesson.
11
Scripting Windows
Dialog boxes and the location object are far from all the window object has to offer developers.
In fact, this object is often used to open and manipulate new windows.
If you were to open a new window with JavaScript and give it the name childWindow, clicking
this link would load www.microsoft.com in the newly opened window. Of course, omit the
second parameter if you do not wish the window to be a target for links.
The third parameter is a list of options and properties that determine how the child window
looks. These options and properties include, but are not limited to, width, height, and whether
user interface elements are visible to the user. There’s a lengthy list of options to choose from,
and you’ll see a shortened list in a few moments. For now, let’s just stick with height and width
with this simple example:
var childWindow = open("http://www.google.com", "googleWindow",
"height=400, width=400");
110 ❘ Lesson 11 Scripting Windows
Here the open() method is called (the window object is implied before open()), and the child’s
window object is stored in the childWindow variable. The first argument passed to the method is the
URL for Google’s home page. Its name is googleWindow, so any hyperlink in the main document
with the same target value opens its URL in this new window. Notice the third argument: It looks
a little different from other arguments you’ve seen so far. The third argument is a string that con-
tains one or more options separated by commas. In this example the height and width options are
assigned values of 400, and a comma separates the two options. Figure 11-1 shows the window this
code opens in Chrome.
Figure 11-1
The third parameter is completely optional; you don’t have to specify any options or settings for the
new window. If you do, however, you can add any of the following options to control how the win-
dow looks or behaves.
resizable yes, no Determines whether or not the user can resize the
window
scrollbars yes, no Shows the horizontal and/or vertical scrollbars if the
page is too large to fit in the window
status yes, no Shows the status bar (Doesn’t work in Opera)
toolbar yes, no Shows the toolbar (Doesn’t work in Opera)
top integer The vertical position of the new window
width integer The width of the new window in pixels
Keep in mind this list is not exhaustive, but these are the most commonly used options for a new
window. Consider the following code to get an idea of how to apply some of these options:
var childWindow = open("http://www.google.com", "googleWindow",
"height=400, width=400, resizable=no, menubar=no");
Here two more options are added to the open() method’s third parameter. The new window is no
longer resizable by the user, and it does not display the menu bar. Figure 11-2 shows the window
generated from this code in Internet Explorer 8.
Figure 11-2
112 ❘ Lesson 11 Scripting Windows
The fourth parameter is a Boolean value that determines whether the URL specified in the first
argument creates a new entry or replaces the current entry in the browser’s history list if the page is
loaded into an already existing window. If true, the URL replaces the current document in the his-
tory list. If false, a new entry is created in the history.
Manipulating Windows
Unfortunately, you can’t change the properties listed in the previous table once a window is opened.
You can, however, change the window’s dimensions and position. Instead of using the height, width,
top, and left properties in the previous table, you must use four special methods that perform these
actions.
Before looking at these methods, especially the positional methods, you need to know how coordi-
nates are translated on the screen. The computer screen is a coordinate system of pixels. The top
left pixel on the screen is the origin, or 0, 0 (x, y). As you move down the y-axis of the screen, each
pixel is a consecutive positive number. So the pixel directly below the origin has a vertical position
of 1, the pixel below that has a vertical position of 2, and so on. As you move to the right on the
x-axis, each pixel is a consecutive positive number. This means the pixel directly to the right of the
origin has a horizontal position of 1, the one next to it has a horizontal position of 2, and so on.
Figure 11-3 illustrates this coordinate plane.
Figure 11-3
Manipulating Windows ❘ 113
You use this coordinate system to position a window by specifying its horizontal and vertical posi-
tion when using the moveTo() method. The following code opens a window and positions it on the
screen at (100, 100). Figure 11-4 shows the results of the following code:
var childWindow = open("http://www.google.com", "", "height=300, width=300");
childWindow.moveTo(100, 100);
Figure 11-4
Here, the moveTo() method moves the window 100 pixels to the right and 100 pixels down from
the origin. The first argument passed to moveTo() is the new horizontal (x) position, and the second
is the new vertical (y) position. This method moves the window in relation to the entire screen. So
this code moves the window 100 pixels from the left and top edges of the screen. If you wanted to
move the window from its current position of (100, 100) by four pixels to the left and two pixels
down, you could use this line of code:
childWindow.moveTo(100 - 4, 100 + 2);
114 ❘ Lesson 11 Scripting Windows
You can, however, use another method called moveBy() to perform the same action. This method
moves the window by the specified values in relation to the window’s current position. So instead of
performing arithmetic, as in the previous example, simply use the moveBy() method and supply the
number of pixels to move the window by, like this:
childWindow.moveBy(-4, 2);
This line of code provides the same results as the previous example. Instead of moving the window
to the coordinates (-4, 2), it moves the window four pixels to the left and two pixels down from its
current position of (100, 100). Given a specific point on the screen, positive numbers move the
window to the right and down; negative numbers move the window to the left and up.
Repositioning windows isn’t the only way you can manipulate them after creating them; you can
also change their height and width with methods similar in concept to the moveTo() and moveBy()
methods. The first method is resizeTo(), and it allows you to specify the new width and height of
the window, like this:
childWindow.resizeTo(100, 200);
Here the resizeTo() method resizes the window to have a width of 100 and a height of 200.
Figure 11-5 shows what this code does to the window created earlier in this section.
Figure 11-5
Coding between Windows ❘ 115
If you want to resize the window in relation to its current dimensions, simply use the resizeBy()
method. The following code shrinks the window’s width by 50 pixels and expands its height by 100:
childWindow.resizeBy(-50, 100);
Browsers allow you to manipulate the parent window by using these methods. It is strongly recom-
mended that you do not do so. Our primary goal as web developers is to provide usable and friendly
web applications to our users. Resizing and moving the main window breaks the friendliness of our
applications. As a general rule of thumb, move and resize only the windows you open, and leave the
windows the user opens alone.
Changing a window’s size or position isn’t the only way to interact with a window created with the
open() method. Parent and child windows can communicate with each other using JavaScript.
childWindow.alert("Hello there!");
Here a new window is opened to www.google.com, and its window object is assigned to the
childWindow variable. The next line uses this object and calls its alert() method, displaying an alert
box in the child window. So by executing this code, the JavaScript code in the main window communi-
cates with the window object of the child window. Using this methodology, you can tell the child win-
dow to navigate to a different page by using the location object, like this:
childWindow.location = "http://www.microsoft.com";
Here the child window navigates to Microsoft’s website by assigning a new value to the location
object.
Cross-window communication is not a one-way street — the child window also has the ability to
communicate with the parent’s window object by using the opener keyword. This keyword is used
inside the child window to reference the parent window. The following code demonstrates the use of
the opener property:
opener.alert("Hello from the child!");
In this code, an alert box displays a message in the parent window even though it is executed from
the child window. You can also access the parent’s location object — simply prefix it with opener
as shown here:
opener.location = "http://www.google.com";
In this line of code, the child window tells the parent window to navigate to Google’s home page.
The fun doesn’t end here, however. Not only can you access the window objects of both parent and
116 ❘ Lesson 11 Scripting Windows
child windows, but you can also access each window’s JavaScript code. The following code is an
HTML page. Let’s refer to it as Page 1:
<html>
<head>
<title>Page 1</title>
<script type="text/javascript">
function showMessage(message) {
alert(message);
}
</body>
</html>
Focus on the <script/> element. It creates a function called showMessage() that accepts one argu-
ment called message. The value passed to this function is then passed to the alert() method. The last
line of the <script/> element opens a new window, which we’ll call Page 2. Its HTML follows:
<html>
<head>
<title>Page 2</title>
<script type="text/javascript">
opener.showMessage("Hello from Page 2!");
</script>
</head>
<body>
</body>
</html>
The script in this page has only one line; it uses the opener object to call the showMessage() method
(remember that global variables and functions are actually properties and methods of the window
object). So this is what happens when you load Page 1 into a browser:
1. Page 1 creates the showMessage() function and opens a new window.
2. Page 2 loads into the new window and calls the showMessage() function in the parent win-
dow, passing it a string value.
3. The showMessage() function executes, displaying “Hello from Page 2!” in an alert box in
Page 1.
The ability for JavaScript code to communicate between windows is pretty cool, and it can be very
helpful when you are writing applications that require multiple windows. There is, however, one
issue with cross-window JavaScript communication: security.
Try It ❘ 117
Security
JavaScript’s success is in part the result of its security policies, and one such policy relevant to this
discussion is the same-origin policy. This states that a JavaScript script in one window can access
and interact with a web page (and its JavaScript) in another window if, and only if, the web pages in
both windows are from the same origin. In other words, if the pages in both windows do not come
from the same domain (among finer details), JavaScript in one window cannot communicate with
the web page and its JavaScript in the other window.
Two web pages or JavaScript scripts are determined to be of the same origin if the following criteria
are met:
➤➤ The protocols must match. HTTP is the primary protocol used on the Internet, and two pages
or scripts must use the same protocol in order to be considered as coming from the same origin.
This means http://www.wrox.com is not of the same origin as https://www.wrox.com. One
URL uses the HTTP protocol, and the other uses HTTPS.
➤➤ The host names or domain names must match. A web page’s domain name is unique. In
order to be considered as coming from the same origin, two pages or scripts must come from
the same domain. This includes subdomains. For example, http://p2p.wrox.com is not the
same domain as http://www.wrox.com. Even though the primary domain, wrox.com, is
the same, the subdomains are not. Therefore, these two URLs are not considered to be from
the same origin.
➤➤ The ports must match. By default, HTTP traffic uses port 80. When you request a website,
your browser uses port 80 to send the HTTP request to the server. So, the URL of http://
www.wrox.com is the same as that of http://www.wrox.com:80. There are some webservers,
however, that are configured to use a different port (a common one is 8080) to provide other
web-based services. To be considered as having the same origin, the ports of both scripts
or pages must match. This means http://www.wrox.com is not from the same origin as
http://www.wrox.com:8080.
Two web pages or JavaScript scripts are considered to be from the same origin only when these three
criteria are met. This means you cannot open a window to http://www.google.com and interact
with its web page and JavaScript. You can change the window’s size and position, and you can access
the location object, but you cannot interact with any JavaScript. JavaScript’s security will not
allow it.
Try It
In this lesson, you learn about creating new windows and how to interact with them by changing their
position and size, and you learn about interacting with the objects and JavaScript code (if security
permits).
118 ❘ Lesson 11 Scripting Windows
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson11 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson11 folder.
Step-by-Step
Write a simple web page that opens a new window with a blank page and write text to it.
1. Open your text editor and type the following code:
<html>
<head>
<title>Page 1</title>
<script type="text/javascript">
var childWindow = open("");
</script>
</head>
<body>
</body>
</html>
The JavaScript in this page opens a blank page in a new window by not specifying a URL
for the window to open.
2. Now add the bold code shown here:
<html>
<head>
<title>Page 1</title>
<script type="text/javascript">
var childWindow = open("");
</head>
<body>
</body>
</html>
This new code uses the document object’s write() method to write directly into the page in
the new window. The first line writes some text and an HTML <br/> element. The second
line writes more text, which, when rendered in the browser, is on a second line.
3. Save the file as lesson11_sample01.htm.
4. Open the HTML file in your browser. You’ll see a new window open, and it will contain the
text written into the document.
To get the sample code files you can download Lesson 11 from the book’s website at www.wrox.com.
Please select Lesson 11 on the DVD to view the video that accompanies this
lesson.
12
Scripting Frames
The previous two lessons focused on the window object — a programmatic representation of
the browser window. The browser window typically contains one web page, and thus only one
window object. The <frameset/> element, however, allows web developers to divide a browser
window into two or more smaller window panes.
Each of these panes is itself a browser window, and as such has a full-fledged window object.
In fact, some browsers allow you to move and resize an individual frame with the moving and
resizing methods you learned in Lesson 11 (it is highly recommended that you do not use these
methods on a frame).
Arguably the most important aspect of dealing with frames as a JavaScript developer is access-
ing code and objects across frames. Over the course of this lesson you’ll see many similarities
between cross-frame scripting and cross-window scripting.
This is a simple frameset page: It divides the page into upper and lower regions. The frame on
top, named frmTop, loads the frame_top.htm page into it, and the bottom frame, frmBottom,
122 ❘ Lesson 12 Scripting Frames
loads frame_bottom.htm. Make note of each frame’s name, as they are key when you are accessing
them through script.
The browser creates a hierarchy of three window objects when it loads this frameset page. The first
window object belongs to the main window — the one that loaded the frameset page. This is referred
to as the parent window.
The second and third window objects are child objects of the par-
ent window. The first of these is created for the first frame in the
frameset (frmTop), and the next is created for the frmBottom
frame. Figure 12-1 is a visual representation of the relationship
and hierarchy of these three window objects — a family tree, if
you will.
This illustration also doubles as a guide to communicating between frames. You can see that the
parent window has a direct relationship to its children. So it is safe to assume that accessing the chil-
dren from the parent in JavaScript is done directly. You access a child frame’s window object much
as you access an opened window’s window object. In Lesson 11 you learned that the open() method
returns the window object of the newly created window, and that you can assign that object to a
variable, like this:
var newWindow = open("");
Retrieving a frame’s window object is even simpler, as the browser automatically creates a variable
using the name of the frame as the variable’s identifier. So to call the alert() method of the top
frame from the frameset page, you write code like this:
frmTop.alert("Hello, Top!");
The same principle is used for accessing the bottom frame. The following bolded code adds a
<script/> element to the frameset page:
<html>
<head>
<title>Frameset Page</title>
<script type="text/javascript">
function sayHello(message) {
alert(message);
}
</script>
</head>
<frameset rows="50%,*">
<frame name="frmTop" src="frame_top.htm" />
<frame name="frmBottom" src="frame_bottom.htm" />
</frameset>
</html>
Inside the <script/> element is a simple function called sayHello(). It accepts one argument that
is then displayed in an alert box. There is nothing in this main page that calls this function; instead,
it is called from within the HTML pages loaded into each frame.
Frames and the window Object ❘ 123
Think back to Lesson 11, and remember how child windows access their parent with the opener
keyword. Accessing the main frameset window object from a page loaded into a frame is just as simple;
the difference is that instead of using opener, you use the parent keyword.
The following is the source code for frame_top.htm. Focus on the bold line:
<html>
<head>
<title>Top Frame</title>
<script type="text/javascript">
parent.sayHello("Hello from Top!");
</script>
</head>
<body>
</body>
</html>
This single statement uses the parent keyword to access the parent’s window object and calls
sayHello(). When the browser loads this page into the top frame, it executes this line of code,
which results in an alert box displaying the text “Hello from Top!”
The source code for frame_bottom.htm is similar:
<html>
<head>
<title>Bottom Frame</title>
<script type="text/javascript">
parent.sayHello("Hello from Bottom!");
</script>
</head>
<body>
</body>
</html>
The code in this page goes through the same process. The only difference is the message that the
alert box displays when the browser loads this page and executes the JavaScript code.
As you can see from these examples, scripting between the parent window and child frames is very
much like scripting between parent and child windows. The only difference is in how you refer to
the child and parent window objects. But what if you want to script between two individual frames?
Using JavaScript to script between frames is slightly different from, but almost as straightforward
as, using it to script between parent and child. Refer back to Figure 12-1 and take note of how, in the
illustration, each window object is linked. Notice that there is no direct link between the two child
frames; instead, the only thing linking them is the parent window. This is indicative of how you access
the code and objects of one child frame from its sibling frame.
Let’s say you want to access the top frame and call its alert() method from the bottom frame. To
do this you must first reference the parent window with the parent keyword, and then use frmTop,
the name of the top frame. The following code shows you what this might look like:
parent.frmTop.alert("Called from frame_bottom.htm");
124 ❘ Lesson 12 Scripting Frames
Here the parent keyword is used to refer to the window object of the main page. Then the top frame’s
window object is accessed via the frame’s name, frmTop, and the alert() method is called. This has
the result of showing an alert box, but it isn’t evident that the alert box belongs to the frmTop frame.
Modify the JavaScript in frame_bottom.htm to write some text in the top frame. Following is a new
version of frame_bottom.htm:
<html>
<head>
<title>Bottom Frame</title>
<script type="text/javascript">
parent.frmTop.document.write("Hello from frame_bottom.htm");
</script>
</head>
<body>
</body>
</html>
Here, the bold line of code is the only thing that changes from the previous line of code. It uses the
parent object to access the frmTop object. From there this code uses frmTop’s document object to
write some text to the page. The result of this code can be seen in Figure 12-2.
Figure 12-2
Despite the extra step of using the parent object, scripting between two frames is still a straightfor-
ward process. You simply navigate through the tree to get to the object you want.
Try It ❘ 125
Here the first parent refers to frmA’s parent, which is frmTop. The next parent references frmTop’s
parent, the main frameset page. From this object you can directly access frmBottom. The more
nested framesets you add, the more tedious it becomes to script between frames. The top object
helps you avoid having to climb the tree to the topmost page. Instead of typing parent.parent
.frmBottom, you simply type this:
top.frmBottom
It’s a shortcut to get to the top of the window object tree if you need it. You’ll see this object used in
the next section.
Try It
In this lesson, you learn how to script frames. You learn how the browser automatically creates a
variable to allow access to a frame. You also learn about the parent and top objects that allow you
to navigate the window object hierarchy created by framesets.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
126 ❘ Lesson 12 Scripting Frames
Create a subfolder called Lesson12 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson12 folder.
Step-by-Step
Take another look at Figure 12-3. Use it as a blueprint and create five web pages to use in framesets.
The JavaScript in frmA will write text into frmBottom, and the JavaScript in frmB will call a func-
tion in frmTop that will call a function in the main window to write text in frmB.
1. Open your text editor and type the following HTML:
<html>
<head>
<title>Frameset Page</title>
<script type="text/javascript">
function writeInB(message) {
frmTop.frmB.document.write(message);
}
</script>
</head>
<frameset rows="50%,*">
<frame name="frmTop" src="frame_top.htm" />
<frame name="frmBottom" src="frame_bottom.htm" />
</frameset>
</html>
Save it as frameset_main.htm. This is the main frameset page that is loaded into the
browser, and it divides the screen into top and bottom frames. The JavaScript in this page
defines a function called writeInB(). It’ll access the frmB frame (a child in frmTop) and
write a message in it using the document.write() method.
2. Open another instance of your text editor and type the following HTML:
<html>
<head>
<title>Top Frame</title>
<script type="text/javascript">
function callParentFunction() {
parent.writeInB("Calling from frmTop");
}
</script>
</head>
<frameset cols="50%,*">
<frame name="frmA" src="frame_a.htm" />
<frame name="frmB" src="frame_b.htm" />
</frameset>
</html>
Save it as frame_top.htm. This is another frameset page that loads in the top frame of
frameset_main.htm. It divides the available screen into two columns. The JavaScript in this
page defines a function called callParentFunction(). It calls the writeInB() function
defined in frameset_main.htm.
Try It ❘ 127
3. Open another instance of your text editor and type the following HTML:
<html>
<head>
<title>Bottom Frame</title>
</head>
<body>
<h1>Bottom Frame</h1>
</body>
</html>
Save this file as frame_bottom.htm. This page loads in the bottom frame in frameset_
main.htm. This page does not contain any JavaScript.
4. Now you need to write the HTML for the pages loaded into the frame_top.htm frameset.
So once again, open another instance of your text editor, and type the following HTML:
<html>
<head>
<title>Frame A</title>
<script type="text/javascript">
top.frmBottom.document.write("Hello from frmA!");
</script>
</head>
<body>
<h1>Frame A</h1>
</body>
</html>
Save this as frame_a.htm. The JavaScript in this page does not define a function, but it does
use the top object to refer to the frameset_main.htm’s window object. Then it accesses the
frmBottom object and writes a message to that frame.
5. The final HTML file you need to write is frame_b.htm. Once again, open a new instance of
your text editor and type the following HTML:
<html>
<head>
<title>Frame B</title>
<script type="text/javascript">
parent.callParentFunction();
</script>
</head>
<body>
<h1>Frame B</h1>
</body>
</html>
Save it as frame_b.htm. The JavaScript code in this file uses the parent object to access the
window object of frame_top.htm, and it calls the callParentFunction() function defined
in that page.
128 ❘ Lesson 12 Scripting Frames
6. Open the frameset_main.htm file in your browser. You should see something similar to
Figure 12-4.
Figure 12-4
To get the sample code files, you can download Lesson 12 from the book’s website at www.wrox.com.
Please select Lesson 12 on the DVD to view the video that accompanies this
lesson.
13
The Document Object Model
In conventional applications, the operating system provides user interface elements and behav-
iors that enable developers to provide a rich experience for the user. Developing such applications
is convenient, as the entire application is typically written in one language. The developer needs
to know only one language to write code to interface with a data store (such as a database), code
to process that data, code to display that data to the user, and code to interact with the user.
Web developers don’t have it that easy. The data store interaction and processing are written
in one language (like PHP, Python, or C#), and the user interface is typically a combination of
HTML, CSS, and JavaScript. HTML is used for basic output/input for the user, which is a rather
static and boring experience. CSS can spice up the application a little, but it isn’t designed to do
much more than determine how the browser renders the elements. So, to create a rich interactive
experience, developers must turn to JavaScript.
But JavaScript is only part of the equation. There has to be an interface that enables a JavaScript
developer to interact with a web page while it’s loaded in the browser; such an interface exists,
and it’s called the Document Object Model (DOM).
To achieve this level of programmability, every HTML element in the document is represented by an
object, and these objects are organized in a tree-like hierarchal structure. This structure is very simi-
lar in concept to the file system on a computer. Figure 13-1 shows a screenshot of Windows Explorer
in Windows 7.
Figure 13-1
At the very top of the tree is C:\, or the root directory. Inside the root are a list of child directories;
these are branches that can contain either more branches (directories) or leaves (files). To find a par-
ticular file, you start at the root and navigate through the appropriate directories until you find it.
The DOM is very similar because HTML documents are structured in a very treelike way. Look at
the following HTML:
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<p>This is some text.</p>
<p>This is more text.</p>
<p>Here is text with a <span>span element</span>.</p>
</body>
</html>
If you were to diagram this HTML document as a tree, it would look like Figure 13-2.
What Is the DOM? ❘ 131
text
Figure 13-2
You start at the top of the tree with what is called the root element, the document. In the DOM, each
point in the tree is called a node. There are several types of nodes, and the more notable ones are:
➤➤ Document node type represents the entire document.
➤➤ Element node type represents an element.
➤➤ Attr node type represents an attribute.
➤➤ Text node type represents text within an element.
The Document node is sometimes referred to as the root node. The root node contains other nodes,
such as the doctype declaration (if specified) and the document element, the <html/> element,
which is also sometimes referred to as the root element.
The document element contains HTML elements that are represented as child nodes. In the case of
this example, the document element node has two child nodes: <head/> and <body/>, and to these
nodes the <html/> element node is a parent node. These two child nodes are also related to each
other as sibling nodes; all child nodes that share the same parent node are siblings.
The <head/> and <body/> nodes also contain their own children. The <head/> element node con-
tains a <title/> node; this contains a text node containing the text Sample Page. This text node is
the end of that particular branch.
The <body/> node contains three child nodes — three <p/> elements. The first two <p/> nodes only
contain text, but the third has three children:
➤➤ The text node of Here is text with a
➤➤ The <span/> element node
➤➤ The text node of .
132 ❘ Lesson 13 The Document Object Model
As you add elements and text to the web page, a corresponding node is added to the DOM repre-
senting that page.
HTML attributes are nodes, too, but they are represented differently in the tree.
For the sake of simplicity, attributes have been omitted from this particular
discussion.
To get any use out of the DOM, it’s important to know how to get around in the DOM tree.
Member Description
childNodes A NodeList (special array) that contains all children of the specified node.
The list is empty if the node has no children.
firstChild The first child node of the specified node. Returns null if no such node exists.
lastChild The last child node of the specified node. Returns null if no such node exists.
nextSibling The node immediately following the specified node. Returns null if no
such node exists.
previousSibling The node immediately preceding this node. Returns null if no such node
exists.
ownerDocument The document that the specified node belongs to.
parentNode The parent node of the specified node. Returns null if no such node exists.
nodeName The name of the node. If the node is an element, this returns the tag name.
If the node is text, it returns "#text".
nodeValue The value of the node. If it is a text node, the actual text is returned; if an
element, null.
hasChildNodes() Returns a Boolean value specifying whether the node has any children.
Selecting Element Nodes ❘ 133
Using some of these members, you can get from the top of the tree (the document) to the <span/>
element in the example HTML from the previous section, like this:
document.firstChild.firstChild.nextSibling.lastChild.childNodes[1];
Let’s break this down object by object. This line starts off at the top of the tree with document, and
its first child is the <html/> element. So document.firstChild returns <html/>. The firstChild
property is used on the <html/> node, getting the <head/> element because it’s the first child node
of <html/>. Then nextSibling is used, returning the <body/> element node. The <body/> node’s
lastChild property returns the third <p/> element, and since this <p/> element has three children,
you get the <span/> element by specifying the second child in the childNodes collection.
This certainly seems like a lot of work, doesn’t it? Maintenance would be a nightmare because even
slight changes to the HTML markup would require you to rewrite this code. Thankfully, the document
object provides you with some methods to make selecting particular element nodes much easier.
alert(paragraphs.length); // 3
</script>
<p>Another paragraph.</p>
<script type="text/javascript">
alert(paragraphs.length); // 4
</script>
</body>
</html>
134 ❘ Lesson 13 The Document Object Model
The bold portion of this code adds a script element to call getElementsByTagName(), selecting all
<p/> elements currently in the document and assigning the resulting NodeList to the paragraphs
variable. Keep in mind that at this point in the document, only three <p/> elements are found in the
document, so the alert() call in the first <script/> element alerts the value of 3.
After the first <script/> element is another <p/> element, bringing the total number to four. Then
another <script/> element adds another line of JavaScript, alerting the length of paragraphs
again; it alerts 4.
So by using the getElementsByTagName() method, you can get to the <span/> element much more
easily by selecting all <span/> elements and getting the first element in the NodeList, as shown in
the following code:
var theSpan = document.getElementsByTagName("span")[0];
That’s much easier, isn’t it? But you can make it easier still by making a slight modification to
the HTML markup and using the getElementById() method. This method’s purpose is simple:
to select only one element that has an id attribute with a specific value. This functionality and
simplicity make the getElementById() method the most frequently used method in JavaScript
development.
The following code modifies the original HTML from the beginning of this lesson by adding an id
attribute to the <span/> element.
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<p>This is some text.</p>
<p>This is more text.</p>
<p>Here is text with a <span id="foo">span element</span>.</p>
</body>
</html>
After this simple change, the <span/> element can be instantly found in the document with the
following code:
var theSpan = document.getElementById("foo");
The getElementById() method is, by far, the easiest way to select single elements in a document.
It’s also much more maintainable than other methods, as long as the id attribute doesn’t change.
If an element with a specified id value cannot be found in the document, getElementById() returns
null. Because of this, where you use the getElementById() method is crucial: It can be reliably used
only after the browser has loaded the desired HTML element into the DOM. Remember in Lesson 1
when I advised you to place <script/> elements at the bottom of the body? This is another reason —
so that all elements can be loaded into the DOM before any script attempts to access them.
Try It ❘ 135
Try It
In this lesson, you learned about the DOM and how to navigate and find nodes in the tree.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson13 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson13 folder.
Step-by-Step
You will write a web page using the HTML provided in this lesson that selects a particular element
and changes the text it contains using the nodeValue property.
1. Open your text editor and type the following code:
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<p>This is some text.</p>
<p>This is more text.</p>
<p>Here is text with a <span id="foo">span element</span>.</p>
</body>
</html>
This is the same HTML provided earlier in the lesson. Save this file as
lesson13_example01.htm.
<body>
<p>This is some text.</p>
<p>This is more text.</p>
<p>Here is text with a <span id="foo">span element</span>.</p>
<script type="text/javascript">
var theSpan = document.getElementById("foo");
This new code retrieves the <span/> element with an id of foo. It then uses the
childNodes NodeList to select the text node contained within the element (firstChild
could be used in place of childNodes[0]), and assigns a new value to the text node.
3. Resave the document and load it into your browser.
To get the sample code files, you can download Lesson 13 from the book’s website at www.wrox.com.
Please select Lesson 13 on the DVD to view the video that accompanies this
lesson.
14
Adding HTML with JavaScript
The Document Object Model (DOM) is about much more than finding particular elements in
the page. Remember that JavaScript’s purpose in web development is to provide a higher level of
interactivity with the user, and there will be times when you want to add elements dynamically to
the web page according to the user’s input.
The DOM allows you to create new HTML elements and add them to the page on-the-fly. You
can do this in one of two ways: with creation methods provided by the DOM standard, and
with the innerHTML property. The results of these two methods look identical on the surface,
but which one you should choose depends on the final result you want to achieve.
</script>
</body>
</html>
It is a simple web page with a sole <script/> element in the body. The script covered in this
lesson will go inside the <script/> element.
138 ❘ Lesson 14 Adding HTML with JavaScript
Creating Elements
The Document data type specifies a method called createElement(). It accepts one argument, a
string containing the tag name of the element you want to create. It creates an element outside of the
currently loaded document and returns an element object. You use it like this:
var el = document.createElement("div");
Here you create a <div/> element by calling the createElement() method and passing it the tag
name of div. You do not include any angle brackets — simply the tag name of the element. The
resulting element object is saved in the el variable.
This new object is an instance of the HTMLElement data type (another data type included with the
DOM), and, more specifically, of an HTMLDivElement. You can see this by calling alert(el) in any
browser except Internet Explorer 8 and previous versions. Figure 14-1 shows the result of alert(el)
in Internet Explorer 9.
The fact that this element is an HTMLDivElement isn’t necessarily
important for the sake of this discussion. But it is important to under-
stand that every HTML element in the HTML specification has a
corresponding data type in the DOM specification. This means there
are many new data types included with the DOM. For the sake of
simplicity, however, you will be introduced to them only when they’re
Figure 14-1
relevant.
In the DOM, all HTML element objects are instances of the HTMLElement and Element data types,
and because of this all element objects have a certain set of properties and methods you can use to
add attributes to the element. The following table lists some of these members:
So, by using a few of these members, you can add more content to an element. The bold lines in the
following code do just that:
var el = document.createElement("div");
el.id = "myDiv";
el.setAttribute("align", "center");
DOM Creation Methods ❘ 139
In this code, the new <div/> element is given an id of myDiv, and an align property is added to the
element with the setAttribute() method. If you were to write the HTML that this code generates,
it would look like this:
<div id="myDiv" align="center"></div>
Keep in mind, however, that this element is not yet loaded into the document. You have created the
element by using the createElement() method of the document, but it currently exists outside the
document. To load it into the document, you need to use the appendChild() method or other inser-
tion methods.
el.id = "myDiv";
el.setAttribute("align", "center");
document.body.appendChild(el);
The insertBefore() method accepts two arguments. The first is the new node to insert, and the
second is a reference node to place the new node in front of. The bold lines in the following code
create a new <div/> element and insert it before el from the previous example:
var el = document.createElement("div");
el.id = "myDiv";
140 ❘ Lesson 14 Adding HTML with JavaScript
el.setAttribute("align", "center");
document.body.appendChild(el);
el2.id = "myDiv2";
el2.setAttribute("align", "center");
document.body.insertBefore(el2, el);
Here a text node containing the text Hello, DOM methods! is created and assigned to the text vari-
able. Text nodes inherit from the Node data type as well, so they can be passed to the appendChild()
method like this:
var el = document.createElement("div");
var text = document.createTextNode("Hello, DOM methods!");
el.id = "myDiv";
el.setAttribute("align", "center");
el.appendChild(text);
document.body.appendChild(el);
In this code, the bold lines create and append a text node to the el element from the previous
examples.
One thing you might notice in this code is that the text node is appended to the <div/> element
before the element is added to the body. Technically, it doesn’t matter in what order you append
nodes to elements and the document. But remember this: The DOM is a liability as far as perfor-
mance is concerned. Every time you make a change to the document, the browser has to redraw the
page. So, the goal is to limit the amount of times you change the document.
DOM Creation Methods ❘ 141
In the example provided here, the text node is added to the <div/> element while both nodes are
outside the document itself. This is ideal, as adding the <div/> element to the document (complete
with a child text node) requires only one write operation to the document.
Also, as the name of the method implies, createTextNode() creates text, not HTML. The
createTextNode() method converts less-than and greater-than signs (< and >) to their HTML
entity equivalents (< and >). To demonstrate this, let’s return to our ongoing example and
add a text node containing the < and > symbols with the following code:
var el = document.createElement("div");
var text = document.createTextNode("Hello, DOM methods!");
el.id = "myDiv";
el.setAttribute("align", "center");
el.appendChild(text);
document.body.appendChild(el);
el2.id = "myDiv2";
el2.setAttribute("align", "center");
el2.appendChild(text2);
document.body.insertBefore(el2, el);
The new lines of code are in bold. The first new line creates a text node containing HTML markup,
which will be converted to HTML entities. The second new line adds that node to the <div/> element
in el2. You can see the results of this code in Figure 14-4.
Figure 14-4
142 ❘ Lesson 14 Adding HTML with JavaScript
Using DOM methods to create elements is verbose and time-consuming. The payoff, however, is that
you have references to the objects you create — you don’t have to search the DOM for them. Making
modifications to the <div/> element contained within the el variable is as easy as using that variable.
So, DOM creation methods are ideal if you plan on modifying those same objects later on, but there
is a faster and easier way to add HTML to a page if all you want to do is display it and forget it.
Here the HTML contained within the el element is changed to some bolded text. In the case of
this code, any preexisting HTML is removed from el and replaced with the HTML assigned to the
innerHTML property. It is possible to keep any preexisting HTML by doing something like what you
see in the following code:
el.innerHTML = el.innerHTML + "<b>This is my new HTML!</b>";
Here the existing HTML is preserved and some new HTML is appended.
The innerHTML property is really that simple, but it is surrounded by controversy. Type “innerHTML”
into Google, and you’ll find many articles for and against its use. Despite the ongoing debate, it is an
incredibly useful tool. When it is used to update only the contents of an element, its speed and simplic-
ity cannot be beat.
Try It
In this lesson, you learn how to add content to the page using JavaScript, the DOM creation methods,
and the innerHTML property.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or they can download a trial for
Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
Try It ❘ 143
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson14 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson14 folder.
Step-by-Step
You will write a web page and its content, a shopping list, using the DOM creation methods and
innerHTML.
</script>
</body>
</html>
}
</script>
</body>
</html>
144 ❘ Lesson 14 Adding HTML with JavaScript
This adds an array called shoppingList. Each element contains an item to purchase at the
store. After the array definition is a for loop to iterate over each item in the shopping list.
3. Add the following bold code:
<html>
<head>
<title>Lesson 14 Example 1</title>
</head>
<body>
<script type="text/javascript">
var shoppingList = [
"Eggs",
"Milk",
"Juice",
"Diapers",
"Bread"
];
var ul = document.createElement("ul");
document.body.appendChild(ul);
</script>
</body>
</html>
This new code creates an unordered list element (<ul/>) outside of the for loop. Its list item
elements (<li/>) are generated with innerHTML inside the loop, so that the existing HTML
is preserved with each iteration of the loop. When the loop exists, the <ul/> element is
appended to the <body/> element. The browser renders the HTML as seen in Figure 14-5.
Figure 14-5
Try It ❘ 145
To get the sample code files, you can download Lesson 14 from the book’s website at www.wrox.com.
Please select Lesson 14 on the DVD to view the video that accompanies this
lesson.
15
Modifying Element Style
Human beings are visual creatures. We can identify visual cues, no matter how subtle, and
decipher their meaning. For example, the Windows operating system differentiates between
active and inactive windows with visual cues—changing the color, the opacity, and other visual
elements of the window. In Windows 7, inactive windows display themselves with more of a
faded, transparent look than the color-rich, drop-shadow-encased active window. It’s a subtle
but effective means of telling users which window is currently active.
The ability to change how a particular user interface element looks is an extremely important
feature of an application programming interface (API), and the DOM, combined with the
styling power of CSS, gives you this ability.
In this lesson, you learn how you can leverage the power of the DOM and CSS to change the
appearance elements in a web page. You can modify an element’s style in two ways:
➤➤ Change individual CSS properties using the style property.
➤➤ Change the value of the element’s class attribute.
Here, an element with an id of someElement is retrieved, and its color property is set to
red. Assuming this element is a <div/> element, this code essentially translates to the follow-
ing HTML:
<div id="someElement" style="color: red"></div>
148 ❘ Lesson 15 Modifying Element Style
The relationship between the HTML style attribute and the JavaScript style property is usually
overlooked by those new to JavaScript and the DOM. The style property is usually, and incor-
rectly, thought of as the doorway to all style information that affects an element — the belief being
that an element’s style, whether defined in an external style sheet or by means of the inline style
attribute, can be retrieved with the style object. For example, assume the following style sheet:
<style type="text/css">
#someElement { color: red; }
</style>
It is not uncommon to see code that attempts to retrieve this information using the style object,
like this:
var someEl = document.getElementById("someElement");
When this code is executed in a browser, an alert box displays an empty string (i.e., nothing) — it
looks Figure 15-1.
Figure 15-1
This happens because the style object has no link to any style rules that affect the HTML element —
only the CSS properties defined in the style attribute. The previous code looks at the style attribute,
searches for the color property, finds none, and returns an empty string. But you can get this informa-
tion once it is set by using either the style property or the style attribute, like this:
<div id="someElement" style="color: red;">Hello</div>
…
Using the style Property ❘ 149
Always remember that the style object links, and only links, to the HTML element’s style attribute.
Just remember that while the HTML attribute contains a string of CSS, the DOM style property is
an object.
By comparing the actual CSS property — color in this case — with the JavaScript equivalent, you
can see they are the same. Many CSS property names, accessed in JavaScript via the style property,
are exactly the same as the CSS properties used in a style sheet. There are some, however, that are
different and have to be changed in order to work in JavaScript.
For example, the CSS property to change an element’s background color is background-color. This
particular CSS property cannot be used in JavaScript as is because the hyphen (-) cannot be used in
an identifier. So in order that an element’s background color can be changed with JavaScript and the
style object, the hyphen in background-color is dropped, and the first letter of color is capital-
ized, like this:
document.getElementById("someElement").style.backgroundColor = "blue";
Many CSS properties use hyphens, and converting them to their JavaScript equivalents follows the
same pattern: Drop the hyphen and camel-case the letter after the hyphen. The JavaScript equivalent
of border-left-color, for example, is borderLeftColor.
There are a few exceptions to the camel-casing rule. Consider the CSS float property, for example.
The word “float” is a reserved word in JavaScript, and as such, the code element.style.float will
not change the float of the specified element. Instead, you must use the cssFloat and styleFloat
(for IE8 and below) properties.
By using the style object, and the properties it allows you to modify, you can essentially replicate a
style rule for a single HTML element, like this:
var someEl = document.getElementById("someElement");
someEl.style.color = "red";
someEl.style.backgroundColor = "gray";
someEl.style.border = "1px solid black";
Here, an element with an id of someElement is retrieved from document, its text color is changed to
red, it is given a background color of gray, and a one-pixel black border is placed around it. Making
many changes to an element’s style in this manner, however, brings up a couple of issues you should
be mindful of:
➤➤ Each change to an element’s style is an individual call to DOM properties — which, as you
learned in the previous lesson, you want to limit as much as possible for performance reasons.
➤➤ The philosophy behind CSS is to separate style from markup and behavior. This separation
makes maintenance and modifications much easier because all the style information is grouped
into style sheets — eliminating the need to search through HTML and JavaScript for style
information.
150 ❘ Lesson 15 Modifying Element Style
For these reasons, editing an element’s style one property at a time is considered the incorrect approach.
There are situations in which you may want to, but for the most part the better solution is to manip-
ulate an element’s CSS class using the className property.
Using the className property addresses the two previous issues rather nicely:
➤➤ Modifying an element’s CSS class, which can contain multiple CSS properties, changes an
element’s style with only one call to the DOM.
➤➤ CSS data is kept separate from JavaScript; the only link between the CSS style sheet and the
JavaScript code is the CSS class name. This makes maintenance much easier.
Another advantage to using the className property is that it requires less JavaScript code. The last
example in the previous section used three lines of code to change an element’s color, background
color, and border. But place that style information in a CSS class, like this:
.a-CSS-class {
color: red;
background-color: gray;
border: 1px solid black;
}
You can also assign multiple CSS classes to the className property, just as you would with the
class attribute. The following code demonstrates this:
someEl.className = "a-CSS-class b-CSS-class";
Try It
In this lesson you learn how to manipulate an element’s style by using the style and className
properties.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Try It ❘ 151
Create a subfolder called Lesson15 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson15 folder.
Step-by-Step
You will write a web page and dynamically change an element’s style.
1. Open your text editor and type the following code:
<html>
<head>
<title>Lesson 15: Example 1</title>
<style type="text/css">
.message-style1 {
color: navy;
font-weight: bold;
background-color: gray;
}
</style>
</head>
<body>
<div id="divMessage">
Here is a message.
</div>
<input type="button" value="Toggle Style" />
<script type="text/javascript">
</script>
</body>
</html>
font-weight: bold;
background-color: gray;
}
</style>
</head>
<body>
<div id="divMessage">
Here is a message.
</div>
<input type="button" value="Toggle Style" />
<script type="text/javascript">
function toggleStyle() {
var divMessage = document.getElementById("divMessage");
The new code adds a function called toggleStyle(). It retrieves the <div/> element with
an id of divMessage, and it sets the element’s className property according to its current
value. If the element has a className of message-style1, then className is set to an empty
string — effectively removing any styles applied to the element. If the element’s className
does not equal message-style1, then className is assigned that value.
3. Now modify the following bold line:
<html>
<head>
<title>Lesson 15: Example 1</title>
<style type="text/css">
.message-style1 {
color: navy;
font-weight: bold;
background-color: gray;
}
</style>
</head>
<body>
<div id="divMessage">
Here is a message.
</div>
<input type="button" value="Toggle Style" onclick="toggleStyle()" />
<script type="text/javascript">
function toggleStyle() {
var divMessage = document.getElementById("divMessage");
} else {
divMessage.className = "message-style1";
}
}
</script>
</body>
</html>
An event handler is added to the <input/> element to execute the toggleStyle() function
when the button is clicked. Don’t worry about events right now; you’ll begin learning about
them in the next lesson. For now, simply know that toggleStyle() executes when you click
the button.
4. Save this HTML file as lesson15_example01.htm. Open it in your browser, and click the
button several times. You will see the <div/> element’s style change with every click.
To get the sample code files you can download Lesson 15 from the book’s website at www.wrox.com.
Please select Lesson 15 on the DVD to view the video that accompanies this
lesson.
16
Introduction to Events
A web application is usually divided into two distinct parts. The first is usually referred to as the
back end or server side. It is powered by PHP, ASP.NET, Ruby, or any other server-side technol-
ogy to validate, process, and format data. While there’s always some level of complexity involved
with an application, the server-side portion of a web application is typically more straight-
forward than the client portion — the second part — because it has to do something only
when data is sent or requested by the user.
The client portion, or front end, is the interface used by the user to input and view informa-
tion, as well as to interact with the application as a whole. The client portion is made up of
HTML, CSS, and JavaScript. HTML and CSS are sufficient for providing a user interface for
a web application, but JavaScript is what can make an interface dynamic and responsive.
The user interface of a web application is usually in a reactive state — it waits for the user to
perform a particular action before it knows to do something. Look at Google’s search page as
an example. When you first visit the page, you see something similar to Figure 16-1.
You see nothing but a search form, but the moment you move your mouse pointer or type in
the search field, the page reacts to your actions. Moving your mouse causes the tool bar at the
top to fade into view, and typing text into the search field causes Google’s autocomplete feature
to activate, as shown in Figure 16-2.
These subtle features on Google’s search page are a prime example of how JavaScript can enrich
the user’s experience. But how does JavaScript know when to execute certain code based on a
user’s actions?
The answer is events. An event is something that happens, usually outside the developer’s control,
and that is handled by code. For example, pressing a key while the cursor is in the search field
on Google’s search page causes an event called keypress to fire, or take place. The developers of
Google’s search page lack control over when you press a key, but they can write what is called
an event handler to handle the keypress event. This event handler executes JavaScript code
every time you press a key while typing in the search field. By reacting to your users’ actions,
you can enrich their experiences while they use your app — just as the developers of Google’s
search page enhance your experience.
156 ❘ Lesson 16 Introduction to Events
Figure 16-1
Figure 16-2
Mouse Events ❘ 157
Unfortunately, events are one area in client-side development in which there are very noticeable dif-
ferences in the various browsers. On one hand you have the browsers that support and implement
the standard DOM event model; these are Internet Explorer 9, Firefox, Chrome, Safari, and Opera.
On the other hand are Internet Explorer 8 and below, which use their own proprietary event model.
Thankfully, there are similarities between the two event models; their differences, and how to cope
with them, will be discussed in later lessons.
Now, though, you’ll be introduced to the many different kinds of events you can “listen” for in
order to execute code when they happen. You’ll apply this information in future lessons.
The events covered in this introduction are part of the W3C DOM event standard,
and most pertain to events attached to HTML elements.
Mouse Events
The mouse is arguably the most important input device when it comes to the Web. Unless inputting
information into a form, the average user doesn’t use the keyboard all that much, and certainly doesn’t
use it to interact with a web page. No, pointing devices reign on the Web, and so the events associated
with the mouse are the most commonly handled.
The mouse can generate a variety of events, but they all fall into two categories: movement-based
and button-based.
Movement-Based Events
On a web page, a user generates an event every time he or she moves the mouse. In some cases,
moving the mouse pointer one pixel in either direction can generate three events (or more, depending
on the browser ). These events are:
➤➤ mousemove: This event occurs when the mouse pointer is moved while it’s over an element.
➤➤ mouseover: This event occurs when the mouse pointer moves over an element.
➤➤ mouseout: This event occurs when the mouse pointer moves away from an element.
Button-Based Events
Movement is just part of the equation when it comes to mouse-based events; it’s also very useful
to know when the user clicks the mouse button. Usually, clicking a button on the mouse generates
three events, and they occur in the following order:
➤➤ mousedown: This event occurs when a mouse button is pressed over an element.
➤➤ mouseup: This event occurs when a mouse button is released over an element.
➤➤ click: This event occurs when the mouse button is clicked over an element. A click event
happens when both a mousedown and a mouseup occur over the same location.
158 ❘ Lesson 16 Introduction to Events
Keyboard Events
The second-most-important input device, for web applications, is the keyboard. It is what users use
to supply data to web applications. There are three keyboard events, and they are similar in concept
to the three primary events of the mouse button. Pressing a key generates the following three events
in the provided order:
➤➤ keydown: This event occurs when a key is pressed down.
➤➤ keyup: This event occurs when a key is released.
➤➤ keypress: This event occurs when a key is pressed and released.
There is usually some confusion about keyboard events, as they may seem redundant. But there
are subtle differences in the ways they are used, and it’s important to know the difference between
a character and a key. A key is a physical button on the keyboard, and a character is a value that
results from a key’s being pressed.
The keypress event is traditionally used to detect the character being typed. In fact, the keypress
event fires only if an alphanumeric or symbol (such as a period or tilde) key was pressed and
released. For example, you can handle the keypress event on a textbox and filter out alphabetical
characters so that only numbers and symbols appear in the textbox.
The keypress event does not fire for keys such as Backspace, Shift, or Enter, but the keyup and
keydown events do. These two events fire when any key is pressed and released. You can actually
detect when the user presses the Backspace key and prevent the deletion of characters — not that
you should do that, of course.
Basic Events
Not all events occur from a user’s actions. Some occur when something happens from within the
HTML document. The user didn’t do anything, but something took place within the window or
document, and you may want to execute some code when one of these events occurs.
For example, there are some events that fire in relation to the window. They are:
➤➤ load: This element occurs when the window is finished loading the document and all other
resources, such as JavaScript, style sheets, and images.
➤➤ unload: This element occurs when the document is unloaded from the window. For example,
it fires on the current page when you navigate to another page.
Try It ❘ 159
➤➤ abort: This element occurs when the loading of the page is aborted, usually because the user
has pressed the cancel button.
➤➤ resize: This event occurs when the window is resized.
➤➤ error: This event occurs when a JavaScript error takes place.
The beautiful thing about events is that the object related to an event, like the window object, doesn’t
care if you, or anyone for that matter, write code to handle the event. All the object knows is that it
needs to execute an event handler when the associated event occurs, and if one exists, it executes it.
Try It
There isn’t anything to try for this lesson. Events are an integral part of client-side development, and
it’s important to understand the concepts involved. This lesson is primarily meant to be a primer on
events. In the next lesson, you learn one of the many ways you can handle events — by using HTML
attributes.
This element has an id of myDiv, and it contains a small bit of text. It’s an ordinary element now,
but you can add some specialness to it by handling the click event. To handle this event, add an
attribute called onclick to the opening tag, like this:
<div id="myDiv" onclick="">Hello, Events!</div>
The name of an attribute used to handle events is the word on followed by the name of the
event. In this example the event handler onclick handles the click event. If you wanted to
handle the mouseover event, the attribute’s name would be onmouseover.
162 ❘ Lesson 17 Assigning Event Handlers with HTML Attributes
Also keep in mind that even though <div/> elements don’t interact with mouse clicks by default, you
can add that functionality by handling the click event. In fact, you can add custom functionality to
just about any HTML element by handling events.
Let’s now add some code to execute when this element is clicked. HTML attributes allow you to
assign any JavaScript code as the attribute’s value. So changing this element’s text color is as simple
as using the this variable, followed by the style object, and then the color CSS property, like this:
<div id="myDiv" onclick="this.style.color = 'red'">Hello, Events!</div>
When you’re using HTML attributes to handle events, the this variable refers to the element that
originated the event. Here this refers to the <div/> element; when the <div/> element is clicked, its
text color changes to red.
Note the use of single quotes around red. Single quotes are used here because the onclick attribute
uses double quotes to contain its value. If you were to use double quotes around red, it would look
like this:
<div id="myDiv" onclick="this.style.color = "red"">Hello, Events!</div>
And this would result in the browser’s thinking the value of the onclick attribute is "this.style
.color = ", which obviously doesn’t result in the intended outcome. Always keep quotation marks
in mind, especially when dealing with HTML attribute event handlers.
Attributes also have the rather unusual trait of allowing more than one JavaScript statement in
the attributes’ value. To demonstrate this, the following code adds another statement that calls the
alert() method:
<div id="myDiv" onclick="this.style.color = 'red'; alert('You clicked me!')">
Hello, Events!
</div>
You can use as many statements as you want; simply use a semicolon at the end of each statement so
that the JavaScript engine knows when a statement ends and a new one begins. But if you want to
execute multiple statements in an event handler, it’s best to group those statements into a function.
Doing so is advantageous for the following reasons:
➤➤ Performing repeated tasks is much easier since all you have to call is a single function, as
opposed to repeating the same statements over and over. In other words, code reuse.
➤➤ Maintaining the code is easier. Organizing the code in function form makes mistakes easier
to spot and fix. Maintenance is much easier when you’re using the same code in multiple ele-
ments’ onclick event handlers; making changes to a function is much simpler than making
changes to multiple attributes.
With this in mind, let’s rewrite the previous example by using a function. Let’s also add another
HTML element that handles the click event and calls the same function, like this:
<script type="text/javascript">
function changeToRed(that) {
that.style.color = "red";
alert("You clicked me!");
Cancelling Default Behavior ❘ 163
}
</script>
Look at the JavaScript function first. It’s called changeToRed(), and it accepts an object called
that. This parameter name is traditionally used to contain the this object (this and that… get it?),
which is its use here. The first line uses the that variable to change the element’s text color to red,
and then an alert box displays a generic message to the user.
Now look at the HTML. The <div/> element was changed to call the changeToRed() function
and passes this as an argument. Another element, a <p/> element, was added, and its onclick event
handler was set to call changeToRed() as well, passing it a reference to that element by using the this
variable. Even though <p/> is a different element from <div/>, the results will be the same: The <p/>
element’s text color will change to red, and an alert box displays a message to the user.
So, by taking the original statements and placing them within a function, you’ve essentially made
the code usable by virtually any element in the page, and also made it easier to maintain.
In the case of this element, the browser goes through the following steps:
1. The browser finds the onclick attribute and executes changeToRed().
2. The <a/> element’s text color changes to red.
3. An alert box pops up and the browser stays on the page until the user clicks the OK button.
4. The browser navigates to another page once the OK button is clicked.
What if you want to disable the link’s default behavior of navigating to a different page? There are
some good reasons to do so; when you get into later lessons, such as Ajax, you’ll see why you may
want to disable a link’s default behavior to enhance the user’s experience on your web page.
But for now, just imagine you want the user to see your totally awesome ability to change the link’s
text color to red. After all, if it weren’t for the alert box, anyone clicking the previous link would
164 ❘ Lesson 17 Assigning Event Handlers with HTML Attributes
have no idea the text color had changed to red, because the browser navigates to a different page
before the user has a chance to see the change. To disable the default behavior of an <a/> element,
or any element for that matter, just ensure that the event handler returns false. This can be done in
two ways.
First, simply tack the statement return false after the call to changeToRed(), like this:
<a href="http://www.wrox.com"
onclick="changeToRed(this); return false">Click me!</a>
But in doing this you’ve made your code more difficult to manage and maintain — especially if
you plan on doing the same thing with other <a/> elements. The better solution is to move the
return false statement into changeToRed(), like this:
function changeToRed(that) {
that.style.color = "red";
alert("You clicked me!");
return false;
}
Then you can simply use the return keyword in the event handler to return the value returned by
changeToRed(), as shown in the following code:
<a href="http://www.wrox.com" onclick="return changeToRed(this)">Click me!</a>
Even though changeToRed() returns false, it returns only to the calling code — the onclick
event handler. The event handler must return false in order to disable the link’s default behavior.
So that’s why the onclick event handler is set to return changeToRed() — because the function
returns false. Now when a user clicks this link, the browser goes through the following process:
1. The browser finds the onclick event handler and executes the return statement, which
executes the changeToRed() function.
2. The <a/> element’s text color changes to red.
3. An alert box pops up.
4. After the user clicks the OK button, changeToRed() returns false to the onclick event
handler.
5. The event handler returns false (because changeToRed() returns false) to the browser.
6. The browser stays on the same page, cancelling the link’s default behavior of navigating to
the page specified in the href attribute.
Now that you know the theory behind HTML attribute event handlers, try applying them to an
actual application.
Try It
In this lesson, you learn how to use HTML attributes to handle events.
Try It ❘ 165
Lesson Requirements
For this lesson, you will need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson17 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson17 folder.
Step-by-Step
You will write a web-based calculator using HTML attribute event handlers.
1. Open your text editor and type the following HTML:
<html>
<head>
<title>Lesson 17: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
166 ❘ Lesson 17 Assigning Event Handlers with HTML Attributes
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">x</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
</script>
</body>
</html>
This is the markup used for this application, and a table makes up the bulk of the HTML.
The first row has a <td/> element with an id of results. This is the display of the calcula-
tor. The subsequent rows consist of four cells on each row, and each cell contains a link.
The href attribute of these links is set to #, which prohibits the browser from navigating
away from the page. However, this is a little unnecessary, as your JavaScript code will can-
cel the links’ default behavior.
2. Now add the JavaScript function that you see bold in the following code:
<html>
<head>
<title>Lesson 17: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
Try It ❘ 167
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">x</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
}
</script>
</body>
</html>
This new function, called addDigit(), accepts an argument called digit. It uses the value
by appending it to the innerHTML of the <td/> element with an id of results. The function
returns false.
3. Add the bold function in the following code:
<html>
<head>
<title>Lesson 17: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
168 ❘ Lesson 17 Assigning Event Handlers with HTML Attributes
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">x</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
</script>
</body>
</html>
This function, called calculate(), retrieves the element with an id of results (the first
<td/> element), and uses the eval() method to execute the contents of the element’s
innerHTML. The eval() method is new to you: It accepts a string value and executes that
string as JavaScript. Although it is useful, eval() is typically thought of as “evil” because it
Try It ❘ 169
leads to sloppy code that can’t be maintained and it makes JavaScript execution slower. There
are, however, some legitimate uses for eval(), one of which is to execute mathematical com-
putations. Its use here is benign, and used in this example for simplicity’s sake.
4. Now add a function to clear the results. It is bold in the following code:
<html>
<head>
<title>Lesson 17: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">x</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
170 ❘ Lesson 17 Assigning Event Handlers with HTML Attributes
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
</script>
</body>
</html>
The reset() function resets the calculator’s value, setting the innerHTML of the results
field to an empty string.
5. Wire up the events for each <a/> element. Don’t forget to use the return keyword before
calling the appropriate function.
<html>
<head>
<title>Lesson 17: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#" onclick="return addDigit(1)">1</a></td>
<td><a href="#" onclick="return addDigit(2)">2</a></td>
<td><a href="#" onclick="return addDigit(3)">3</a></td>
<td><a href="#" onclick="return addDigit('+')">+</a></td>
Try It ❘ 171
</tr>
<tr>
<td><a href="#" onclick="return addDigit(4)">4</a></td>
<td><a href="#" onclick="return addDigit(5)">5</a></td>
<td><a href="#" onclick="return addDigit(6)">6</a></td>
<td><a href="#" onclick="return addDigit('-')">-</a></td>
</tr>
<tr>
<td><a href="#" onclick="return addDigit(7)">7</a></td>
<td><a href="#" onclick="return addDigit(8)">8</a></td>
<td><a href="#" onclick="return addDigit(9)">9</a></td>
<td><a href="#" onclick="return addDigit('*')">x</a></td>
</tr>
<tr>
<td><a href="#" onclick="return reset()">Clear</a></td>
<td><a href="#" onclick="return addDigit(0)">0</a></td>
<td><a href="#" onclick="return calculate()">=</a></td>
<td><a href="#" onclick="return addDigit('/')">/</a></td>
</tr>
</table>
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
</script>
</body>
</html>
Note that the links for clearing and totaling the calculator call the reset() and calculate() func-
tions, respectively. All other links call the addDigit() function, and pass it a value representing
their respective number or operation.
To work the calculator, click the appropriate links to perform arithmetic operations. Figure 17-1
shows an example of the calculator in action.
172 ❘ Lesson 17 Assigning Event Handlers with HTML Attributes
Figure 17-1
To get the sample code files, you can download Lesson 17 from the book’s website at www.wrox.com.
Please select Lesson 17 on the DVD to view the video that accompanies this
lesson.
18
Using Early DOM Event
Handlers
The HTML event handler attributes certainly work, and every browser continues to support
them despite the fact that they were invented in very early versions of Netscape. However, as
you may have surmised, handling events with HTML attributes can become a tedious endeavor
mainly because you have to locate and add attributes to every element you want the user to
interact with. Keep in mind that all those years ago, the majority of websites were primarily
static, and the developers responsible for generating those websites had complete control over
each page’s HTML. It was also a time before the idea of separating markup (HTML) from style
(CSS) and behavior (JavaScript) really gained traction.
The fourth generation of browsers, primarily Internet Explorer 4 and Netscape 4, introduced
a new way of assigning event handlers by using what are now referred to as DOM Level 0 event
handlers. Think of a “level” as a version; these event handlers are called DOM Level 0 event
handlers because they existed before any W3C DOM standard. Yet all browsers support them.
Even to this day, DOM Level 0 event handlers are widely used and are an integral part of
many web applications.
Early DOM event handlers are so popular because they are supported by all browsers (some-
thing you’ll appreciate in coming lessons), and you can use them in JavaScript code, effectively
decoupling JavaScript from HTML.
For example, you handle the window object’s load event by creating a function and assigning it to
the onload property, like this:
function handleOnload() {
alert("Loaded!");
}
onload = handleOnload;
Notice the lack of parentheses to handleOnload in the last line of this code. When assigning an
event handler, it is important that you assign either a function or null (null tells the browser not to
handle the event). Failing to assign one of these two values to an event handler property results in an
error.
In the case of this code, executing handleOnload() when assigning it to the onload event handler
does not actually assign the function — it assigns the result of the function’s being executed. The
result of a function is determined by a return statement within the function. Since handleOnload()
does not contain a return statement, the result of the function’s being executed is undefined. In the
following line, undefined is assigned to onload:
onload = handleOnload(); // error; undefined assigned to onload
The value of undefined is not a function, nor is it null, so this code results in an error. It is possible,
however, to write a function that returns another function. Let’s modify handleOnload() to do just
that with the following code:
function handleOnload() {
return function() {
alert("Loaded!");
}
}
Since handleOnload() now returns a function, and that function is the desired function to handle
the event, you can execute handleOnload() as you assign it to an event handler, as seen in the first
line of this code:
onload = handleOnload(); // now it's ok; it returns a function
onload = handleOnload; // does not cause an error, but does not alert a message
The second line of this code is technically correct, but it does not result in the desired outcome of
an alert window displaying the message “Loaded!” when the event fires. This is because the func-
tion handleOnload(), not the function handleOnload() returns, is assigned as the event handler.
Always be mindful of how you assign your event handlers; assign the function you want to have
execute when the event fires.
Despite the examples in this lesson, you will more than likely see event handlers assigned in slightly
a different way — by having an anonymous function assigned to them, like this:
onload = function() {
alert("Loaded!");
};
Try It ❘ 175
The advantage to this format is readability. The event handler and the function assigned to it are
grouped together in the same statement — making it much easier to read and understand what the
code does. In the grand scheme of things, it doesn’t really matter how you assign a function to an
event handler — as long as you do it correctly, of course.
Unlike the event handlers in Lesson 17, you cannot pass a value to DOM Level 0 event handlers.
So you can’t pass this as an argument as you did with the HTML attribute event handlers. You
can, however, use this inside the event handler to refer to the element or object where the event
occurred. For example:
var someElement = document.getElementById("divElement");
someElement.onclick = function() {
this.style.color = "red";
};
This code gets an element with an id of divElement, and it assigns a function to handle the click
event. When a user clicks the element, the color of the text it contains changes to red. Although you
lose the ability to pass data to DOM Level 0 event handlers, you can still access the object where the
event fired by using this inside the function.
The browser actually passes an event object to DOM Level 0 event handlers.
You learn about this object in Lessons 21 and 22.
Try It
In this lesson, you learn how to use early DOM event handlers.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
176 ❘ Lesson 18 Using Early DOM Event Handlers
Create a subfolder called Lesson18 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson18 folder.
Step-by-Step
You will rewrite the calculator application from Lesson 17. Instead of using HTML attribute event
handlers, you will use the DOM Level 0 event handlers to decouple the HTML from the JavaScript.
1. Open your text editor and type the following HTML:
<html>
<head>
<title>Lesson 18: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">*</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
Try It ❘ 177
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
</script>
</body>
</html>
Save it as lesson18_example01.htm. This is almost the exact same page as in Lesson 17;
the only differences are the content in the <title/> element, that none of the <a/> elements
use the onclick attribute, and that the text of the multiplication link was changed to an
asterisk (*) instead of x. The addDigit(), calculate(), and reset() functions remain
untouched.
2. Now handle the load event as shown by the bold code:
<html>
<head>
<title>Lesson 18: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
178 ❘ Lesson 18 Using Early DOM Event Handlers
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">*</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
onload = function() {
var links = document.getElementsByTagName("a");
var length = links.length;
};
Try It ❘ 179
</script>
</body>
</html>
This isn’t entirely necessary since the code runs after the HTML is loaded into the browser.
However, it does keep the global scope from being polluted with unnecessary variables —
the links and length variables in this case.
3. Now loop through the elements in the links node list:
<html>
<head>
<title>Lesson 18: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">*</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
180 ❘ Lesson 18 Using Early DOM Event Handlers
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
onload = function() {
var links = document.getElementsByTagName("a");
var length = links.length;
}
};
</script>
</body>
</html>
This is a normal for loop, looping through every <a/> element in the links node list. The
first statement creates two variables, link and innerHTML. The link variable contains the
<a/> object at the specified index, and the innerHTML variable contains the <a/> element’s
HTML.
4. Use a switch element to assign the appropriate functionality for each <a/> element object.
Do so based on the element’s innerHTML:
<html>
<head>
<title>Lesson 18: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
Try It ❘ 181
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">*</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
182 ❘ Lesson 18 Using Early DOM Event Handlers
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
onload = function() {
var links = document.getElementsByTagName("a");
var length = links.length;
switch (innerHTML) {
case "Clear":
link.onclick = reset;
break;
case "=":
link.onclick = calculate;
break;
}
}
};
</script>
</body>
</html>
If the link’s innerHTML is "Clear", you know it is the link used to clear the contents of
the result field; assign its onclick property the reset() function. Similarly, if the link’s
innerHTML is an equals sign (=), assign the calculate() function to the link’s onclick
event handler.
5. Wire up the events for the other links:
<html>
<head>
<title>Lesson 18: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
Try It ❘ 183
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">*</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
184 ❘ Lesson 18 Using Early DOM Event Handlers
function getHandlerFunction(innerHTML) {
return function() {
addDigit(innerHTML);
return false;
};
}
onload = function() {
var links = document.getElementsByTagName("a");
var length = links.length;
switch (innerHTML) {
case "Clear":
link.onclick = reset;
break;
case "=":
link.onclick = calculate;
break;
default:
link.onclick = getHandlerFunction(innerHTML);
}
}
};
</script>
</body>
</html>
Because there are only two links that have special capabilities (clear and calculate), the
rest of the links can fall under the default switch.
To handle the click event of these links, write a function called getHandlerFunction(),
as shown in bold in the preceding code. It should accept one argument containing the ele-
ment’s innerHTML, and it should return a function to handle the link’s click event.
6. Load the page in your browser. It will behave exactly as Lesson 17’s calculator does.
To get the sample code files, you can download Lesson 18 from the book’s website at www.wrox.com.
Please select Lesson 18 on the DVD to view the video that accompanies this
lesson.
19
Using Standard DOM and
IE Event Handlers
One problem with DOM Level 0 event handlers is that only one function can be assigned to an
event handler; assigning another function to the same event handler property overwrites any
previous function value assigned to that property. This isn’t such a big problem if you own all
the code within the web page, as you can write one function that calls as many functions as
needed, like the following code:
onload = function() {
doSomething();
doSomethingElse();
andDoSomethingElse();
};
But if you use code written by a third party, it might rely on the same event handler for initial-
ization, like this:
// code from third party module or library
onload = function() {
initializeLibrary();
};
If that is the case, the outcome will be one of the following: Either your function overwrites
the third party’s function or vice versa. Nice, eh? It is fortunate that there is a solution to this
particular problem, enabling you to add as many event handlers to a specific event as you
want. Unfortunately, the solution varies between browsers that support the standard DOM
event model (Firefox, Chrome, Safari, Opera, and Internet Explorer 9) and those that support
the Internet Explorer (IE) event model (versions 8 and below; version 9 supports both event
models).
186 ❘ Lesson 19 Using Standard DOM and IE Event Handlers
Before we get any further, let’s talk about event capture and bubbling.
Life as a client-side developer in the late 1990s was tough. OK, that’s a lie. It was one of Dante’s
nine circles of Hell. The two browsers vying for dominance were Netscape 4 and Microsoft Internet
Explorer 4. These browsers had different and incompatible DOMs, and thus different event models.
Netscape 4’s event model captured the event, whereas Internet Explorer used event bubbling.
When it came time for the W3C to work on a standard for an event model, it took both implementa-
tions and fused them together. So in the standard event model there are three phases to an event:
➤➤ Event capturing
➤➤ The event reaches the event target (the object in the DOM you specified a listener for).
➤➤ Event bubbling
When the event finally reaches the event target, the bubbling phase begins — bubbling is the exact
opposite of capture. The event starts at the target (the <span/> element in this case) and executes
the click event listeners for the target. The event then bubbles up to each parent in the DOM tree,
executing the click listeners at each parent until it reaches the window object (Figure 19-3).
Figure 19-2 Figure 19-3
In standards-compliant browsers you have to choose whether you want to use event capture or event
bubbling. Since the only version of Internet Explorer to support the standard event model is IE9, the
decision is practically made for you: bubbling. That is, of course, if you want your code to work the
same in all browsers. In other words, the third argument passed to addEventListener() is almost
always false, meaning bubbling (passing true indicates capture).
Let’s finally apply this with some code. The following is an example of addEventListener() in use:
var spanEl = document.getElementById("someElement");
function clickHandler1() {
alert("You clicked me!");
}
function clickHandler2() {
alert("You clicked me (I like repeating myself)!");
}
This code first retrieves an element from the document with getElementById(). Then, two functions are
defined; both will handle the click event for the <span/> element. It then calls addEventListener()
to add a listener for the click event on this element (notice the omission of on), and the listener
is set to use event bubbling. Then another event listener is registered for the click event using the
clickHandler2() function. When the user clicks this <span/> element an alert box will display
the text “You clicked me!” Then, after the user clicks the box’s OK button, another alert box will
display the text “You clicked me (I like repeating myself)!” When you assign multiple functions to
handle the same event on the same object, the functions are called in the order they are assigned.
188 ❘ Lesson 19 Using Standard DOM and IE Event Handlers
If you want to remove a listener registered with addEventListener(), use the removeEventListener()
method. It accepts the same three arguments passed to addEventListener(), and the values passed
to removeEventListener() must exactly match the values that were passed to addEventListener()
when you registered the listener. For example, the following code shows the incorrect and the correct
ways to remove the second listener registered in the previous code:
// incorrect
spanEl.removeEventListener("click", clickHandler2, true);
// correct
spanEl.removeEventListener("click", clickHandler2, false);
In the first statement, the third argument passed to removeEventListener() is true; the statement
attempts to remove a listener for the click event with the provided function, which is using event
capture. While this statement does not result in an error, it is incorrect because the listener was reg-
istered to use event bubbling.
Now look at the second statement. It too attempts to remove a listener for the click event with the
provided function, and the third argument is false. Since all three values match the values originally
passed to addEventListener(), that specific listener is removed and the associated function will not
execute when the event fires.
In the standard event model, the this variable used inside a function handling
an event refers to the object that fired the event.
IE Event Handlers
Around the time that the W3C was working on the DOM Level 2 event specification, Microsoft
released version 5 of IE. The browser offered new ways to register and unregister event handlers: the
attachEvent() and detachEvent() methods.
Using these two methods is similar to using the standard event model; you retrieve an object from
the document, call the attachEvent() method to register an event handler (or detachEvent() to
remove a handler) and pass it two arguments:
➤➤ The event, prepended with on, to handle
➤➤ The function to execute when the event fires
The proprietary IE event model doesn’t support event capture — only bubbling. So there is no third
parameter for these methods. The following code shows the IE equivalent of the registration code in
the previous section:
var spanEl = document.getElementById("someElement");
spanEl.attachEvent("onclick", clickHandler1);
spanEl.attachEvent("onclick", clickHandler2);
Try It ❘ 189
Here the attachEvent() method is called twice, registering two onclick event handlers. Notice
the use of on in onclick. Unlike with addEventListener() and removeEventListener(), you
must use the name of the event handler as opposed to the name of the event. The result of this code
is almost the same as earlier: Two alert boxes display two messages when the <span/> element is
clicked. Except in this case, the handlers are executed in reverse order. Unlike addEventListener(),
multiple functions assigned to handle the same event on the same object execute in the reverse order
of how they were assigned.
Removing the second event handler is as simple as calling detachEvent() and passing the same
values:
// remove the second listener
spanEl.detachEvent("onclick", clickHandler2);
In the proprietary IE event model, the this variable used inside a function han-
dling an event refers to the window object.
Try It
In this lesson, you learn how to assign event handlers using standard and IE methods.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson19 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson19 folder.
190 ❘ Lesson 19 Using Standard DOM and IE Event Handlers
Step-by-Step
You will rewrite the calculator application from Lesson 18 using standard and IE-specific event
handlers.
1. Open your text editor and type the following HTML:
<html>
<head>
<title>Lesson 19: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">*</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
function addDigit(digit) {
Try It ❘ 191
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
function getHandlerFunction(innerHTML) {
return function() {
addDigit(innerHTML);
return false;
};
}
onload = function() {
var links = document.getElementsByTagName("a");
var length = links.length;
switch (innerHTML) {
case "Clear":
func = reset;
break;
case "=":
func = calculate;
break;
default:
func = getHandlerFunction(innerHTML);
}
};
</script>
</body>
</html>
Save it as lesson19_example01.htm. There are a few changes in this version, and the impor-
tant ones are in bold. A new variable, called func, is declared and initialized as null. The
purpose of this variable is to contain the function used in the call to addEventListener(),
making the code a little easier to read while saving you from typing link.addEventListener()
several times.
The case statements within the switch block remain the same, except that func is assigned
the appropriate function in each case. Finally, a new line is added to the for loop, adding a
click event listener to the <a/> element.
2. Now copy and paste that code into a new instance of your text editor. Make the changes
specified in the following code (the changes are in bold):
<html>
<head>
<title>Lesson 19: Example 02</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
Try It ❘ 193
<td><a href="#">*</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
}
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
function getHandlerFunction(innerHTML) {
return function() {
addDigit(innerHTML);
return false;
};
}
onload = function() {
var links = document.getElementsByTagName("a");
var length = links.length;
switch (innerHTML) {
case "Clear":
func = reset;
194 ❘ Lesson 19 Using Standard DOM and IE Event Handlers
break;
case "=":
func = calculate;
break;
default:
func = getHandlerFunction(innerHTML);
}
link.attachEvent("onclick", func);
}
};
</script>
</body>
</html>
To get the sample code files, you can download Lesson 19 from the book’s website at www.wrox.com.
Please select Lesson 19 on the DVD to view the video that accompanies this
lesson.
20
Writing Cross-Browser
Event Handlers
In ye olden days, the common approach to cross-browser scripting was similar to the solution
at the end of Lesson 19: Create multiple pages for each browser type. The solution worked,
but the amount of time involved grew according to how many different browsers you wanted
to support. As years moved on, the accepted approach was simply to branch code (using an if
statement to determine what method to use) when needed, like this:
// assume el variable is an element object
if (typeof addEventListener === "function") {
el.addEventListener("click", function() {
alert("You clicked me!");
}, false);
} else if (typeof attachEvent !== "undefined") {
el.attachEvent("onclick", function() {
alert("You clicked me!");
});
} else {
el.onclick = function() {
alert("You clicked me!");
};
}
What you see here is a type of browser detection called feature detection — the detection of certain
features, like addEventListener(). The idea is to determine whether the browser supports a par-
ticular feature and, if so, to use it. If the feature isn’t supported, you fall back to a feature you know
will work. In the preceding example the code checks for the addEventListener() method (which
all standards-compliant browsers support) and uses it if it is found. If the browser does not sup-
port addEventListener(), then the code attempts to identify the browser as a version of IE
and, if so, uses attachEvent(). If the browser does not support either addEventListener() or
attachEvent(), then the code falls back to the DOM Level 0 onclick event handler.
196 ❘ Lesson 20 Writing Cross-Browser Event Handlers
The JavaScript engine in IE8 and below can do quirky things because of how dif-
ferent components in the browser are made. For example typeof attachEvent
actually returns “object” instead of “function.” That is why the preceding code
checks if attachEvent is not undefined. User-defined functions, however, are
type “function.”
Most of the time, however, you write code for two types of browsers: standard compliant browsers
and IE8 and below. So, the preceding code can be simplified to the following:
// assume el variable is an element object
if (typeof addEventListener === "function") {
el.addEventListener("click", function() {
alert("You clicked me!");
}, false);
} else {
el.attachEvent("onclick", function() {
alert("You clicked me!");
});
}
This code follows the same idea as before, except now attachEvent() is the fallback instead of the
DOM Level 0 onclick event handler.
How you check for a feature is important. Nine times out of ten you want to check for standards com-
pliance first because the standards will rarely change. For example, you can check for attachEvent()
first, such as this:
// WRONG!
if (typeof attachEvent !== "undefined") {
el.attachEvent("onclick", function() {
alert("You clicked me!");
});
} else {
el.addEventListener("click", function() {
alert("You clicked me!");
}, false);
}
This does work, but consider the case of IE 9. It supports not only its own proprietary event model,
but the standard event model as well. As a developer, you want to use the features put forth by the
standards as much as possible, and you can ensure that this happens by checking for standard features
before proprietary ones.
Now, in the old days, code branching such as this would be littered throughout a JavaScript file.
Naturally, the next evolution was to refactor that code into its own function, like this:
function setEventHandler(obj, evt, fn) {
if (typeof addEventListener === "function") {
obj.addEventListener(evt, fn, false);
} else {
obj.attachEvent("on" + evt, fn);
Writing an Event Utility Object ❘ 197
}
}
This code creates a function called setEventHandler(). It accepts three arguments: the object to
assign an event handler to, the name of the event to handle (without the on prefix), and the function
to handle the event. Inside the function, an if statement determines whether addEventListener()
or attachEvent() should be used to assign an event handler to the provided object. Notice the
call to attachEvent() inside the else block. Remember that the name of the event was passed to
the function without the on prefix, so the prefix must be added in the call to attachEvent().
The final statement of this code shows how the function is called by adding a click event handler
on an element object. This way you call the setEventHandler() function whenever you need to
wire up events for a particular object. But this approach, too, has its own drawbacks — one being
suboptimal performance. Every call to setEventHandler() causes a slight performance hit for the
following reasons:
➤➤ The JavaScript engine has to make a decision before it can execute addEventListener() or
attachEvent(). This decision takes longer than simply executing one of the event handler
methods.
➤➤ The engine has to look up the addEventListener identifier.
These problems bring us to modern JavaScript techniques, which you study in this lesson.
This code uses object literal notation to create a singleton object. You do not need to create a new
data type for this utility because it does not need any instance data; it’s simply a utility in which
all the necessary data is passed to its methods. Now add an if statement to branch your code, as
shown before. Check if addEventListener is a feature supported by the browser:
var eventUtility = {};
} else {
}
198 ❘ Lesson 20 Writing Cross-Browser Event Handlers
Now the eventUtility object needs a method to add event handlers to an object. Call this method
addEvent, and assign it a function that calls either addEventListener() or attachEvent(), such
as the following code in bold:
var eventUtility = {};
This new code creates an addEvent() method as a member of the eventUtility object. Actually,
there are two definitions of addEvent(), but only one is actually assigned to eventUtility.addEvent.
Notice the three parameters of addEvent() in both declarations; they are the same. This is very
important because you want to be able to call addEvent() and have it work correctly regardless
of what browser the user is using. The following code will add an event handler to an element to
handle the click event, and it’ll work in any modern browser (even IE6):
eventUtility.addEvent(el, "click", function() {
alert("You clicked me!");
});
Let’s take a look and see how this is more advantageous to the setEventHandler() function shown
at the beginning of this lesson.
➤➤ Now the if statement, with its attendant performance problems (with the addEventListener
lookup and the actual decision), is executed only once — when the browser loads the code.
Unfortunately, this decision cannot be circumvented.
➤➤ The addEvent() method is immediately available to call because the browser executes the
if … else statement and assignment operations immediately after the browser loads the
JavaScript code — much as it does with the setEventHandler() function.
➤➤ Calls to addEvent() execute only the code needed to assign an event handler to an object.
There are no decisions or extraneous lookups for other identifiers. This is in contrast to
setEventHandler(), which needs extra code in order to work.
Even though the eventUtility object makes working with events a little easier (and faster), the
implementation doesn’t quite match that of techniques used by modern-day JavaScript experts. So
let’s look at how this code can be transformed into modern-day JavaScript.
Modernizing Your Event Utility ❘ 199
}
};
Notice that this method does not specify any parameters. That is because this function declaration
is actually going to execute and return another function to addEvent. Let’s add some of that code now:
var eventUtility = {
addEvent : function() {
}()
};
The only thing this code adds is a set of parentheses at the end of the function statement. Remember
that in order for a function to execute, the function’s identifier must be followed by a set of paren-
theses, as in this call to alert():
alert("Hello, JavaScript!");
You can apply the same concept to anonymous functions by simply adding a set of parentheses to
the end of the function statement, like this:
function() {
alert("Hello, JavaScript!");
}();
This code doesn’t actually work because it has a syntax error, but you can make it work by sur-
rounding the function and parentheses with another set of parentheses, like this:
(function() {
alert("Hello, JavaScript!");
}());
So when the JavaScript engine loads this code, it executes the anonymous function that, in turn, exe-
cutes the alert() method inside it. This type of function is sometimes referred to as a self-executing
function.
Returning to your event utility object, you have an anonymous function that executes after the
browser loads the JavaScript code, but it isn’t returning anything worthwhile to addEvent. So let’s
change that with the following bold code:
var eventUtility = {
addEvent : (function() {
200 ❘ Lesson 20 Writing Cross-Browser Event Handlers
This new code first encapsulates the self-executing function in parentheses. It then adds an if block
that looks somewhat similar to code in your original eventUtility code; it returns a function to
addEvent as opposed to directly assigning the function to eventUtility.addEvent as in the previ-
ous version. Let’s finish this new version by adding an else block for browsers that do not support
addEventListener. The new code is in bold:
var eventUtility = {
addEvent : (function() {
if (typeof addEventListener === "function") {
return function(obj, evt, fn) {
obj.addEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.attachEvent("on" + evt, fn, false);
};
}
}())
};
Once again, the contents of the else block are somewhat similar to those of the previous version.
The main difference is that a function is returned to addEvent instead of being assigned directly; the
end result, however, is the same. When the self-executing function does its thing, it returns one of
two functions to addEvent; which function depends on what event model the browser supports.
This event utility, as shown in this lesson, does not offer a means to remove
an event handler. However, the code in the Try It section, as well as the code
download, does. The functionality operates according to the same principles
discussed in this lesson.
Try It
In this lesson, you learn how to write your own event utility to handle cross-browser issues.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Try It ❘ 201
Create a subfolder called Lesson20 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson20 folder.
Step-by-Step
You will rewrite the calculator application from Lessons 17, 18, and 19 using the event utility object.
1. Open your text editor and type the following JavaScript:
var eventUtility = {
addEvent : (function() {
if (typeof addEventListener === "function") {
return function(obj, evt, fn) {
obj.addEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.attachEvent("on" + evt, fn);
};
}
}()),
removeEvent : (function() {
if (typeof removeEventListener === "function") {
return function(obj, evt, fn) {
obj.removeEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.detachEvent("on" + evt, fn);
};
}
}())
};
Save it as eventutility.js.
202 ❘ Lesson 20 Writing Cross-Browser Event Handlers
2. Now open another instance of your text editor and type the following HTML:
<html>
<head>
<title>Lesson 20: Example 02</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">*</a></td>
</tr>
<tr>
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function addDigit(digit) {
var resultField = document.getElementById("results");
resultField.innerHTML += digit;
return false;
Try It ❘ 203
function calculate() {
var resultField = document.getElementById("results");
resultField.innerHTML = eval(resultField.innerHTML);
return false;
}
function reset() {
var resultField = document.getElementById("results");
resultField.innerHTML = "";
return false;
}
function getHandlerFunction(innerHTML) {
return function() {
addDigit(innerHTML);
return false;
};
}
onload = function() {
var links = document.getElementsByTagName("a"),
length = links.length;
switch (innerHTML) {
case "Clear":
func = reset;
break;
case "=":
func = calculate;
break;
default:
func = getHandlerFunction(innerHTML);
}
To get the sample code files, you can download Lesson 20 from the book’s website at www.wrox.com.
Please select Lesson 20 on the DVD to view the video that accompanies this
lesson.
21
The Standard Event Object
Over the course of the past several lessons you’ve learned how you can handle events with
HTML attributes, DOM object properties, and DOM object methods. But one vital piece of
information has been withheld from you: how to access a special object that contains infor-
mation about any event. Not surprisingly, the manner in which you access this object differs
according to the user’s browser: standards-supporting browsers use the standard Event object,
and legacy versions of Internet Explorer (IE) use their own proprietary event object.
This lesson focuses on the standard Event object. You’ll be introduced to IE’s legacy event
object in the next lesson.
This code sets an event handler on the document object for the click event. When you click
anywhere on the page, you see an alert box similar to that shown in Figure 21-1 (assuming a
standards-compliant browser).
206 ❘ Lesson 21 The Standard Event Object
Figure 21-1
The MouseEvent mentioned in Figure 21-1 is a data type that inherits from Event, and it contains
information related to the click event, such as which mouse button was clicked, the mouse pointer’s
coordinates, and a variety of other information. You’ll look at MouseEvent objects in future lessons.
For now, let’s just focus on basic Event objects.
Event Objects
Most of the time an Event object contains the majority of information you’ll want about a particular
event. It contains information regarding the type of event that occurred and the element that received
the event.
This property returns a string containing the name of the event (without a preceding on). This is
extremely helpful when you use one function to handle a variety of events. For example, look at the
following code:
function eventHandler(event) {
if (event.type === "click") {
Event Objects ❘ 207
In this code a function called eventHandler() accepts an argument called event (event is the modern
conventional identifier for the parameter of event handlers. Sometimes you may see just plain e used;
that’s old-school). Inside the function, an if statement determines whether or not the event that caused
the function to execute was the click event. If so, an alert box displays the message “You clicked
me!” There’s also an else...if statement that checks if the event was the keypress event, and an
alert box says, “You pressed a key!” if this event did indeed fire. The two final lines of this code set
the eventHandler() function to handle the click and keypress events on the document object.
Using one function to handle a variety of events is actually quite common; the ability to discern
what type of event occurred is an important ability that the Event object gives you.
This is a snippet of an HTML page showing the contents of the <body/> element. It has a <div/>
element styled to look like a red square. You can set up an event handler to change the background
color of this element when it is clicked, and you can do so in a variety of ways. One is to assign a
click event handler for the <div/> element itself, like this:
eventUtility.addEvent(document.getElementById("divElement"), "click",
function(event) {
var bgColor = event.target.style.backgroundColor;
var color = "red";
event.target.style.backgroundColor = color;
});
208 ❘ Lesson 21 The Standard Event Object
This event handler toggles the background color of this <div/> element between red and green. The
first statement of the event handler creates two variables: one to contain the current background
color of the element, called bgColor, and another to contain the text “red,” called color.
The next statement determines the element’s background color. If it is currently red, color is changed
to green; otherwise, color stays as red. The final line changes the element’s backgroundColor to
the value contained within the color variable.
This code works, but what if you want to add another <div/> element to the document and provide
the same functionality for it? Here’s the HTML:
<body>
<div id="divElement"
style="width: 100px; height: 100px; background-color: red; "></div>
<br/><br/>
<div id="divElement2"
style="width: 100px; height: 100px; background-color: red; "></div>
</body>
Well, you could set up another click event handler for the new element, but a better solution is to
use something called event delegation. Event delegation is a technique whereby you set a single event
handler on a parent element. This way any event that occurs within that parent element, including
its children, gets fired, and the event’s target is whatever element (the parent or children) the event
originated at.
This might be easier to understand with Figure 21-2.
Figure 21-2 shows an element that contains two child elements.
Assume a click event handler is set on the parent element. You
can click anywhere within the bounds of the parent element, and
the click event is handled by your handler. This includes Child-A
and Child-B. So when you click Child-B, it receives the click event,
but because it is a child of the parent (and because the event bubbles
up to the parent), the event handler executes and the event’s target
is Child-B.
So, forget all about the event handler for the divElement <div/>
element, and look at the following code, which uses event Figure 21-2
delegation:
eventUtility.addEvent(document, "click", function(event) {
var eSrc = event.target;
eSrc.style.backgroundColor = color;
Try It ❘ 209
}
});
One thing about event delegation: It does involve a little bit more code. So let’s look at it starting
with the first line. A variable called eSrc is set to contain the event’s target; it makes referring to the
event target much easier (eSrc is your author’s convention for naming this variable; it is short for
“event source”).
The next bit of code uses the tagName property to determine whether the element is a <div/> element
(the tagName property usually returns the element’s tag name in all-uppercase letters, but in some
instances, like with XHTML documents, it doesn’t. So, it’s a good habit to use toUpperCase() to
ensure uniformity. Remember that because the click event handler was set up on the document
object, any mouse click anywhere in the page will cause the click event to fire and the event han-
dler to execute. So you need to ensure that the event target is the element that you want to manipu-
late. In the case of this example, we want to manipulate any <div/> element in the page, so our
simple check of the element’s tagName property is sufficient. From there the code is largely the same
as before. The code determines the element’s background color and changes it.
When you need to handle one or more events on a variety of elements, use event delegation. It
actually performs better and more efficiently than one or more event handlers set on a variety of
elements.
The event’s type and target are major pieces of information, and you’ll use them quite often. Let’s
apply this knowledge with a simple example.
Try It
In this lesson you learn how to retrieve basic information regarding an event in the standard event
model.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
210 ❘ Lesson 21 The Standard Event Object
Create a subfolder called Lesson21 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson21 folder.
Step-by-Step
You will write a simple toolbar with three buttons. They will have a normal state, a state when the
mouse pointer moves over them, and a state when they’re clicked. Use event delegation to handle the
events for the buttons.
1. Open your text editor and type the following HTML:
<html>
<head>
<title>Lesson 21: Example 01</title>
<style type="text/css">
#divContainer {
background-color: silver;
height: 50px;
padding: 2px;
}
span {
display: inline-block;
width: 50px;
height: 50px;
}
.button-normal {
background-color: gray;
}
.button-over {
background-color: navy;
}
.button-click {
background-color: yellow;
}
</style>
</head>
<body>
<div id="divContainer">
<span class="button-normal">
</span>
<span class="button-normal">
</span>
<span class="button-normal">
</span>
</div>
<script type="text/javascript" src="eventutility.js"></script>
Try It ❘ 211
<script type="text/javascript">
</script>
</body>
</html>
span {
display: inline-block;
width: 50px;
height: 50px;
}
.button-normal {
background-color: gray;
}
.button-over {
background-color: navy;
}
.button-click {
background-color: yellow;
}
</style>
</head>
<body>
<div id="divContainer">
<span class="button-normal">
</span>
<span class="button-normal">
</span>
<span class="button-normal">
</span>
</div>
212 ❘ Lesson 21 The Standard Event Object
}
}
</script>
</body>
</html>
This code adds a function called mouseHandler(), and it accepts a single argument that the
browser passes to the function. The first statement creates two variables, eSrc and type.
These variables hold the event target and event type, respectively.
After the variable declarations, an if statement determines if the event target is a <span/>
element. This check is sufficient since they’re the only <span/> elements in the document.
If there were other <span/> elements, you could use the className property to determine
whether the target had one of the three CSS classes.
3. Now add more code; it’s bold in the following:
<html>
<head>
<title>Lesson 21: Example 01</title>
<style type="text/css">
#divContainer {
background-color: silver;
height: 50px;
padding: 2px;
}
span {
display: inline-block;
width: 50px;
height: 50px;
}
.button-normal {
background-color: gray;
}
.button-over {
background-color: navy;
}
.button-click {
background-color: yellow;
}
</style>
</head>
Try It ❘ 213
<body>
<div id="divContainer">
<span class="button-normal">
</span>
<span class="button-normal">
</span>
<span class="button-normal">
</span>
</div>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function mouseHandler(event) {
var eSrc = event.target;
var type = event.type;
This code goes through an if … else if … else if chain checking for the different event
types. If a mouseover event occurs, you want to change the element’s className property
to button-over — but only if its CSS class isn’t button-click. You want clicked buttons to
persist their state, as that mimics the behavior of pretty much every toolbar ever created. Next
check for the mouseout event, and change the CSS class of the element to button-normal
(again, only if the class isn’t button-click). Finally comes the check for the click event,
and you will toggle the CSS class between button-click and button-over. You use
button-over here because, when you click a button in the toolbar, the mouse pointer is
over the element.
214 ❘ Lesson 21 The Standard Event Object
4. Add the code that sets the click, mouseover and mouseout event handlers to the document
object. It is bold in the following code:
<html>
<head>
<title>Lesson 21: Example 01</title>
<style type="text/css">
#divContainer {
background-color: silver;
height: 50px;
padding: 2px;
}
span {
display: inline-block;
width: 50px;
height: 50px;
}
.button-normal {
background-color: gray;
}
.button-over {
background-color: navy;
}
.button-click {
background-color: yellow;
}
</style>
</head>
<body>
<div id="divContainer">
<span class="button-normal">
</span>
<span class="button-normal">
</span>
<span class="button-normal">
</span>
</div>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function mouseHandler(event) {
var eSrc = event.target,
var type = event.type;
}
} else if (type === "mouseout") {
if (eSrc.className !== "button-click") {
eSrc.className = "button-normal";
}
} else if (type === "click") {
if (eSrc.className !== "button-click") {
eSrc.className = "button-click";
} else {
eSrc.className = "button-over";
}
}
}
}
Save the file and load it into any browser (except IE8 and below). You’ll see that as you
move your mouse pointer over a button, the background color changes to navy. When you
move your mouse pointer off a button, it will change back to gray . When you click a button,
the element’s background color will change to yellow. Clicking a button that is yellow will
change it to navy.
To get the sample code files, you can download Lesson 21 from the book’s website at www.wrox.com.
Please select Lesson 21 on the DVD to view the video that accompanies this
lesson.
22
Internet Explorer’s Event
Object
Internet Explorer’s (IE) event model has its roots in early versions of the browser, and it has
remained virtually the same for over 12 years. In fact, the organizations behind Chrome, Safari,
and Opera saw fit to implement it in their browsers — only Firefox doesn’t support it. Despite
this rather broad support, IE’s event model is still considered to be incorrect, and its use in any
standards-supporting browser is discouraged.
The key to IE’s event model is an object called event. This object is populated with event
information every time an event is handled. In this lesson you will learn how to access the
event object and glean event information from it.
Here an event handler is assigned to the click event for the document object. When you click
anywhere in the document an alert box, shown in Figure 22-1, simply displays the text “[object]”.
When an event handler is assigned with the attachEvent() method, which the
eventUtility.addEvent() method uses, the event object is also passed as a parameter
to the handling function, much as the standard Event object is in standards-supporting
browsers. The following code shows this:
eventUtility.addEvent(document, "click", function(event) {
alert(event);
218 ❘ Lesson 22 Internet Explorer’s Event Object
});
There is no difference between the global event object and the event object passed to the handling
function.
Figure 22-1
It returns a string containing the name of the event without the preceding on, and you can use it
exactly as you would the standard Event object. The following example is an exact duplicate of the
code from Lesson 21:
function eventHandler(event) {
if (event.type === "click") {
alert("You clicked me!");
} else if (event.type === "keypress") {
alert("You pressed a key!");
}
}
This code uses the type property to determine the event that occurred and display an appropriate
message for that event. It is a simple property supported by all browsers. Unfortunately, discerning
the target of the event, while simple, isn’t the same as with the standard Event object.
Once again, this is a snippet of an HTML page. The <div/> element is styled to look like a red square,
and you can set up an event handler to change the background color of this element when it is clicked,
like this:
eventUtility.addEvent(document.getElementById("divElement"), "click",
function(event) {
var bgColor = event.srcElement.style.backgroundColor;
var color = "red";
event.srcElement.style.backgroundColor = color;
});
Just as in Lesson 21, this event handler toggles the background color of this <div/> element between
red and green. The first statement initializes two variables. The first, bgColor, contains the current
220 ❘ Lesson 22 Internet Explorer’s Event Object
background color of the element, and color, the second, contains the text red. The function then
determines what color to change the element’s background to, and then does so by changing the ele-
ment’s backgroundColor. This code achieves the same results as the code in Lesson 21, but uses the
srcElement property instead of target.
Also, because of the bubbling nature of IE’s event model, you can use event delegation to provide the
same functionality to as many elements as you want. Here, once again, is the updated HTML from
the previous lesson:
<body>
<div id="divElement"
style="width: 100px; height: 100px; background-color: red; "></div>
<br/><br/>
<div id="divElement2"
style="width: 100px; height: 100px; background-color: red; "></div>
</body>
The event handler to toggle the background color of both <div/> elements looks like this:
eventUtility.addEvent(document, "click", function(event) {
var eSrc = event.srcElement;
eSrc.style.backgroundColor = color;
}
});
This code was extremely easy to modify for IE. Only the first line of the function was changed from
event.target to event.srcElement. The remainder of the function is untouched. So the tagName
property allows you to determine whether or not the event source was a <div/> element. If it was,
the element’s background color is changed to either red or green.
Try It
In this lesson, you learn how to use IE’s event object to retrieve basic information regarding an
event.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Try It ❘ 221
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson22 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson22 folder.
Step-by-Step
You will rewrite the toolbar script from Lesson 21 for IE. Use event delegation to handle the events
for the buttons.
1. Open your text editor and type the following HTML. It is the final HTML from lesson21_
example01.htm:
<html>
<head>
<title>Lesson 21: Example 01</title>
<style type="text/css">
#divContainer {
background-color: silver;
height: 50px;
padding: 2px;
}
span {
display: inline-block;
width: 50px;
height: 50px;
}
.button-normal {
background-color: gray;
}
.button-over {
background-color: navy;
}
.button-click {
background-color: yellow;
}
</style>
</head>
222 ❘ Lesson 22 Internet Explorer’s Event Object
<body>
<div id="divContainer">
<span class="button-normal">
</span>
<span class="button-normal">
</span>
<span class="button-normal">
</span>
</div>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function mouseHandler(event) {
var eSrc = event.target,
type = event.type;
2. Save it as lesson22_example01.htm.
3. Modify the HTML and JavaScript using the following bold lines of code:
<html>
<head>
<title>Lesson 22: Example 01</title>
<style type="text/css">
#divContainer {
background-color: silver;
height: 50px;
Try It ❘ 223
padding: 2px;
}
span {
display: inline-block;
width: 50px;
height: 50px;
}
.button-normal {
background-color: gray;
}
.button-over {
background-color: navy;
}
.button-click {
background-color: yellow;
}
</style>
</head>
<body>
<div id="divContainer">
<span class="button-normal">
</span>
<span class="button-normal">
</span>
<span class="button-normal">
</span>
</div>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function mouseHandler(event) {
var eSrc = event.srcElement,
type = event.type;
}
}
}
}
The change is minor: You change the <title/> element and the first statement of the
mouseHandler() function (from event.target to event.srcElement).
4. Open this page in any browser supporting the IE event model (any version of IE since IE5,
Chrome, Safari, or Opera), and see that it behaves exactly like the version supporting the
standard event model.
To get the sample code files, you can download Lesson 22 from the book’s website at www.wrox.com.
Please select Lesson 22 on the DVD to view the video that accompanies this
lesson.
23
Writing Cross-Browser
Event Code
The challenge in dealing with two different event models is deciding how to meld them together
to make working with them as painless as possible. It’s a problem that all JavaScript developers
face at one point or another, and their solutions are as varied as they are. The best solutions,
however, are the simplest.
In this lesson you’ll look at simple solutions to cross-browser event discrepancies, and at the
end of the lesson you’ll add functionality to the eventUtility object.
This function, called getTarget(), determines whether to use the target or srcElement property.
It’s a simple solution to a simple problem. Let’s add a tiny bit of complexity to it so that it fits into
the patterns used by the eventUtility object. Add the following bold code to the eventUtility
object:
var eventUtility = {
addEvent : (function() {
// removed for printing
}()),
removeEvent : (function() {
// removed for printing
}()),
getTarget : (function() {
if (typeof addEventListener !== "undefined") {
return function(event) {
return event.target;
}
} else {
return function(event) {
return event.srcElement;
}
}
}())
};
This eventUtility.getTarget() method looks very much like the addEvent() and removeEvent()
methods. It determines whether to use standards-based code or IE’s proprietary code by checking
if the addEventListener identifier is not undefined. Notice a slight change to the feature detection
code. Originally the getTarget() function checked for event.target to determine standards com-
pliance, but the eventUtility.getTarget() method uses addEventListener for detection. This is
because of how getTarget() and eventUtility.getTarget() are called.
The original getTarget() function was accepting an event object as an argument, so you could use
that object to determine what properties it implements. The eventUtility.getTarget() method,
however, isn’t being called as the result of an event; it is immediately executing when it is loaded by
the browser. Since addEventListener() and event.target are both pieces of the standard event
model, it’s pretty safe to test for addEventListener() and assume the browser also supports the
target property.
So now you have a method that you can call to get the target of an event. Let’s look at how you can
add more functionality to eventUtility.
of the links, and looking at the URL in the location bar. You’ll see a pound sign (#) at the end of the
URL, as shown in Figure 23-1.
Figure 23-1
This isn’t such a bad thing because seeing return false in an event handler doesn’t make it very
clear what that statement does. But at the same time, the ability to cancel an event’s default behavior
is very useful. Fortunately there is another way to cancel an event, but once again you’ll have to deal
with two different implementations.
The standard Event object (as supported by Chrome, Firefox, Safari, Opera, and IE9) has a method
called preventDefault(). It doesn’t accept any arguments; you simply call the method and the browser
cancels the default behavior of the event. So instead of returning false, you simply add the following
line of code to the function handling the event:
// call preventDefault() on event object to cancel event
event.preventDefault();
Canceling an event in IE8 and below requires a different approach. IE’s event object has a property
called returnValue, and you assign it a value of false in order to cancel the event. The following
code demonstrates this:
// set returnValue as false to cancel event
event.returnValue = false;
So you have two completely different ways to perform the same action in a variety of browsers.
As with the getTarget() method you added to eventUtility, a method is the simplest and most
228 ❘ Lesson 23 Writing Cross-Browser Event Code
effective means of performing event cancellation across browsers. Let’s skip writing a separate func-
tion and add the method to the eventUtility object. The following code adds a new method called
preventDefault(). It is in bold text:
var eventUtility = {
addEvent : (function() {
// removed for printing
}()),
removeEvent : (function() {
// removed for printing
}()),
getTarget : (function() {
// removed for printing
}()),
preventDefault : (function() {
if (typeof addEventListener !== "undefined") {
return function(event) {
event.preventDefault();
}
} else {
return function(event) {
event.returnValue = false;
}
}
}())
};
The self-executing function returns one of two functions according to whether or not addEventListener
is defined. Once again, it’s assumed that if the browser supports addEventListener() it supports
the standard Event object’s preventDefault() method, as opposed to IE’s returnValue property.
Now that you’ve added these two new pieces to the eventUtility object, let’s revisit the calculator
and toolbar examples.
Try It
In this lesson, you learn how to handle cross-browser discrepancies by writing a unified application
programming interface (API) that performs work in all browsers.
Lesson Requirements
For this lesson, you will need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
Try It ❘ 229
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson23 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson23 folder.
Step-by-Step
You will rewrite the toolbar script from Lesson 21 for IE. Use event delegation to handle the events
for the buttons.
1. Open eventutility.js in your text editor and add the getTarget() and preventDefault()
methods to the object. They are bold in the following code:
var eventUtility = {
addEvent : (function() {
if (typeof addEventListener !== "undefined") {
return function(obj, evt, fn) {
obj.addEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.attachEvent("on" + evt, fn);
};
}
}()),
removeEvent : (function() {
if (typeof removeEventListener !== "undefined") {
return function(obj, evt, fn) {
obj.removeEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.detachEvent("on" + evt, fn);
};
}
}()),
getTarget : (function() {
if (typeof addEventListener !== "undefined") {
return function(event) {
return event.target;
}
} else {
return function(event) {
return event.srcElement;
}
}
}()),
preventDefault : (function() {
if (typeof addEventListener !== "undefined") {
return function(event) {
230 ❘ Lesson 23 Writing Cross-Browser Event Code
event.preventDefault();
}
} else {
return function(event) {
event.returnValue = false;
}
}
}())
};
2. You will now write a modified version of the calculator. This new version will contain sev-
eral improvements: for example, it will use the new functionality of the event utility, event
delegation, and an API to control the calculator (as opposed to functions). Open another
instance of your text editor and type the following code:
<html>
<head>
<title>Lesson 23: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td colspan="4" id="results"></td>
</tr>
<tr>
<td><a href="#">1</a></td>
<td><a href="#">2</a></td>
<td><a href="#">3</a></td>
<td><a href="#">+</a></td>
</tr>
<tr>
<td><a href="#">4</a></td>
<td><a href="#">5</a></td>
<td><a href="#">6</a></td>
<td><a href="#">-</a></td>
</tr>
<tr>
<td><a href="#">7</a></td>
<td><a href="#">8</a></td>
<td><a href="#">9</a></td>
<td><a href="#">*</a></td>
</tr>
<tr>
Try It ❘ 231
<td><a href="#">Clear</a></td>
<td><a href="#">0</a></td>
<td><a href="#">=</a></td>
<td><a href="#">/</a></td>
</tr>
</table>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
var calculator = {
resultField : document.getElementById("results"),
reset : function() {
this.resultField.innerHTML = "";
},
calculate : function() {
this.resultField.innerHTML = eval(this.resultField.innerHTML);
},
addDigit : function(digit) {
this.resultField.innerHTML += digit;
}
};
</script>
</body>
</html>
As you can see from this code, the reset(), calculate(), addDigit(), and
getHandlerFunction() functions are gone. Instead there is a calculator object, defined
with object literal notation, and it contains a resultField property (the HTML element),
a reset() method, a calculate() method, and an addDigit() method. This new
calculator object is an improvement over the previous list of functions. Not only is the
calculator’s functionality organized into a single entity (the calculator object), but the
HTML element object representing the result field is cached in the resultField property.
The various methods can use this property instead of having to perform a DOM search
every time they’re called.
3. Now add the event delegation code, shown in bold here:
<html>
<head>
<title>Lesson 23: Example 01</title>
<style type="text/css">
td {
border: 1px solid gray;
width: 50px;
}
#results {
height: 20px;
}
</style>
</head>
<body>
232 ❘ Lesson 23 Writing Cross-Browser Event Code
calculate : function() {
this.resultField.innerHTML = eval(this.resultField.innerHTML);
},
addDigit : function(digit) {
this.resultField.innerHTML += digit;
}
};
switch (innerHTML) {
case "Clear":
calculator.reset();
Try It ❘ 233
break;
case "=":
calculator.calculate();
break;
default:
calculator.addDigit(innerHTML);
}
eventUtility.preventDefault(event);
}
});
</script>
</body>
</html>
The only event you need to handle is the click event, and you need to handle it at the document
level. The first line of the event handler gets the event target by using the new getTarget()
method. Next, the code determines if the target is an <a/> element, and then the switch block
determines what calculator method to execute. The final statement within the if block
cancels the default event’s behavior, and you’ll see evidence of its working by running it in
any browser and seeing no pound sign in the URL.
4. Now you will modify the toolbar script to use the new getTarget() method. Open
lesson22_example01.htm and save it as lesson23_example02.htm. Change the bold line
in the following code:
<html>
<head>
<title>Lesson 23: Example 02</title>
<style type="text/css">
#divContainer {
background-color: silver;
height: 50px;
padding: 2px;
}
span {
display: inline-block;
width: 50px;
height: 50px;
}
.button-normal {
background-color: gray;
}
.button-over {
background-color: navy;
}
.button-click {
background-color: yellow;
}
234 ❘ Lesson 23 Writing Cross-Browser Event Code
</style>
</head>
<body>
<div id="divContainer">
<span class="button-normal">
</span>
<span class="button-normal">
</span>
<span class="button-normal">
</span>
</div>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function mouseHandler(event) {
var eSrc = eventUtility.getTarget(event),
type = event.type;
It’s a simple change, but the page now works in every modern browser. Go ahead and open
it and you’ll find it works just fine.
To get the sample code files, you can download Lesson 23 from the book’s website at www.wrox.com.
Please select Lesson 23 on the DVD to view the video that accompanies this
lesson.
24
Dragging and Dropping
JavaScript can add rich interactivity to any web page, interactivity that once was available only
with conventional applications. For example, take a look at your computer’s desktop. Chances
are you have one icon that you can click and drag to anywhere you want. That’s a level of
interactivity that users want, and it would be impossible to supply that interactivity on a web
page without JavaScript or plugins like Flash, Silverlight, and so on.
In this lesson, you learn how to:
➤➤ Get the mouse pointer’s location.
➤➤ Move an element on the page.
➤➤ Use events to facilitate drag-and-drop.
Client Coordinates
To get this information, look once again to the event object in both the standards and Internet
Explorer (IE) event models. Thankfully, both event models are in lockstep when it comes to this
type of information. You won’t have to expand the functionality of the eventUtility object at all,
as the properties have the same names and uses.
There are actually two sets of coordinates relating to the mouse pointer that you can retrieve from
an event object. The first set is the pointer’s location in relation to the entire screen. You get this
information by using the screenX and screenY properties, like this:
eventUtility.addEvent(document, "click", function(event) {
alert("X Coordinate is: " + event.screenX); // pointer's
horizontal position
alert("Y Coordinate is: " + event.screenY); // pointer's vertical
position
});
This information can be useful, but it isn’t suited well for drag-and-drop. The screen is a fixed size,
so regardless of where your mouse moves on the screen, you’re limited to only the dimensions of the
screen when using screenX and screenY. Most of all, the screenX and screenY coordinates are
in relation to the entire screen, which isn’t information you want. A better solution is to use a set of
coordinates that are in relation to the client area, the part of the window that displays the document
(also called the viewport). Figure 24-2 outlines this area.
Figure 24-2
Getting the Mouse Pointer’s Location ❘ 237
You can get this information by using the clientX and clientY properties, like this:
eventUtility.addEvent(document, "click", function(event) {
alert("X Coordinate is: " + event.clientX); // pointer's horizontal
position
alert("Y Coordinate is: " + event.clientY); // pointer's vertical
position
});
Like the screen coordinate system, the client coordinate system is a fixed size (the size of the viewport).
For example, clicking the top-left corner of the viewport always results in a clientX value of 0 —
regardless of whether the page is horizontally scrolled. The clientX and clientY coordinates are in
relation to the viewport, not the document, but you can obtain two more pieces of information to com-
bine with the client coordinates to accurately get the pointer’s position: the document.body.scrollTop
and document.body.scrollLeft properties.
The scrollTop and scrollLeft are properties of all element objects, and they get the vertical or
horizontal position of the scrollbar for the element. When used on the body object, these properties
return the amount of pixels the scrollbars have been scrolled. When the scrollbar is at the top (or to
the left for horizontal scrollbars), the position is 0.
To get the mouse pointer’s accurate position in the document, you combine the client coordinates
with the body’s scroll positions, like this:
var x = event.clientX + document.body.scrollLeft;
var y = event.clientY + document.body.scrollTop;
Element Coordinates
Every element has a set of properties that allow you to get position and size information for that ele-
ment. They aren’t part of any W3C standard, but every major browser has implemented them since
they were first introduced in IE4. They are:
➤➤ offsetLeft: Gets the left position of an element relative to its nearest positioned ancestor.
➤➤ offsetTop: Gets the top position of an element relative to its nearest positioned ancestor.
➤➤ offsetWidth: Gets the width of an element relative to its nearest positioned ancestor. The
value returned by this property also includes the element’s padding, margin, and border
width.
➤➤ offsetHeight: Gets the height of an element relative to its nearest positioned ancestor. The
value returned by this property also includes the element’s padding, margin, and border
width.
These four properties are extremely helpful when you need to know the position and dimensions of
an element as it is rendered by the browser. For the purposes of drag-and-drop the only properties
you’ll need to look at are offsetLeft and offsetTop.
238 ❘ Lesson 24 Dragging and Dropping
To find the mouse pointer’s coordinates in relation to an element, you use the element’s offsetLeft
and offsetTop properties and then subtract the event object’s clientX and clientY properties,
like this:
eventUtility.addEvent(document, "click", function(event) {
var eSrc = eventUtility.getTarget(event);
var x = event.clientX + document.body.scrollLeft;
var y = event.clientY + document.body.scrollTop;
In this code, the eventUtility object gets the event target by using the getTarget() method and
assigning the returned value to eSrc. Then two variables, relativeX and relativeY, are created
and assigned their respective values by the offset coordinates’ being calculated with the mouse
pointer’s coordinates. Again, these relative coordinates are important because it is necessary for
accurately emulating the behavior users are accustomed to seeing in their operating systems.
With these two sets of coordinates, you are well on your way to having all the pieces to writing a
drag-and-drop script. There’s just one more piece to the puzzle: events.
The mousedown event initiates the drag process, the mousemove event is used to move the element in
sync with the mouse pointer (dragging), and the mouseup event ends the drag process and drops the
element at the pointer’s location.
So let’s put all this knowledge to work and try it out.
Try It
In this lesson, you learn about the event object’s clientX and clientY properties and an element’s
offsetLeft and offsetTop properties. In this section you learn how to use them in creating a drag-
and-drop script.
Try It ❘ 239
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson24 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson24 folder.
Step-by-Step
1. Start by typing the following HTML:
<html>
<head>
<title>Lesson 24: Example 01</title>
<style type="text/css">
.draggable {
position: absolute;
cursor: move;
}
.box {
width: 100px;
height: 100px;
}
.navy {
background-color: navy;
}
.green {
background-color: Green;
}
</style>
</head>
<body>
<div class="navy box draggable"></div>
240 ❘ Lesson 24 Dragging and Dropping
</script>
</body>
</html>
This is the basis of your web page. It has a style sheet containing four CSS classes: draggable,
box, navy, and green.
The draggable class sets the element’s position to absolute, making it possible for an
element with this class to be moved anywhere in the page. It also changes the mouse pointer
to the move symbol. This is a visual cue for the user so that she knows the element is movable.
This class is used to make an element draggable. The JavaScript you write will determine
whether the element uses this class, and the user will be able to drag it to anywhere on the page.
The box class is purely for presentation. When applied to an element, it renders the element
as a square with 100-pixel sides.
The navy and green classes, too, are purely for presentation. They change the element’s
background color to navy and green, respectively.
In the <body/> of the page are two <div/> elements. The first uses the navy, box, and
draggable classes to render the element as a navy box with an absolute position. The sec-
ond uses the green, box, and draggable classes.
2. Now you want to add some JavaScript. The first little bit is the mouseHandler() function,
which you use to handle a variety of events. The new code is in bold:
<html>
<head>
<title>Lesson 24: Example 01</title>
<style type="text/css">
.draggable {
position: absolute;
cursor: move;
}
.box {
width: 100px;
height: 100px;
}
.navy {
background-color: navy;
}
.green {
background-color: Green;
}
Try It ❘ 241
</style>
</head>
<body>
<div class="navy box draggable"></div>
<div class="green box draggable"></div>
switch (type) {
case "mousedown":
if (eSrc.className.indexOf("draggable") > -1) {
}
break;
case "mouseup":
break;
case "mousemove":
break;
}
}
With this code, some of the pieces start coming together. Event handlers for the mousedown,
mouseup, and mousemove events are set up for the document object, and the mouseHandler()
function handles all three events.
The first statement in the function creates four variables. The first is eSrc, the event target.
Next is type, the event type. Next are the x and y variables to contain the mouse pointer’s
coordinates.
Because this function handles multiple events, you need to determine what event took place.
A switch statement uses the event type, and a case block is written for each of the three
events.
When the user causes a mousedown event to fire, you use the indexOf() string method to
determine if the target has a CSS class of draggable. You want to drag only those elements
242 ❘ Lesson 24 Dragging and Dropping
with this CSS class, so all the code to initiate the drag-and-drop sequence will be placed
inside the if code block.
3. To facilitate the drag-and-drop process, create an object outside the mouseHandler() func-
tion called dragObj. Its responsibility is to initialize the element for dragging, as well as to
contain information regarding the state of the dragging process. Add the following code:
<html>
<head>
<title>Lesson 24: Example 01</title>
<style type="text/css">
.draggable {
position: absolute;
cursor: move;
}
.box {
width: 100px;
height: 100px;
}
.navy {
background-color: navy;
}
.green {
background-color: Green;
}
</style>
</head>
<body>
<div class="navy box draggable"></div>
<div class="green box draggable"></div>
function mouseHandler(event) {
var eSrc = eventUtility.getTarget(event);
var type = event.type;
var x = event.clientX + document.body.scrollLeft;
var y = event.clientY + document.body.scrollTop;
switch (type) {
case "mousedown":
if (eSrc.className.indexOf("draggable") > -1) {
}
break;
case "mouseup":
break;
case "mousemove":
break;
}
}
4. Now use dragObj to facilitate the drag-and-drop sequence. Add the following bold code:
<html>
<head>
<title>Lesson 24: Example 01</title>
<style type="text/css">
.draggable {
position: absolute;
cursor: move;
}
.box {
width: 100px;
height: 100px;
}
.navy {
background-color: navy;
}
.green {
background-color: Green;
}
</style>
</head>
<body>
<div class="navy box draggable"></div>
<div class="green box draggable"></div>
function mouseHandler(event) {
var eSrc = eventUtility.getTarget(event);
var type = event.type;
Try It ❘ 245
switch (type) {
case "mousedown":
if (eSrc.className.indexOf("draggable") > -1) {
dragObj.dragging = true;
dragObj.setDragObj(eSrc, x, y);
}
break;
case "mouseup":
dragObj.dragging = false;
break;
case "mousemove":
if (dragObj.dragging) {
dragObj.dragTo(x, y);
}
break;
}
}
The first new lines of code go inside the if block in the mousedown case. You set the dragging
property of dragObj to true, and you call the setDragObj() method to get the element
ready to drag.
Next you add one line of code under the mouseup case; you set the dragging property to
false.
Then, in the mousemove case, you determine if dragObj.dragging is true. If the user is
holding down the mouse button while it’s over an element with the CSS class of draggable,
then dragObj.dragging will be true. If it is true, you want to call the dragTo() method
to move the element to the current mouse pointer’s coordinates.
When the user releases the mouse button, dragObj.dragging will be false, and the element
will stop moving when the mousemove event fires.
5. Save this file as lesson24_example01.htm, and open it in any browser. Drag the green box
to another location on the page, and then do the same for the navy box.
One thing you may notice is that the navy box is always behind the green box. In traditional
drag-and-drop implementations, whatever item is currently being dragged should be the
topmost item on the screen. For example, drag your text editor window and your browser
windows over each other, and you’ll see that the window you’re currently dragging is over
the other window, as shown in Figure 24-3.
246 ❘ Lesson 24 Dragging and Dropping
Figure 24-3
You can add the same functionality to your drag-and-drop script by modifying an element’s
z-index CSS property.
6. In order to make this work as easily as possible, add a zIndex property to dragObj and give
it an initial value of 1, as shown in the following bold code:
<html>
<head>
<title>Lesson 24: Example 01</title>
<style type="text/css">
.draggable {
position: absolute;
cursor: move;
}
.box {
width: 100px;
height: 100px;
}
.navy {
background-color: navy;
}
.green {
Try It ❘ 247
background-color: Green;
}
</style>
</head>
<body>
<div class="navy box draggable"></div>
<div class="green box draggable"></div>
function mouseHandler(event) {
var eSrc = eventUtility.getTarget(event);
var type = event.type;
var x = event.clientX + document.body.scrollLeft;
var y = event.clientY + document.body.scrollTop;
switch (type) {
case "mousedown":
if (eSrc.className.indexOf("draggable") > -1) {
dragObj.dragging = true;
dragObj.setDragObj(eSrc, x, y);
}
break;
case "mouseup":
dragObj.dragging = false;
break;
case "mousemove":
if (dragObj.dragging) {
dragObj.dragTo(x, y);
}
break;
}
}
</script>
</body>
</html>
This new code also adds a line to setDragObj(), setting the style.zIndex property to an
incremented value of the dragObj.zIndex property.
7. Resave the file and refresh your browser. Now drag the two boxes around on the page.
You’ll see that whichever box you drag becomes the topmost box –– as with the drag-and-
drop feature of your operating system.
To get the sample code files, you can download Lesson 24 from the book’s website at www.wrox.com.
Please select Lesson 24 on the DVD to view the video that accompanies this
lesson.
25
Timers and Animating Elements
Users have come to expect a certain level of polish in the applications they use — particularly
their operating systems. Every new version adds more visual goodness that, while not entirely
necessary, enhances the user’s experience. Features like menus that fade in and out, and windows
that scale while maximizing and minimizing, are perfect examples of how unobtrusive anima-
tions can add visual flair while still maintaining the original functionality of showing or hiding
a menu or resizing a window.
In Lesson 24 you allowed a user to click an element and drag it to wherever he wishes; you
essentially wrote an animation script. The animation was limited and dependent upon the user’s
action, but the element moved to a different set of coordinates in sync with the mouse pointer — a
movement animation.
Animations such as the fading in and out in menus aren’t so dependent on user interaction. Sure,
the user has to initiate an event to cause the animation to run, but a menu fades in over a certain
period — not in relation to where the user moves the mouse pointer.
This type of animation is akin to motion pictures or cartoons — a character’s fluid movement
is broken into a number of frames that are shown in rapid sequence. Each frame is shown for a
certain time (usually one thirtieth of a second) to mimic movement on the screen.
On a web page, you modify an element (the character) bit-by-bit (or frame by frame) over a
certain period using timers. In this lesson you’ll learn about two methods that are used for
timing. Then you’ll use the timers to create an animation.
of time has elapsed. It accepts two arguments: a function to execute and the amount of time in mil-
liseconds to wait before executing the code. Its syntax looks like the following:
function doSomething() {
alert("Hello, timeout!");
}
The setTimeout() method returns a numeric value, which is the timer’s unique ID number. While
it’s not necessary to use this ID number, it does come in handy, as you’ll see in a few moments.
In this code the setTimeout() method is called by passing the doSomething() function as the first
argument, and a number value of 1000 as the second argument. Remember, the second parameter is
the amount of time in milliseconds. The number 1000 actually represents one second.
After the setTimeout() method executes, the JavaScript engine waits one second and then exe-
cutes doSomething(). The engine, however, does not delay any statements that may appear after
the setTimeout() call — it delays only the execution of doSomething(). For example, look at the
following code:
function doSomething() {
alert("Hello, timeout!");
}
This code adds a statement to the end of the previous example. When this code executes, the
setTimeout() call queues the doSomething() function to be called in one second, and then an
alert box displays the text “After setTimeout() call.” Then, and only after a second has passed since
the call to setTimeout(), doSomething() executes. So the engine does not delay all code; it delays
only the code specified as the first argument to setTimeout().
Even though setTimeout() is a one-shot timer, you can use a form of recursion (a function’s calling
itself) to call setTimeout() every time doSomething() executes. Look at the following code, which
removes the alert() call from the previous example and adds a new line (bolded) to doSomething():
function doSomething() {
alert("Hello, timeout!");
timer = setTimeout(doSomething, 1000);
}
It’s a simple modification, but it drastically changes how the code behaves. It creates an infinite loop.
The call to setTimeout() outside the function initiates the loop by setting a one-second timeout
before executing doSomething(). Then, when the second expires, doSomething() executes, which
sets another one-second timeout to call doSomething() again. Every time doSomething() executes,
a new one-second timeout is set to call doSomething().
Setting a Delay — the setTimeout() Method ❘ 251
Notice how the timer variable is reused. Since doSomething() executes when a timeout expires,
the timeout ID stored in timer becomes useless. So instead of a new variable being created, timer is
reused to store the new timeout’s ID. This is advantageous because, as you may have guessed, a loop
such as the preceding one could potentially be troublesome. A never-ending loop, especially one that
pops up an alert window every second, can be very annoying.
Thankfully, when setTimeout() was added to Netscape 2 a companion method called clearTimeout()
was included as well. As its name implies it cancels a timeout set with setTimeout(), and it accepts
a timeout ID as an argument. Look at the following code to see it in action:
function doSomething() {
alert("Hello, timeout!");
timer = setTimeout(doSomething, 1000);
}
Calling clearTimeout() and passing it timer cancels the timeout set in the statement before it can
expire. Remember that setTimeout() doesn’t stall the execution of the rest of the code. As a result,
the timeout is cleared and doSomething() never executes.
Many years ago setTimeout() was often used to execute a function a certain number of times at
particular time intervals, as the following code demonstrates:
function doSomething() {
if (counter < 5) {
alert("Hello, timeout!");
counter++;
timer = setTimeout(doSomething, 1000);
}
}
var counter = 0;
var timer = setTimeout(doSomething, 1000);
This code adds a counter variable, and as long as the value of counter is less than 5, setTimeout()
delays the execution of doSomething() again. If counter is greater than 5, the timeout isn’t issued
again and the loop exits.
This method of repeating a function with a time delay is still used today, but setTimeout() is usu-
ally more appropriately used to defer execution of a function. If you want to repeat a function call at
regular time intervals, look to the setInterval() method.
252 ❘ Lesson 25 Timers and Animating Elements
doSomething() will execute every second until either it is canceled in code or a different page is
loaded in the browser. Like setTimeout(), the setInterval() method returns a unique timer ID
that can be passed to the clearInterval() companion method. The following code re-creates the
timeout with a timer code from the previous section, using setInterval() and clearInterval():
function doSomething() {
if (counter < 5) {
alert("Hello, timeout!");
counter++;
} else {
clearTimeout(timer);
}
}
var counter = 0;
var timer = setInterval(doSomething, 1000);
There’s no need to call setInterval() more than once; its job is to repeat the provided function
for as long as it’s allowed to. So the code determines if counter is less than 5 and, if it isn’t, calls
clearTimeout() to clear the timer.
setInterval() waits until the specified amount of time has passed before exe-
cuting the provided function for the first time.
The setInterval() and clearInterval() methods are widely used to perform animations, and
you’ll use them now to create an animation script that resizes a box.
Try It
In this lesson, you learn about timers and what they’re used for. In this section you use one of them,
setInterval(), to animate an element.
Try It ❘ 253
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson25 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson25 folder.
Step-by-Step
1. Start by typing the following HTML:
<html>
<head>
<title>Lesson 25: Example 01</title>
<style type="text/css">
.box {
width: 100px;
height: 100px;
background-color: navy;
}
</style>
</head>
<body>
<script type="text/javascript">
</script>
</body>
</html>
This is the basis of your web page. It has a style sheet containing a CSS class called box. This
class sets the height and width of the element to 100px, and gives it a background color of navy.
Inside the <body/> is a <div/> element with an ID of divNavyBox, and it uses the box class.
254 ❘ Lesson 25 Timers and Animating Elements
2. Now add some JavaScript code. Create an object called animated, and give it the members in
the following code:
<html>
<head>
<title>Lesson 25: Example 01</title>
<style type="text/css">
.box {
width: 100px;
height: 100px;
background-color: navy;
}
</style>
</head>
<body>
<script type="text/javascript">
var animated = {
timer : null,
el : document.getElementById("divNavyBox"),
startGrowAnimation : function() {
},
startShrinkAnimation : function() {
},
stopAnimation : function() {
clearInterval(this.timer);
},
doAnimation : function(amount) {
}
};
</script>
</body>
</html>
This code adds a few members to the animated object. The timer property contains the
interval’s unique ID; the el property contains the <div/> element object; and the four
methods start, stop, and perform the animations. The stopAnimation() method is simple:
It calls clearInterval() and passes the timer property.
3. Let’s start with the doAnimation() method. Its purpose is to perform the growing and
shrinking animations. It accepts one argument, a numeric value, which can be either positive
or negative. If positive, the box grows by the amount specified. If negative, the box shrinks
by the amount specified. The method’s body is in bold:
<html>
<head>
<title>Lesson 25: Example 01</title>
Try It ❘ 255
<style type="text/css">
.box {
width: 100px;
height: 100px;
background-color: navy;
}
</style>
</head>
<body>
<script type="text/javascript">
var animated = {
timer : null,
el : document.getElementById("divNavyBox"),
startGrowAnimation : function() {
},
startShrinkAnimation : function() {
},
stopAnimation : function() {
clearInterval(this.timer);
},
doAnimation : function(amount) {
var size = this.el.offsetWidth;
if ((amount > 0 && size < 200) "" (amount < 0 && size > 0)) {
this.el.style.width = size + amount + "px";
this.el.style.height = size + amount + "px";
} else {
this.stopAnimation();
}
}
};
</script>
</body>
</html>
Because the <div/> element is in the shape of a square, the doAnimation() method keeps
things simple by retrieving only the element’s width with the element’s offsetWidth
property.
Next, an if statement determines whether to grow or shrink the element by determining
whether amount is positive or negative. It also determines how big or small the element gets.
If amount is positive, the element will grow to a maximum of 200 pixels high and wide. If
negative, the element will shrink to a minimum of zero pixels. The element’s dimensions are
adjusted by the specified amount as long as its size is within the hard-coded limits and the
stopAnimation() method executes when either limit is reached.
256 ❘ Lesson 25 Timers and Animating Elements
<script type="text/javascript">
var animated = {
timer : null,
el : document.getElementById("divNavyBox"),
startGrowAnimation : function() {
this.stopAnimation();
this.timer = setInterval(function() {
animated.doAnimation(5);
}, 10);
},
startShrinkAnimation : function() {
},
stopAnimation : function() {
clearInterval(this.timer);
},
doAnimation : function(amount) {
var size = this.el.offsetWidth;
if ((amount > 0 && size < 200) "" (amount < 0 && size > 0)) {
this.el.style.width = size + amount + "px";
this.el.style.height = size + amount + "px";
} else {
this.stopAnimation();
}
}
};
</script>
</body>
</html>
The first line of this code calls stopAnimation(). This is absolutely vital because if
startGrowAnimation() is called while another animation is in progress, the timer prop-
erty will be overwritten with a new timer ID, and the interval timer that was running will
stop only when the page is reloaded. By calling stopAnimation() first, you can stop the
current animation (if there is one) and safely begin the grow animation.
Try It ❘ 257
The second statement calls setInterval() and passes an anonymous function to it. Inside
the anonymous function is a call to animated.doAnimation(), with a value of 5 being passed.
An anonymous function is used here because you need to pass a value to the doAnimation()
method in order for it to work.
5. Now add the code for startShrinkAnimation(). It’s very similar to that for
startGrowAnimation(). The new code is bold in the following:
<html>
<head>
<title>Lesson 25: Example 01</title>
<style type="text/css">
.box {
width: 100px;
height: 100px;
background-color: navy;
}
</style>
</head>
<body>
<script type="text/javascript">
var animated = {
timer : null,
el : document.getElementById("divNavyBox"),
startGrowAnimation : function() {
this.stopAnimation();
this.timer = setInterval(function() {
animated.doAnimation(5);
}, 10);
},
startShrinkAnimation : function() {
this.stopAnimation();
this.timer = setInterval(function() {
animated.doAnimation(-5);
}, 10);
},
stopAnimation : function() {
clearInterval(this.timer);
},
doAnimation : function(amount) {
var size = this.el.offsetWidth;
if ((amount > 0 && size < 200) "" (amount < 0 && size > 0)) {
this.el.style.width = size + amount + "px";
this.el.style.height = size + amount + "px";
} else {
this.stopAnimation();
}
}
};
258 ❘ Lesson 25 Timers and Animating Elements
</script>
</body>
</html>
The only difference here is the value passed to doAnimation(). It is -5, so the element will
shrink in size by five pixels.
6. Now add the following <a/> elements to call the start and stop methods:
<html>
<head>
<title>Lesson 25: Example 01</title>
<style type="text/css">
.box {
width: 100px;
height: 100px;
background-color: navy;
}
</style>
</head>
<body>
<a href="#" onclick="animated.startGrowAnimation(); return false;">Grow Box</a>
<a href="#" onclick="animated.startShrinkAnimation(); return false;">Shrink
Box</a>
<a href="#" onclick="animated.stopAnimation(); return false;">Stop
Animation</a>
<div id="divNavyBox" class="box"></div>
<script type="text/javascript">
var animated = {
timer : null,
el : document.getElementById("divNavyBox"),
startGrowAnimation : function() {
this.stopAnimation();
this.timer = setInterval(function() {
animated.doAnimation(5);
}, 10);
},
startShrinkAnimation : function() {
this.stopAnimation();
this.timer = setInterval(function() {
animated.doAnimation(-5);
}, 10);
},
stopAnimation : function() {
clearInterval(this.timer);
},
doAnimation : function(amount) {
var size = this.el.offsetWidth;
if ((amount > 0 && size < 200) "" (amount < 0 && size > 0)) {
this.el.style.width = size + amount + "px";
this.el.style.height = size + amount + "px";
Try It ❘ 259
} else {
this.stopAnimation();
}
}
};
</script>
</body>
</html>
7. Save this file as lesson25_example01.htm, and open it in any browser. Click the links to
grow and shrink the element, and also click the stop link to stop a running animation.
To get the sample code files, you can download Lesson 25 from the book’s website at www.wrox.com.
Please select Lesson 25 on the DVD to view the video that accompanies this
lesson.
26
Forms — A Primer
In early versions of browsers, forms were generally the only means by which a web application
could interact with the user. The user could input data into a variety of form controls and send
that data to the server, and a server-side application would return a web page that incorporated
the data in some way. In many cases a server-side application would have to validate every field
in a form, and notify the user of any errors when responding to a form submission. This experi-
ence could be frustrating for the user, as sometimes one or more fields would be cleared of any
data the user had entered.
Forms are still an important part of any web application that requires input from the user, but
today’s Web is far more sophisticated than that of yesteryear. With JavaScript, you can validate
some form fields before any information is sent to the server — providing a much better experi-
ence for the user.
Over the course of the next few lessons, you learn how to script a variety of form controls. But
first let’s look at some basics of scripting forms with JavaScript.
This code gets the first form found in the document, and in most cases this is sufficient because
the majority of web pages contain only one form. Problems can arise, however, if your page
262 ❘ Lesson 26 Forms — A Primer
does contain more than one form, and the order in which they’re defined in the HTML varies from
page to page.
For this reason, you can access any form by using the value set in the <form/> element’s name attri-
bute. The following code displays the HTML of a form with a specified name of theForm:
<form name="theForm"></form>
To find this form in the DOM, you can simply use the form’s name as a property of the document
object, like this:
var theForm = document.theForm;
This approach is not part of the W3C DOM standard, but it has been incorporated in every JavaScript-
supporting browser since Netscape 2.0. It is the clearest way to access a form with JavaScript, and
you can be assured that this approach will not go away.
Once you have a <form/> element object, you have the ability to submit the form by calling the
submit() method, or you can clear the form or reset the form control elements to their default val-
ues with reset(). The <form/> element object also has an elements property, which is a collection
of all elements contained within the form.
Probably the most frequently used property is the onsubmit event handler. It maps to the <form/>
element’s onsubmit attribute. Handling the submit event allows you to validate form fields and
cancel the form’s submission to the server if an error is found. So if you ask for a user’s e‑mail address
and he or she provides the answer “Use the Force, Luke,” you can identify the invalid data and pro-
hibit the user from submitting the form until his or her data validates.
Calling the submit() method does cause the submit event to fire.
Of course, the use of form validation implies the use of form control elements. Let’s look briefly at
the various form controls and some common properties their DOM objects expose.
Figure 26-1 Figure 26-2
Figure 26-3 Figure 26-4
Try It
In this lesson, you are briefly introduced to forms and common properties and methods of form
controls.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson26 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson26 folder.
Step-by-Step
1. Type the following HTML:
<html>
<head>
<title>Lesson 26: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="trySubmit(event)">
<input type="submit" value="Submit" />
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function trySubmit(event) {
if (confirm("Do you want to stop submission?")) {
eventUtility.preventDefault(event);
}
}
</script>
</body>
</html>
266 ❘ Lesson 26 Forms — A Primer
Save this as lesson26_example01.htm. The page consists of one <form/> named theForm.
It has an onsubmit event handler set to the trySubmit() function. Notice that a variable
called event is being passed to the function. This ability was not mentioned in Lesson 17,
but it’s worth mentioning now.
When assigning an event handler using an HTML attribute, you can pass the event object
to the handling function by using the word event. This gives you access to the event object
in the handling function because, by default with HTML attribute event handlers, the
event object isn’t passed to the function. You must pass it yourself if you want access to that
object.
The trySubmit() function accepts the event object as an argument. It asks the user if he or
she wants to cancel the form submission. If the user clicks OK, the submission is stopped. If
the user clicks Cancel, the form submits (although it really doesn’t send data to any server).
2. It isn’t clear if trySubmit() actually works, so add the bold line in the following code:
<html>
<head>
<title>Lesson 26: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="trySubmit(event)">
<input type="submit" value="Submit" />
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
alert("Page Loaded");
function trySubmit(event) {
if (confirm("Do you want to stop submission?")) {
eventUtility.preventDefault(event);
}
}
</script>
</body>
</html>
The call to alert() happens as the page loads into the browser. So when the user clicks the
submit button, alert() won’t execute if the form submission is cancelled. But if the form is
allowed to submit, the browser will reload the page and call alert() again.
To get the sample code files, you can download Lesson 26 from the book’s website at www.wrox.com.
Please select Lesson 26 on the DVD to view the video that accompanies this
lesson.
27
Scripting Buttons
Probably the most common form control is the button. Even though you can submit a form by
calling its submit() method (and thus without using a submit button), most forms have a button
because of the relationship between a form and its controls.
Button controls can be created in one of two ways. You can use the <input/> element, like this:
<input type="submit" name="myButton" value="Button Text" />
Regardless of how you create a button, you can directly access its DOM object in one of two
ways. First, you can get to the button through the form. The following code shows how to
access the <input/> element named myButton (from the top of the page), assuming it’s in a
form named theForm:
var button = document.theForm.myButton;
Form control objects are automatically added as properties to the forms they reside in. So this code
first gets the <form name="theForm" /> element object and then gets the myButton property. The
second way to get to the button through the form is the ever-easy document.getElementById()
method, but in the case of the myButton button, getElementById() will not work because it
has no id attribute.
After you have access to the DOM object, you can manipulate it however you see fit. For
example, the following code sets a click event handler (using the event utility) to modify the
button every time it’s clicked:
var count = 0;
var button = document.theForm.myButton;
This code creates two variables called count and button. The former is used to count how many
times the button is clicked, and the latter gets the <input/> element’s DOM object. With a reference to
that object, the code then sets an event handler to handle the click event. When the button is clicked,
the button’s value property is changed to reflect the number of times the button has been clicked. A
button’s value property gets or sets the text that is displayed within the button.
There is a flaw in this event handler, however. Assuming that the myButton button is part of the
theForm form, clicking the button causes the form to submit. Its type attribute is set to submit, so
this isn’t very surprising. This is easy enough to fix; simply call eventUtility.preventDefault()
and the form will not submit. The default action of a submit button is to submit the form it resides
in. So when you click a submit button, a series of events fire in the following order:
1. mousedown
2. mouseup
3. click
4. submit
The submit event does not fire until after the click event fires, and since submit is the default event of
a submit button, calling preventDefault() will prevent the submit event from firing. The same can
be said for buttons of type reset. The reset event fires after click, and calling preventDefault() in
the handler for click suppresses the reset event.
For the most part these same concepts apply to buttons created with the <button/> element. According
to the HTML specification, it doesn’t really matter how you create a button; the <button/> element
does allow you to add content to it, however, as shown in the following HTML code:
<button name="myButton3" type="submit">
<p id="buttonContent">Button Text</p>
</button>
Here a <p/> element with an id of buttonContent contains the text “Button Text”, and it is added
as a child to <button/>. This content renders inside the <button/>, so the content dictates what the
user sees as opposed to the value property of the <input/> element.
What makes <button/> elements so different, as far as scripting is concerned, is that the content
within the button can also be the target of an event. So in order to replicate the functionality from
Try It ❘ 269
the click event handler for the myButton button, a few changes must be made to the event handler
function.
Assume the myButton3 button replaces the myButton button in the theForm form, and look at the
following code:
var count = 0;
var button = document.theForm.myButton3;
The event handler in this code gets the <p/> element by using getElementById(), and then sets
its innerHTML to reflect the number of times the button has been clicked. This is absolutely the easi-
est approach. Otherwise you would have to acquire the event target, check to see if the target is the
<p/> element, and, if it’s not, search for that element in the DOM tree. So if you plan on using the
<button/> element, and you want to modify its contents, put an id attribute on the elements you
want to modify.
Even though you can modify a button in this way, chances are pretty good that the only modifica-
tion you’ll make to a button is disabling it. If you visit discussion boards often you may have noticed
something along the lines of a double post. A double post is caused when the user fills out the form,
clicks the submit button, and then clicks it again before the browser loads the server’s response from
the first submission. It’s a common problem wherever forms appear, and it can cause serious prob-
lems if the user double-posts a form to make a purchase.
You’ll look at how to disable a button in the following Try It section.
Try It
In this lesson, you learn about the button control, and the differences between creating them with
<input/> and <button/> elements.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
270 ❘ Lesson 27 Scripting Buttons
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson27 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson27 folder.
Step-by-Step
1. Type the following HTML:
<html>
<head>
<title>Lesson 27: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<input name="btnSubmit" type="submit" value="Submit" />
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
</script>
</body>
</html>
Save this as lesson27_example01.htm. The page consists of one <form/> named theForm.
It has an onsubmit event handler set to the validateForm() function. Inside the form is a
submit button named btnSubmit.
2. Now add the bold lines in the following code:
<html>
<head>
<title>Lesson 27: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<input name="btnSubmit" type="submit" value="Submit" />
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function validateForm(event) {
var button = document.theForm.btnSubmit;
button.disabled = true;
This code adds the validateForm() function, and it accepts an event object as an argument.
The first line of the function gets the submit button and stores it in the button variable. Then
you disable the button by assigning true to the disabled property. The last line of this code
simulates a validation error: It calls the confirm() method to ask if you want to simulate a
validation error. If you click OK, notValidated is true. If Cancel, notValidated is false.
3. Nothing happens when you click OK or Cancel in the confirm box at this point, so add the
following bold code:
<html>
<head>
<title>Lesson 27: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<input name="btnSubmit" type="submit" value="Submit" />
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function validateForm(event) {
var button = document.theForm.btnSubmit;
button.disabled = true;
if (notValidated) {
alert("Invalid data was found in the form. Please fix and submit
again");
button.disabled = false;
eventUtility.preventDefault(event);
}
}
</script>
</body>
</html>
This new code checks the value of notValidated. If it’s true (meaning a simulated valida-
tion error took place), an alert box tells the user that something is invalid and to fix the error.
After the alert box is dismissed, the button is re-enabled by having its disabled property set
to false, and the call to eventUtility.preventDefault() prevents the form from being
submitted.
Of course, if the Cancel button is clicked, meaning that notValidated is false, then the
form submits and reloads the page to simulate the validation of all fields.
In some of the future lessons, you build off this example and perform actual validation routines.
To get the sample code files, you can download Lesson 27 from the book’s website at www.wrox.com.
Please select Lesson 27 on the DVD to view the video that accompanies this
lesson.
28
Scripting Text Elements
If buttons are the most common form control, text-based input elements are a close second.
There are several types of text elements, making them the best tool for inputting data to sub-
mit to your web application. They are:
➤➤ The textbox
➤➤ The password textbox
➤➤ The hidden textbox
➤➤ The Multiline textbox
The differences between the textbox types are minimal. Let’s start with the simple textbox.
The Textbox
You create a textbox with the <input/> element and set its type attribute to the value of
text, like this:
<input type="text" name="textBox" />
This HTML creates a textbox named textBox. To access it in a form, you get the element
either by its id attribute (which this example does not have), or through the form it belongs to.
Assuming this textbox resides in a form called theForm, the following code gets a reference to
this textbox:
var txtBox = document.theForm.textBox;
When you create a textbox, you can set its value attribute to contain a text value. The value
attribute dictates what appears inside the textbox when the browser renders it. This attribute
274 ❘ Lesson 28 Scripting Text Elements
maps directly to the value property, so you can get or set the text in a textbox by using the value
property like this:
txtBox.value = "Some new data";
The contents of the value property are always a string, even if numeric characters are entered. So when
you expect a textbox to be used for numeric data, it’s always a good idea to convert the textbox’s value
to a number by using the parseInt() or parseFloat() methods. Not only does the conversion save
you from unwanted string concatenation operations (using the + operator), but it also helps you spot
invalid data.
In addition to the common properties and methods discussed in Lesson 26, textbox objects have a
method called select(); it selects all the text inside the textbox. When used in combination with
the focus() method, select() is useful when you want to direct a user to a particular textbox’s
value, such as when the user enters invalid data.
Also, textbox objects have more events than just the focus and blur events discussed in Lesson 26.
They have:
➤➤ change: Fires when the textbox loses focus and only if the text inside the textbox changed.
➤➤ select: Fires when a textbox’s text is selected (calling select() fires this event too).
➤➤ keydown: Fires when a key on the keyboard is pressed down.
➤➤ keyup: Fires when a pressed key on the keyboard is released.
➤➤ keypress: Fires when a key on the keyboard is pressed and released.
Password textboxes have the same methods and events as normal textboxes.
Hidden textboxes are created with the <input/> element, like this:
<input type="hidden" name="hiddenTextBox />
Because you use the <input/> element to create this textbox, it has the same properties, methods,
and events as the normal and password textboxes. Yes, hidden textboxes even have the keyboard
events, but good luck getting them to fire.
The cols and rows attributes determines the size of the <textarea/> element when rendered by the
browser, but CSS can be used to set more exact dimensions. These two attributes are available to
you in the DOM, but rarely, if ever, will you want to change them dynamically.
Like the other textboxes, the <textarea/> element object has a value property, and it gets and sets the
text between the opening and closing tags of the element. Also like the other textboxes, <textarea/>
objects have the select() method, as well as the change, keydown, keyup, and keypress events.
Try It
In this lesson, you learn about the various textbox controls. You also learn basic form validation
and how to cope with invalid data with the value property and select() and focus() methods.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
276 ❘ Lesson 28 Scripting Text Elements
Create a subfolder called Lesson28 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson28 folder.
Step-by-Step
1. Type the following HTML:
<html>
<head>
<title>Lesson 28: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
</p>
<p>
Age: <input type="text" name="txtAge" />
</p>
<p>
Email: <input type="text" name="txtEmail" />
</p>
<p>
Password: <input type="password" name="txtPassword1" />
</p>
<p>
Retype Password: <input type="password" name="txtPassword2" />
</p>
</script>
</body>
</html>
Save this as lesson28_example01.htm. The page consists of one <form/> named theForm.
It has an onsubmit event handler set to the validateForm() function. Inside the form are
several textboxes with which the user can input his or her name, age, e‑mail and password,
as well as a submit button.
2. The first bit of JavaScript you’ll write is a function called isEmpty(). It is bold in the following
code; add it to your file.
<html>
<head>
<title>Lesson 28: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
Try It ❘ 277
</p>
<p>
Age: <input type="text" name="txtAge" />
</p>
<p>
Email: <input type="text" name="txtEmail" />
</p>
<p>
Password: <input type="password" name="txtPassword1" />
</p>
<p>
Retype Password: <input type="password" name="txtPassword2" />
</p>
Its job is simple: Return a Boolean value based on whether the string is empty.
3. Now start writing the validateForm() function. Add the bold code in this sample:
<html>
<head>
<title>Lesson 28: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
</p>
<p>
Age: <input type="text" name="txtAge" />
</p>
<p>
Email: <input type="text" name="txtEmail" />
</p>
<p>
Password: <input type="password" name="txtPassword1" />
</p>
<p>
Retype Password: <input type="password" name="txtPassword2" />
</p>
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value);
button.disabled = true;
}
</script>
</body>
</html>
This is the beginning of the function. It defines a variety of variables, the majority of which
get the DOM object of each control in the form. The value of the txtAge textbox is converted
to a number, which is assigned to the final variable, age.
4. Now begin to validate the textboxes in the order in which they appear. Add the bold code:
<html>
<head>
<title>Lesson 28: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
</p>
<p>
Age: <input type="text" name="txtAge" />
</p>
<p>
Email: <input type="text" name="txtEmail" />
</p>
<p>
Password: <input type="password" name="txtPassword1" />
</p>
<p>
Retype Password: <input type="password" name="txtPassword2" />
</p>
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value);
button.disabled = true;
} else {
alert("Please enter your name.");
txtName.focus();
}
button.disabled = false;
eventUtility.preventDefault(event);
}
</script>
</body>
</html>
This is the basic pattern used to validate all the textboxes. The idea is to ensure that as one
textbox passes validation, the validation of the next one begins in the if block of the previ-
ous textbox’s validation check. Once the final validation check is performed and the check
passes, the function simply returns and the browser submits the form.
If any textbox fails validation, its else block sends a message to the user and gives focus to
the textbox. Then the button is re-enabled and the form is prevented from being submitted.
It’s a quick and easy way to validate a form.
5. Add the code to validate the age textbox. Use the isNaN() method to determine if what the
user entered was a number. Also check to make sure the number entered is greater than zero.
That code is bold here:
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value);
button.disabled = true;
280 ❘ Lesson 28 Scripting Text Elements
// validate age
if (!isNaN(age) && age > 0) {
} else {
alert("Please enter your age.");
txtAge.focus();
txtAge.select();
}
} else {
alert("Please enter your name.");
txtName.focus();
}
button.disabled = false;
eventUtility.preventDefault(event);
}
For printing, the HTML and isEmpty() function were omitted. This code adds the age
validation, and it looks similar to the code that validates the name textbox.
6. Now add code to validate the e‑mail address. Ensure that the @ symbol is present in the textbox’s
value, and that it isn’t the first character in the string. That code is bold here:
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value);
button.disabled = true;
// validate age
if (!isNaN(age) && age > 0) {
} else {
alert("Please enter a valid email address.");
Try It ❘ 281
txtEmail.focus();
txtEmail.select();
}
} else {
alert("Please enter your age.");
txtAge.focus();
txtAge.select();
}
} else {
alert("Please enter your name.");
txtName.focus();
}
button.disabled = false;
eventUtility.preventDefault(event);
}
The same thing as before happens if the validation test fails. An alert box asks the user to
enter a valid e‑mail address, and focus is given to the textbox.
7. Now add the code to validate the passwords. This is a two-step process: Make sure that one
of the password fields isn’t empty, and then make sure both fields have the same text. Add
the bold code:
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value);
button.disabled = true;
// validate age
if (!isNaN(age) && age > 0) {
} else {
alert("Password cannot be blank.");
txtPassword1.focus();
txtPassword1.select();
}
} else {
alert("Please enter a valid email address.");
txtEmail.focus();
txtEmail.select();
}
} else {
alert("Please enter your age.");
txtAge.focus();
txtAge.select();
}
} else {
alert("Please enter your name.");
txtName.focus();
}
button.disabled = false;
eventUtility.preventDefault(event);
}
It doesn’t matter which password field you check to see if it’s empty. This code checks the
first password field. If that check passes, the values of both password textboxes are com-
pared. If all validation rules are passed, the function returns and the browser submits the
form. If not, an alert box tells the user what he or she needs to do in order to enter valid
information, and the browser is prohibited from submitting the form.
To get the sample code files, you can download Lesson 28 from the book’s website at www.wrox.com.
Please select Lesson 28 on the DVD to view the video that accompanies this
lesson.
29
Scripting Selection Boxes
The HTML specification provides the <select/> element so page authors can add a menu of
options to a form. The menu the <select/> element generates can look like a drop-down list
from which you can select only one item, or like a list box that allows you to select multiple
items. These selection boxes have one or more <option/> elements inside the opening and
closing <select> tags. The following HTML shows an example:
<select id="dayOfTheWeek" name="dayOfTheWeek" size="5">
<option value="0">Sunday</option>
<option value="1">Monday</option>
<option value="2">Tuesday</option>
<option value="3">Wednesday</option>
<option value="4">Thursday</option>
<option value="5" selected="selected">Friday</option>
<option value="6">Saturday</option>
</select>
The number of options visible in the browser is determined by the <select/> element’s size
attribute, and it is this attribute that determines how the browser renders the <select/> element.
If size is greater than 1, the browser renders the element as a list box showing the specified num-
ber of options at a time. If size is 1 or isn’t specified, the browser renders the selection box as a
drop-down list.
The <select/> element DOM object has a property called options, a collection of <option/>
element objects. Every <option/> element inside the <select/> element is an item in the
options collection. So, using the previous <select/> element, you can access the <option/>
element for Monday, like this:
var dayOfWeek = document.getElementById("dayOfTheWeek");
This code first gets the <select/> element object from the DOM via the getElementById() method
(you can also access a <select/> element object through its containing form). Then, using the
options collection, the code gets the <option/> element object at the second position and assigns
the <option/> object to the mondayOption variable. The final line of this code uses the <option/>
object’s value property, which gets the value contained within the element’s value attribute — 1 in
this case.
You can get the option that is currently selected by combining the options collection with the
<select/> object’s selectedIndex property, like this:
var selectedDay = dayOfWeek.options[dayOfWeek.selectedIndex]; // Friday option
alert(selectedDay.value); // 5
Because the <option/> element for Friday has the selected attribute, it is rendered as the selected
option in the menu, and the <select/> object’s selectedIndex property gets the index of that option
in the options collection.
You can also use the selectedIndex property to change the selected option by assigning it the index
of the option you want to select. Let’s say you want Tuesday to be the selected day. The <option/>
element for Tuesday is at index position 2. So, to change the selected option to Tuesday, you could
use the following code:
dayOfWeek.selectedIndex = 2;
After this code executes, the browser changes the selected option from Friday to Tuesday.
After you have a reference to an <option/> object, you can retrieve two more pieces of information
about that option other than its value. You can use the index property of the <option/> to get its
position in the options collection, or you can use its text property to get or set new text associated
with it. Most of the time, however, you simply want its value.
As with most everything in the DOM, you’re not stuck with whatever is hardcoded into the HTML.
You can add and remove options from <select/> elements as you need to.
Removing Options
Options are extremely easy to remove from a selection box. To remove an option you simply assign
a value of null at the desired index position in the options collection, like this:
dayOfWeek.options[4] = null;
This code removes the Thursday option from the <select/> element. Fortunately, removing an
<option/> object from the options collection automatically reorders the remaining options by
shifting down the ones at higher index positions. Now the option for Friday is at index 4 and
Saturday is at index 5. You don’t have to write any code to reposition and reorder the remaining
<option/> elements; the DOM does it for you.
Adding Options ❘ 285
You can also remove an option with the <select/> object’s remove() method. It accepts a numeric
value as an argument specifying the index position you want to remove. So, instead of setting the
Thursday <option/> to null, you could do this:
dayOfWeek.remove(4);
Now that Thursday is gone from the week, the world is in disarray. We had better put it back…
Adding Options
Creating an <option/> element object is easy. Simply call the Option() constructor and pass it two
arguments:
var thursOption = new Option("Thursday", 4);
This code creates a new <option/> object. Its text is Thursday and its value is 4. Now that you have
created an option, it’s time to add it to the <select/> element. Unfortunately, adding options to a
<select/> object isn’t as straightforward as removing them.
One way that you can add a new <option/> object is by assigning the new object at a particular
index in the options collection. In doing so, however, you actually overwrite an existing option.
Look at the following code:
dayOfWeek.options[4] = thursOption;
This code assigns the new <option/> object to the element at index 4. You may remember that the
<option/> object at position 4 is now the Friday option. So what this code actually does is over-
write the Friday option with the new Thursday option. Oops. You can, however, add a new option
to the end of the options collection without overwriting anything, like this:
dayOfWeek.options[dayOfWeek.options.length] = thursOption;
This code uses the length property to assign the thursOption object to the very end of the options
collection — putting Thursday after Saturday. Oops again. While this may be fine in some cases, it
hardly makes sense to have a list of days out of order. You could write code to reorder the options
and put them in the correct order, but that requires a lot of extra code (and your author is lazy).
For this reason <select/> objects have a method called add(), which allows you to add an <option/>
object at a specified position. This method accepts two arguments: the option to add, and the existing
option before which you want to place it. So adding the new option for Thursday can look like this:
var fridayOption = dayOfWeek.options[4]; // Friday
dayOfWeek.add(thursOption, fridayOption);
This code gets the <option/> object for Friday, which is found at index position 4. Then a call to
add() adds the new <option/> for Thursday before the <option/> for Friday. All three affected
<option/> objects (Thursday, Friday, and Saturday) are reorganized and given new index positions.
The <option/> for Thursday is back at index 4, Friday is at index 5, and Saturday is at index 6.
286 ❘ Lesson 29 Scripting Selection Boxes
All is well again…unless you try to run this code in Internet Explorer (IE) 7 (or IE8 non-standards
mode). IE7 craps out (yes, that’s a technical term) when add() is called.
It’s not that IE7 (and IE8 in non-standards mode) doesn’t have the add() method for <select/>
objects. It does, but it expects the second argument to be the index at which you want to put the
new option. So for IE7 the code would look like this:
dayOfWeek.add(thursOption, 4);
This code inserts the new Thursday option at index position 4, and IE7 then reorganizes the options
collection, pushing the Friday and Saturday options to positions 5 and 6, respectively.
This is a problem, but it’s a problem with an easy solution: exception handling. This is actually a
topic covered in Lesson 37, and as such it won’t be given much coverage here. When a browser craps
out (again, it’s technical) because of JavaScript code, the JavaScript engine throws an exception. The
term “throw” is used throughout computer science, and in this case it means the engine essentially
generates an exception, or error, during the execution of a program.
Exceptions happen. They’re a part of programming, so a process called exception handling is used
to handle code that may throw an exception. The process goes something like this:
➤➤ You try to execute code that can cause an exception. If it works, great! If not, an exception is
thrown, and…
➤➤ You catch the exception and execute code that is typically safe to execute — such as code to
send a message to the user or achieve the same results as the code that caused the exception.
To put this process into code, you use the try...catch statement shown here:
try {
dayOfWeek.add(thursOption, fridayOption);
} catch (exceptionObject) {
dayOfWeek.add(thursOption, 4);
}
This code uses try...catch by trying to execute the add() method by passing the Friday
<option/> object. If that code throws an exception, code execution drops to the catch block and
executes add() by specifying the index position 4. Now, when the browser executes this code, the
new Thursday <option/> is placed in the correct position regardless of the browser used to execute
the code.
Don’t worry about the exceptionObject in the previous code. Exception han-
dling is covered in more detail in Lesson 37.
All right, so let’s apply some of this information to the form you’ve been working with since
Lesson 27.
Try It ❘ 287
Try It
In this lesson, you learn to script selection boxes by getting the selected option, getting data related
to that option, and adding and removing options.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson29 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson29 folder.
Step-by-Step
1. Type the following HTML:
<html>
<head>
<title>Lesson 29: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
</p>
<p>
Age: <input type="text" name="txtAge" />
</p>
<p>
Email: <input type="text" name="txtEmail" />
</p>
<p>
Password: <input type="password" name="txtPassword1" />
</p>
<p>
Retype Password: <input type="password" name="txtPassword2" />
</p>
288 ❘ Lesson 29 Scripting Selection Boxes
<p>
Your Favorite Day: <select id="dayOfTheWeek" name="dayOfTheWeek">
</select>
</p>
function validateForm(event) {
var theForm = document.theForm,
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value);
button.disabled = true;
// validate age
if (!isNaN(age) && age > 0) {
} else {
alert("Password cannot be blank.");
txtPassword1.focus();
txtPassword1.select();
}
} else {
alert("Please enter a valid email address.");
txtEmail.focus();
Try It ❘ 289
txtEmail.select();
}
} else {
alert("Please enter your age.");
txtAge.focus();
txtAge.select();
}
} else {
alert("Please enter your name.");
txtName.focus();
}
button.disabled = false;
eventUtility.preventDefault(event);
}
</script>
</body>
</html>
Save this as lesson29_example01.htm. It is almost the same as the HTML page at the end
of Lesson 28, but it now has a <select/> element with an id and name of dayOfTheWeek. It
is empty, so you need to write a function to populate the <select/> element with options.
2. Add a function called populateDays(), and inside the function create an array called days
containing the seven days of the week. Also add a line at the end of the <script/> element
to execute populateDays(). The new code is bold:
<html>
<head>
<title>Lesson 29: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
</p>
<p>
Age: <input type="text" name="txtAge" />
</p>
<p>
Email: <input type="text" name="txtEmail" />
</p>
<p>
Password: <input type="password" name="txtPassword1" />
</p>
<p>
Retype Password: <input type="password" name="txtPassword2" />
</p>
<p>
Your Favorite Day: <select id="dayOfTheWeek" name="dayOfTheWeek">
</select>
</p>
290 ❘ Lesson 29 Scripting Selection Boxes
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
// removed for printing
}
populateDays();
</script>
</body>
</html>
3. Now create a few more variables. Get the <select/> element by using the getElementById()
method and assign it to a variable called dayOfWeek. Also, create a variable to contain today’s
day by creating a new Date object and calling the getDay() method. The new code is bold:
<html>
<head>
<title>Lesson 29: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
</p>
<p>
Age: <input type="text" name="txtAge" />
</p>
<p>
Email: <input type="text" name="txtEmail" />
</p>
<p>
Password: <input type="password" name="txtPassword1" />
</p>
<p>
Retype Password: <input type="password" name="txtPassword2" />
</p>
<p>
Your Favorite Day: <select id="dayOfTheWeek" name="dayOfTheWeek">
</select>
</p>
Try It ❘ 291
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
// removed for printing
}
populateDays();
</script>
</body>
</html>
Notice the code for the today variable. The code new Date() is encapsulated within a pair
of parentheses. This is a trick used by professionals when they don’t need a particular object
for more than one piece of data. In the case of this script you don’t need anything from the
Date object except the value returned by getDay(). So an anonymous Date object is cre-
ated and the getDay() method is called on that anonymous object. Remember that getDay()
returns a numeric value associated with the day of the week. Sunday is 0, Monday is 1, Tuesday
is 2, and so on.
4. Loop through the days array and add an option for each day. Use the index variable i as an
option’s value:
<html>
<head>
<title>Lesson 29: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
</p>
<p>
Age: <input type="text" name="txtAge" />
</p>
<p>
Email: <input type="text" name="txtEmail" />
</p>
<p>
292 ❘ Lesson 29 Scripting Selection Boxes
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
// removed for printing
}
populateDays();
</script>
</body>
</html>
The bold code shows the for loop iterating over the days array. For each element in the
array a new Option object is created and added to the <select/> element. Note that the
length of the options collection is retrieved and passed as the second parameter to add().
This adds the new <option/> object at the end of the options collection. This code works
in all browsers, so you don’t have to worry about catching any exceptions.
5. Finally, set the selected option to today’s date using the selectedIndex property. New code
is in bold:
<html>
<head>
<title>Lesson 29: Example 01</title>
</head>
<body>
Try It ❘ 293
dayOfWeek.selectedIndex = today;
}
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
// removed for printing
}
populateDays();
</script>
</body>
</html>
294 ❘ Lesson 29 Scripting Selection Boxes
This new line of code uses the value contained within the today variable to select the cur-
rent day from the drop-down list.
Be sure to save the file and run it in any modern browser. You’ll find that it works like a
charm.
To get the sample code files, you can download Lesson 29 from the book’s website at www.wrox.com.
Please select Lesson 29 on the DVD to view the video that accompanies this
lesson.
30
Scripting Checkboxes and
Radio Buttons
Checkboxes and radio buttons are used in two different ways. You use radio buttons for a
list of multiple options that are mutually exclusive and from which users can select only one.
Checkboxes are for multiple options from which users can choose any number. Despite this
distinction, the primary purpose of both is to give users a number of options to choose from.
They also have something else in common: Their DOM objects share the same properties,
methods, and events. But scripting them can be a little different due to how they are represented
in the DOM. This lesson teaches you how to write JavaScript against these controls, starting
with the checkbox.
Scripting Checkboxes
You generate checkboxes by using the <input/> element and setting its type attribute to
checkbox, as shown in the following HTML:
<input type="checkbox" name="color" value="Red" checked="checked" />
This HTML creates a checkbox named color with a value of Red that is checked by default
because of the presence of the checked attribute. In the DOM, just as with all other form con-
trols, the name and value attributes are available to you as properties of the checkbox’s DOM
object. The checked attribute maps to a property of the same name, but the property’s value
is either true or false (as opposed to the checked attribute’s value in the previous HTML
code). So you can change the checked state of a checkbox by assigning a Boolean value to the
checked property, like this:
var chkBox = theForm.color;
chkBox.checked = false;
296 ❘ Lesson 30 Scripting Checkboxes and Radio Buttons
This code gets a reference of the checkbox DOM object (assuming it’s in a form called theForm) and
uses the checked property to uncheck the box. Scripting a checkbox individually, as in this example,
is a common approach to checkbox scripting. Sometimes you need to do something specific when a
particular checkbox is checked, and the action performed as a result of the click event depends on
the checkbox’s checked state. For example, you may want to enable or disable parts of a form based
on whether or not a particular checkbox is checked. You’ll do something similar in code later in this
lesson.
You can also group two or more checkboxes together by using the same name for each checkbox,
like this:
<input type="checkbox" name="color" value="Red" checked="checked" />
<input type="checkbox" name="color" value="Blue" checked="checked" />
<input type="checkbox" name="color" value="Green" checked="checked" />
This HTML adds two more checkboxes, and all three <input/> elements have the same name: color.
So this is a checkbox group named color. In the DOM, grouping checkboxes creates a NodeList of
checkboxes, and you access this NodeList by using the name of the checkbox group, like this:
var colorBoxes = theForm.color;
alert(colorBoxes.length); // 3
This code gets a reference to the color checkbox group and assigns it to the colorBoxes variable.
Remember from way back in Lesson 13 that a NodeList is an array-like object, and as such has a length
property. So the alert box shows a value of 3. The length property makes it possible to loop through
the NodeList and determine which checkboxes in the group are checked. Look at the following code:
var colors = [];
if (chkBox.checked) {
colors.push(chkBox.value);
}
}
This code loops through the checkboxes in the color group, determines which checkboxes are
checked, adds the value of the checked checkboxes to the colors array, and then displays the picked
colors to the user.
Radio buttons are almost always placed in radio button groups. You can script them as you would
script checkbox groups.
Try It ❘ 297
This HTML creates the radio button version of the color checkbox group from the previous section.
In it are three radio buttons, and users can choose only one of these items, since they are grouped
together.
Even though it is omitted in this HTML example, radio button controls have a checked attribute
and property, and you can write code like the checkbox group code from the previous section to
determine if a radio button in a particular group has been checked, like this:
var colorButtons = theForm.color;
if (rdoButton.checked) {
// do something with verified data
}
}
You use something similar to this code next in the Try It section.
Try It
In this lesson, you learn how to script single and multiple checkboxes, as well as radio button
groups. You learn that in addition to the standard form control properties, both checkboxes and
radio controls have a checked property to determine whether or not the control is checked.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
298 ❘ Lesson 30 Scripting Checkboxes and Radio Buttons
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson30 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson30 folder.
Step-by-Step
1. Type the following HTML and JavaScript:
<html>
<head>
<title>Lesson 30: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
</p>
<p>
Age: <input type="text" name="txtAge" />
</p>
<p>
Email: <input type="text" name="txtEmail" />
</p>
<p>
Password: <input type="password" name="txtPassword1" />
</p>
<p>
Retype Password: <input type="password" name="txtPassword2" />
</p>
<p>
Your Favorite Day: <select id="dayOfTheWeek" name="dayOfTheWeek">
</select>
</p>
<p>
Please choose your favorite color:
</p>
<p>
<input type="radio" name="color" value="Red" /> Red
</p>
<p>
<input type="radio" name="color" value="Blue" /> Blue
</p>
<p>
<input type="radio" name="color" value="Green" /> Green
</p>
<p>
</p>
<p>
<input type="checkbox" name="userAgree"
onclick=" userAgreeClick(event)" />
Try It ❘ 299
dayOfWeek.selectedIndex = today;
}
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
var theForm = document.theForm,
txtName = theForm.txtName,
txtAge = theForm.txtAge,
txtEmail = theForm.txtEmail,
txtPassword1 = theForm.txtPassword1,
txtPassword2 = theForm.txtPassword2,
button = theForm.btnSubmit,
age = parseInt(txtAge.value);
button.disabled = true;
// validate age
if (!isNaN(age) && age > 0) {
txtPassword1.focus();
txtPassword1.select();
}
} else {
alert("Password cannot be blank.");
txtPassword1.focus();
txtPassword1.select();
}
} else {
alert("Please enter a valid email address.");
txtEmail.focus();
txtEmail.select();
}
} else {
alert("Please enter your age.");
txtAge.focus();
txtAge.select();
}
} else {
alert("Please enter your name.");
txtName.focus();
}
button.disabled = false;
eventUtility.preventDefault(event);
}
populateDays();
</script>
</body>
</html>
Save this as lesson30_example01.htm. This is a modified version of the form you ended up
with at the end of Lesson 29. The bold code shows you what was added to the file. A radio
button group, called color, was added to the page, and a checkbox named userAgree was
added as well. It has an onclick event handler that calls the function userAgreeClick(),
which you write in the next step. Also, a disabled attribute was added to the submit button,
disabling it by default.
2. The checkbox’s purpose is similar to that of checkboxes on other forms you may have seen on
the Internet, where you must agree to a set of terms before submitting. When the user clicks
the checkbox, the submit button should be enabled. Write the userAgreeClick() function to
enable the button. It is bold in the following code:
<html>
<head>
<title>Lesson 30: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<!-- Removed for Printing -->
Try It ❘ 301
<p>
Please choose your favorite color:
</p>
<p>
<input type="radio" name="color" value="Red" /> Red
</p>
<p>
<input type="radio" name="color" value="Blue" /> Blue
</p>
<p>
<input type="radio" name="color" value="Green" /> Green
</p>
<!-- Removed for Printing -->
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript">
function populateDays() {
// removed for printing
}
function userAgreeClick(event) {
var eSrc = eventUtility.getTarget(event);
var button = theForm.btnSubmit;
button.disabled = !eSrc.checked;
}
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
// removed for printing
}
populateDays();
</script>
</body>
</html>
The first statement of this function gets the event target (the checkbox) and the form’s
submit button. Then the button’s disabled property is set to the opposite of whatever the
checkbox’s checked property is. When checked is true the button’s disabled property
should be false, and vice versa.
3. The color radio button group does not have a default option checked so you need to
write a function to ensure that users pick one of the three options. Name the function
validateColor(). It is bold in this code:
<html>
<head>
<title>Lesson 30: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
302 ❘ Lesson 30 Scripting Checkboxes and Radio Buttons
function userAgreeClick(event) {
// removed for printing
}
function validateColor() {
var colorButtons = document.theForm.color;
return false;
}
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
// removed for printing
}
populateDays();
</script>
</body>
</html>
The job of this function is simply to return true if one of the radio buttons is checked, and
false otherwise. A for loop loops through the radio button group and checks each radio
button’s checked property. If one is checked, the function returns true. If the loop iterates
over each color radio button without finding a checked one, the function returns false.
Try It ❘ 303
4. Now modify the validateForm() function to use the validateColor() function. Changes
are in bold in the following code:
function userAgreeClick(event) {
// removed for printing
}
function validateColor() {
var colorButtons = document.theForm.color;
return false;
}
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
var theForm = document.theForm,
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value);
å
button.disabled = true;
// validate age
if (!isNaN(age) && age > 0) {
// validate color
if (validateColor()) {
return;
} else {
304 ❘ Lesson 30 Scripting Checkboxes and Radio Buttons
} else {
alert("Passwords do not match. Please reenter them.");
txtPassword1.focus();
txtPassword1.select();
}
} else {
alert("Password cannot be blank.");
txtPassword1.focus();
txtPassword1.select();
}
} else {
alert("Please enter a valid email address.");
txtEmail.focus();
txtEmail.select();
}
} else {
alert("Please enter your age.");
txtAge.focus();
txtAge.select();
}
} else {
alert("Please enter your name.");
txtName.focus();
}
button.disabled = false;
eventUtility.preventDefault(event);
}
populateDays();
</script>
</body>
</html>
Since validateColor() returns a Boolean value, it’s used as the condition in the new if
statement. If it validates, validateForm() returns and the form submits. If not, an alert
box displays a message to the user asking him or her to pick a color.
Make sure you save the file, and run it in any browser to test the new functionality.
To get the sample code files, you can download Lesson 30 from the book’s website at www.wrox.com.
Please select Lesson 30 on the DVD to view the video that accompanies this
lesson.
31
An Introduction to Ajax
Web applications are sometimes considered inferior to conventional applications — t hat is,
applications that run natively on your chosen operating system. That mindset is easy to develop.
Imagine a conventional application that has a form that users can use to input data and have it
stored in a database. A host of features are common in such conventional applications, such as
real-time validation, seamlessly fast response times, and real-time error reporting.
Compare all that to a comparable web application. Users input data into the form and submit
it. There is some real-time validation that you can do with JavaScript, but often with such web
applications the server is responsible for comprehensive validation. The experience is disjointed
when users click the form’s submit button: The page has to reload with new data from the
server, so not only does the page flicker, but it has to be downloaded again. If an error occurs,
users don’t know it until the page is completely loaded in the browser.
There are a variety of factors that contribute to the differences between conventional and web
applications, but perhaps the largest hurdle web developers face is the HyperText Transfer
Protocol (HTTP). The Internet relies upon several components in order to function, and prob-
ably the most important component is HTTP, a request-response protocol. When you open a
browser and go to any website, your browser sends an HTTP request to the website’s server to
retrieve an HTML page. The server processes your request and sends an HTTP response back
to your browser. If the server is able to fill your request, the response contains the HTML of
the page you requested. As the browser finds images, CSS, and JavaScript files referenced in the
HTML, it has to make additional HTTP requests for those resources. If, for some reason,
the server is not able to fill your request, its response is a simple error code.
It is the request-response nature of HTTP that results in web applications that feel disconnected
and disjointed. Every time users click a link or a button in a form, the browser sends a request and
waits for a response from the server. While the browser waits, users typically see a blank web page.
Users with a broadband connection usually see a flicker of white before the browser loads the
requested HTML. Regardless, web applications, by default, still lack the seamless, real-time feel
of conventional applications.
306 ❘ Lesson 31 An Introduction to Ajax
Because of this, web developers in the early 2000s began to develop new techniques to make their web
applications behave more like conventional applications. These techniques enhanced the applications’
performance and usability by enabling them to send and receive data behind the scenes without reload-
ing the entire page.
At the very heart of these techniques was JavaScript and its capability to use hidden frames and a
component developed by Microsoft called XMLHttpRequest to make HTTP requests transparent to
users. Way back then, the techniques for making transparent HTTP requests were simply referred to
as remote scripting. Today they’re referred to as Ajax.
What Is Ajax?
The simplest definition of Ajax is the use of JavaScript to send and receive data using HTTP without
reloading the page. The term originates from an article written by Jesse James Garrett in February
2005 entitled “Ajax: A New Approach to Web Applications” (http://www.adaptivepath.com/
ideas/essays/archives/000385.php). In his article, Garrett stated that the gap between conven-
tional desktop applications and web applications was closing. He pointed to Google’s Suggest and
Maps as groundbreaking web applications because of their rich experiences and responsiveness.
In his article, Garrett also coined the term Ajax. It originally stood for Asynchronous JavaScript +
XML because, at the time, XML was the accepted format for client/server communication. XML is
still used, although other formats have proven more efficient. However, the term Ajax is now defined
as the use of JavaScript to request and receive data from the server without refreshing the page,
regardless of what format the data is in. It allows developers to create applications that behave like
rich and responsive conventional desktop applications.
A textual representation of Ajax is insufficient, so let’s look at some actual uses on the Web. Garrett’s
examples of Google’s Suggest and Maps are still relevant, so let’s look at them.
Google Suggest
Google Suggest is an application that stayed in Google Labs for quite some time before being
pushed into full-time service on the main search page. It offers suggestions for search terms as
users type. With each keystroke, Google Suggest uses Ajax by sending an HTTP request to the
server to get search suggestions based on the text contained within the search box. Figure 31-1
shows Suggest in action.
It’s fast and responsive, and it gives users a better experience when they’re using Google’s search —
all without reloading the page.
Google Maps
After Google purchased Keyhole and rebranded it as Google Earth, it quickly released a web-based
version called Google Maps (http://maps.google.com). Google Maps incorporates the use of Ajax,
as well as DHTML, to provide an experience rarely found in web applications. Look at Figure 31-2
for the default page of Google Maps.
What Is Ajax? ❘ 307
Figure 31-1
Figure 31-2
308 ❘ Lesson 31 An Introduction to Ajax
Entering a location in the textbox and clicking the Search Maps button does not reload the page.
Instead, Maps uses Ajax to send and receive data based on the search criteria and dynamically loads
the map images in the main area of the page.
The map area is completely interactive. You can drag the map around and zoom in and out using the
mouse’s scroll wheel. The responsiveness and interactivity rival those of the desktop-based Google
Earth. While DHTML techniques are used extensively, Google Maps’ success is due to Ajax.
Second, you need a webserver because Ajax relies on HTTP communication. The pages in which you
want to use Ajax must be served from an actual webserver; otherwise the HTTP requests will fail.
There are several webservers freely available to you, but two stand out and will suit your needs:
➤➤ The Apache HTTP Server (http://httpd.apache.org/)
➤➤ Microsoft’s Internet Information Services (IIS)
The Apache HTTP Server is the most popular webserver in the world. The majority of the websites
found on the Internet are served by Apache, and you can find a version for just about every operat-
ing system. Apache isn’t for the faint of heart, however. Configuring the server beyond the default
settings requires editing one or more text files. That being said, the default settings are generally fine
for developing applications on your computer.
Microsoft’s IIS has been freely available to users since Windows 2000, but only users of the Professional/
Business or higher versions of XP, Vista, and Windows 7 were able to install it and use it on their
machines. IIS is configured through a graphical user interface.
There is a version of IIS that runs on all major Windows operating systems (XP, Vista, and 7) called
IIS Express. At the time of this writing, it is only included in Microsoft’s WebMatrix beta; no stand-
alone version of IIS Express is available to download.
Requirements for Ajax ❘ 309
The choice is yours; either server will enable you to learn Ajax in the upcoming lessons.
To get the sample code files, download Lesson 31 from the book’s website at www.wrox.com.
Please check the DVD for Lesson 31: It walks you through the installation of
Apache on OS X and Windows and the installation of IIS.
32
Using XMLHttpRequest Objects
In Lesson 31 you learned that there are a variety of ways to incorporate Ajax into a web appli-
cation. The older technique of using hidden frames can still be found in many modern web
applications, but the most popular way to add Ajax functionality to an application is by using
the XMLHttpRequest (XHR) object. Despite its name, you can retrieve more than XML with
XHR. In fact, it’s more commonly used to retrieve plain text.
The XHR object originated as a component, called XmlHttp, in Microsoft’s MSXML
library. The component was first released with Internet Explorer (IE) 5. XmlHttp allowed
developers to make HTTP requests, using JavaScript or VBScript, to retrieve XML data.
Microsoft updated the MSXML library with every major version of IE, and each new ver-
sion included an improved XmlHttp component.
As the popularity of Microsoft’s XmlHttp grew, the developers behind Mozilla built their
own version (calling it XMLHttpRequest) for Firefox. Even though it had a different name,
the Mozilla developers copied the majority of the properties and methods of Microsoft’s
XmlHttp, making cross-browser use easier. Soon after, the developers of Opera and Safari
copied Mozilla’s XHR object, making transparent HTTP requests through pure JavaScript
possible in all modern browsers.
Even though IE7+ supports the XHR object and constructor, it actually creates
an XmlHttp object from the MSXML library. So while IE7+ does support the
XMLHttpRequest identifier, the actual object is not native to the browser.
Because XmlHttp is an ActiveX component, instantiating an XmlHttp object requires the creation of
an ActiveX object. Creating ActiveX objects consists of calling the ActiveXObject constructor and
passing it a string containing the ActiveX component you want to create, like this:
var xhr = new ActiveXObject("Microsoft.XMLHttp");
This code creates the first version of Microsoft’s XmlHttp. There are actually several versions, but
Microsoft recommends using one of the following:
➤➤ MSXML2.XmlHttp.6.0
➤➤ MSXML2.XmlHttp.3.0
You want to use the latest version possible because it will contain bug fixes and performance gains
over previous versions. Unfortunately it’s impossible to know if everyone has the latest version
installed, but you can write code that determines what version users do have, in order to create
appropriate XmlHttp objects.
Using XHR Objects ❘ 313
The idea behind this code is to loop through the two recommended version names and attempt to
create an XmlHttp object with each — starting with version 6 because it’s the latest version. If a user’s
computer does not have version 6 installed, the browser throws an error when you attempt to create
a version 6 XmlHttp object. You can catch that error and continue the loop to attempt the creation of a
version 3 object. The following code demonstrates this process:
var versions = [ "MSXML2.XmlHttp.6.0",
"MSXML2.XmlHttp.3.0" ];
The first statement in this code defines an array called versions, and each of its elements is a version
string for instantiating an XmlHttp ActiveX object. Next, a for loop iterates over the array, and inside
the loop, you see a try...catch block. Remember, the try block “tries” to execute code, and the
catch block catches an error if one is thrown from the execution of code in the try block.
This portion of the process is crucial, as the code attempts to create XmlHttp ActiveX objects based
on the current version number. The line that can fail is bolded in the previous code. If the code tries
to create a version 6 XmlHttp object and fails, the catch block catches the error and does nothing.
The loop then iterates and the code tries to create a version 3 object. When an XmlHttp object is
successfully created, the xhr variable contains an XmlHttp object. Otherwise it contains the value
undefined.
Later in this lesson, you write a function that combines both approaches to creating XHR and
XmlHttp objects. But before doing that, let’s get started looking at some of these objects’ features.
For the sake of simplicity, both XMLHttpRequest and XmlHttp objects will be
referred to as XHR objects unless a distinction between the two needs to be made.
This code initializes a GET request to be sent to retrieve a text file called info.txt using asynchronous
mode. The choice between asynchronous and synchronous mode is an important one because it deter-
mines how the XHR affects your application.
Asynchronous mode means that the XHR object sends a request, waits for a response, and processes
the response without interrupting the loading of the rest of the page or the execution of other scripts.
Synchronous mode means the browser stops and waits for the XHR object to send the request, receive
a response, and process the response before continuing to load the page or execute other scripts. The
mode you use can drastically alter the performance of your application.
Synchronous requests are simpler than asynchronous requests. With synchronous requests you initial-
ize the request, send it, and then process the server’s response. The problem is that synchronous
requests cause the browser to stop everything until it receives a response. In fact, users can get the
feeling that something is wrong with the page, and you don’t want that. Asynchronous mode is a
better choice even though it requires extra code and complexity.
When you make an asynchronous request the XHR object goes through five different stages called
ready states, which have numerical values. They are:
➤➤ 0: The object has been created but not initialized.
➤➤ 1: The object has been initialized but the request has not been sent.
➤➤ 2: The request has been sent.
➤➤ 3: A response has been received from the HTTP server.
➤➤ 4: The requested data has been fully received.
All XHR objects have a readyState property, and it contains one of the aforementioned numerical
values. When the readyState property’s value changes, the XHR’s readystatechange event fires
and calls the onreadystatechange event handler. The onreadystatechange event handler is usually
defined like this:
xhr.open("GET", "info.txt", true);
xhr.onreadystatechange = function() {
// more code here
};
Next you need to determine whether the requested data is completely available, because you can’t
work with data if you don’t have it. The ready state you’re primarily interested in is the fifth state,
which has a value of 4. In order to ensure you have the requested data, check the readyState prop-
erty’s value inside the onreadystatechange event handler, like this:
xhr.open("GET", "info.txt", true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
alert("We got a response");
Determining the Success of a Request ❘ 315
}
};
xhr.send(null);
The onreadystatechange event handler fires every time the value of readyState changes; therefore,
the function handling the event executes four times. In this code, an if statement checks to see whether
readyState is 4, meaning the request is complete. If it is, an alert box displays a message stating that
a response was received from the server. The final line of this code sends the request with the send()
method. You’ll learn more about send() in Lessons 33 and 34.
Even when an XHR’s ready state is 4 (fully complete), it is not guaranteed that you have the data
you wanted. An error may have occurred.
You may never actually see the status codes, but they exist behind the scenes and every HTTP-
enabled client can understand them. For example, when users go to Google’s homepage at http://
www.google.com, Google’s webserver sends a status code of 200 as part of its response to the
browser. This code is the standard response for successful HTTP requests. It literally means “OK.”
However, if users point their browsers to http://www.google.com/javascript/, Google’s web-
server responds with a status code of 404. This means that the specified resource could not be found
on the server (for example, it doesn’t exist).
You can determine whether or not your request was successful by using the XHR object’s status
property, which contains a three-digit HTTP response code, and comparing it to an HTTP response
code, like this:
if (xhr.status === 200) {
alert("Everything is OK.");
}
316 ❘ Lesson 32 Using XMLHttpRequest Objects
This code uses the status property to determine whether the request completed successfully by com-
paring the status property to the status code of 200. But this check is actually inadequate if you want
to determine the success of a request. The 200 status code works in most cases, but the status codes
from 200 to 299 are considered successful status codes. Additionally, status code 304 is considered a
success because it signifies that the requested resource has not changed since it was last requested. So,
in order to adequately determine the success of a request, your code should check if the response status
is within the 200s or is equal to 304, like this:
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304 ) {
alert("Everything is OK.");
} else {
alert("Something went wrong.");
}
This code first assigns the XHR’s status property to a variable called status. It then checks if
status is within the range of 200 through 299 or equal to 304. This way, all successful status codes
result in the message, “Everything is OK.” If the request results in any other status code, it is consid-
ered an error and the user receives the message “Something went wrong.”
Status code checking should be done for both asynchronous and synchronous
requests.
Security
There is no doubt that XHR adds a tremendous amount of power to your applications, but this
power does not come without limitations. XHR is a JavaScript component, and as such, it is bound
to the rules and limitations of the language — notably JavaScript’s security.
In order to thwart malicious coders, JavaScript cannot access scripts or documents from a differ-
ent origin — only scripts or documents from the same origin. The same-origin policy dictates that
two pages (or scripts) are from the same origin only if the protocol, port, and host are the same.
Consider the following two URLs:
http://www.wrox.com
http://www.wrox.com/WileyCDA/
These two URLs are of the same origin. They share the same protocol (HTTP), the same host (www
.wrox.com), and the same port (80 is default). Because these are of the same origin, JavaScript on
one page can access the other. Now look at the next two URLs:
http://www.wrox.com
https://www.wrox.com/WileyCDA/
Try It ❘ 317
These two URLs are not of the same origin because their protocols and ports don’t match (the default
HTTPS port is 443). So JavaScript on one of these pages cannot access the other page.
Why is this important? It’s important because Ajax relies on JavaScript. The same-origin policy
prohibits any JavaScript component, XHR included, to access any resource from another origin.
Developers get around this issue by writing a server-side service to act as a proxy to retrieve the
resource from another origin to return to the XHR object.
You’ve learned about a lot of the components of XHR objects in this lesson, so let’s combine them
all to give you a clearer picture of how they fit together.
Try It
In this lesson, you are introduced to several basic pieces of the XHR object: how to create one,
how to determine the object’s ready state for asynchronous mode, and how to determine whether a
request was successful. You combine all these pieces to create a foundation you use in later lessons.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Additionally, you need webserver software installed on your computer. Refer to Lesson 31 on the
DVD for installation instructions. Create a subfolder called Lesson32 in the root directory of your
webserver. Store the files you create in this lesson in the Lesson32 folder.
Step-by-Step
1. Write a function to create an XHR object for all browsers. Call the function createXHR(),
and have it return an XHR object. Its code follows:
function createXHR() {
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
} else {
var versions = [ "MSXML2.XmlHttp.6.0",
318 ❘ Lesson 32 Using XMLHttpRequest Objects
"MSXML2.XmlHttp.3.0" ];
return null;
}
This code first uses feature detection to determine whether the browser supports the
XMLHttpRequest identifier, and returns an XHR object if it does. The check for XHR
here is important because IE7+ can use either the XMLHttpRequest constructor or the
ActiveXObject constructor to create an XmlHttp object, and the XHR constructor in IE7+
is guaranteed to create the latest version of XmlHttp available. So the ActiveX code is used
only if the browser doesn’t support the XMLHttpRequest identifier.
2. Now call the createXHR() function and assign the resulting XHR object to a variable called
xhr. Then assign a function to the onreadystatechange event handler, as shown in the fol-
lowing code:
var xhr = createXHR();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
}
};
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) "" xhr.status === 304 ) {
// code for success goes here
} else {
alert("Something went wrong.");
}
}
};
Try It ❘ 319
By adding this code, you make the onreadystatechange event handler ready to use and
process the response from the server. It checks the XHR object’s ready state, and then it
checks to make sure a proper response was received from the server.
4. Save this file as lesson32_example01.js.
To get the sample code files, download Lesson 32 from the book’s website at www.wrox.com.
Please select Lesson 32 on the DVD to view the video that accompanies this
lesson.
33
Making Ajax Requests Using
GET
Open a web browser and go to http://www.google.com. You’ve just sent a GET request to
Google’s webserver. Now point your browser to http://www.google.com/search?q=JavaScript.
Once again you’ve sent a GET request to Google. Click any link in the search results. Yep, you
just sent a GET request.
The HTTP protocol defines nine HTTP methods (also known as verbs) that determine what
type of action is taken on the requested resource. The GET verb indicates that the client
requesting a resource simply wants to get the data that the resource contains. GET requests
are typically thought of as safe requests because they’re not meant to cause any change on the
server; they are simply used to request a resource’s data.
As mentioned in Lesson 32, there are two types of requests you can make with XMLHttpRequest
(XHR) objects: GET and POST. The majority of requests users make on the Internet are GET
requests. Similarly, the majority of requests you’ll make with XHR will be GET requests.
xhr.open("GET", "info.txt");
The first argument passed to open() tells the XHR object to make a GET request to the URL
specified as the second argument, and since the third argument is omitted, the request is sent
in asynchronous mode.
322 ❘ Lesson 33 Making Ajax Requests Using GET
It’s important to fully understand how XHR objects handle the URL you pass to open(). In the
case of the preceding code, the XHR object will attempt to find a file called info.txt in the same
directory that the code executes in. So, for example, if this code is executed within a web page
located at http://www.yourdomain.com/ajax/get_ajax.htm, the XHR object sends a request to
the http://www.yourdomain.com/ajax/info.txt URL. The URL passed to open() in the previ-
ous code is referred to as a relative URL — it is relative to the page currently loaded in the browser.
The other type of URL is an absolute URL. Absolute URLs contain the protocol, domain, and path
of the resource. The URL http://www.yourdomain.com/ajax/info.txt is an absolute URL, and
as long as you abide by the same-origin policy discussed in Lesson 32, you can use absolute URLs as
well. The following code uses the absolute URL for info.txt in this example:
var xhr = createXHR();
xhr.open("GET", "http://www.yourdomain.com/ajax/info.txt");
It doesn’t really matter if you use relative or absolute URLs. Absolute URLs are clear and less prone
to errors; relative URLs are shorter but can cause problems if you do not pay attention to where
resources are located in relation to the page.
After you initialize a request you want to handle the readystatechange event, check for ready state
4, and also ensure that the request didn’t fail. The following code does all that:
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
alert(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
This code looks similar to a snippet in Lesson 32, but there’s something different in the bolded line.
A new property of the XHR object, called responseText, is passed to the alert() method on a
successful request. The responseText property returns the body of the response — the requested
resource’s data in text format. So whatever text is contained within info.txt is displayed in the
alert box.
The final step in the process is to send the request, and you do so by calling the send() method. It
accepts one argument, which contains the data that makes up the request’s body. GET requests do
not send data, so you must pass null to the method, like this:
xhr.send(null);
Asynchronous Programming ❘ 323
After calling send(), the XHR object then monitors the readyState property and fires the
readystatechange event when its value changes.
Asynchronous Programming
After the send() method executes, the XHR object functions independently of JavaScript code that
may be executing after the call to send(). This is because the XHR object is operating in asynchronous
mode, and programming with asynchronous objects requires a different approach from regular pro-
gramming. Look at the following code:
var xhr = createXHR();
xhr.open("GET", "info.txt");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
// code removed
} else {
alert("An error occurred");
}
}
};
xhr.send(null);
alert(xhr.responseText); // wrong!!
This code makes a GET request for the info.txt file, as in the previous section, but there’s one little
change. Notice that the line of code calling alert() to display the contents of responseText was
moved out of the onreadystatechange event handler and placed after the call to send() (the changes
are in bold).
Depending on your browser, you’ll see either an empty alert box or an error. This is because the
browser continues to execute any JavaScript code found after the call to send(). When the final line
of code executes and attempts to get a value from the responseText property, the XHR object has
not yet received a complete response from the server. So in some browsers responseText is empty
and an alert box displays an empty string, while others throw an error.
Perhaps this behavior is better explained with a visual
aid. Picture an island in the ocean just off the mainland
(Figure 33-1).
The island is connected to the mainland via a one-way
bridge, and vehicles travelling on that bridge can travel
only from the island to the mainland. An asynchronous
XHR object is like this island, and the rest of the page’s
JavaScript code is like the mainland. All code relying on
the XHR object’s data is on the island; if code from the
324 ❘ Lesson 33 Making Ajax Requests Using GET
mainland tried to access code on the island, some kind of error would occur (like a wreck). But the
code on the XHR island can access code on the mainland whenever it needs to.
Because this particular request was made using asynchronous mode, all code relying on the request’s
response must be handled within the onreadystatechange event handler (on the island). A common
practice is to call a function (on the mainland) when the response is complete and pass the response
data as an argument in order to do something with that data. The following code demonstrates this:
function processResponse(data) {
alert(data);
}
xhr.open("GET", "info.txt");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
processResponse(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(null);
This code calls the processResponse() function with a successful request, and it makes the code
easier to read and maintain.
You can make asynchronous XHR requests even easier to read, maintain, and use by refactoring
your XHR code into a function. The only two pieces of information that can vary from request to
request are the URL and the function to call when the request is successful (also known as a callback
function). The following code defines a function called makeGetRequest():
function makeGetRequest(url, callback) {
var xhr = createXHR();
xhr.open("GET", url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(null);
}
Try It ❘ 325
The request code in makeGetRequest() is almost identical to the code in the previous example. The
only changes are that the url variable is in place where info.txt originally was, and that the call-
back function is invoked in where processResponse() was called. Using makeGetRequest() makes
asynchronous requests trivial, as shown in the following code:
function processResponse(data) {
alert(data);
}
makeGetRequest("info.txt", processResponse);
In this new code processResponse() stays unchanged, but a call to makeGetRequest() passes
info.txt and the processResponse() function as arguments. So now you can make a request
using one simple line of code and do something with the request’s data when the request completes
successfully.
Try It
In this lesson, you learn how to make GET requests using XHR objects and work with the data you
retrieve with them.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Additionally, you need webserver software installed on your computer. Refer to Lesson 31 on the
DVD for installation instructions. Create a subfolder called Lesson33 in the root directory of your
webserver. Store the files you create in this lesson in the Lesson33 folder.
Step-by-Step
1. Create a new text file and add exactly the following text to it:
Jeremy,Jason,Jeffrey
326 ❘ Lesson 33 Making Ajax Requests Using GET
Note that there are no spaces between the commas and names. Feel free to add more names.
Just make sure to separate them with commas and keep the names all in one line. Save this
text file as lesson33_example_data.txt.
2. Create another new file and type the following JavaScript code. Feel free to copy the createXHR()
function from lesson32_example01.js from Lesson 32:
function createXHR() {
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
} else {
var versions = [ "MSXML2.XmlHttp.6.0",
"MSXML2.XmlHttp.3.0" ];
return null;
}
xhr.open("GET", url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(null);
}
These are the functions you’ve created thus far to aid you in Ajax requests. Save the file as
xhr.js and place it in the Lesson33 directory.
3. Copy the eventUtility.js file either from the code download or from a previous lesson to
the Lesson33 directory.
Try It ❘ 327
</script>
</body>
</html>
This page has a form called theForm, and it has a textbox control named txtName, and a button
named btnSubmit. The eventUtility.js and xhr.js files are also added to the page via two
<script/> elements, with a third <script/> element that will contain inline JavaScript code.
5. The form has an onsubmit event handler that calls a function called validateName(). This
function should verify that the textbox is not empty, and it should make a request to retrieve the
lesson33_example_data.txt file. Type the following function inside the empty <script/>
element:
function validateName(event) {
var nameToVerify = document.theForm.txtName.value;
var button = document.theForm.btnSubmit;
eventUtility.preventDefault(event);
}
If the textbox is empty, an alert message tells the user to enter a name. Otherwise, a call to
makeGetRequest() requests the text file and calls a function called processData() on a
successful request.
The button is also disabled before the request is sent. Latency is an issue when making
HTTP requests, and the user can generate many requests by repeatedly clicking the button.
By disabling the button when a request is made, you ensure that only one request can be
sent at any given time.
The event utility’s preventDefault() method prevents the form from being submitted.
328 ❘ Lesson 33 Making Ajax Requests Using GET
6. Write the processData() function. It should split the data contained in the text file into
an array and determine whether the name entered into the textbox has a match in the file.
It should also tell the user whether or not a match was found. After everything’s been pro-
cessed, enable the button once again. Type the following function inside the inline <script/>
element:
function processData(data) {
var names = data.split(",");
var nameToVerify = document.theForm.txtName.value;
var button = document.theForm.btnSubmit;
var nameFound = false;
if (nameFound) {
alert("That name is taken.");
} else {
alert("That name isn't taken");
}
button.disabled = false;
}
The names in the text file are separated by commas, so the String data type’s split()
method splits the string using a comma as a delimiter. Next a for loop iterates over the
names array and compares each element in the array to the value contained within the
textbox. Both values are converted to their lowercase equivalents to make the search case-
insensitive. If a match is found in the array, the nameFound variable is set to true and the
loop ends. Otherwise, the loop continues to iterate over each element until a match is found
or the loop exits.
After the loop exits, the function displays a message to the user. The contents of the message
depend on nameFound’s value. Finally, the button is re-enabled to allow the user to search
for another name.
Save the file as lesson33_example01.htm and point your browser to http://localhost/
Lesson33/lesson33_example01.htm. Begin typing names into the textbox and pressing
the button to see if the name you typed is found in the text file.
To get the sample code files, download Lesson 33 from the book’s website at www.wrox.com.
Please select Lesson 33 on the DVD to view the video that accompanies this
lesson.
34
Making Ajax Requests Using
POST
If GET requests make up the majority of HTTP requests on the Web, POST requests come in
second. POST requests are typically generated by forms. While not every form sends its data
using the POST method, many are used to POST information to the server.
POST requests are the exact opposite of GET requests: They are used to submit data to a web
application so the application can do something with that data. The request’s data is sent in
the request’s body, unlike with GET requests, in which data can be sent in the URL’s query
string. Despite these differences, making POST requests with XMLHttpRequest (XHR) objects
is strikingly similar to making GET requests. There are, of course, some differences, and you’ll
learn about them in this lesson.
Assembling Data
POST requests are designed to send information from the browser to the server. Because of
this the majority of POST requests are made through forms. Forms are the primary means
by which users input and send data. The difference with Ajax, however, is in how that data
is sent.
Without Ajax, the browser is responsible for gathering the form’s data, formatting it, and
POSTing it to the server. With Ajax, that responsibility falls to you. So before sending a POST
request you must assemble the form’s data and format it.
Let’s look at an example form:
<form name="theForm" method="post" action="ajax_test.php">
<p>
Name: <input type="text" name="txtName" value="" />
</p>
<p>
330 ❘ Lesson 34 Making Ajax Requests Using POST
This form has two textboxes: one for a name and one for an e‑mail address. It also has a button to
submit the form, and the form POSTs to a file on the server called ajax_test.php.
If a user enters the value Jeremy in the name textbox and jeremy@xyz in the e-mail textbox, the
browser assembles the data into the following format when the user clicks the submit button:
txtName=Jeremy&txtEmail=jeremy%40xyz.com&btnSubmit=Submit
This looks very much like the query portion of a URL. There are three name/value pairs separated
by ampersands (&). They are:
➤➤ txtName=Jeremy
➤➤ txtEmail=jeremy%40xyz.com
➤➤ btnSubmit=Submit
On the left side of each equals sign (=) is the name of a form control from the form, and on the right
side is the value of that control. Notice the value of txtEmail. Instead of jeremy@xyz.com, it’s
jeremy%40xyz.com. The @ symbol is a special character that has its own meaning in HTTP, and it
must be encoded before being transmitted as part of a request’s body. Other special characters are:
➤➤ , (comma)
➤➤ /
➤➤ ?
➤➤ :
➤➤ &
➤➤ =
➤➤ +
➤➤ $
➤➤ #
Assembling Data ❘ 331
When you are POSTing data to a server with XHR, the request’s body must be in this format.
Having to assemble and format all the information contained within a form may sound daunting, but
a function can do it all for you. Look at the beginnings of a function called getRequestBody() in the
following code:
function getRequestBody(form) {
var pieces = [];
var elements = form.elements;
return pieces.join("&");
}
This function accepts one argument, a <form/> element object. So you can pass the function any form
and have it generate the body for a request. The first two statements in this function create two vari-
ables. The first is an array called pieces, and each element in this array will be a name/value pair. The
second variable, elements, is assigned the form’s elements collection property. The final line of this
function returns the result of calling the pieces array’s join() method.
Next, the function loops through the elements collection and constructs a name=value string for
each form control. The new code is in bold:
function getRequestBody(form) {
var pieces = [],
elements = form.elements;
return pieces.join("&");
}
Inside the for loop three new variables are initialized. The first variable, element, contains the form
control element object as the specified index. The second variable, name, is assigned an encoded ver-
sion of the element’s name property by passing the un-encoded property to the encodeURIComponent()
function. The encodeURIComponent() function is a built-in function that encodes special characters
into their transmittable equivalents. The third variable, value, is assigned an encoded version of the
form control’s value property. Finally, the name and value variables are concatenated and added to
the pieces array.
Now you can assemble and format a form’s contents by simply calling this function and passing it
the <form/> element object you want to send, like this:
var requestBody = getRequestBody(document.theForm);
332 ❘ Lesson 34 Making Ajax Requests Using POST
This code passes the form with the name theForm to the getRequestBody() function to generate
properly formatted data and assign it to the requestBody variable.
xhr.open("POST", "ajax_test.php");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
processResponse(xhr.responseText); // callback function
} else {
alert("An error occurred");
}
}
};
xhr.send(data);
The first major change is the addition of the data variable, and it contains a formatted string of
name/value pairs to send in the request. The next big change is in the call to the open() method:
The first argument is now POST instead of GET. After the call to open() is a new line of code that
calls the XHR object’s setRequestHeader() method. This method allows you to set an HTTP
header with a supplied value. In the case of POST requests you need to set the Content-Type header
with the value application/x-www-form-urlencoded. Otherwise the request will not work. The
last major change is in the call to send(). Instead of passing null you pass the data generated by
getRequestBody().
You can simplify POST requests by writing a function like the makeGetRequest() function you
wrote in Lesson 33. The following code defines a function called postFromForm():
function postFromForm(url, form, callback) {
var xhr = createXHR();
var data = getRequestBody(form);
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(data);
}
This function accepts three arguments: the URL to request, the <form/> object that you want to
gather data from, and the callback function to execute when the request completes successfully. So
using this function means writing something like the following code:
function processResponse(responseData) {
alert(responseData);
}
Here, a POST request containing the data from a form named theForm is sent to ajax_text.php,
and on a successful request, the processResponse() function processes the server’s response.
Try It
In this lesson, you learn how to make POST requests using XHR objects by formatting the request
body’s data, supplying the Content-Type header, and sending data with the request.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
334 ❘ Lesson 34 Making Ajax Requests Using POST
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Additionally, you need webserver software and PHP installed on your computer. Refer to Lesson 31
on the DVD for installation instructions. Create a subfolder called Lesson34 in the root directory of
your webserver. Store the files you create in this lesson in the Lesson34 folder.
Step-by-Step
1. Copy the ajax_post.php file from the code download to the Lesson34 directory. This is a
PHP file that generates random messages based on the information POSTed to it.
2. Write an Ajax utility object. Create a new file called ajaxUtility.js and type the
following:
var ajaxUtility = {
createXHR : function() {
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
} else {
var versions = [ "MSXML2.XmlHttp.6.0",
"MSXML2.XmlHttp.3.0" ];
return null;
},
xhr.open("GET", url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
Try It ❘ 335
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(null);
},
getRequestBody : function(form) {
var pieces = [];
var elements = form.elements;
return pieces.join("&");
},
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(data);
}
};
This code creates a new object called ajaxUtility. Its createXHR() and makeGetRequest()
methods resemble the functions of the same name from the xhr.js file. The other two meth-
ods, getRequestBody() and postFromForm(), are the same as the two functions of the
same name from this lesson. Creating a utility like this is advantageous because it keeps all
code grouped together in the ajaxUtility object.
336 ❘ Lesson 34 Making Ajax Requests Using POST
3. Copy the eventUtility.js file from the Lesson33 directory to the Lesson34 directory.
4. Type the following HTML:
<html>
<head>
<title>Lesson 34: Example 01</title>
</head>
<body>
<form name="theForm" method="post" action="ajax_post.php"
onsubmit="addUser(event)">
<p>
Name: <input type="text" name="txtName" value="" />
</p>
<p>
Email: <input type="text" name="txtEmail" value="" />
</p>
<p>
<input type="submit" name="btnSubmit" value="Submit" />
</p>
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript" src="ajaxUtility.js"></script>
<script type="text/javascript">
</script>
</body>
</html>
This page contains the same form from earlier in this lesson, but it POSTs to ajax_post.php
and has an onsubmit event handler. The eventUtility.js and ajaxUtility.js files are
also added to the page via two <script/> elements, with a third <script/> element that will
contain inline JavaScript code.
5. The form’s onsubmit event handler calls the addUser() function. This function should verify
that the form is filled out and should POST to the ajax_post.php file. Type the following
function inside the empty <script/> element:
function addUser(event) {
var theForm = document.theForm;
var name = theForm.txtName.value;
var email = theForm.txtEmail.value;
var button = theForm.btnSubmit;
button.disabled = true;
eventUtility.preventDefault(event);
}
Try It ❘ 337
This function first disables the submit button to prevent the user from submitting more than
one request at a time. Then the function determines if the textboxes contain information and
asks the user to complete the form if either textbox is empty. If both textboxes contain data you
pass the theForm object to the ajaxUtility.postFromForm() method to send the request.
Instead of the string literal ajax_post.php’s being passed to the postFromForm() method,
the form’s action property is used instead. This is a nice little technique that allows your
XHR object to POST data to whatever URL is specified within the <form/> element’s action
attribute. So if you change the value of the action attribute you don’t have to change the URL
in the call to postFromForm().
Finally, the event utility’s preventDefault() method prevents the form from being
submitted.
6. Write the processData() function. All it needs to do is alert the response data and re-enable
the button. Type the following function inside the inline <script/> element:
function processData(data) {
var button = document.theForm.btnSubmit;
alert(data);
button.disabled = false;
}
Please select Lesson 34 on the DVD to view the video that accompanies this
lesson.
35
JSON
In Lessons 33 and 34, you sent a request to the server and received data that you did some-
thing with; in Lesson 33, you retrieved a list of names separated by commas and parsed that
data to make a functional application. Lesson 34 had you make a POST request to the server,
which responded with text that you displayed in an alert box. The data returned from the server
in those two lessons was simple, and you will rarely deal with such a simplistic set of data in a
real-world application. So this lesson introduces you to an interchange format suitable for repre-
senting complex data. It is today’s standard in Ajax communication: JavaScript Object Notation
(JSON). First, let’s take a look at how JSON came about.
<person>
<firstName>John</firstName>
<lastName>Doe</lastName>
<address>
<street>1234 XYZ</street>
<city>Dallas</city>
<state>Texas</state>
<country>USA</country>
</address>
<phoneNumbers>
<phoneNumber type="home">111-123-4567</phoneNumber>
<phoneNumber type="cell">111-234-5678</phoneNumber>
340 ❘ Lesson 35 JSON
</phoneNumbers>
<company><![CDATA[John & Joe's Lobster House]]></company>
</person>
XML’s strict rule set and the ability to create your own formats make it a popular choice for sharing
text-based data such as RSS feeds. XML is used everywhere. In fact, you’d be hard pressed to find
a web service or a programming interface running on a remote server that doesn’t send and receive
data in XML.
Naturally, the use of XML as an interchange format trickled into browser/server communication. It
seemed perfect for the browser because XML documents could be loaded into a DOM, and develop-
ers could traverse the XML document’s tree to get the data they needed. As Ajax gained in popularity,
XML was the de facto format for exchanging data (remember, the x in Ajax stands for XML).
While XML is great for sending and receiving data between networks and computer systems, there
are some problems when it comes to using it as a data interchange format with the browser. First,
XML documents are inherently verbose because XML is a markup language. Refer back to the
sample XML document earlier in this section. Every XML document has an XML declaration at the
top of the file, and each element containing data must have a beginning and ending tag. In addition,
data containing XML’s special/reserved characters has to be represented as character data, as shown
in the <company/> element in this XML document.
The verbose nature of XML makes this simple document weigh in with a size of 507 bytes.
Admittedly, 507 bytes isn’t very large, but imagine a document describing over 50 people. The file
could get very large fast. File sizes are directly linked to a web application’s performance. You want
to squeeze as much performance out of your application as possible, and smaller file sizes can help
with that.
The second problem with XML is the DOM API. The DOM was developed as a means of program-
matically representing structured documents, like HTML and XML. In order for developers to work
with an XML document in JavaScript, the document is loaded into its own DOM object (independent
from the HTML page’s DOM object). JavaScript developers work with DOMs all the time, but the
API is cumbersome and unfriendly. For example, the code to directly access the <country/> element’s
value (in the <address/> element) looks like this in IE:
doc.documentElement.childNodes[2].childNodes[3].nodeValue
JavaScript Object Notation Saves the World ❘ 341
This code uses doc as the top-level DOM object (much as document is the top-level DOM object of
HTML pages), documentElement gets the <person/> element, childNodes[2] gets the <address/>
element, childNodes[3] accesses the <country/> element, and the nodeValue property gets the
value contained within the element. That’s a lot of code to get to one element’s value, and it’s cryptic,
making maintenance difficult.
The previous code listing is actually incorrect if the XML document is loaded
into a standard XML DOM object. Code for accessing the <country/> element
in standard XML DOM objects are actually more complicated.
So XML is inefficient for data interchange for JavaScript developers. What, then, is the alternative?
This code uses object and array literals to describe the same data as the XML in the previous sec-
tion. The object is called person, and fulfills the same purpose as the <person/> root element in the
XML file. The <firstName/>, <lastName/>, and <address/> elements are converted to properties
of the person object. Next, <phoneNumbers/> is converted to an array literal, and each item in the
array is an object representing a <phoneNumber/> element.
Now, this is a JavaScript object, not JSON. The primary difference between JavaScript object/array
literals and JSON is that JSON does not have variables, properties, methods, or any other construct
of the JavaScript language. JSON is a text format meant to represent data only, so the JSON equiva-
lent of this JavaScript code is the following:
{
"firstName" : "John",
"lastName" : "Doe",
"address" : {
"street" : "1234 XYZ",
"city" : "Dallas",
"state" : "Texas",
"country" : "USA"
},
"phoneNumbers" : [
{
"type" : "home",
"number" : "111-123-4567"
},
{
"type" : "cell",
"number" : "111-234-5678"
}
],
"company" : "John & Joe's Lobster House"
}
This is called a JSON structure. Notice that the property names become string values: JSON is seri-
alized JavaScript objects and arrays. Serialization is the process of converting objects into text, and
serialization (along with deserialization — converting text into objects) is what makes JSON a suit-
able transmission format for JavaScript developers. JSON is advantageous for JavaScript developers
for two reasons:
➤➤ JSON data is smaller than XML data, and it gets even smaller when you remove all unnec-
essary whitespace. The JSON data in this example is 462 bytes, and you can shrink that to
256 bytes. Even when compressed into one line, the XML equivalent is still larger at 399 bytes.
➤➤ Deserializing JSON means you get a JavaScript object that you can use in your program. So
after you deserialize the previous JSON and assign the resulting object to a variable called
person, you can easily find out what country John Doe lives in by using the following code:
var country = person.address.country;
These two reasons, size and ease of use, make JSON perfect for JavaScript developers. So let’s look
at how you can serialize to and deserialize from JSON.
Converting to and from JSON ❘ 343
JSON support doesn’t end with these browsers, however. Before JSON support was built into
JavaScript, Crockford wrote a JavaScript script that can be used in any browser. You can find this
script at www.json.org/json2.js or in the code download accompanying this book at www.wrox
.com. This utility creates a JSON object in browsers that do not support the native JSON object, and
its functionality exactly mirrors that of the native JSON object. So until all older browsers are phased
out, it’s a good idea to include the json2.js file in any page in which you plan on using JSON.
The JSON object has two methods:
➤➤ parse(): Deserializes JSON to create a JavaScript object or array.
➤➤ stringify(): Serializes a JavaScript object or array into JSON.
First let’s look at the stringify() method. It accepts a JavaScript value as an argument and returns
a string containing the JSON text. To get an idea of how stringify() works, let’s imagine that you
work at a car dealership, and that one of your duties is to write a web application that allows visitors
to search for vehicles based on make, model, and year. Part of your code creates a data type called
Car. Following is its definition:
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
The constructor has three parameters: make, model, and year. It uses these values to populate the
make, model, and year properties that Car objects will have. Now let’s simulate a user’s input by
creating a Car object with some data and passing it to the JSON.stringify() method, like this:
var toyotaCamry = new Car("Toyota", "Camry", 2002);
The first line of this code creates a Car object representing a 2002 Toyota Camry and assigns it to the
toyotaCamry variable. In the next line the toyotaCamry object is passed to the JSON.stringify()
344 ❘ Lesson 35 JSON
method, and the resulting string is returned to the carJson variable. This gets you the same result as
doing the following:
var carJson = '{"make":"Toyota","model":"Camry","year":2002}';
Notice that the number 2002 is not a string. By design, the JSON.stringify() method does not
serialize numeric or Boolean values, in order to preserve their value.
Once you have your data in JSON form, you can do whatever you want with it. More than likely,
though, you serialized an object to send it to the server, and you can do so by adding the following
method to the ajaxUtility object:
var ajaxUtility = {
// other methods snipped for printing
xhr.open("POST", url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(data);
}
};
Unlike POSTing data with the ajaxUtility.postFromForm() method you wrote in the last lesson,
POSTing JSON doesn’t require the application/x-www-form-urlencoded Content-Type header.
So to send the JSON in carJson with this new method, all you need to do is this:
ajaxUtility.makePostRequest(serverUrl, carJson, processResponse);
While there are times when you will want to send data to the server using JSON because of the
data’s complexity, the simple data in this example would be better sent using the ajaxUtility
.postFromForm() method.
Let’s imagine that you sent the car query in a request to a server application that determines if the
specified car is in stock. The server processes the request and responds with the following JSON:
{
"searchedCar" : {
"make" : "Toyota",
"model" : "Camry",
"year" : 2002
},
Converting to and from JSON ❘ 345
"isFound" : false,
"results" : [
{
"make" : "Toyota",
"model" : "Camry",
"year" : 2005
},
{
"make" : "Honda",
"model" : "Accord",
"year" : 2002
}
]
}
This JSON structure has three primary pieces of data. First it contains the searchedCar object
structure that contains the make, model, and year that were searched for. Next it contains a struc-
ture called isFound containing a Boolean value determining whether or not the searched-for car
was found. Last is a structure, called results, that contains a list of cars that the user might find
suitable.
Of course, the data in this format isn’t overly helpful to you, so you want to deserialize it using the
JSON.parse() method. Now, your request was sent via the ajaxUtility, so the following function
was executed when the request completed successfully:
function processResponse(data) {
var results = JSON.parse(data);
In the processResponse() function, the first line calls the JSON.parse() method, passing it the
value of the data parameter, and assigns the resulting object to the results variable. Now you have
an actual JavaScript object than you can use in your code, so you can easily provide the user with
information regarding his or her query, like this:
function processResponse(data) {
var results = JSON.parse(data);
if (results.isFound) {
// do something with the matched data
} else {
// do something to let the user know no match was found
// do something with the suggestions
}
}
By parsing JSON into a JavaScript object you gain immediate access to the data the JSON structure
contained in your JavaScript code, and you don’t have to traverse a DOM to get it.
The ease of transforming JSON into JavaScript makes JSON the most popular means of transporting
data to and from a JavaScript application. Let’s expand on this car dealership scenario in the next
section.
346 ❘ Lesson 35 JSON
Try It
In this lesson, you learn how to leverage JSON to send, receive, and work with complex data in
JavaScript.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users,
Notepad is available by default on your system or you can download Microsoft’s free Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/).
Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Additionally, you need webserver software and PHP installed on your computer. Refer to Lesson 31
on the DVD for installation instructions. Create a subfolder called Lesson35 in the root directory
of your webserver, and copy the eventUtility.js and ajaxUtility.js files into it. Also copy the
car_dealership.php and json2.js files from the code download and paste them in the Lesson35
folder. Store the files you create in this lesson in the Lesson35 folder.
The car_dealership.php file responds with one JSON structure. The server’s response is as
follows:
{
"searchedCar" : {
"make" : "value",
"model" : "value",
"year" : numericValue
},
"isFound" : booleanValue,
"results" : [
{
"make" : "value",
"model" : "value",
"year" : numericValue
},
// more object structures if necessary
]
}
This JSON structure defines data containing the search query, a Boolean value indicating whether
or not a match was found, and an array structure called results containing one or more car-like
object structures, each having make, model, and year information.
Try It ❘ 347
Step-by-Step
1. Open your text editor, and then type the following:
<html>
<head>
<title>Lesson 35: Example 01</title>
</head>
<body>
<div id="divResults">
<div id="divSearchMessage"></div>
<div id="divSearchResults"></div>
</div>
<form name="theForm" method="post" action="car_dealership.php"
onsubmit="submitForm(event);">
<p>
Make: <input type="text" name="txtMake" value="" />
</p>
<p>
Model: <input type="text" name="txtModel" value="" />
</p>
<p>
Year: <input type="text" name="txtYear" value="" />
</p>
<p>
<input type="submit" name="btnSubmit" value="Submit" />
</p>
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript" src="ajaxUtility.js"></script>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript">
function submitForm(event) {
var data = ajaxUtility.getRequestBody(document.theForm);
ajaxUtility.makeGetRequest("car_dealership.php?" + data,
processResponse);
eventUtility.preventDefault(event);
}
function getCarString(carObj) {
return [carObj.year, carObj.make, carObj.model].join(" ");
}
function processResponse(data) {
</script>
</body>
</html>
348 ❘ Lesson 35 JSON
if (result.isFound) {
message = "We found " + result.results.length + " matches for " + carStr;
} else {
message = "We could not find " + carStr + ". You might like: ";
}
messageDiv.innerHTML = message;
}
This new code parses the JSON data and assigns the resulting object to the result variable,
and a reference to the <div/> element with an id of divSearchMessage is retrieved from
the document and assigned to the messageDiv variable. Another variable, called message,
is set to an empty string. It will contain a message to tell the user how the search went. Then a
variable called length is assigned the length of the result.results array. Finally, carStr is
assigned the return value obtained by calling getCarString() with the result.searchedCar
property passed as an argument.
Next some code using the result object’s isFound property determines if a match was found,
and an appropriate message is constructed and displayed in the <div/> element for messages.
3. The processResponse() function still needs to display a list of suggestions for searches
without a match. Add the bold lines in the following code:
function processResponse(data) {
var result = JSON.parse(data);
var messageDiv = document.getElementById("divSearchMessage");
Try It ❘ 349
if (result.isFound) {
message = "We found " + length + " matches for " + carStr;
} else {
message = "We could not find " + carStr + ". You might like: ";
resultsDiv.innerHTML = results.join("<br/>");
}
messageDiv.innerHTML = message;
}
This new code adds two new variables: resultsDiv and results. The resultsDiv variable
points to the <div/> element with an id of divSearchResults, and the results variable is
an array.
The next new code is a for loop that loops through the result.results array. Each element
in this array is passed to the getCarString() function, and the resulting string value is pushed
into the results array.
When the loop exists, the results.join() method is called, concatenating the elements of
the results array and separating each element with a <br/> element. The resulting string is
assigned to the resultsDiv.innerHTML property to display the list of cars on the page.
4. Save the file again and point your browser to http://localhost/Lesson35/lesson35_
example01.htm. Fill out the form, submit it, and watch the random results.
To get the sample code files, download Lesson 35 from the book’s website at www.wrox.com.
Please select Lesson 35 on the DVD to view the video that accompanies this
lesson.
Handling Errors, Debugging,
and Best Practices
Undefined Variables
JavaScript goes out of its way to be accommodating to developers. It inserts semicolons at the
ends of statements if you omit them, and it allows you to assign a value to a variable without
first declaring the variable with the var keyword. For example, the following code creates a
new global variable called myVariable and initializes it with a string value:
myVariable = "Hello, Global!";
While this code executes without any type of error, it is considered bad practice to omit the
var keyword from variable declarations. Omitting the var keyword has an effect on the vari-
able’s scope — namely, making the variable global. So always use var when declaring a new
variable.
Using a variable before it has been given a value results in an error. For example, the following
code attempts to use a variable before it has been initialized:
alert(someVariable); // error
354 ❘ Lesson 36 Common Coding Mistakes
Always make sure to initialize a variable with a value before attempting to use it.
Similarly, be sure to pass arguments to functions that require a value. Consider the following code:
function addByTwo(num) {
return num + 2;
}
alert(result); // NaN
This code defines a function called addByTwo(), which accepts a single argument. The function
returns the value of the parameter plus two. The final two lines of this code call addByTwo(), assign-
ing the result to the result variable and alerting result. When addByTwo() is called in this code,
JavaScript assigns the value of undefined to the num parameter because no value was passed to it.
Since undefined is not a number, the addition operation fails and results in NaN. In general, failing
to pass a required value to a function will result in the function’s failure.
In addition, you must ensure that function definitions that do have parameters correctly use the
parameters’ identifiers in the function body. Take a look at the following code:
function someFunction(someParameter) {
alert(someParamter);
}
someFunction("Hello, World!");
This code has an error. The someFunction() declaration defines a parameter called someParameter.
However, this parameter isn’t used within the function’s body. Instead, the alert() method attempts
to alert the value of someParamter (notice the missing e between m and t). A slight typo, such as the
omission of one letter, can cause your code to fail. Be mindful of your identifiers, and make sure they
match their definitions.
Case-Sensitivity
Slight typos are a nuisance. Just like omitting a single character, not matching an identifier’s case
can result in your code’s failure. For example, see if you can spot all the errors in the following code:
var myName = "Jeremy";
If (myname === "Jeremy") {
alert(myName.toLowercase());
}
Always be aware of case; knowing the identifier naming conventions can help with that:
➤➤ All keywords and reserved words are lowercase.
➤➤ All variables, non-constructor functions, properties, and methods begin with a lowercase
letter and use camel-case afterward.
➤➤ All data type constructor identifiers begin with an uppercase letter and use camel-case
afterward.
doSomething();
This code is a mess, and it is a good illustration of why you should strive to format your code.
Formatting makes code easier to read, which in turn makes errors easier to identify. So let’s format
the code as follows:
function doSomething() {
var x = 1,
y = 2;
if (x + y) * 2 < 10) {
if (x + y < 5) {
alert("Alert something");
}
}
doSomething();
Now you can easily identify one of the errors: A closing curly brace is missing at the end of the func-
tion definition. The second error is a bit trickier to find. Look at the first if statement and you’ll notice
that there is one opening parenthesis and two closing parentheses. An opening parenthesis needs to be
added to properly enclose the addition operation. It’s very easy to miss an opening or closing parenthesis
if you have many parentheses in one statement.
356 ❘ Lesson 36 Common Coding Mistakes
doSomething();
Operator Misuse
It’s common to misuse an operator, especially when dealing with assignment (=) and equality (==).
Look at the following example:
var number = 100;
if (number = 200) {
alert("Number is 200");
} else {
alert("Number is " + number);
}
At first glance you might think the result of this code will be an alert box displaying the message
"Number is 100". That is not the case. This code illustrates the very common mistake of using the
assignment operator instead of the equality operator. Instead of comparing number with the literal
of 200, the code inside the if statement’s condition assigns number the value of 200.
What makes misuse like this worse is that JavaScript doesn’t throw an error. Assigning a value to
a variable inside a condition is perfectly valid, so the only indication you get is abnormal behavior
from your application. In a large chunk of code, operator misuse is very easy to overlook. So keep
it in mind if your script behaves differently from how you expected.
Please select Lesson 36 on the DVD to view the video that accompanies this
lesson.
37
Handling Errors
The common mistakes covered in Lesson 36 are caused by the developer’s breaking some of the
syntax rules in JavaScript. As you will undoubtedly find out in your own time, errors in your
application can occur even if you follow JavaScript’s syntax rules. These are called runtime
errors because they occur while the program is running. Runtime errors are caused by a variety
of factors:
➤➤ A flaw in your code’s logic, such as a failure to validate user input before processing it
➤➤ Conditions beyond your control, such as a network error on the user’s computer
➤➤ Murphy’s Law (anything that can go wrong will go wrong)
Technically, syntax errors are usually runtime errors as well, because the errors
occur while the browser loads and attempts to execute the code.
When writing an application you want to be informed of every error that occurs so that you
can fix it. But when you deploy your app, the last thing you want users to see is an error mes-
sage that means nothing to them. Bugs will occur in our software — it’s a given. But we can
do our best to prevent them, and handle them when they do occur, to provide the best user
experience we can.
Preventing Errors
There are a number of things you can do if you want error-free web pages. First, ensure that
your HTML markup is up to snuff. Close all elements that need closing, and properly nest ele-
ments. Many HTML-validator services are available for free on the Internet. A popular one is
the W3C’s Markup Validation Service at validator.w3.org.
358 ❘ Lesson 37 Handling Errors
Second, thoroughly check your pages in as many browsers as possible. This is easier for developers
who use Windows because they have every major browser readily available; developers using OS X
or a flavor of Linux have a limited set of browsers to choose from. However, with virtualization
technology becoming ubiquitous in our computer systems, it’s getting far easier for OS X and Linux
users to test in Internet Explorer (IE) by installing Windows on a virtual machine. Alternatively,
you can decide which browsers you want to support for your application and verify that your code
works in them.
Third, validate user input. Users may or may not knowingly input invalid data in your form con-
trols. For example, you may have a textbox that asks for numeric data, and somewhere in your code
do some math with it. What would happen if the user typed an alphabetic character in that textbox?
The answer is that your code would not work. Either an error would be thrown, or more likely your
math operations would return incorrect results — thus breaking your application.
Essentially, thoroughly testing your app, following the best practices for HTML, CSS, and
JavaScript, and validating user input will get you on the right track. Let’s say you’ve done all that,
and your app works perfectly in your development environment. Unfortunately, things can still go
wrong, and issues can crop up that are beyond your control.
Your author wrote a content-management system many years ago that was used by many people to
update content on their web pages. While it was in development, Jeremy tested his app and found
and fixed bugs. When it was released and in use, some users complained that parts of the application
didn’t work correctly and that they were receiving errors when adding or editing new content. Come
to find out, these users used a particular brand of security software that injected its own JavaScript
files into the page, and in doing so caused the application’s JavaScript to fail at certain key points.
There was really no way Jeremy could have planned for this, and the only way he could fix the prob-
lem was to handle the error using a try...catch block to catch the thrown errors and provide an
alternative means for the users of that particular security software to add or edit their data.
To use try … catch statements, you place the code that could fail inside the try block. Then you
put code inside the catch block, which executes only if an exception is thrown by the try code.
Look at the following code:
try {
alert("No code to fail here");
} catch(exception) {
alert("Ooops. " + exception.message);
}
In this code, an alert() call is placed inside the try block. There is no code in try that can throw
an error, but if one were thrown, code execution would drop to the catch block. After the catch
keyword is a pair of parentheses with an identifier inside. When an exception is thrown, an object
of type Error is created and passed to the catch block. So in this code, exception works like a
function’s parameter (in fact, you can think of catch like a function). Error objects have a property
called message, which contains a description of the thrown exception. Let’s add a deliberate error to
this code:
try {
alert("No code to fail here");
abert("But now there is");
} catch(exception) {
alert("Ooops. " + exception.message);
}
The bold line in this code adds a call to abert(), which doesn’t exist. So when JavaScript attempts to
execute the bolded statement, the engine throws an exception, which is caught by the catch block.
In IE9 the alert() method displays the message "Ooops. Function expected" because exception
.message returns the string "Function expected" — no function called abert() exists.
Throwing Exceptions
It may seem strange, but there are times when you want to throw your own exception. For example,
this can be useful when users input invalid data. To throw an exception you use the throw keyword
followed by the object you want to throw. Generally that object is an instance of Error. The follow-
ing code throws an Error object:
throw new Error("This is my own exception");
But the real power behind throwing your own exceptions is the ability to throw any object you
want. So if you are validating a form, and the data in a textbox isn’t valid, you could do something
like this:
throw {
element : txtTextBox,
message : "Data is invalid"
};
This code throws an object defined using object literal notation. It has a property called element
that contains an HTML element object, and a message property containing a description of the
360 ❘ Lesson 37 Handling Errors
error. When throwing your own objects, it’s a good idea to include a message property because it
helps emulate objects of the Error data type.
Try It
In this lesson, you learn how to handle exceptions that may arise from unforeseen circumstances by
encapsulating code inside a try...catch block. You also learn how to throw your own exceptions.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows
users, Notepad is available by default on your system or you can download Microsoft’s Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/),
both of which are free. Mac OS X users can use TextMate, which comes as part of OS X, or down-
load a trial of Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Create a subfolder called Lesson37 in the JS24Hour folder you created in Lesson 1. Store the files
you create in this lesson in the Lesson37 folder. Copy your eventUtility.js file into this folder.
Step-by-Step
You will revisit the form-validation routine you wrote in Lesson 28 and use try … catch, along
with throwing your own exceptions, to make the code easier to read and maintain. If you like, copy
lesson28_example01.htm from the Lesson28 directory and paste it into the Lesson37 directory,
rename it lesson37_example01.htm, and make it look like the HTML in Step 1. Or feel free to
forego copy/paste and start with Step 1.
1. Open your text editor and type the following:
<html>
<head>
<title>Lesson 37: Example 01</title>
</head>
<body>
<form name="theForm" action="" onsubmit="validateForm(event)">
<p>
Name: <input type="text" name="txtName" />
</p>
<p>
Try It ❘ 361
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value, 10);
button.disabled = true;
}
</script>
</body>
</html>
</p>
<p>
Password: <input type="password" name="txtPassword1" />
</p>
<p>
Retype Password: <input type="password" name="txtPassword2" />
</p>
function isEmpty(value) {
return (value === "");
}
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value, 10);
button.disabled = true;
}
</script>
</body>
</html>
3. Now start validating each field. Throw a ValidationException object if a form field does not
validate. Start with the txtName textbox. Add the following bolded code to the validateForm()
function. (Note that the HTML and other JavaScript is removed for printing.)
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value, 10);
button.disabled = true;
try {
if (isEmpty(txtName.value)) {
Try It ❘ 363
This new code checks to see if the txtName textbox is empty and, if so, throws an exception.
4. Now validate the field for the user’s age. Ensure that the data is a number and that age is
greater than zero. The new code is bold:
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value, 10);
button.disabled = true;
try {
if (isEmpty(txtName.value)) {
throw new ValidationException(txtName, "Please enter your name.");
}
The same basics apply here. If age is not a number or is less than 1, throw an exception.
5. Validate the txtEmail field. Add the bold code:
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value, 10);
button.disabled = true;
try {
if (isEmpty(txtName.value)) {
throw new ValidationException(txtName,
"Please enter your name.");
}
}
}
6. Now validate the password fields. They should match, and they can’t be empty. The new
code is bold:
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value, 10);
button.disabled = true;
try {
if (isEmpty(txtName.value)) {
throw new ValidationException(txtName, "Please enter your name.");
}
if (isEmpty(txtPassword1.value)) {
throw new ValidationException(txtPassword1,
"Password cannot be blank");
}
7. Now add the catch block. Use the alert() method to alert the exception’s message, and
use the element property to give focus to the element. Also don’t forget to prevent the default
action of the form’s submit event, and enable the button. Add the following bold code:
function validateForm(event) {
var theForm = document.theForm;
var txtName = theForm.txtName;
var txtAge = theForm.txtAge;
var txtEmail = theForm.txtEmail;
var txtPassword1 = theForm.txtPassword1;
var txtPassword2 = theForm.txtPassword2;
var button = theForm.btnSubmit;
var age = parseInt(txtAge.value, 10);
button.disabled = true;
try {
if (isEmpty(txtName.value)) {
throw new ValidationException(txtName, "Please enter your name.");
}
if (isEmpty(txtPassword1.value)) {
throw new ValidationException(txtPassword1,
"Password cannot be blank");
}
alert(exception.message);
element.focus();
element.select();
eventUtility.preventDefault(event);
button.disabled = false;
}
}
366 ❘ Lesson 37 Handling Errors
In this new code, the exception.element property is assigned to the element variable.
Next, an alert box displays the error message to the user, and then the focus() and select()
methods are called on the element object. Then, since an error occurred, the eventUtility
.preventDefault() method prevents the form from being submitted. Finally, the button is
enabled to allow the user to resubmit the form.
Open this file in your browser and test it. You’ll find that it works very much as it did in Lesson 28,
with less code!
To get the sample code files, download Lesson 37 from the book’s website at www.wrox.com.
Please select Lesson 37 on the DVD to view the video that accompanies this
lesson.
38
Debugging with Firebug
Before JavaScript became the world’s most popular programming language, it was actually
looked down upon as being a quirky language that wasn’t worth any real developer’s time.
This outlook on JavaScript was primarily caused by two issues:
➤➤ Developers didn’t fully understand the language.
➤➤ There were no decent development tools available.
The popularity of web applications and Ajax essentially pushed JavaScript into the limelight.
As a result, developers finally began to understand the language, and some of them started
building development tools for JavaScript.
Today, there are a variety of tools available to developers. Probably the most important are the
JavaScript debuggers present in all major browsers. With a debugger, you can pause the code
execution of your script, and with a process known as stepping, you can execute the lines one
at a time to find bugs in your code.
Debugging is universally necessary across all browsers and languages. Although some debug-
ging tools offer features that other tools may not have, all debugging tools are based upon the
following concepts:
➤➤ Breakpoints: tell the debugger where it should break or halt code execution. You can
set a breakpoint on any line in your code; the debugger will pause there.
➤➤ Stepping: lets you execute one statement of code at a time. There are three ways to step
through code:
1. Step Into: executes the next line of code. In the case of a function, the debugger
executes the function and breaks at the first statement inside the function.
2. Step Over: also executes the next line of code. If the line is a function, the debugger
executes the function and breaks at the line after the function call.
368 ❘ Lesson 38 Debugging with Firebug
3. Step Out: returns to the caller when you’re inside a function. It executes the function
and then breaks at the return point of the function.
Although all debuggers offer support for these key concepts, and every modern browser has a JavaScript
debugger, the most popular JavaScript debugger is a Firefox add-on called Firebug. Even if you
don’t use Firefox as your browser, you, like many other developers, may decide to install it just so
you can use Firebug.
Unfortunately, Firebug does not come with Firefox by default; you have to install the add-on. You
can download the latest version at www.getfirebug.com or from Mozilla’s add-on site at addons
.mozilla.org/en-US/firefox/addon/1843/.
Because Firebug is an add-on, you will not find an installer for your operating system. Instead, you
have to visit one of the provided URLs with Firefox, click the install button on the web page, and
follow the instructions. After the add-on is installed, you need to restart Firefox in order to use the
new add-on.
Go ahead and install Firebug and proceed to the next section when you’re ready.
By default Firebug opens as a panel at the bottom of the window (Figure 38-1), but you can pop it
out into its own window by clicking the middle panel control button at the top right corner.
At the top of the panel is a list of tabs that give you information about the web page. The far left tab
is the Console tab, shown in Figure 38-2. The top portion is an information area that lists errors
and results of the JavaScript code you execute in the lower portion.
Getting Familiar with the UI ❘ 369
Figure 38-1
Figure 38-2
By default the console allows only single-line JavaScript statements to be input and executed, but
you can click the up arrow to allow multi-statement JavaScript code. When you do so, the Console
tab divides into a left panel (the informational panel) and a right panel (the JavaScript input panel).
The next tab is the HTML tab, and it provides information regarding the HTML structure as it’s
rendered in the browser (Figure 38-3).
Figure 38-3
370 ❘ Lesson 38 Debugging with Firebug
On the left side is a tree that lets you drill down to any element in the page. You can actually edit
the HTML on-the-fly to see how a change affects its rendering. It’s a powerful tool for any web
developer. On the right is a panel that provides you with CSS and layout information of the element
you have selected in the left panel, as well as the element’s DOM object.
The next tab is the CSS tab (Figure 38-4). It displays the source code of all the CSS style sheets refer-
enced in the page.
Figure 38-4
By clicking the menu you can select any style sheet and edit it to see how a change makes the page
look in the browser. While it isn’t overly useful for JavaScript developers, it is handy for those
responsible for a page’s look-and-feel.
The Script tab is next, shown in Figure 38-5, and this is where you’ll spend the majority of your time
in this lesson.
The left panel displays the source code of all the JavaScript referenced in the page. You can view the
other JavaScript files by clicking the menu and choosing the JavaScript file you want to view. The
area to the very left of the source code panel, where the line numbers are shown, is called the gutter.
You’ll use the gutter to set and remove breakpoints.
Figure 38-5
Getting Familiar with the UI ❘ 371
The right-hand panel of the Script tab is an information area that allows you to specify watches,
view the call stack, and view the statements that have breakpoints.
The next tab is the DOM tab (Figure 38-6). It displays everything loaded by the DOM, as well as
JavaScript variables, functions, and objects in the currently loaded page.
Figure 38-6
Finally is the Net tab, which is disabled by default, but which gives you instructions on how to enable
it if you want. This tab shows you all the requests made on this page, as shown in Figure 38-7.
Figure 38-7
It gives you just about all the information you need regarding each request: headers, response code,
what domain the request was sent to, and the amount of time it took to download the resource. It’s
a very useful tool when you’re trying to optimize the network portion of a web application.
So that’s a quick rundown of the UI. Let’s focus on the tools central to JavaScript development,
starting with the Script tab.
Setting Breakpoints
When the debugger reaches a breakpoint, it pauses code execution and waits for an action from you.
This is useful because it allows you to inspect your code while it executes. To create a breakpoint,
simply left-click in the gutter on the line where you want to break. Breakpoints are denoted by a red
circle in the gutter on the line where you clicked (Figure 38-8).
372 ❘ Lesson 38 Debugging with Firebug
Figure 38-8
You can also hard-code a breakpoint in your JavaScript code by using the debugger keyword, like this:
function someFunction() {
var someVariable = "Doing some work";
debugger;
someVariable = "Doing more work";
}
When code execution reaches the debugger statement in this code, Firebug will break and wait for
an action from you.
To view all the breakpoints that you’ve set, you can click the
Breakpoints tab in the right panel (Figure 38-9). Each entry in the
list lets you enable or disable a breakpoint by checking or unchecking
the checkbox, and also features the name of the function containing
the statement the breakpoint is on (if applicable), the file name and
line number of the source file, the source text of the breakpoint, and
a delete button to remove the breakpoint.
Figure 38-9
➤➤ Next is Step Over (F10), and it executes the current line of code and pauses at the next
statement.
➤➤ Last is the Step Over button (Shift+F11). When evoked inside a function, Step Out executes
the remaining code of the function and returns to the caller.
Try It ❘ 373
Try It
In this lesson, you are introduced to Firebug, the most popular JavaScript debugger. You learn how
to set breakpoints, step through code, and use the console to get information and change values as
you need to.
Lesson Requirements
For this lesson, you will need a text editor; any plain text editor will do. For Microsoft Windows
users, Notepad is available by default on your system, or you can download Microsoft’s Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/),
both of which are free. Mac OS X users can use TextMate, which comes as part of OS X, or down-
load a trial of Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need Firefox and the Firebug add-on installed. If you do not have Firefox installed on your
computer, visit www.getfirefox.com to download and install it. After it is installed, install the
Firebug add-on as described previously in this lesson.
Create a subfolder called Lesson38 in the JS24Hour folder, and then open your text editor and type
the following HTML. The bold line has a deliberate typo.
<html>
<head>
<title>Lesson 38: Example 01</title>
</head>
<body>
<script type="text/javascript">
function writeTimesTable(timesTable) {
var writeString = "";
documents.write(writeString);
}
}
writeTimesTable(2);
</script>
</body>
</html>
Save this file as lesson38_example01.htm, and make sure it is in the Lesson38 folder.
374 ❘ Lesson 38 Debugging with Firebug
Step-by-Step
1. Open lesson38_example01.htm in Firefox. Firebug should automatically open. If not,
that’s fine.
If your status bar is enabled, look at the lower right of the browser window
and you’ll see that an error occurred in this page (Figure 38-11).
2. If your status bar is enabled, click the “1 Error” message in the status bar.
3. If your status bar is disabled, and if it isn’t open already, open Firebug by pressing the F12
key, and click the Console tab.
4. You’ll now see the error. Notice that you are given the error message, the source code that
caused the error, and the number of the file and line where the error occurred.
5. Go back to your text editor and fix the error by removing the s from documents. Save the
file again, and refresh the page in your browser.
6. Now set a breakpoint on line 13. The code on that line is:
writeString = writeString + (timesTable * counter);
To set a breakpoint, click the Script tab and left-click the gutter at line 13. You should now
see a red circle next to the line number.
7. Reload the page. Notice that it looks as if nothing has happened. That’s because Firebug has
paused JavaScript execution. Look at the source code in the Scripts tab. You’ll see an arrow
inside the red circle, and the line will be highlighted (Figure 38-12). The debugger has not yet
executed this line of code — it is waiting for an action from you. You’ll give it an action shortly.
8. Look over at the pane on the right. The Watch tab should be active. If not, click it.
The breakpoint caused the debugger to pause at line 13, which is inside the writeTimesTable()
function. The items being displayed in the Watch tab are variables that have scope inside
writeTimesTable(). You can see that this is the window object, the counter variable is 1,
timesTable is 2, and writeString is "1 * 2 = ". You can click where it says “New watch
expression” to add a new watch. There aren’t any that you want to add at this time.
Figure 38-12
9. Now Step Into the next line by pressing the F11 key (or clicking the Step Into button). The
debugger executes the currently highlighted line and then pauses at the next line.
10. Look back at the Watch tab and at the value of writeString. It is now as follows:
"1 * 2 = 2"
Try It ❘ 375
17. Now press the enter key on your keyboard. It will seem as if nothing happens.
18. This time remove the breakpoint by clicking the red circle in the gutter and press F8 to
Continue (or click the button). The page in Firefox should look like Figure 38-13.
This happened because you changed the value of writeString while paused at line 13.
Figure 38-13
376 ❘ Lesson 38 Debugging with Firebug
To get the sample code files, download Lesson 38 from the book’s website at www.wrox.com.
Please select Lesson 38 on the DVD to view the video that accompanies this
lesson.
39
Coding Guidelines
Whether you’re working on a project by yourself or with a group of other developers, it’s very
important that you follow a set of guidelines as you write code. Following guidelines promotes
code quality, making it easier for you and others to find bugs, read, and maintain. The heart of
most sets of guidelines is code conventions inherent to the programming language. For example,
when you were introduced to variables and identifiers in Lesson 2, you were taught how identifiers
should follow the code conventions already in use by the language itself.
Aside from naming identifiers, JavaScript developers follow a varying set of guidelines,
usually based on conventions, to make their code easier to read and maintain. Let’s look at
some of them.
Use Whitespace
Whitespace, while not actually code, is one of the most important pieces of a programming
language. It allows you to visually organize your code into readable and maintainable chunks
of text. Without whitespace code would be a jumbled mess of text. Look at the following code
for an example:
function doSomethingUseless(num){if(num===1){num=num+1;
doSomethingUseless(1);}
else{num=num*2;for(var i=0;i<num;i++){
num--;}
return num;
}
}
This code is difficult to read. By scanning it you can see that it’s a function with some decisions
and loops, but the lack of whitespace makes it difficult to discern what the code actually does.
So let’s add some whitespace to make it more readable:
function doSomethingUseless(num) {
if (num === 1) {
378 ❘ Lesson 39 Coding Guidelines
num = num + 1;
doSomethingUseless(num);
} else {
num = num * 2;
for (var i = 0; i < num; i++) {
num--;
}
return num;
}
}
Variable Declarations
Something else that makes code easier to read and maintain is declaring all variables at the begin-
ning of a function (using the var keyword, of course). Consider the following code as an example:
function nothingSpecial(value) {
var multiplier = 2;
var result = value;
return result;
}
Placing variables declarations at the top of a function also has the side effect of pointing out possible
errors. For example, this code uses a variable called num inside the for loop. It’s obviously important
to the nothingSpecial() function because it’s used in the calculation of result.
So is num supposed to be global, or is it supposed to be a local variable? Can you count on its having
a value before trying to use it in a calculation? By declaring variables at the top of a function you
can catch little things like the num variable since you know what variables to expect in the function.
Also remember that JavaScript has no block-level scope. Placing variable declarations at the begin-
ning of a function makes that fact explicit.
Use Literals ❘ 379
Use Literals
The performance of your web application is linked to how fast it downloads to the user’s browser.
Obviously, larger files take more time to download. With broadband connections becoming more
and more the norm, it may seem that file size doesn’t matter. After all, a 50KB file takes .05 seconds
to download on a 1MB/s line, but it can add up. So cutting out unnecessary characters not only
makes typing easier, but also trims down the size of your code.
Perhaps the best way to type less is to use literal notation when creating objects and arrays instead
of calling their constructors. Remember, object literals use curly braces ({}) and array literals use
square brackets ([]). For example, the following code creates an object with three properties, one
of which is an array, using the Object and Array constructors:
var person = new Object();
person.firstName = "John";
person.lastName = "Doe";
person.friends = new Array();
person.friends[0] ="Jim";
person.friends[1] = "Jane";
person.friends[2] = "Bartholomew";
That’s a lot of typing to create one person object: 203 bytes worth of characters. Contrast that with
the following code:
var person = {
firstName : "John",
lastName : "Doe",
380 ❘ Lesson 39 Coding Guidelines
friends : [
"Jim",
"Jane",
"Bartholomew"
]
};
This code uses literal notation to create the same person object. Even though it has two more lines
than the previous code, it’s less typing and weighs in at 146 bytes. That’s a significant decrease in
size, and the size difference would only grow as more properties or array elements were added.
Many professionals use a tool called JSLint (http://www.jslint.com/), written by Douglas
Crockford, to analyze code and check its quality. The guidelines mentioned in this lesson are just a
few of the items JSLint looks at, and future lessons touch on other items scrutinized by JSLint.
40
Separating JavaScript
and HTML
Web applications are naturally separated into different layers. The HTML layer is responsible
for getting data to the user, the CSS layer is responsible for the visual representation of that data,
and JavaScript adds behavior to the page. Despite this separation, developers have a tendency
to tie HTML, CSS, and JavaScript together by using inline style sheets or scripts, as well as by
using the style and event handler attributes (such as onclick).
Proof of this is found in this book. In many examples you’ll find the use of inline scripts or
event handler attributes. It makes explanations and demonstrations easier while at the same
time making maintenance a headache. The more tightly you integrate multiple layers, the more
work you face when making changes down the road.
Using inline scripts and event handler attributes is technically correct. After all, the examples
in this book use them, but their use made the examples harder to maintain and, in the case of
errors, fix. Consider the following code as an example:
<form name="theForm" onsubmit="submitFom(event);" />
<script type="text/javascript">
function submitForm(event) {
alert("Submitting!");
}
</script>
There is an error in this code. But where? Is it in the <form/> element’s onsubmit attribute
or in the JavaScript code? You have a 50 percent chance of picking the correct spot to start
debugging, and if you pick wrong you’ve wasted time. The actual error is in the onsubmit event
handler: It calls submitFom(), not submitForm(). Now, this is a very simple example, so
imagine having to debug several HTML attributes and inline scripts on multiple pages.
That’s a daunting task.
382 ❘ Lesson 40 Separating JavaScript and HTML
This example also brings up another way in which an error might occur: The form could be sub-
mitted before the submitForm() function is available to the browser. In other words, the submit-
Form() function isn’t loaded by the browser when the user submits the form. It’s one of those errors
that you personally may not experience when developing your application, but that users on varying
connections, especially slow ones, could experience.
Decoupling your JavaScript and HTML is one of the tenants of progressive enhancement, a Web
development philosophy that champions accessibility. The idea behind progressive enhancement is to
create a bare-bones document that works in all browsers (no CSS or JavaScript whatsoever). Then the
developer adds enhancements to the page via external style sheets or JavaScript. Progressive enhance-
ment enables users with the latest browsers to experience the full-feature page; whereas, users with
JavaScript-disabled browsers can still use the page’s basic functionality without having to download
the unnecessary JavaScript files.
The previous form submittal example is a perfect illustration of how progressive enhancement can
improve a user’s experience. Examine the following HTML:
<form name="theForm" onsubmit="submitForm(event);" />
<script type="text/javascript">
function submitForm(event) {
alert("Submitting!");
}
</script>
Browsers with JavaScript disabled still have to download the inline JavaScript because it’s part of
the HTML document. Compare that code with the following HTML:
<form name="theForm" />
The JavaScript code within formStuff.js contains all the code necessary to provide enhanced features
and usability for users of modern web browsers. For users of browsers with JavaScript enabled, the
external file is downloaded. Users of browsers with JavaScript disabled don’t need the file; the browser
ignores the external file and doesn’t download it. So by following the progressive enhancement philoso-
phy (and thus separating your JavaScript and HTML), you provide features for users that want them
while improving performance for users that don’t.
Decoupling HTML and JavaScript means that JavaScript files are served separately from HTML
documents. This gives you the ability to compress JavaScript files with gzip to make them smaller,
serve them as cacheable so they only have to be downloaded once, and minify them, or remove
unnecessary characters, using various tools like Yahoo!’s UI Compressor (http://developer.yahoo
.com/yui/compressor/) or Douglas Crockford’s JSMIN (http://crockford.com/javascript/jsmin).
So how can you separate your JavaScript and HTML? The first step is to remove all inline JavaScript
and put it in external files. Second, remove all event handler attributes from your HTML elements, and
finally, wire up events in your JavaScript.
Try It ❘ 383
Try It
In this lesson, you learn how to separate your JavaScript from HTML to avoid errors and make your
application easier to maintain and debug.
Lesson Requirements
For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows
users, Notepad is available by default on your system or you can download Microsoft’s Visual Web
Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/),
both of which are free. Mac OS X users can use TextMate, which comes as part of OS X, or down-
load a trial of Coda (www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
Additionally, you need a webserver installed with PHP support. Refer to Lesson 31 on the DVD for
installation instructions.
This lesson modifies Lesson 35’s JSON example, so you need a directory called Lesson40 in your web-
server’s root directory, containing the eventUtility.js, ajaxUtility.js, car_dealership.php,
and the json2.js files used in Lesson 35. You can also copy lesson35_example01.htm and paste it
into the lesson40 folder. Just remember to rename it to lesson40_example01.htm.
Step-by-Step
1. If you didn’t copy lesson35_example01.htm, open your text editor and type the following:
<html>
<head>
<title>Lesson 40: Example 01</title>
</head>
<body>
<div id="divResults">
<div id="divSearchMessage"></div>
<div id="divSearchResults"></div>
</div>
<form name="theForm" method="post" action="car_dealership.php"
onsubmit="submitForm(event);">
<p>
Make: <input type="text" name="txtMake" value="" />
</p>
<p>
Model: <input type="text" name="txtModel" value="" />
384 ❘ Lesson 40 Separating JavaScript and HTML
</p>
<p>
Year: <input type="text" name="txtYear" value="" />
</p>
<p>
<input type="submit" name="btnSubmit" value="Submit" />
</p>
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript" src="ajaxUtility.js"></script>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript">
function submitForm(event) {
var data = ajaxUtility.getRequestBody(document.theForm);
eventUtility.preventDefault(event);
}
function getCarString(carObj) {
return [carObj.year, carObj.make, carObj.model].join(" ");
}
function processResponse(data) {
var result = JSON.parse(data),
var messageDiv = document.getElementById("divSearchMessage");
var resultsDiv = document.getElementById("divSearchResults");
var message = "";
var results = [];
var length = result.results.length;
var carStr = getCarString(result.searchedCar);
if (result.isFound) {
message = "We found " + length + " matches for " + carStr;
} else {
message = "We could not find " + carStr + ". You might like: ";
resultsDiv.innerHTML = results.join("<br/>");
}
messageDiv.innerHTML = message;
}
</script>
</body>
</html>
Save it as lesson40_example01.htm.
Try It ❘ 385
2. Copy the inline JavaScript code and paste it into a new text file. Save this new file as lesson40_
example01.js. Be sure to save it in the lesson40 directory.
3. Now remove the inline code from your HTML file and reference the new lesson40_example01
.js file as shown in the following HTML:
<html>
<head>
<title>Lesson 40: Example 01</title>
</head>
<body>
<div id="divResults">
<div id="divSearchMessage"></div>
<div id="divSearchResults"></div>
</div>
<form name="theForm" method="post" action="car_dealership.php"
onsubmit="submitForm(event);">
<p>
Make: <input type="text" name="txtMake" value="" />
</p>
<p>
Model: <input type="text" name="txtModel" value="" />
</p>
<p>
Year: <input type="text" name="txtYear" value="" />
</p>
<p>
<input type="submit" name="btnSubmit" value="Submit" />
</p>
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript" src="ajaxUtility.js"></script>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="lesson40_example01.js"></script>
</body>
</html>
<p>
Model: <input type="text" name="txtModel" value="" />
</p>
<p>
Year: <input type="text" name="txtYear" value="" />
</p>
<p>
<input type="submit" name="btnSubmit" value="Submit" />
</p>
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript" src="ajaxUtility.js"></script>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="lesson40_example01.js"></script>
</body>
</html>
This code uses the eventUtility object to add a submit event handler to the form.
Now your JavaScript is completely separate from your HTML. Any change you make to the
JavaScript code is done without your touching any of the HTML. Also, any JavaScript error that
might occur can originate only from the JavaScript contained in the external files. There’s no more
guessing involved in determining if the error originated from HTML or JavaScript.
Even by completely separating HTML and JavaScript, the form in this can still
be submitted before the submitForm(), or the code that assigns it to handle the
submit event is loaded by the browser. Premature form submission cannot be
prevented.
You’ll continue this best practices study in Lesson 41 when you learn how to decouple JavaScript
and CSS.
To get the sample code files, download Lesson 32 from the book’s website at www.wrox.com.
Please select Lesson 40 on the DVD to view the video that accompanies this
lesson.
41
Separating JavaScript and CSS
The HTML layer is arguably the most important because of its responsibility for getting data
to the user, and the interaction between the HTML and JavaScript layers is important to the
user’s overall experience. JavaScript can interact with another layer, the CSS layer, which is
responsible for how the page is displayed in the browser. Just as the HTML and JavaScript
layers can be tightly integrated, the CSS and JavaScript layers can be coupled.
In Lesson 15, you learned how to change an element’s style by using an element object’s style
property, like this:
var someEl = document.getElementById("someElement");
someEl.style.color = "red";
someEl.style.backgroundColor = "gray";
someEl.style.border = "1px solid black";
This code retrieves an element with an id of someElement and then changes various CSS
properties to change how the element renders in the browser. The code works fine, but it
suffers from problems like those of coupled HTML and JavaScript in that it makes mainte-
nance more difficult. Not only do you have to revisit this code whenever you want to change
the color, backgroundColor, and border properties, but making additional style changes
requires an additional line of code for every property you want to change. Code such as this
essentially makes JavaScript share the presentation role that is supposed to be the role of CSS.
Another issue is performance. To use the style object you access a DOM object — a poorly
performing operation. As you add more lines of code to change an element’s style, you not
only make your application slower, but you potentially cause the browser to re-render the
page multiple times.
Admittedly, it’s impossible to completely separate CSS and JavaScript (unless you don’t use
JavaScript to change an element’s style), but you can minimize the integration of CSS and
388 ❘ Lesson 41 Separating JavaScript and CSS
JavaScript by using the className property to add CSS classes to and remove them from an element’s
className property, as you did in Lesson 15, like this:
someEl.className = "cssClassName";
By doing so, you make only one call to the DOM instead of multiple, and any modification to the CSS
is made where it needs to be made — in the CSS file. If you want to add another CSS class to the ele-
ment represented by someEl, you can write the following code:
someEl.className = someEl.className + " anotherCssClass";
This code adds a CSS class called anotherCssClass, and when you want to remove it you can do this:
someEl.className = someEl.className.replace(" anotherCssClass", "");
There are, however, exceptions to this rule of separation. There are times when changing an element’s
CSS class cannot provide the functionality you want. Remember the drag-and-drop script from
Lesson 24? You had to move an element by changing the top and left CSS properties while also
manipulating the element’s z-index. This type of fine control is impossible to achieve with CSS classes.
So when you need that level of control, setting individual CSS properties with JavaScript is acceptable.
42
Avoiding Global Scope
In Lesson 39, I mentioned that it’s good practice to avoid the global scope. This is crucial when you
incorporate someone else’s code in your page, as it helps you avoid naming conflicts between the
variable and function names given in yours and the other party’s code. What exactly is a naming
conflict? A naming conflict occurs when you use an identifier that is used in another script on the
same level of scope.
Let’s look at an example. The following code is a truncated version of the eventUtility
object used throughout this book:
// your code
var eventUtility = {
addEvent : (function() {
if (typeof addEventListener !== "undefined") {
return function(obj, evt, fn) {
obj.addEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.attachEvent("on" + evt, fn);
};
}
}())
};
This code creates the eventUtility object with the addEvent() method. This code is defined
in the global scope, and your event-driven code heavily relies on this particular object and
method. But you wanted to add some extra functionality to your page, and you incorporated
code written by someone else. It too uses a global object called eventUtility, but it’s differ-
ent from yours. Its code is as follows:
// third party code
var eventUtility = {
addEventHandler : function(obj, evt, fn) {
390 ❘ Lesson 42 Avoiding Global Scope
The browser loads this code after loading yours. Since they both share the same identifier and
they exist on the same level of scope (global), this second eventUtility object overwrites your
eventUtility object. That’s a huge problem because the rest of your code in the page makes exten-
sive use of the eventUtility.addEvent() method, and it no longer exists because eventUtility
was overwritten by someone else’s code. So your page breaks, and you could spend a lot of time
trying to figure out why. Obviously, you want to avoid naming conflicts. Let’s see how you can do
just that.
Using Functions
In Lesson 6 you learned about scope and that functions have their own levels of scope; the variables,
objects, and functions defined inside another function cannot be accessed from outside that function.
In Lesson 20, while writing a cross-browser event utility, you learned about self-executing functions —
anonymous functions that were executed immediately after they were defined. These two ideas, func-
tional scope and self-executing functions, can be combined in order to hide your code from the global
scope.
Let’s look at an example. The following is some code using a self-executing function to isolate code
from the global scope:
var someVariable = "Hello, Global!"; ///
(function() {
}());
The first line of this code creates a global variable called someVariable. It can be accessed any-
where in the page, and as a result it can potentially be overwritten by some other data.
Next a self-executing function is defined, and its body consists of a local variable called someVariable
(the same name as the global variable) and a call to alert() to display the value of someVariable.
Remember, Lesson 6 discussed how a variable or function in a local scope can override (not the same
as overwrite) a global variable or function with the same name. The global variable or function is still
intact with its value, but in the local scope the variable or function defined locally is used in place of
the global variable or function. So when this self-executing function executes, the value of the local
someVariable variable is displayed in an alert window.
After the function finishes, the final line of this code executes and alerts the text "Hello, Global!"
because the global someVariable variable was not altered.
Using Functions ❘ 391
Let’s look at another example. This one uses the eventUtility example from earlier in the lesson:
(function() {
var eventUtility = {
addEvent : (function() {
if (typeof addEventListener !== "undefined") {
return function(obj, evt, fn) {
obj.addEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.attachEvent("on" + evt, fn);
};
}
}())
};
function documentLoad(event) {
alert("Document loaded!");
}
}());
function documentLoad(event) {
alert("Event handled with third party");
}
This code first executes a self-executing function. Inside this function your eventUtility object is
defined and used to assign an event handler to handle the document object’s load event. When the
event fires, an alert window displays the text "Document loaded!"
After the function executes, another event handler is assigned to handle the document’s load
event using the third-party eventUtility object. When a page loads with this JavaScript
you’ll see two alert boxes: The first tells you the document was loaded, and the second says,
"Event handled with third party" in browsers supporting addEventListener().
So by putting your code into a self-executing function, you can effectively protect it from the rest
of the JavaScript code loaded in the page. But there is a problem here. The eventUtility object is
something you want to use throughout your application — possibly in different JavaScript files. If
it’s hidden inside a function, how can other pieces of code use it? Shouldn’t it be globally accessible
so that you can use it wherever you need to? Yes, it should, and you can make it so by emulating
namespaces.
392 ❘ Lesson 42 Avoiding Global Scope
Emulating Namespaces
Many programming languages have built-in constructs called namespaces to help avoid nam-
ing collisions. A namespace is a container that provides context for the objects it contains while
protecting the names of those objects from colliding with the names of other objects outside the
namespace. Namespaces aren’t a foolproof means of preventing naming collisions, but they do a
good enough job.
Unfortunately, JavaScript does not support a formal namespace feature, but you can emulate
namespaces by using object literals.
A namespace should be something meaningful, and yet somewhat unique to you, such as your name
or your site’s domain name. Your author’s website is www.wdonline.com, so you could create a
pseudo-namespace for him by typing the following:
var wdonline = {};
Then you can start to tack on the code you need to globally access, as in the following code:
wdonline.eventUtility = {
addEvent : (function() {
if (typeof addEventListener !== "undefined") {
return function(obj, evt, fn) {
obj.addEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.attachEvent("on" + evt, fn);
};
}
}())
};
In the case of this code, the wdonline object is global, but the likelihood of wdonline being over-
written by a third party’s code is slim. The eventUtility object is now a property of wdonline,
giving it protection from being overwritten. If the chances of wdonline’s being overwritten are slim,
they’re even smaller for wdonline.eventUtility.
The reality is that you cannot get away from the global scope if you want certain pieces of your code
to be accessible anywhere in the page, but you can do your best to avoid naming collisions by using
self-executing functions and pseudo-namespaces.
Try It ❘ 393
Try It
In this lesson, you learn how to minimize the impact your code has on the global scope in order to
reduce the likelihood of naming collisions.
Lesson Requirements
To practice the techniques outlined in this lesson, you will refactor the eventUtility and
ajaxUtility objects to include the use of self-executing anonymous functions and pseudo-
namespaces. To do this, you will change the example in Lesson 35, the car dealership search
using JSON.
You need a text editor. For Microsoft Windows users, Notepad is available by default on your
system or you can download Microsoft’s Visual Web Developer Express (www.microsoft.com/
express/web/) or Web Matrix (www.asp.net/webmatrix/), both of which are free. Mac OS X
users can use TextMate, which comes as part of OS X, or download a trial version of Coda
(www.panic.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
You also need a webserver installed with PHP support. Refer to Lesson 31 on the DVD for installa-
tion instructions.
Create a subfolder called Lesson42 in your webserver’s root directory. Copy the contents of the
Lesson35 directory into Lesson42, and rename the lesson35_example01.htm file to lesson42_
example01.htm.
Step-by-Step
1. Open lesson42_example01.htm in your text editor, and change the <title/> element to
contain the text Lesson 42: Example 01.
2. Create a new file called lesson42_example01.js, and move all the in-line JavaScript to that
new file. Also remove the onsubmit event handler from the <form/> element. The altered
HTML should look like this:
<html>
<head>
<title>Lesson 42: Example 01</title>
</head>
<body>
394 ❘ Lesson 42 Avoiding Global Scope
<div id="divResults">
<div id="divSearchMessage"></div>
<div id="divSearchResults"></div>
</div>
<form name="theForm" method="post" action="car_dealership.php">
<p>
Make: <input type="text" name="txtMake" value="" />
</p>
<p>
Model: <input type="text" name="txtModel" value="" />
</p>
<p>
Year: <input type="text" name="txtYear" value="" />
</p>
<p>
<input type="submit" name="btnSubmit" value="Submit" />
</p>
</form>
<script type="text/javascript" src="eventutility.js"></script>
<script type="text/javascript" src="ajaxUtility.js"></script>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="lesson42_example01.js"></script>
</body>
</html>
3. Open the eventUtility.js file in your text editor. You will create a pseudo-namespace
called js24Hour and add the eventUtility object to the pseudo-namespace. Add the fol-
lowing code at the beginning of the file:
if (typeof js24Hour === "undefined") {
var js24Hour = {};
}
This code adds flexibility to your pseudo-namespace. Remember that the eventUtility
and ajaxUtility objects exist in two separate files. If you create a js24Hour object in both
files you will overwrite one of them. By using this code you check to see if js24Hour does
not exist, and create it if necessary. This way both your eventUtility and ajaxUtility
objects exist within the same js24Hour pseudo-namespace.
4. Make changes to the file as shown in bold in the following code:
if (!js24Hour) {
var js24Hour = {};
}
js24Hour.eventUtility = {
addEvent : (function() {
if (typeof addEventListener !== "undefined") {
return function(obj, evt, fn) {
obj.addEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.attachEvent("on" + evt, fn);
Try It ❘ 395
};
}
}()),
removeEvent : (function() {
if (typeof removeEventListener !== "undefined") {
return function(obj, evt, fn) {
obj.removeEventListener(evt, fn, false);
};
} else {
return function(obj, evt, fn) {
obj.detachEvent("on" + evt, fn);
};
}
}()),
getTarget : (function() {
if (typeof addEventListener !== "undefined") {
return function(event) {
return event.target;
}
} else {
return function(event) {
return event.srcElement;
}
}
}()),
preventDefault : (function() {
if (typeof addEventListener !== "undefined") {
return function(event) {
event.preventDefault();
}
} else {
return function(event) {
event.returnValue = false;
}
}
}())
};
This code adds the eventUtility object to the js24Hour pseudo-namespace. There are no
other changes. Save the file.
5. Now open ajaxUtility.js and make the changes as marked in bold in the following code:
if (typeof js24Hour === "undefined") {
var js24Hour = {};
}
js24Hour.ajaxUtility = {
createXHR : function() {
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
} else {
var versions = [ "MSXML2.XmlHttp.6.0",
"MSXML2.XmlHttp.3.0" ];
try {
var xhr = new ActiveXObject(versions[i]);
return xhr;
} catch (error) {
// do nothing
}
}
}
return null;
},
xhr.open("GET", url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(null);
},
getRequestBody : function(form) {
var pieces = [];
var elements = form.elements;
return pieces.join("&");
},
xhr.open("POST", url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
Try It ❘ 397
xhr.send(data);
},
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(this.getRequestBody(form));
}
};
Once again the first lines ensure that you do not overwrite any existing js24Hour object.
Save the file.
6. Now you need to open lesson42_example01.js and make changes to this file. First, surround
the code in this file with a self-executing anonymous function. Then, because you removed
the onsubmit event handler from the <form/> element’s HTML, you need to wire up the
submitForm() function to handle the form’s submit event. Finally, you need to modify
the submitForm() function to use the js24Hour.ajaxUtility and js24Hour.eventUtility
objects. The changes to this file are bold in the following code:
(function() {
function submitForm(event) {
var data = ajaxUtility.getRequestBody(document.theForm);
eventUtility.preventDefault(event);
}
398 ❘ Lesson 42 Avoiding Global Scope
function getCarString(carObj) {
return [carObj.year, carObj.make, carObj.model].join(" ");
}
function processResponse(data) {
var result = JSON.parse(data);
var messageDiv = document.getElementById("divSearchMessage");
var resultsDiv = document.getElementById("divSearchResults");
var message = "";
var results = [];
var length = result.results.length;
var carStr = getCarString(result.searchedCar);
if (result.isFound) {
message = "We found " + length + " matches for " + carStr;
} else {
message = "We could not find " + carStr + ". You might like: ";
resultsDiv.innerHTML = results.join("<br/>");
}
messageDiv.innerHTML = message;
}
}());
Using a self-executing anonymous function in this file protects this code from any acciden-
tal damage from other JavaScript that may be loaded in the page. The first two statements in
the self-executing function cache the js24Hour.ajaxUtility and js24Hour.eventUtility
objects in the ajaxUtility and eventUtility variables, respectively. Caching these objects
enables you to use them inside the self-executing function exactly as you have in previous
lessons.
This code also decouples your JavaScript code from your HTML by using your event utility
to assign a function to handle the form’s submit event.
7. Save lesson42_example01.js and point your browser to http://localhost/Lesson42/
lesson42_example01.htm. Fill out the form and submit it, and you’ll see that it works just
as it did in Lesson 35.
You can also download the sample code for all lessons from the book’s website at www.wrox.com.
Please select Lesson 42 on the DVD to view the video that accompanies this
lesson.
43
Optimizing Your Code
The applications you use every day have more than likely been in development for many years.
The code behind them has been refined to make them more efficient and responsive to the user
than they were at first. When first written, code is rarely the best it could be — usually far
from it.
There’s an old adage that most professional developers follow: “Make it run, and then make
it run well.” When you first write an application, don’t worry about optimizing your code.
Another adage is “Premature optimization is the root of all evil.” Worry about making your
application work, and after it works, then start optimizing your code.
There are three major aspects of JavaScript optimization. They are:
➤➤ Refactoring code
➤➤ Optimizing DOM code
➤➤ Using event delegation
Refactoring Code
It’s been recounted many times, but it’s worth repeating: The faster your code downloads,
the faster your application feels to the user. You don’t have control over your users’ connec-
tion speed, but you do have control over how big your code files are. The size of your code files
depends on a variety of factors, but two things that often add to code size are code duplication
and unnecessary statements.
400 ❘ Lesson 43 Optimizing Your Code
var valueThree = 2;
valueThree = valueThree + 150;
valueThree = valueThree / 2;
valueThree = "The value is: " + valueThree; // results in "The value is: 76"
This code performs the same exact operations on three different variables. Later in that lesson, that
code was refactored to use the following function:
function augmentValue(originalValue) {
var augmentedValue = originalValue;
augmentedValue = augmentedValue + 150;
augmentedValue = augmentedValue / 2;
augmentedValue = "The value is: " + augmentedValue;
return augmentedValue;
}
This refactor session saved two lines of code. Now, that may not seem like much right now, but let’s
look at the benefits. First, the code is reusable. If you need to augment more values in the same way,
you can simply call the function as shown in the following example:
var valueFour = augmentValue(10); // results in "The value is: 80"
var valueFive = augmentValue(40); // results in "The value is: 90"
var valueSix = augmentValue(0); // results in "The value is: 75"
Now you begin to see real code savings from reusing the augmentValue() function. Before refactor-
ing, each set of operations on one variable took four lines of code, including the variable declaration.
If you were to perform the same set of operations on six variables, you’d do so in 24 lines of code.
By refactoring the code you shrink the number of lines to 13 (seven for the function and six for each
variable) — that’s a nice savings.
Refactoring Code ❘ 401
Second, the code is more maintainable. Let’s imagine that you wanted to add one to each value after
dividing the number by two. Before refactoring you would have had to modify each set of operations
like the bolded lines in the following code:
var valueOne = 100;
valueOne = valueOne + 150;
valueOne = (valueOne / 2) + 1;
valueOne = "The value is: " + valueOne; // results in "The value is: 125"
Making changes like this makes your code more prone to error (what if you missed a variable?), and
consumes too much time. You can achieve the same modification by editing one line of code, shown
in bold in the following example:
function augmentValue(originalValue) {
var augmentedValue = originalValue;
augmentedValue = augmentedValue + 150;
augmentedValue = (augmentedValue / 2) + 1;
augmentedValue = "The value is: " + augmentedValue;
return augmentedValue;
}
So by identifying duplicated code and refactoring it, you not only gain a performance increase by
reducing the amount of code, you also save time and avoid potential errors.
Reducing Statements
The augmentValue() function in the previous section certainly eliminated duplicated code, but the
function’s body can be made to be more efficient and consume less space. The first change you can
make is to remove the augmentedValue variable. The value passed to this function is supposed to be
a number. Numbers are primitive values in JavaScript, so the value passed to this function is copied
and contained within the originalValue variable. Because of this there’s no need to copy the value
in originalValue to augmentedValue. So the first refactor would result in the following:
function augmentValue(originalValue) {
originalValue = originalValue + 150;
originalValue = (originalValue / 2) + 1;
originalValue = "The value is: " + originalValue;
return originalValue;
}
You can refactor this function some more by combining statements. The JavaScript engine can
execute one statement containing multiple operations faster than it can execute multiple statements
performing one operation each. While you could combine all the statements in this function into
402 ❘ Lesson 43 Optimizing Your Code
one, a better approach (as far as maintainability is concerned) would be to combine statements that
perform similar operations. For example, there are two statements that perform arithmetic operations.
You can combine them into one statement, as shown in this code:
function augmentValue(value) {
originalValue = ((value + 150) / 2) + 1;
return "The value is: " + originalValue;
}
First, the originalValue parameter was changed to just value. This was done for two reasons:
➤➤ Before the removal of the augmentedValue variable, the originalValue identifier made
sense. Now, since augmentedValue is no longer used, originalValue is not meaningful.
➤➤ Because originalValue is no longer meaningful, it makes sense to use the word value
alone. It’s short and saves space, while providing a meaningful name for the parameter.
Next, the first two statements are combined. This isn’t always a good idea. If there are many math-
ematical operations to perform, they would be easier to read and maintain if they were in multiple
statements. But here, combining them is fine.
Last, the concatenation and return statements are combined. Because there are no other operations
that need to be performed after value is concatenated with a string, it makes sense to simply return
the result of the concatenation operation.
Now the augmentValue() function is much smaller than it was before. That, combined with the
refactoring of duplicated code into the augmentValue() function, makes this code efficient and
ready for download.
Combining statements also applies to variables. Lesson 2 introduced the idea of initializing multiple
variables within one statement as demonstrated in the following code:
var variableOne = "data",
variableTwo = 10,
variableThree = [];
Each variable initialization is separated by a comma. This code executes faster than the following code:
var variableOne = "data";
var variableTwo = 10;
var variableThree = [];
This book, for the sake of clarity, opted to declare and initialize multiple variables in separate state-
ments like this code. Many professionals declare variables in this manner, but bear in mind that the
single statement approach is more efficient.
The DOM’s slowness is primarily caused by two factors: re-rendering of the page caused by changes
in the DOM, and the sheer amount of data the DOM manages. You can optimize your code that
interacts with the DOM by limiting the number of updates you make to the DOM.
This code creates 20 <p/> elements, adds a text node to them, and adds them to the page. Figure 43-1
shows you what it looks like in the browser.
Figure 43-1
404 ❘ Lesson 43 Optimizing Your Code
There is technically nothing wrong with this code, but it is horribly inefficient because the DOM is
updated 20 separate times, once for each <p/> element that is added to the page. With each update
the browser has to redraw the page. This kind of page update can be noticeable by the user and
inhibit performance. To solve this problem you want to reduce the number of updates made to the
DOM, and you can do so by creating a document fragment.
Document fragments are lightweight document objects that can contain element objects that you
can manipulate as if they were inside the HTML document. To create a document fragment you use
the createDocumentFragment() method of the document object, like this:
var fragment = document.createDocumentFragment();
After you have created a fragment you can start adding element objects to it. In the case of this
example you can append each <p/> element to a document fragment and then add the fragment to
the HTML document. The following code illustrates this:
var fragment = document.createDocumentFragment();
document.body.appendChild(fragment);
Here, each <p/> element is appended to the document fragment. After the loop exits, the fragment is
appended to the document’s body node.
Using a document fragment in this way is advantageous because it reduces the amount of DOM updates
to one. Even though you append each <p/> element to the fragment, the fragment exists outside of
the DOM. So you don’t cause an update to occur until you append the fragment to document.body.
The document fragment object doesn’t generate HTML itself, but its children do. So the resulting
HTML in this code is exactly the same as it was without a document fragment.
Using innerHTML
Also in Lesson 14 you learned about the innerHTML property that you can use to add HTML to an
element. Instead of creating node objects you simply construct a string containing HTML markup
and assign it to an element’s innerHTML property. It’s simple, quick, and efficient — unless of course
you make multiple updates to the DOM. Let’s recreate the example from the previous section by
using innerHTML to add <p/> elements to the page with the following code:
for (var i = 0; i < 20; i++) {
var el = "<p>Paragraph " + i + "</p>";
document.body.innerHTML = document.body.innerHTML + el;
}
As in the previous section, this code creates 20 <p/> elements and adds them to the document’s
body. As you might have realized, this code is inefficient because you make 20 updates to the
Use Event Delegation ❘ 405
body — causing the browser to render the page with every update. You could fix this by creating a
variable and adding each HTML string to it, like this:
var html = "";
document.body.innerHTML = html;
But this code is inefficient too, because strings are immutable in JavaScript. The string value in html
has to be copied, destroyed, and recreated with every loop iteration. A better solution is to create an
array and push each HTML string to it, as shown in the following code:
var html = [];
document.body.innerHTML = html.join();
In this code a string value is added as an element in the html array. After the loop exits, the html
array’s join() method is called to convert the array to a string value, which is then assigned to the
body’s innerHTML property. By building a HTML string, or in this case an array, you can limit the
number of updates to the page.
You want the color of these <div/> elements to change when they’re clicked. You could add an
onclick event handler to each element, but you also want to limit the amount of event handlers
used in the page. To solve this problem, use event delegation by handling the document object’s
click event, like this:
var eventUtility = js24Hour.eventUtility;
eSrc.style.backgroundColor = color;
}
});
When the user clicks anywhere in the document, the event handler executes, and the code deter-
mines whether the element that received the mouse click is a <div/> element. If it is, its background
color is changed.
Using event delegation is an efficient way to handle events in your page. Simply assign a single event
handler for a particular event (click in this example), and use it to handle the same event for all
elements in the page.
Try It
In this lesson, you learn how to optimize your code after you have written it by refactoring, limiting
DOM updates, and using event delegation.
Lesson Requirements
You need a text editor. For Microsoft Windows users, Notepad is available by default on your
system or you can download Microsoft’s Visual Web Developer Express (www.microsoft.com/
express/web/) or Web Matrix (www.asp.net/webmatrix/), both of which are free. Mac OS X
users can use TextMate, which comes as part of OS X, or download a trial of Coda (www.panic
.com/coda/). Linux users can use the built-in VIM.
You also need a modern web browser. Choose any of the following:
➤➤ Internet Explorer 8+
➤➤ Google Chrome
➤➤ Firefox 3.5+
➤➤ Apple Safari 4+
➤➤ Opera 10+
You also need a webserver installed with PHP support. Refer to Lesson 31 on the DVD for installa-
tion instructions.
Try It ❘ 407
Create a subfolder called Lesson43 in your webserver’s root directory. Copy all the files from the
Lesson42 directory and paste them into the Lesson43 folder. Rename the lesson42_example01.htm
file to lesson43_example01.htm. As an exercise in code optimization you’ll refactor the ajaxUtility
object.
Step-by-Step
1. Open the ajaxUtility.js file in your text editor and look at it; specifically, look at the
makeGetRequest(), makePostRequest(), and postFromForm() methods. They are listed
here for your convenience:
makeGetRequest : function(url, callback) {
var xhr = this.createXHR();
xhr.open("GET", url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(null);
}
xhr.open("POST", url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(data);
}
xhr.open("POST", url);
408 ❘ Lesson 43 Optimizing Your Code
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(this.getRequestBody(form));
}
There’s a lot of code duplication here. All these methods create an XHR object, open the
connection, assign the onreadystatechange event handler (which calls the callback func-
tion), and send the request. These operations can be refactored into their own method, so
let’s do it.
2. Create a new method called openAndSend(). It will be responsible for creating the XHR
object, opening a GET or POST request, setting the onreadystatechange event handler, and
sending data (if applicable). So it should accept four arguments: the request type, the URL
to send the request to, the data to send in the request, and the callback function. Its code
follows:
openAndSend : function(type, url, data, callback) {
var xhr = this.createXHR();
xhr.open(type, url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(data);
}
Compare this code to the makeGetRequest() and makePostRequest() methods. They look
very similar, don’t they? In fact, the only differences are in the values passed to the XHR
object’s open() and send() methods.
3. So let’s refactor makeGetRequest() and makePostRequest() to call openAndSend().
Following is the new code:
makeGetRequest : function(url, callback) {
this.openAndSend("GET", url, null, callback);
Try It ❘ 409
},
In makeGetRequest(), openAndSend() is called by passing four things: the string "GET" for
the request type, the url parameter, null for the data, and the value passed to the callback
parameter. The makePostRequest() was altered to pass "POST", as well as the url, data,
and callback parameters.
Notice how these two methods did not change from an API perspective; you still call
makeGetRequest() and makePostRequest() as you did before. But underneath the hood
these methods do something different by calling the new openAndSend() method to do
all the work. When you’re refactoring code that’s already in use, it’s important to do your
best to keep the external portions the same. That way you can drop in the newly revised
ajaxUtility.js file without having to rewrite any code relying on the ajaxUtility object.
For example, when you’re finished refactoring this code you can use it in any example in this
book that uses the ajaxUtility object, without having to make any additional changes.
4. Now let’s look at the postFromForm() method. It’s the same as the makeGetRequest()
and makePostRequest() methods except for one small detail: It sets a Content-Type header
using the XHR object’s setRequestHeader() method. At first glance it looks as if we can’t
refactor this method the way you did the other two. Fortunately, however, you can.
The only thing postFromForm() does is set that header, so you can add another parameter
to openAndSend() to determine whether or not the Content-Type header is set — essentially
refactoring again. Following is the revised openAndSend() with the changes in bold:
openAndSend : function(type, url, data, callback, contentType) {
var xhr = this.createXHR();
xhr.open(type, url);
if (contentType) {
xhr.setRequestHeader("Content-Type", contentType);
}
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(data);
}
410 ❘ Lesson 43 Optimizing Your Code
In this revision, the contentType parameter is a string value (a content type). If data is
passed to it, the value passed to the contentType argument is used to set the Content-Type
header using the XHR object’s setRequestHeader(). If omitted, the header code is skipped
and the method continues executing as normal.
5. Now change postFromForm() to call openAndSend(), like this:
postFromForm : function(url, form, callback) {
this.openAndSend("POST", url, this.getRequestBody(form), callback,
"application/x-www-form-urlencoded");
}
Even though you changed the parameter list of openAndSend(), you do not have to change
the makeGetRequest() and makePostRequest() methods. Parameters in JavaScript are
always optional. You technically don’t have to pass anything to a function in order to exe-
cute it, but the function may not do what it’s supposed to do if you do not provide it with
the data it needs.
When an argument is not passed, the parameter contains a value of undefined. So when
makeGetRequest() and makePostRequest() call openAndSend() without passing a value
as the contentType parameter, contentType becomes undefined, a false value.
So once again your refactor session changed the way something worked without causing you
to rewrite the code that depended on it.
6. The ajaxUtility.js file should now look like this:
js24Hour.ajaxUtility = {
createXHR : function() {
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
} else {
var versions = [ "MSXML2.XmlHttp.6.0",
"MSXML2.XmlHttp.3.0" ];
return null;
},
getRequestBody : function(form) {
var pieces = [],
Try It ❘ 411
elements = form.elements;
return pieces.join("&");
},
xhr.open(type, url);
if (isForm) {
xhr.setRequestHeader("Content-Type", contentType);
}
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) "" status === 304) {
callback(xhr.responseText);
} else {
alert("An error occurred");
}
}
};
xhr.send(data);
}
};
7. Save ajaxUtility.js. Let’s test it to make sure everything works as it should. Point your
browser to http://localhost/Lesson43/lesson43_example01.htm. Fill out the form,
submit it, and you’ll see that it works exactly as it should.
412 ❘ Lesson 43 Optimizing Your Code
Please select Lesson 43 on the DVD to view the video that accompanies this
lesson. You can also download the sample code for all lessons from the book’s
website at www.wrox.com.
APPENDIX
System Requirements
Most reasonably up-to-date computers with a DVD drive should be able to play the screencasts
that are included on the DVD. You might also find an Internet connection helpful for down-
loading updates to this book.
You need a text editor to finish nearly every lesson. Most operating systems have one installed by
default, but you might want to download and install another text editor that offers more features
(especially if you’re using Windows). I use Microsoft’s Visual Web Developer Express 2010 in the
screencasts (www.microsoft.com/express/).
You also need a web browser. Almost all major consumer operating systems have one installed
by default, but you can download other browsers (or update yours) if you want. The following
list details the latest versions of the most used browsers and where you can download their
latest versions:
➤➤ Microsoft Internet Explorer 8 (www.microsoft.com/ie/ — for Windows only)
➤➤ Google Chrome 7 (www.google.com/chrome/)
414 ❘ Appendix What’s on the DVD?
The interface won’t launch if you have autorun disabled. In that case, click
Start ➪ Run (For Windows Vista, Start ➪ All Programs ➪ Accessories ➪ Run).
In the dialog box that appears, type D:\Start.exe. (Replace D with the proper
letter if your DVD drive uses a different letter. If you don’t know the letter, see
how your CD drive is listed under My Computer.) Click OK.
2. Read through the license agreement, and then click the Accept button if you want to use
the DVD.
3. The DVD interface appears. Simply select the lesson video you want to view.
You can also download all of the solutions to the Try Its at the book’s website. If you get stuck and
don’t know what to do next, visit the p2p forums (p2p.wrox.com), locate the forum for the book,
and leave a post. You can also contact me through my website at www.wdonline.com/contact/.
Troubleshooting
If you have difficulty installing or using any of the materials on the companion DVD, try the follow-
ing solutions:
➤➤ Turn off any anti-virus software that you may have running: Installers sometimes mimic virus
activity and can make your computer incorrectly believe that it is being infected by a virus. (Be
sure to turn the anti-virus software back on later.)
➤➤ Close all running programs: The more programs you’re running, the less memory is available
to other programs. Installers also typically update files and programs; if you keep other pro-
grams running, installation may not work properly.
➤➤ Reference the ReadMe: Please refer to the ReadMe file located at the root of the CD-ROM
for the latest product information at the time of publication.
➤➤ Reboot if necessary: If all else fails, rebooting your machine can often clear any conflicts in
the system.
Customer Care
If you have trouble with the CD-ROM, please call the Wiley Product Technical Support phone
number at (800) 762-2974. Outside the United States, call 1(317) 572-3994. You can also contact
Wiley Product Technical Support at http://support.wiley.com. John Wiley & Sons will provide
technical support only for installation and other general quality control items. For technical support
on the applications themselves, consult the program’s vendor or author.
To place additional orders or to request information about other Wiley products, please call (877)
762-2974.
Index
417
<a/> – attachEvent
A someVariable, 390
try, 359
<a/> trySubmit(), 266
for, 180 window, 102
calculator, 233 alert dialog box, 103 – 104, 187, 189, 323
click, 163 align, 139
innerHTML, 180 amount, 255
link, 180 AND logical operator, 38 – 40
onclick, 177 animated, 254
return, 170 animation
switch, 180 clearInterval(), 252
abbrName(), 80 elements, 249 – 259
abort, 159 anonymous functions, 29, 257, 398
absolute, 240 Apache, 308
absolute URL, 322 API. See Application Programming Interface
ActiveX, 312 appendChild(), 139, 403 – 404
ActiveXObject, 318 Application Programming Interface (API), 101
add(), 286, 292 cross-browser, 228 – 234
addDigit(), 167, 171, 177, 231 DOM, 129
addEvent, 198, 200 UI, 147
addEvent(), 226 arguments, 18
eventUtility, 198, 389 – 390 functions, 86
if...else, 198 insertBefore(), 139
setEventHandler(), 198 parameters, 27
addEventListener, 226 push(), 69
else, 200 arithmetic operators, 13 – 15, 37
self-executing functions, 228 Array, 68 – 71
addEventListener(), 186 – 188 arrays, 67 – 72
addEvent, 198 elements, 68, 71 – 72
attachEvent(), 194, 195 join(), 405
click, 187 joining, 70 – 71
event.target, 226 JSON, 342
addUser(), 336 literals, 68, 83, 379
Ajax. See Asynchronous JavaScript + XML strings, 71, 78
ajax_post.php, 334 assignment operators, 14
ajax_text.php, 333 Asynchronous JavaScript + XML (Ajax), 305 – 309
ajaxUtility.js, 334 – 335, 410 – 411 GET, 321 – 328
<script/>, 336 POST, 329 – 337
ajax.Utility.js, 407 asynchronous mode
alert(), 26, 62, 103 – 104, 199 GET, 323 – 325
catch, 365 XHR, 313 – 314
doSomething(), 250 attachEvent, 196
frames, 122, 123 attachEvent(), 188 – 189
responseText, 322, 323 addEvent, 198
<script/>, 116 addEventListener(), 194, 195
someParameter, 354
418
Attr – Clear
else, 197 C
onclick, 189, 196
Attr, 131 cache
attributes, HTML, 161 – 172, 266 external scripts, 5
augmentedValue, 28, 401 – 402 resultField, 231
augmentValue(), 27 – 29, 401 – 402 calculate, 184
calculate(), 31, 168, 171, 177, 182, 231
calculator, 231, 233
B call(), 95
call stack, 368
back end, 155
callback functions, 324
base data type, 94
callParentFunction(), 126, 127
bits, 20
camel-case, 11
bitwise operators, 38
case, 43 – 4 4, 241
block of code, 27, 60
case-sensitivity, 10 – 11
blur(), 264
mistakes, 354 – 355
blur, 264
sort(), 72
<body/>, 133
catch, 365
<div/>, 139, 207, 240, 253
CDATA, 4
<p/>, 131
change, 274
<script/>, 5
charAt(), 76, 78
<ul/>, 144
charset, 3
BOM. See Browser Object Model
checkbox, 295 – 296
Boolean, 19 – 20
checkboxes, 295 – 296
"boolean", 21
disabled, 300
borderLeftColor, 149
DOM, 295
border-left-color, 149
<input/>, 296
box, 240, 253
checked, 295 – 296
break, 43 – 4 4
disabled, 301
breakpoints, 367, 371 – 372
radio buttons, 297
Browser Object Model (BOM), 101
child directories, 130
btnSubmit, 270, 327
child elements, 208
built-in data types, 67 – 79
child frames, 123
button, 268
child node, 139
<button/>, 267, 268
child objects, 122
buttons, 267 – 271
child window, 109, 115
click, 267 – 268
childNodes, 132, 136
count, 268
className
radio, 297
button-over, 213
submit(), 267
CSS, 150
button-click, 211, 213
HTMLElement, 138
buttonContent, 268
message-style, 152
button-normal, 211, 213
mouseover, 213
button-over, 211, 213
<span/>, 212
clear, 184
Clear, innerHTML, 182
419
clearInterval – declarations(DOM)
421
Element – external scripts
422
falsy values – getCurrentPoint
F forms, document, 261
4xx, 315
falsy values, 20 frame_bottom.htm, 121 – 122, 127
feature detection, 195 frames, 121 – 128
Firebug alert(), 122, 123
CSS, 370 window, 121 – 125
debugging, 367 – 376 <frameset/>, 121
DOM, 371 frameset_main.htm, 126, 127
HTML, 369 – 370 frame_top.htm, 121, 126, 127
UI, 368 – 372 frmBottom, 121 – 122, 125
firstChild, 132 frmTop, 121, 125
childNodes, 136 front end, 155
<html/>, 133 func, null, 192
first-class status, functions, 26 Function, 91 – 98
firstName, 88, 94 – 95 call(), 95
5xx, 315 getDistance, 92
float, 149 prototype, 92 – 93
floating-point numbers, 13 functions, 25 – 33
focus, 264 anonymous, 29, 257, 398
focus() arguments, 86
element, 366 callback, 324
form controls, 264 declarations, 26 – 29
for expressions, 29
<a/>, 180 first-class status, 26
i, 57 global scope, 390 – 391
loops, 50 – 51, 180 methods, 67
iteration, 328 to other functions, 30 – 31
<ul/>, 144 recursion, 250
form, 264 returning, 31
<form/>, 261 scope, 58 – 60
elements, 262 self-executing, 199, 228, 392, 398
getRequestBody(), 331 values, 28, 29 – 30
name, 262 window, 102
onsubmit, 262, 266, 381, 385
submit(), 262
forms, 261 – 266 G
controls, 262 – 264
Garrett, Jesse James, 306
blur(), 264
GET
disabled, 264
Ajax, 321 – 328
focus(), 264
asynchronous mode, 323 – 325
name, 263
info.txt, 323
type, 264
XHR, 313, 321 – 323, 408
value, 263
getAttribute(), 138
HTML, 262
getCurrentPoint(), 85 – 86
POST, 329 – 332
423
getDate – id
getDate(), 74 H
GetDay(), 74
getDistance, 92, 93 handleOnLoad(), 174
getDistance(), 87, 91 hasChildNodes(), 132
identifiers, 93 <head>, 5, 131
Point, 92 height, 110
Point(), 92 hidden textboxes, 274 – 275
getElementById(), 134, 187 HTML
form controls, 264 adding, 137 – 145
<p/>, 269 attributes, 161 – 172, 266
<select/>, 284, 290 <button/>, 268
getElementsByTagName(), 133 – 134 decoupling from, 381 – 386
getFullName(), 88, 90, 96 – 98 DOM, 101, 130
getFullYear(), 74 errors, 381 – 386
getHandlerFunction(), 184, 231 escape sequences, 16
getHours(), 75 event, 266
getMilliseconds(), 75 events, 158
getMinutes(), 75 event handlers, 161 – 172, 266
getMonth(), 74 external scripts, 5
getMonthName(), 80 Firebug, 369 – 370
getRequestBody(), 335 forms, 262
<form/>, 331 innerHTML, 142
<select/>, 332 mouse pointer, 235
getSeconds(), 75 nodes, 132
getTarget(), 231 <script/>, 3 – 5
eventUtility, 238 style, 148
eventUtility.js, 229 XML, 340
srcElement, 226 html, 405
target, 226 <html/>, 131, 133
getTime(), 74 HTMLDivElement, 138
global scope, 57 – 58 HTMLElement, 138
avoidance of, 379, 389 – 398 HTTP. See HyperText Transfer Protocol
functions, 390 – 391 HTTPS, 117
namespaces, 392 hyperlinks, 110
window, 101 – 103 HyperText Transfer Protocol (HTTP), 117, 305
globalFunction(), 57 – 60
globalVar, 57, 61 I
Google Maps, 306 – 308
Google Suggest, 306 – 307 i, for, 57
guidelines, 377 – 386 id
gutter, 370 buttonContent, 268
<div/>, 139
HTMLElement, 138
myDiv, 161
<p/>, 268
424
identifiers – <input/>
425
insertBefore – JSON.parse
J
JavaScript Object Notation (JSON), 339 – 349
arrays, 342
conversion to and from, 343 – 345
deserialization, 342
<div/>, 348 – 349
<input/>, 348
POST, 344
processResponse(), 348
result, 348
serialization, 342
submitForm(), 348
variables, 342
whitespace, 67
join()
arrays, 70, 71, 405
string concatenation, 81
jQuery, 379
.js, 4
JSLint, 380
JSMIN, 382
JSON. See JavaScript Object Notation
JSON
parse(), 343
stringify(), 343
JSON.parse(), 345
426
keyboard events – loops()
K keypress, 155, 158
document, 207
keyboard events, 158 else...if, 207
keydown, 158, 274 textboxes, 274
keyup, 158, 274
keywords, 10
identifiers, 26
indexOf(), 77
lastIndexOf(), 77
list of, 22
L
language, 3
lastChild, 132, 133
lastIndexOf(), 76, 77
lastName, 88, 94 – 95
latency, 327
left
CSS, 388
style, 243
window options, 110
length, 67, 69, 179
document.forms, 261
NodeList, 296
link, 180
<link/>, 5
links, loops, 179 – 180
literals, 13, 379 – 380
arrays, 68, 83, 379
constructors, 379
objects, 83, 379
namespaces, 392
load, 158, 174
localVar, 59 – 60
location
child window, 115
navigation, 106
reload(), 105
window, 105 – 106
window options, 110
logical operators, 38 – 41
loopCounter, 50 – 51
loops, 49 – 56
for, 50 – 51, 180
427
loosely-typed languages() – mouseover()
iteration, 328 type, 212
counters, 50 mousemove, 157
do-while, 52 – 53 drag-and-drop, 238
infinite, 50, 250 dragObj.dragging, 245
iteration, 49 mouseHandler(), 241
links, 179 – 180 mouseout, 157, 213
post-test, 49 mouseover, 157
pre-test, 49 className, 213
while, 52 onmouseover, 161
loosely-typed languages, 10
lowerCaseMessage, 67
M
makeGetRequest(), 324 – 325, 333, 335, 407 – 410
makePostRequest(), 407 – 410
Markup Validation Service, 357
members, 66
menubar, 110
message, 116
messageParts, 80
message-style, 152
<meta/>, 105
methods, 65
Date, 74
DOM, 137 – 142
dragObj, 243
functions, 67
prototype, 93
String, 76
time, 75
window, 102
mistakes, 353 – 356
mouse
events, 157 – 158
pointer
drag-and-drop, 235 – 238
elements, 238
HTML, 235
mousedown, 157
drag-and-drop, 238
indexOf(), 241
mouseHandler(), 241
mouseHandler(), 240 – 241
dragObj, 242
eSrc, 212
<title/>, 224
428
mouseup() – openAndSend
opener, 116 pop(), 70
operands, 11 position, 94
operators, 11. See also specific operators or POST
operator types Ajax, 329 – 337
misuse of, 356 Content-Type, 332
precedence, 37 – 38 data, 332
Option, 292 forms, 329 – 332
<option/>, 283 – 285 JSON, 344
options, 292 makeGetRequest(), 333
OR logical operator, 38, 40 open(), 332
originalValue, 27 – 28, 401 – 402 send(), 332
overrideVar(), 62 XHR, 313, 332 – 333, 408
overriding, 390 postFromForm(), 333, 335, 407, 409
ownerDocument, 132 post-test loops, 49
pre-test loops, 49
preventDefault(), 227 – 228, 327, 337
P click, 268
<p/> eventUtility.js, 229
<body/>, 131 submit, 268
click, 186 previousSibling, 132
<div/>, 163 primitive data types, 12 – 21, 66
document fragments, 403 – 404 processData(), 328, 337
getElementById(), 269 processResponse(), 324 – 325, 333
getElementsByTagName(), 134 JSON, 348
id, 268 JSON.parse(), 345
<script/>, 134 progressive enhancement, 382
<span/>, 133 prompt(), 102, 104
parameters, 26 prompt dialog box, 104
arguments, 27 properties, 65
paramOne, 59 – 60 definitions, 84
parent, 124 DOM, 149
parent elements, 208 dragObj, 242
parent object, 127 prototype, 93
parent window, 122 read-only, 67
child frames, 123 variables, 67
parentNode, 132 window, 102
parse(), 343 prototype
parseFloat(), 18 – 19, 274 Function, 92 – 93
parseInt(), 18 – 19, 274 getDistance, 93
password, 274 methods, 93
Person(), 90 properties, 93
PHP, 330 prototype chaining, 95
pieces, 331 pseudo-namespaces, 392
Point, 91 push(), 69 – 70
getDistance(), 92
Point(), 92
430
radio buttons() – scope
R augmentedValue, 28
handleOnLoad(), 174
radio buttons, 297 innerHTML, 184
radix, 19 returnValue, 227
read-only, 67 reverse()
ready states, 314 Array, 70
readyState elements, 72
onreadystatechange, 314 – 315 sort(), 72
XHR, 323 root directory, 130
readystatechange, 314, 323 root element, 131
rectangle(), 63 root node, 131
rectangle, 63 rows, 275
recursion, 250 runtime errors, 357
refactoring, 399 – 402
relative URL, 322
relativeLeft, 242 S
relativeTop, 242
sayCoordinates(), 84
relativeX, 238
sayHello()
relativeY, 238
<script/>, 122
reload(), 105
window, 123
remove(), 285
scope, 57 – 64. See also global scope
removeAttribute(), 138
function, 60
removeEvent(), 226
functions, 58 – 60
removeEventListener(), 186 – 188
if, 60
replace(), 76, 78 – 79
no block-level, 60
reserved words
switch, 60
identifiers, 26
list of, 22
reset, 268
reset(), 171, 177
innerHTML, 170
onclick, 182
resizable, 111
resize, 159
resizeBy(), 115
resizeTo(), 114
response, 322
responseText
alert(), 322, 323
onreadystatechange, 323
XHR, 322
responseXML, 322
result, 348
resultField, 231
results, id, 166, 167
return
<a/>, 170
431
Wiley Publishing, Inc.
End-User License Agreement
READ THIS. You should carefully read these terms and condi- period of sixty (60) days from the date of purchase of this Book. If
tions before opening the software packet(s) included with this book WPI receives notification within the warranty period of defects in
“Book”. This is a license agreement “Agreement” between you materials or workmanship, WPI will replace the defective Software
and Wiley Publishing, Inc. “WPI”. By opening the accompanying Media.
software packet(s), you acknowledge that you have read and accept (b)WPI AND THE AUTHOR(S) OF THE BOOK DISCLAIM ALL
the following terms and conditions. If you do not agree and do not OTHER WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
want to be bound by such terms and conditions, promptly return WITHOUT LIMITATION IMPLIED WARRANTIES OF
the Book and the unopened software packet(s) to the place you MERCHANTABILITY AND FITNESS FOR A PARTICULAR
obtained them for a full refund. PURPOSE, WITH RESPECT TO THE SOFTWARE, THE
1. License Grant. WPI grants to you (either an individual or entity) PROGRAMS, THE SOURCE CODE CONTAINED THEREIN,
a nonexclusive license to use one copy of the enclosed software AND/OR THE TECHNIQUES DESCRIBED IN THIS BOOK.
program(s) (collectively, the “Software”) solely for your own WPI DOES NOT WARRANT THAT THE FUNCTIONS
personal or business purposes on a single computer (whether a CONTAINED IN THE SOFTWARE WILL MEET YOUR
standard computer or a workstation component of a multi-user net- REQUIREMENTS OR THAT THE OPERATION OF THE
work). The Software is in use on a computer when it is loaded into SOFTWARE WILL BE ERROR FREE.
temporary memory (RAM) or installed into permanent memory (c) This limited warranty gives you specific legal rights, and you
(hard disk, CD-ROM, or other storage device). WPI reserves all may have other rights that vary from jurisdiction to jurisdiction.
rights not expressly granted herein.
6. Remedies.
2. Ownership. WPI is the owner of all right, title, and interest,
(a) WPI’s entire liability and your exclusive remedy for defects in
including copyright, in and to the compilation of the Software
materials and workmanship shall be limited to replacement of the
recorded on the physical packet included with this Book “Software
Software Media, which may be returned to WPI with a copy of
Media”. Copyright to the individual programs recorded on the
your receipt at the following address: Software Media Fulfillment
Software Media is owned by the author or other authorized copy-
Department, Attn.: JavaScript 24-Hour Trainer, Wiley Publishing,
right owner of each program. Ownership of the Software and all
Inc., 10475 Crosspoint Blvd., Indianapolis, IN 46256, or call
proprietary rights relating thereto remain with WPI and its licensers.
1-800-762-2974. Please allow four to six weeks for delivery. This
3. Restrictions on Use and Transfer. Limited Warranty is void if failure of the Software Media has
(a) You may only (i) make one copy of the Software for backup resulted from accident, abuse, or misapplication. Any replacement
or archival purposes, or (ii) transfer the Software to a single hard Software Media will be warranted for the remainder of the original
disk, provided that you keep the original for backup or archival warranty period or thirty (30) days, whichever is longer.
purposes. You may not (i) rent or lease the Software, (ii) copy or (b) In no event shall WPI or the author be liable for any damages
reproduce the Software through a LAN or other network system whatsoever (including without limitation damages for loss of busi-
or through any computer subscriber system or bulletin-board sys- ness profits, business interruption, loss of business information, or
tem, or (iii) modify, adapt, or create derivative works based on the any other pecuniary loss) arising from the use of or inability to use
Software. the Book or the Software, even if WPI has been advised of the pos-
(b) You may not reverse engineer, decompile, or disassemble the sibility of such damages.
Software. You may transfer the Software and user documentation (c) Because some jurisdictions do not allow the exclusion or limita-
on a permanent basis, provided that the transferee agrees to accept tion of liability for consequential or incidental damages, the above
the terms and conditions of this Agreement and you retain no cop- limitation or exclusion may not apply to you.
ies. If the Software is an update or has been updated, any transfer
7. U.S. Government Restricted Rights. Use, duplication, or disclosure
must include the most recent update and all prior versions.
of the Software for or on behalf of the United States of America, its
4. Restrictions on Use of Individual Programs. You must follow the agencies and/or instrumentalities “U.S. Government” is subject to
individual requirements and restrictions detailed for each individual restrictions as stated in paragraph (c)(1)(ii) of the Rights in Technical
program in the “About the CD” appendix of this Book or on the Data and Computer Software clause of DFARS 252.227-7013,
Software Media. These limitations are also contained in the indi- or subparagraphs (c) (1) and (2) of the Commercial Computer
vidual license agreements recorded on the Software Media. These Software - Restricted Rights clause at FAR 52.227-19, and in similar
limitations may include a requirement that after using the program clauses in the NASA FAR supplement, as applicable.
for a specified period of time, the user must pay a registration fee
8. General. This Agreement constitutes the entire understanding of
or discontinue use. By opening the Software packet(s), you agree to
the parties and revokes and supersedes all prior agreements, oral
abide by the licenses and restrictions for these individual programs
or written, between them and may not be modified or amended
that are detailed in the “About the CD” appendix and/or on the
except in a writing signed by both parties hereto that specifically
Software Media. None of the material on this Software Media or
refers to this Agreement. This Agreement shall take precedence
listed in this Book may ever be redistributed, in original or modified
over any other documents that may be in conflict herewith. If
form, for commercial purposes.
any one or more provisions contained in this Agreement are
5. Limited Warranty. held by any court or tribunal to be invalid, illegal, or otherwise
(a) WPI warrants that the Software and Software Media are free unenforceable, each and every other provision shall remain in full
from defects in materials and workmanship under normal use for a force and effect.