Sie sind auf Seite 1von 214

Javascript: JSON and Ajax

Lesso n 1: Int ro duct io n t o J SON


Review o f Arrays and Objects
JavaScript Object No tatio n (JSON)
Why Use JSON?
Ho w to Use JSON in JavaScript
JSON.stringify()
Deserializing an Object

JSON o r XML?

Lesso n 2: Int ro duct io n t o Ajax


What Is Ajax?
Witho ut Ajax
With Ajax
The Ajax Request/Respo nse Mo del
Benefits o f Ajax
What Yo u Need in Order to Use Ajax

Lesso n 3: Yo ur First Ajax Applicat io n


The HTML & CSS
The JSON data
The JavaScript
The XMLHttpRequest Object
Handling the Respo nse
Testing the Ready State
Testing the Status
Pro cessing the Data
Overview o f XMLHttpRequest Request and Respo nse

Asynchro nicity Ro cks!


Co ntent Types and Headers
Cro ss-Do main Security Restrictio ns

Lesso n 4: T he T o -Do List Applicat io n


Create the Files fo r the To -Do List Applicatio n
Creating the JSON Data File
Creating the HTML and CSS
Adding the Co ntent

Create an array o f To -Do Objects


Update the Page with To -Do Items

Lesso n 5: Saving Dat a wit h Ajax


Adding and Saving a To -Do List Item
Add a Fo rm to Yo ur Page and Style It
Style the Fo rm
Pro cess the Fo rm with JavaScript
Create a New To -Do Object
Adding the New To -Do Item to the Page
The Case o f the Disappearing To -Do Item
Saving the Data with Ajax
Creating the PHP Server Script
Adding the JavaScript
Sending the Request

Lesso n 6 : Do cum e nt Fragm e nt s


Using Do cument Fragments to Add Elements to a Page
Using Do cument Fragments in the To -Do List Applicatio n
Co mbining Co mmo n Co de
Impro ving the Co de with a Do cument Fragment

Enhancing the To -Do List Applicatio n

Lesso n 7: Int ro duct io n t o Lo cal St o rage


What is Lo cal Sto rage?
Explo ring Lo cal Sto rage in the Bro wser
A Clo ser Lo o k at the lo calSto rage Object
Using the lo calSto rage Object with JavaScript
Lo cal Sto rage Sto res Strings

Iterating Thro ugh Lo cal Sto rage


Remo ving Items fro m Lo cal Sto rage
Co o kies (No t the Kind Yo u Can Eat)

Lesso n 8 : Updat ing t he T o -Do List Applicat io n f o r Lo cal St o rage


Sto ring Objects in Lo cal Sto rage
Saving To -Do Items in Lo cal Sto rage
Adding an ID to a To do Item

Getting To -Do Items fro m Lo cal Sto rage


The String substring() metho d

Lesso n 9 : De le t ing T o -Do List It e m s


Adding a Way to Delete a To -Do Item
The Delete Butto n Click Handler

A Pro blem With Our ID Scheme


An ID Scheme Using Time
Deleting Items fro m Lo cal Sto rage and the To -Do List
Mo ving the ID to the Parent Element

Lesso n 10 : St rings and St ring Me t ho ds


String Basics
Basic String Co mparisio n and Searching
Impro ving the Search
Chaining

The Substring() and Split() Metho ds


Regular Expressio ns
Regular Expressio ns Create a Pattern
Regular Expressio n Patterns
Matching Characters in a Range [ ]
Matching Multiple Characters with *, +, and { }
Matching Characters at a Specific Po sitio n
Matching Wo rds that End in "es"

Summary o f String Pro perties and Metho ds

Lesso n 11: Dat e s and Dat e Fo rm at t ing


What's the Date and Time Right No w?
Other Metho ds fo r Getting the Date and Time

Dates and Time Zo nes


Setting a Date and Time
Setting Date and Time Elements Separately with Metho ds
Co mparing and Setting Dates Relative to the Present

Co nverting Strings to Dates


Dates and HTML Input Types

Lesso n 12: Handling Exce pt io ns wit h T ry/Cat ch


What Causes an Exceptio n?
Thro wing Exceptio ns and the Finally Clause
Thro wing Exceptio ns
The Finally Clause

Using Exceptio ns and Try/Catch

Lesso n 13: Ge o lo cat io n and Go o gle Maps


Ho w Geo lo catio n Wo rks
Ho w the Bro wser Retrieves Yo ur Lo catio n

Getting Yo ur Lo catio n into a Web Page with Geo lo catio n


Handling Erro rs
Adding a Go o gle Map
Adding a Marker to the Map

Lesso n 14: Dat e s and Dat e Fo rm at t ing


Final Pro ject

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Introduction to JSON
Welco me to JavaScript 2! In this co urse, yo u'll learn advanced JavaScript, JSON and AJAX and ho w to use them to suit yo ur
pro fessio nal and creative go als.

Course Objectives
When yo u co mplete this co urse, yo u will be able to :

use JSON to serialize data fo r sto rage in the bro wser o r o n the server.
sto re and retrieve data using Ajax and Lo calSto rage.
o ptimize yo ur DOM manipulatio n co de with Do cument Fragments.
use Strings and Dates mo re effectively in yo ur co de.
catch erro rs with Exceptio ns.
add lo catio n and maps to yo ur applicatio ns with Geo lo catio n and Go o gle Maps.
mo dularize yo ur co de with Mo dernizr.
build a dynamic, interactive, fro nt-end web applicatio n.

Fro m beginning to end, yo u will learn by do ing yo ur o wn JavaScript-based pro jects using JSON and AJAX, and then handing
them in fo r instructo r feedback. These pro jects will result in an impressive final applicatio n which will add to yo ur po rtfo lio and
will co ntribute to certificate co mpletio n. Besides a bro wser and internet co nnectio n, all so ftware is pro vided o nline by the
O'Reilly Scho o l o f Techno lo gy.

Thro ugho ut the co urse, we'll build a To Do applicatio n that uses fo rm validatio n, lo cal sto rage, and Ajax. Ajax is a term used to
describe metho ds o f co mmunicating with reso urces external to yo ur JavaScript pro gram in o rder to send and retrieve data—
reso urces like a file, a database, o r a web service—as well as respo nding to user interactio ns and updating yo ur web
applicatio n dynamically. One o f the key co mpo nents o f Ajax is the data, so after a quick review o f o bjects and arrays, we'll take a
lo o k at ho w we can fo rmat data that we want to use in o ur web applicatio ns.

Learning with O'Reilly School of T echnology Courses


As with every O'Reilly Scho o l o f Techno lo gy co urse, we'll take a user-active appro ach to learning. This means that yo u
(the user) will be active! Yo u'll learn by do ing, building live pro grams, testing them and experimenting with them—
hands-o n!

To learn a new skill o r techno lo gy, yo u have to experiment. The mo re yo u experiment, the mo re yo u learn. Our system
is designed to maximize experimentatio n and help yo u learn to learn a new skill.

We'll pro gram as much as po ssible to be sure that the principles sink in and stay with yo u.

Each time we discuss a new co ncept, yo u'll put it into co de and see what YOU can do with it. On o ccasio n we'll even
give yo u co de that do esn't wo rk, so yo u can see co mmo n mistakes and ho w to reco ver fro m them. Making mistakes
is actually ano ther go o d way to learn.

Abo ve all, we want to help yo u to learn to learn. We give yo u the to o ls to take co ntro l o f yo ur o wn learning experience.

When yo u co mplete an OST co urse, yo u kno w the subject matter, and yo u kno w ho w to expand yo ur kno wledge, so
yo u can handle changes like so ftware and o perating system updates.

Here are so me tips fo r using O'Reilly Scho o l o f Techno lo gy co urses effectively:

T ype t he co de . Resist the temptatio n to cut and paste the example co de we give yo u. Typing the co de
actually gives yo u a feel fo r the pro gramming task. Then play aro und with the examples to find o ut what else
yo u can make them do , and to check yo ur understanding. It's highly unlikely yo u'll break anything by
experimentatio n. If yo u do break so mething, that's an indicatio n to us that we need to impro ve o ur system!
T ake yo ur t im e . Learning takes time. Rushing can have negative effects o n yo ur pro gress. Slo w do wn and
let yo ur brain abso rb the new info rmatio n tho ro ughly. Taking yo ur time helps to maintain a relaxed, po sitive
appro ach. It also gives yo u the chance to try new things and learn mo re than yo u o therwise wo uld if yo u
blew thro ugh all o f the co ursewo rk to o quickly.
Expe rim e nt . Wander fro m the path o ften and explo re the po ssibilities. We can't anticipate all o f yo ur
questio ns and ideas, so it's up to yo u to experiment and create o n yo ur o wn. Yo ur instructo r will help if yo u
go co mpletely o ff the rails.
Acce pt guidance , but do n't de pe nd o n it . Try to so lve pro blems o n yo ur o wn. Go ing fro m
Acce pt guidance , but do n't de pe nd o n it . Try to so lve pro blems o n yo ur o wn. Go ing fro m
misunderstanding to understanding is the best way to acquire a new skill. Part o f what yo u're learning is
pro blem so lving. Of co urse, yo u can always co ntact yo ur instructo r fo r hints when yo u need them.
Use all available re so urce s! In real-life pro blem-so lving, yo u aren't bo und by false limitatio ns; in OST
co urses, yo u are free to use any reso urces at yo ur dispo sal to so lve pro blems yo u enco unter: the Internet,
reference bo o ks, and o nline help are all fair game.
Have f un! Relax, keep practicing, and do n't be afraid to make mistakes! Yo ur instructo r will keep yo u at it
until yo u've mastered the skill. We want yo u to get that satisfied, "I'm so co o l! I did it!" feeling. And yo u'll have
so me pro jects to sho w o ff when yo u're do ne.

Lesson Format
We'll try o ut lo ts o f examples in each lesso n. We'll have yo u write co de, lo o k at co de, and edit existing co de. The co de
will be presented in bo xes that will indicate what needs to be do ne to the co de inside.

Whenever yo u see white bo xes like the o ne belo w, yo u'll type the co ntents into the edito r windo w to try the example
yo urself. The CODE TO TYPE bar o n to p o f the white bo x co ntains directio ns fo r yo u to fo llo w:

CODE TO TYPE:
White boxes like this contain code for you to try out (type into a file to run).

If you have already written some of the code, new code for you to add looks like this.

If we want you to remove existing code, the code to remove will look like this.

We may also include instructive comments that you don't need to type.

We may run pro grams and do so me o ther activities in a terminal sessio n in the o perating system o r o ther co mmand-
line enviro nment. These will be sho wn like this:

INTERACTIVE SESSION:

The plain black text that we present in these INTERACTIVE boxes is


provided by the system (not for you to type). The commands we want you to type look lik
e this.

Co de and info rmatio n presented in a gray OBSERVE bo x is fo r yo u to inspect and absorb. This info rmatio n is o ften
co lo r-co ded, and fo llo wed by text explaining the co de in detail:

OBSERVE:
Gray "Observe" boxes like this contain information (usually code specifics) for you to
observe.

The paragraph(s) that fo llo w may pro vide additio n details o n inf o rm at io n that was highlighted in the Observe bo x.

We'll also set especially pertinent info rmatio n apart in "No te" bo xes:

Note No tes pro vide info rmatio n that is useful, but no t abso lutely necessary fo r perfo rming the tasks at hand.

T ip Tips pro vide info rmatio n that might help make the to o ls easier fo r yo u to use, such as sho rtcut keys.

WARNING Warnings pro vide info rmatio n that can help prevent pro gram crashes and data lo ss.

T he CodeRunner Screen
This co urse is presented in Co deRunner, OST's self-co ntained enviro nment. We'll discuss the details later, but here's
a quick o verview o f the vario us areas o f the screen:

These video s explain ho w to use Co deRunner:

File Management Demo

Co de Edito r Demo

Co ursewo rk Demo

Review of Arrays and Objects


In the JavaScript 1 co urse, yo u learned abo ut arrays and o bjects. Yo u learned that an array is a co llectio n o f values,
usually related in so me way. Fo r instance, yo u can create an array o f co lo rs like this:

OBSERVE:
var springColors = [ "AF7575", "EFD8A1", "BCD693", "AFD7DB", "3D9CA8" ];

...o r an array o f temperatures like this:

OBSERVE:
var temperatures = [ 47.5, 63.2, 56.7, 53, 51.2 ];

Yo u also learned abo ut o bjects. Yo u learned that an o bject usually describes a thing, fo r instance a pet cat, and
co nsists o f pro perties o f that thing, each o f which has a name and a value:

OBSERVE:
var pickles = {
type: "cat",
name: "Pickles",
weight: 7
};

Let's make a simple HTML page with a JavaScript pro gram to create an array and an o bject, and display their values in
the co nso le. Create a new file in the Co de Edito r as sho wn:
CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>Introduction to JSON</title>
<meta charset="utf-8">
<script src="intro.js"></script>
</head>
<body>

</body>
</html>

Save this ( ) as int ro .ht m l in yo ur javascript 2/ fo lder (to create the fo lder, right-click the Ho m e fo lder in the File
Bro wser, select Ne w f o lde r... o r press Ct rl+n, type the new fo lder name javascript 2, and press Ent e r). As yo u can
see, this web page has no co ntent, but it do es have a link to a JavaScript file, int ro .js. Let's make that next. Click the
Ne w File ( ) ico n in the Co de Edito r to o lbar and type the co de sho wn:

CODE TO TYPE:
var springColors = [ "AF7575", "EFD8A1", "BCD693", "AFD7DB", "3D9CA8" ];
var temperatures = [ 47.5, 63.2, 56.7, 53, 51.2 ];
var pickles = {
type: "cat",
name: "Pickles",
weight: 7
};
window.onload = init;

function init() {
console.log(springColors);
console.log(temperatures);
console.log(pickles);
}

Save this ( ) as int ro .js in yo ur javascript 2/ fo lder. Make sure yo u have int ro .ht m l o pen, and click Preview (
). A new empty bro wser windo w (o r tab) appears.

To see the results o f the JavaScript co de, yo u need to o pen a co nso le windo w. If yo u need to , yo u can review the
video and the guide o n ho w to access the co nso le in yo ur bro wser:

The develo per to o ls are a little different in every bro wser, and they changing frequently as new versio ns o f bro wsers
are released, so yo u may have to be a little industrio us and do so me experimenting o n yo ur o wn. We've created a
basic guide to get yo u started, and a sho rt video that explains ho w to access the develo per to o ls, fo r Safari, Chro me,
and Firefo x, and a video fo r Micro so ft Internet Explo rer 9 .

When yo u o pen the co nso le, if yo u do n't see any results, just relo ad the page (intro .html). Yo u'll see the values o f the
two arrays and the o bject.
Take a careful lo o k at ho w these values are displayed; we'll co me back to co mpare these values with o ther values
sho rtly.

JavaScript Object Notation (JSON)


In this co urse, yo u'll learn ho w to co mmunicate with web services fro m yo ur JavaScript pro grams. A web service is a
file o r pro gram o n the web that o ften pro vides data yo u can use in yo ur web applicatio n. But what kind o f data, yo u
might be asking? All kinds! There are many types o f web services available o n the internet that o ffer their data fo r use
(Twitter, fo r example).

When yo u get data fro m a web service—even if it's just a lo cal file o n yo ur o wn co mputer—yo u need to kno w what
fo rmat the data is in. It co uld be just a plain text file co ntaining wo rds, o r a co mma-separated values (CSV) file, o r even
an XML file (eXtensible Markup Language, a co mmo n data interchange fo rmat).

The fo rmat we'll use in this co urse is called JavaScript Object Notation o r JSON. Why are we using this fo rmat?
Because the data fo rmat is almo st identical to the way yo u write arrays and o bjects in JavaScript, so it's easy fo r yo u (a
human) to read, and easy fo r JavaScript to understand! It's also a co mmo n fo rmat used to represent data in web
applicatio ns.

Let's start by lo o king at an example o f an array and an o bject written in JSON fo rmat. First, here's the springCo lo rs
array written in JSON, and also as we wro te it earlier in o ur Javascript pro gram:

OBSERVE:
JSON: ["AF7575","EFD8A1","BCD693","AFD7DB","3D9CA8"]
JavaScript: var springColors = [ "AF7575", "EFD8A1", "BCD693", "AFD7DB", "3D9CA8" ];

Co mpare the two fo rmats, here and where it printed to the co nso le. Pretty similar, right?
What abo ut the t e m pe rat ure s array?

OBSERVE:
JSON: [47.5,63.2,56.7,53,51.2]
JavaScript: var temperatures = [ 47.5, 63.2, 56.7, 53, 51.2 ];

Again, co mpare these to ho w the array lo o ks when it's printed in the co nso le. It lo o ks the same, right?

Finally, let's take a lo o k at the pickle s o bject:

OBSERVE:
JSON: {"type":"cat","name":"Pickles","weight":7}
JavaScript: var pickles = {
type: "cat",
name: "Pickles",
weight: 7
};

This time, there's a slight difference: each o f the pro perty names ("type," "name," and "weight") is enclo sed in do uble
quo tatio n marks. Otherwise, the JSON o bject is written the same way as the o bject in JavaScript. No te that the JSON
o bject is different fro m o bjects displayed in the co nso le, but that do esn't affect ho w yo u write the co de to create the
o bjects.

JSON can represent all o f the co re values yo u typically write in JavaScript: strings, numbers, and Bo o lean values, as
well as arrays and o bjects that use these kinds o f values in them. JSON can even represent o bjects that co ntain o ther
o bjects and arrays. Fo r instance, yo u can add the "likes" pro perty to the pickle s o bject, like this:

CODE TO TYPE:
var springColors = [ "AF7575", "EFD8A1", "BCD693", "AFD7DB", "3D9CA8" ];
var temperatures = [ 47.5, 63.2, 56.7, 53, 51.2 ];
var pickles = {
type: "cat",
name: "Pickles",
weight: 7,
likes: ["sleeping", "purring", "eating butter"]
};
window.onload = init;

function init() {
console.log(springColors);
console.log(temperatures);
console.log(pickles);
}

and , and the pickle s o bject in the co nso le no w displays the like s pro perty. If we represent the pickle s
o bject in JSON, it lo o ks like this:

OBSERVE:
{"type":"cat","name":"Pickles","weight":7,"likes":["sleeping", "purring", "eating butte
r"]}

The o nly difference in the JSON representatio n o f the like s pro perty o f the o bject and the Javascript is that in JSON,
the pro perty name is enclo sed in do uble quo tatio n marks ("likes").

So , why wo uld yo u want to represent an o bject o r array in JSON fo rmat, and how do yo u do it? I'm glad yo u asked...

Why Use JSON?


Imagine yo u run a pet ado ptio n center, and yo u need a web applicatio n that do es two things: allo ws yo u to enter new
pets up fo r ado ptio n, and sho ws yo u all the current pets available fo r ado ptio n.
Instead o f editing the who le HTML page each time yo u make a change to the list o f pets fo r ado ptio n, yo u want to be
able to lo ad the images o f individual pets dynamically into yo ur page whenever the page is lo aded. If yo u sto re the
data separate fro m the HTML and the JavaScript, then yo u wo n't have to edit the page each time (and risk making a
mistake) and it will be a lo t easier to update.

In additio n, yo u have to represent the data abo ut the pets so meho w. Yo u need to sto re the data in a fo rmat that yo ur
web applicatio n can understand. Yo u co uld invent a fo rmat, o r yo u co uld use an existing fo rmat like CSV, XML,
o r...JSON! Using JSON makes getting the data into yo ur applicatio n really co nvenient. Let's lo o k at ho w yo u might
represent a list o f pets available fo r ado ptio n using JSON:

OBSERVE:
["Pickles", "Tilla"]

This is an array o f all the pets available fo r ado ptio n. It includes o nly their names; yo u co uld have much mo re
info rmatio n abo ut each pet if yo u use o bjects. Create a new file that lo o ks like this:

CODE TO TYPE:
[ { "type":"cat",
"name":"Pickles",
"weight":7,
"likes":["sleeping","purring","eating butter"]
},
{ "type":"dog",
"name":"Tilla",
"weight":25,
"likes":["sleeping","eating","walking"]
}
]

Save the file in yo ur javascript 2/ fo lder as pe t s.jso n. Make sure yo u're in HTML mo de, and yo u can click Preview
( ) to see the co ntents o f the file. Of co urse, it's just data, so it wo n't actually do anything.

Make sure to type the co de abo ve just as you see it! Yo u can co py and paste the JSON into yo ur file if yo u
Note want, just to be certain.

We're no t go ing to do anything with this file just yet; we'll get to that in a later lesso n. Fo r no w, we'll learn ho w to turn an
array o f o bjects, just like the o ne abo ve, into a JSON string in yo ur JavaScript.

How to Use JSON in JavaScript


(Or: Se rializing a J avaScript Obje ct )

Let's say yo u have a JavaScript pro gram with two pet o bjects. Update int ro .js as sho wn:
CODE TO TYPE:
var springColors = [ "AF7575", "EFD8A1", "BCD693", "AFD7DB", "3D9CA8" ];
var temperatures = [ 47.5, 63.2, 56.7, 53, 51.2 ];
var pickles = {
type: "cat",
name: "Pickles",
weight: 7,
likes: ["sleeping", "purring", "eating butter"]
};

function Pet(type, name, weight, likes) {


this.type = type;
this.name = name;
this.weight = weight;
this.likes = likes;
}

window.onload = init;

function init() {
console.log(springColors);
console.log(temperatures);
console.log(pickles);

var pickles = new Pet("cat", "Pickles", 7, ["sleeping", "purring", "eating butter"]


);
console.log(pickles);

var tilla = new Pet("dog", "Tilla", 25, ["sleeping","eating","walking"]);


console.log(tilla);
}

Save it, o pen int ro .ht m l, and . In the co nso le, yo u see the two Pet o bjects yo u created:

No w, let's turn these o bjects into JSON. Update int ro .js as sho wn:
CODE TO TYPE:

function Pet(type, name, weight, likes) {


this.type = type;
this.name = name;
this.weight = weight;
this.likes = likes;
}

window.onload = init;

function init() {
var pickles = new Pet("cat", "Pickles", 7, ["sleeping", "purring", "eating butter"]
);
console.log(pickles);
var picklesJSON = JSON.stringify(pickles);
console.log(picklesJSON);

var tilla = new Pet("dog", "Tilla", 25, ["sleeping","eating","walking"]);


console.log(tilla);
var tillaJSON = JSON.stringify(tilla);
console.log(tillaJSON);
}

Save it, o pen int ro .ht m l, and . In the co nso le, yo u see the two Pet o bjects yo u created as befo re, and
under each o ne, their JSON representatio ns:

Co mpare the JavaScript o bjects with the JSON versio ns. They are very similar.

JSON.stringify()
To co nvert a JavaScript o bject to JSON, use the built-in J SON o bject and its metho d, st ringif y():

OBSERVE:
var pickles = new Pet("cat", "Pickles", 7, ["sleeping", "purring", "eating butte
r"]);
var picklesJSON = JSON.stringify(pickles);

First, we create a Pet o bject, pickle s. Yo u've seen this kind o f JavaScript befo re. Then, we use the
st ringif y() metho d o f the J SON o bject, and pass in the pickle s o bject as the argument. We get back a
JSON versio n o f the o bject, which we sto re in the variable pickle sJ SON. We did the same thing with t illa to
turn the t illa o bject into JSON.

The J SON o bject is built-in to JavaScript in all modern bro wsers, just like the do cum e nt o bject,
but yo u need to make sure yo u're using a fairly recent versio n o f yo ur favo rite bro wser. That
Note means IE9 +, Safari 5+, Chro me 18 +, Firefo x 11+, o r Opera 11+. So me o lder versio ns o f so me o f
these bro wsers have the JSON o bject, but fo r this co urse, use o ne o f these versio ns (o r later).

So no w let's create an array o f the two o bjects, and co nvert the array to JSON. Update int ro .js as sho wn:

CODE TO TYPE:

function Pet(type, name, weight, likes) {


this.type = type;
this.name = name;
this.weight = weight;
this.likes = likes;
}

window.onload = init;

function init() {
var pickles = new Pet("cat", "Pickles", 7, ["sleeping", "purring", "eating b
utter"]);
console.log(pickles);
var picklesJSON = JSON.stringify(pickles)
console.log(picklesJSON);

var tilla = new Pet("dog", "Tilla", 25, ["sleeping","eating","walking"]);


console.log(tilla);
var tillaJSON = JSON.stringify(tilla);
console.log(tillaJSON);

var petsArray = [ pickles, tilla ];


var petsArrayJSON = JSON.stringify(petsArray);
console.log(petsArrayJSON);
}

We are passing an array to JSON.stringify(). This is no t a pro blem. As with o bjects, yo u can turn an array into
JSON.

Save it, o pen int ro .ht m l, and . In the co nso le, yo u see the two Pet o bjects as befo re.
Underneath the two o bjects, yo u see the JSON string representatio n o f the array co ntaining the two o bjects:

Co mpare the JavaScript o bjects' appearance with the appearance o f the JSON-fo rmatted array o f o bjects.
JSON is called "JavaScript Object No tatio n" because it lo o ks just like JavaScript o bjects. The advantage is
that a JSON-fo rmatted o bject is a string. That means yo u can sto re it in a file, just like yo u did befo re when
yo u created the pe t s.jso n file.
If yo u co mpare the o utput in the co nso le that sho ws the JSON fo rmatted array to what yo u typed into
pe t s.jso n, yo u'll see they are exactly the same (igno re the extra white space yo u added in the file).

Turning an o bject into a string like this is kno wn as serializing an object. It is incredibly useful because it
allo ws yo u to sto re o bjects in plain text files, o r transfer them between web applicatio ns, even if tho se
applicatio ns are written in different languages. Let's say yo u have a Pet o bject in JavaScript, ho w wo uld yo u
give that o bject to , say, a PHP pro gram? The o nly way yo u can do that is to serialize the o bject. Once the
o bject is serialized, yo u can give it to the PHP pro gram, and if the PHP pro gram kno ws that the fo rmat is
JSON, the pro gram can deserialize the o bject (turn the string back into an o bject again), and vo ila! Yo u've just
transferred an o bject between two pro grams written in entirely different languages. That's co o l.

Deserializing an Object
What if yo u've go t an o bject represented in JSON and yo u want to get the o bject back? Update int ro .js as
sho wn:

CODE TO TYPE:

function Pet(type, name, weight, likes) {


this.type = type;
this.name = name;
this.weight = weight;
this.likes = likes;
}

window.onload = init;

function init() {
var pickles = new Pet("cat", "Pickles", 7, ["sleeping", "purring", "eating b
utter"]);
console.log(pickles);
var picklesJSON = JSON.stringify(pickles);
console.log(picklesJSON);

var anotherPickles = JSON.parse(picklesJSON);


console.log(anotherPickles);

var tilla = new Pet("dog", "Tilla", 25, ["sleeping","eating","walking"]);


console.log(tilla);

var petsArray = [ pickles, tilla ];


var petsArrayJSON = JSON.stringify(petsArray);
console.log(petsArrayJSON);
}

Save it, o pen int ro .ht m l, and . In the co nso le, yo u see the JSON representatio n o f the
pickle s o bject, and belo w that yo u see a new Pet o bject that was co nverted back into an o bject fro m JSON:

So what did we just do ?

OBSERVE:
var pickles = new Pet("cat", "Pickles", 7, ["sleeping", "purring", "eating butte
r"]);
var picklesJSON = JSON.stringify(pickles);
console.log(picklesJSON);
First, we created a new pickle s o bject. Then we co nverted that o bject to JSON using J SON.st ringif y(), and
sto red the result in the variable pickle sJ SON. Next, we printed pickle sJ SON to the co nso le.

OBSERVE:
var anotherPickles = JSON.parse(picklesJSON);
console.log(anotherPickles);

We to o k pickle sJ SON and we passed it to the J SON.parse () metho d. J SON.parse () do es the o ppo site o f
what J SON.st ringif y() do es: it takes a piece o f data fo rmatted as JSON, and co nverts it back into a
JavaScript o bject (o r array). This returns an o bject, ano t he rPickle s, which we print to the co nso le.

It's impo rtant to reco gnize that yo u get back ano ther o bject, not the same o bject as o ur o riginal pickle s
o bject. It's like pickle s, in the sense that it has the same pro perties and pro perty values, but no w we have two
different o bjects, pickle s and ano t he rPickle s.

So , J SON.st ringif y() takes an o bject and se rialize s it into JSON fo rmat; and J SON.parse () takes a piece
o f data in JSON fo rmat and de se rialize s it into a JavaScript o bject.

This allo ws yo u to transfer o bjects (o r arrays) between JavaScript pro grams, between web services and web
applicatio ns, and beyo nd. In the next co uple o f lesso ns, yo u'll see ho w yo u can use JSON with Ajax (a way
o f co mmunicating with external reso urces fro m yo ur JavaScript pro gram) to lo ad and save data fro m yo ur
web applicatio n.

JSON or XML?
Yo u may have heard o f XML o r even used it in web applicatio ns befo re, as a data exchange fo rmat. Yo u might be
wo ndering whether we co uld also use XML to represent o bjects and arrays, and exchange data with web services.
Yes, we co uld. In general, yo u may cho o se to use XML o r JSON to represent yo ur data; it depends o n the co ntext, the
type o f data, the web applicatio n, o r the o ther pro grams o r web services with which yo u might be exchanging data.

The go o d news is that anything yo u can represent in XML, yo u can also represent in JSON. While XML has additio nal
features that execute tasks like validating yo ur data to make sure it's co rrect, the kinds o f data that can be represented
are the same. So why cho o se JSON?

JSON is simpler to parse (that is, it's simpler to co nvert data in JSON fo rmat to a JavaScript o bject o r array). A piece o f
data in JSON fo rmat is typically smaller in size than the same data in XML, and it's so mewhat easier fo r us humans to
read. JSON is well suppo rted in JavaScript and web applicatio n to o ls, but it is no t so well suppo rted in o ther areas (fo r
example, library systems, co ntent management systems, and so o n), where XML has bro ad suppo rt. Fo r instance, yo u
can get text edito rs that are specifically designed to help yo u write XML.

If yo u want to use yo ur data in a variety o f different co ntexts, yo u might cho o se XML. Ho wever, fo r web applicatio ns
specifically, JSON is easier to wo rk with; that's what we'll be using in this co urse.

Take so me time to practice JSON and do the pro ject befo re mo ving o n to the next lesso n. We'll use JSON to sto re and retrieve
values thro ugho ut the rest o f the co urse.

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Introduction to Ajax
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

to create web pages that behave mo re like applicatio ns than pages.


use Ajax to add new data to yo ur web page witho ut having to relo ad the page o r lo ad an entirely new page.
send and receive data while yo u type, using asynchro no us co mmunicatio n.
update a page dynamically with new info rmatio n, o ften with the data received fro m a server.

What Is Ajax?
We've mentio ned Ajax a co uple o f times no w. So , what is Ajax and ho w are we go ing to use it?

A few years ago , befo re any websites used Ajax, if yo u wanted to get info rmatio n o n the web, yo u clicked a link, waited
fo r the current page to go away, and then waited fo r a new page to lo ad with the info rmatio n yo u wanted. Fo r instance, if
yo u were using a website like Amazo n and searching a database o f bo o ks, yo u entered a bo o k title in the search bar,
and waited fo r a new page to lo ad with the results. Or if yo u were bro wsing Go o gle fo r search results, each time yo u
clicked "next," yo u waited fo r a new page to lo ad with the results. This is the way we all expect the web to behave mo st
o f the time.

No w imagine yo u are using a deskto p applicatio n like an edito r. When yo u select a wo rd and apply, say, "bo ld" to that
wo rd, yo u do n't have to wait fo r the page to lo ad, right? The wo rd just turns bo ld right then and there. This is the way we
expect applicatio ns to behave; we can see the results o f the actio ns we take immediately, witho ut having to wait fo r a
new page to lo ad with the result.

Ajax is a term that is used to describe a co llectio n o f techniques. These techniques allo w yo u to create web pages that
behave mo re like applications than pages. So me o f these techniques yo u already kno w, like ho w to add and remo ve
elements fro m the DOM so yo u can make yo ur page respo nd to user input, o r ho w to change the style o f elements o n
the fly so yo u can change the way the page lo o ks. Yo u execute all o f that witho ut relo ading the page o r lo ading an
entirely new page.

Ajax is also able to add new data to yo ur web page, again witho ut having to relo ad the page o r lo ad an entirely new
page. That's impo rtant. Let's see what that means exactly; we'll take a lo o k at ho w fo rms wo rk witho ut Ajax first, and
then with Ajax, so yo u can see the difference.

Without Ajax
Imagine o nce again that yo u have a pet ado ptio n center. The main page fo r yo ur ado ptio n center website includes a
fo rm at the to p fo r submitting new pets available fo r ado ptio n, and an area belo w where yo u display all available pets. If
yo u submit a new pet using the fo rm, it wo uld wo rk this way witho ut Ajax:
No w, if yo u wanted to submit a seco nd pet, the same pro cess wo uld be required:
Each time yo u submit data fo r ano ther pet, the PHP script has to recreate the entire page: the HTML and the data. This
means yo u have to wait fo r the entire page to lo ad each time yo u submit a new pet.

With Ajax
No w let's take a lo o k at what this interactio n lo o ks like when we use Ajax:
We take these steps:

Submit the fo rm data to a PHP script.


Stay o n the current page, no t lo ading a new page.
Request pet data fro m the server script. Instead o f respo nding with an entire web page, the server script
respo nds with just the data we need.
Take the data we get back fro m the server and update the page by adding a new list item to the DOM
co ntaining the pet info rmatio n.

All these steps co mbined co mprise "Ajax."

T he Ajax Request/Response Model


Yo u already kno w a lo t abo ut ho w to update yo ur page dynamically based o n user interactio n, so we'll fo cus o n the
o ther primary capability o f Ajax in the next few lesso ns: sending data to a server, requesting data fro m a server, and
using data received fro m a server to update a page. In Ajax, this is kno wn as the Ajax Request/Response Model. Let's
co mpare the traditio nal mo del with the Ajax mo del again. Here's the traditio nal mo del:

And here's the Ajax mo del:


Benefits of Ajax
One o f the first co mpanies to use Ajax in a big way was Go o gle. Yo u can see Ajax at wo rk in many Go o gle
applicatio ns, including Go o gle Maps and Go o gle Search. Try go ing to use Go o gle Search no w. As yo u start typing in
wo rds, Go o gle will suggest wo rd co mpletio ns fo r yo u. That's Ajax. Here's ho w it wo rks:
This search page is:

No t waiting until yo u submit the fo rm to send data to the server. Instead, the page sends the info rmatio n yo u
type as yo u type it.
Requesting small amo unts o f additio nal data, no t an entirely new page.
No t interrupting yo ur user interactio n experience with the page while the data yo u've typed is sent to the
server. Yo u just keep typing as yo u no rmally wo uld.
Getting the respo nse fro m the server—the wo rds that begin with the letters yo u've typed so far—and
updating the page dynamically by adding tho se wo rds to a list in the page.

It's no t until yo u finish typing and submit yo ur search request that a who le new page is lo aded. Just imagine what this
"suggest" experience wo uld be like if a new page was lo aded each and every time yo u typed a character into the
search bo x—so slo w and anno ying!

The ability to send and receive data while yo u type is called Asynchro no us co m m unicat io n. It means that yo ur
page can request data and wait fo r data witho ut ho lding up the user interface o f yo ur web page. Yo u can keep do ing
what yo u're do ing o n the web page while yo ur page sends and waits fo r and receives data behind the scenes. This
Asynchro no us co mmunicatio n is the "A" in Ajax.

So , yo u've seen the two key features and benefits o f using Ajax in yo ur page:

The ability to send data to and receive data fro m the server witho ut causing the page yo u're o n to hang o r
lo ad a who le new page
The ability to update a page dynamically with new info rmatio n, o ften with the data received fro m a server

What You Need in Order to Use Ajax


Ajax isn't a single to o l, but rather several techno lo gies that wo rk to gether to create web pages that act mo re like
applicatio ns. Creating Ajax web pages has beco me so po pular that we no w refer to pages like this as web
applications. Yo u'll find all kinds o f web applicatio ns o n the web, fro m mapping applicatio ns to games to utilities.
Almo st anything yo u can do o n the deskto p, yo u can no w do o n the web. So , what exactly do yo u need to create an
Ajax web applicatio n?

A We b Bro wse r Web applicatio ns that use Ajax are built using web techno lo gies, and used within a web
bro wser.
HT ML & CSS Yo u'll need HTML and CSS to create a web page to display the co ntent o f yo ur web
applicatio n in the bro wser.
J SON Yo u'll use the JSON fo rmat fo r the data that yo u'll send, sto re, and retrieve using yo ur web
applicatio n. JSON isn't required fo r Ajax applicatio ns; we co uld use ano ther fo rmat instead, but we'll use
JSON in this co urse.
J avaScript JavaScript is where yo u'll do mo st o f the wo rk when yo u're building a web applicatio n. Yo u'll
use a built-in JavaScript o bject, the XMLHttpRequest o bject, to request and receive data fro m a web server.
Yo u'll see ho w this wo rks in the next lesso n. Yo u'll use JavaScript to get data fro m a fo rm, update a page by
adding and remo ving elements fro m the DOM, as well as updating the style o f yo ur page.
A We b Se rve r The web server receives requests fo r data fro m yo ur web applicatio n, and respo nds with that
data. So metimes this is as simple as ho sting a file co ntaining data o n the web server, and requesting and
receiving all o f the co ntents o f that file. Other times, yo u'll make requests to mo re co mplicated server
applicatio ns that update and get results fro m a database o r retrieve data fro m a web service like Twitter o r
Facebo o k. Fo r this co urse, we'll keep it relatively simple because we're fo cusing o n JavaScript, no t server
script develo pment, but yo u'll get the idea (and yo u can wo rk o n mo re co mplicated server scripts if yo u
decide to take the PHP o r Ruby o n Rails co urses).

So get ready! In the next lesso n, yo u'll be building yo ur first Ajax web applicatio n!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Your First Ajax Application
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

create a simple HTML file with a little CSS fo r an Ajax applicatio n.


use this JSON file as input data fo r an Ajax applicatio n.
dynamically update yo ur page with co ntent lo aded fro m a file using Ajax.
use XMLHttpRequest to request the data.
test and pro cess the data yo u get back fro m a request.
use co ntent types and headers.

It's time to build yo ur first Ajax applicatio n! As yo u learned in the previo us lesso n, yo u need these pieces:

A web bro wser: check—yo u're reading this! :-)


The HTML & CSS: we'll create a small HTML file sho rtly.
The JSON Data: yo u need data; we'll be using the pets.jso n file yo u created in the Intro ductio n to JSON lesso n fo r
this example.
The JavaScript: yo u'll write the JavaScript co de that uses Ajax.
Web Server: we'll use the O'Reilly Scho o l o f Techno lo gy web servers, just like we've do ne all alo ng. If yo u want to
use Ajax o n yo ur o wn co mputer at ho me, yo u'll need to set up a web server. We'll leave that up to yo u. Ano ther
o ptio n wo uld be to get signed up fo r a ho sted website, and uplo ad all yo ur files there.

T he HT ML & CSS
Let's create a simple HTML file with a little CSS fo r o ur Ajax applicatio n. This applicatio n will lo ad the pets data yo u
entered earlier and then update the web page with that data:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>Your First Ajax Application: Pets</title>
<meta charset="utf-8">
<script src="pets.js"></script>
<style>
body {
font-family: Helvetica, Arial, sans-serif;
}
legend {
font-weight: bold;
}
</style>
</head>
<body>
<div>
<fieldset>
<legend>Pets available for adoption</legend>
<div id="pets">
</div>
</fieldset>
</div>
</body>
</html>

Save this file in yo ur /javascript 2 fo lder as pe t s.ht m l and click . Yo u see the web page, it lo o ks like
like this:
We link to a JavaScript file named pe t s.js in the head o f the HTML; this is where yo u'll write JavaScript that uses Ajax
to get the pets data and update the page. We'll update the page by adding the JSON we get fro m the pe t s.jso n file to
the " pe t s" <div> in the bo dy o f the HTML. We'll get to that in a bit.

In the HTML, we use a <fieldset> element. In case yo u haven't seen it befo re, yo u can use it to create a gro up o f items.
It's o ften used with fo rms to gro up fo rm elements to gether. The text in the <legend> element is displayed at the to p o f
the fieldset. Our list o f pets will appear inside the fieldset o nce we start adding pets to it. We added a bit o f CSS to
change the fo nt to a sans-serif fo nt (which lo o ks better o n co mputer screens) and to make the legend text bo ld.

T he JSON data
Next, o pen the /javascript 2/pe t s.jso n file yo u created in the first lesso n in HTML mo de, and click . We'll
use this JSON file as input data fo r yo ur first Ajax applicatio n. Yo u see a web page with the JSON data, it lo o ks like
this:
T he JavaScript
No w co mes the really fun part—the JavaScript! Create a new file and type in this co de:

CODE TO TYPE:
window.onload = init;

function init() {
getPetData();
}

function getPetData() {
var request = new XMLHttpRequest();
request.open("GET", "pets.json");
request.onreadystatechange = function() {
var div = document.getElementById("pets");
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
div.innerHTML = this.responseText;
}
else {
div.innerHTML = "Error: no data";
}
}
};
request.send();
}

Save this file in yo ur /javascript 2 fo lder as pe t s.js. Open pe t s.ht m l, and click Preview ( ). Or, if yo ur
pe t s.ht m l web page is already o pen, click Re lo ad (to lo ad the new JavaScript). Yo u see the pets fro m the pe t s.jso n
file in yo ur bro wser; it lo o ks like this:

We're displaying o nly the JSON text, so it do esn't lo o k very pretty, but yo u've just dynamically updated yo ur page with
co ntent lo aded fro m a file using Ajax. Co ngratulatio ns!

Let's take a lo o k at what's go ing o n in this co de. Yo u pro bably reco gnize so me o f it, like where we set up the o nlo ad
handler:

OBSERVE:
window.onload = init;

function init() {
getPetData();
}

function getPetData() {
...
}

First we set up an o nlo ad handle r f unct io n by setting the o nlo ad pro pe rt y o f t he windo w o bje ct to the init
functio n. This tells JavaScript that after the bro wser has co mpleted lo ading the page, it sho uld call the init () functio n.

init () calls ano ther functio n, ge t Pe t Dat a(). ge t Pe t Dat a() is respo nsible fo r getting the data in the pe t s.jso n file
and lo ading it into the page.

No w we'll take a clo ser lo o k at what's go ing o n the ge t Pe t Dat a() functio n.

T he XMLHttpRequest Object
The XMLHt t pRe que st o bject is the heart o f mo st Ajax pro grams. It lets yo u talk to the wo rld o utside the bro wser. Yo u
use it to send requests to a web server to retrieve data. As is the case with so me o ther built-in JavaScript o bjects like
do cum e nt , windo w and J SON, XMLHt t pRe que st is a built-in o bject that is available in all mo dern bro wsers.
XMLHttpRequest is no t suppo rted by IE6 . Still, even tho ugh IE6 isn't used much anymo re, it's wo rth
Note paying so me attentio n to , just in case yo u need to suppo rt it with an Ajax applicatio n. In o ur example,
yo u'll need to test to see whther the bro wser is IE6 and if it is, use the ActiveXObject instead.

OBSERVE:
function getPetData() {
var request = new XMLHttpRequest();
request.open("GET", "pets.json");
request.onreadystatechange = function() {
...
};
request.send();
}

Let's break it do wn. First, we create a new XMLHt t pRe que st o bject and save it in the re que st variable. The
XMLHt t pRe que st o bject co ntains pro perties and metho ds that yo u can use to make requests fo r data and receive
respo nses to tho se requests.

We use XMLHt t pRe que st here to request the data in the file pe t s.jso n. To make the request fo r this data, we have
to create a request; we do that with the re que st .o pe n() metho d. We pass two arguments to this metho d: the m e t ho d
we're using to make the request, in this case GET , and the reso urce we're requesting, in this case the file pe t s.jso n.
This is the same kind o f GET request yo u make with an HTML fo rm: it sends an HTTP request to a web server to "get"
a reso urce. It is also co mmo n to make a request using POST ; the differences between GET and POST are beyo nd the
sco pe o f this co urse.

The re que st .o pe n() metho d do esn't actually send the request; to do that we need to use the metho d
re que st .se nd(). But befo re we actually send the request, we want to indicate what we're go ing to do when we get a
respo nse!

The web server—in this case, the O'Reilly Scho o l o f Techno lo gy web server— will get yo ur request fo r the file
pe t s.jso n. It will respo nd by sending back the data that yo u requested. What do we do when we get a respo nse? Well,
we'll call a functio n that we've assigned to the re que st .o nre adyst at e change pro perty. This is similar to the way
windo w.o nlo ad wo rks; with windo w.o nlo ad yo u assign a functio n to the pro perty that the bro wser will call when the
page has finished lo ading. All yo u do is create the functio n; the bro wser calls it fo r yo u.

re que st .o nre adyst at e change wo rks the same way. We assign a functio n to the o nre adyst at e change pro perty o f
the re que st o bject. We're in essence saying, "When yo ur re ady st at e change s, call this functio n."

So , what's a ready state? Remember, the request object is the XMLHt t pRe que st o bject that yo u created to make all
this Ajax co mmunicatio n happen; when it sends o ut a request, it go es thro ugh a series o f ready states that indicate
whether the respo nse is ready fo r yo u to use. The ready state is o ne o f several po ssible integer values that indicate the
status o f the XMLHttpRequest o bject. Fo r instance, it co uld be sending the request, awaiting a respo nse, o r it co uld be
do ne and ready fo r yo u to pro cess the results. Here are the po ssible values fo r the ready state:

Num be r Pro pe rt y De script io n


0 UNSENT o pen() hasn't been called yet.
1 OPENED o pen() has been called.
2 HEADERS_RECEIVED The headers have been received.
3 LOADING The respo nse bo dy is being received.
4 DONE The respo nse is co mplete and ready fo r pro cessing.

Here's an o verview o f ho w XMLHttpRequest wo rks:


Handling the Response
No w, let's delve into the functio n that gets called when o ur JavaScript gets a respo nse fro m the server. This is
the callback function:
OBSERVE:
function getPetData() {
var request = new XMLHttpRequest();
request.open("GET", "pets.json");
request.onreadystatechange = function() {
var div = document.getElementById("pets");
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
div.innerHTML = this.responseText;
}
else {
div.innerHTML = "Error: no data";
}
}
};
request.send();
}

The callback f unct io n is an anonymous function. Yo u've seen ano nymo us functio ns befo re, fo r instance,
when we set the value o f an o bject pro perty to a metho d. The end result is that using an ano nymo us functio n
is exactly the same as using a name, but it bypasses the step where the functio n is defined with a name. Fo r
instance, instead o f:

OBSERVE:
request.onreadystatechange = function() {
...
};

...yo u co uld write:

OBSERVE:
request.onreadystatechange = handleRequest;
function handleRequest() {
...
}

In bo th cases, yo u're setting the value o f the o nre adyst at e change pro pe rt y to a functio n; in the first
example, that f unct io n has no nam e , while in the seco nd, it has the name handle Re que st . Using an
ano nymo us functio n just skips the step where yo u name the functio n.

Ano nymo us functio ns are used frequently in JavaScript, so it's a go o d idea to get used to seeing them
written.

T esting the Ready State


In o ur Ajax example, we set the value o f the re que st .o nre adyst at e change pro perty to a callback
f unct io n that will be called when a change o ccurs in the ready state o f the request object. Let's take ano ther
lo o k at the co de to see what happens in the f unct io n that's called when the ready state changes:
OBSERVE:

function getPetData() {
var request = new XMLHttpRequest();
request.open("GET", "pets.json");
request.onreadystatechange = function() {
var div = document.getElementById("pets");
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
div.innerHTML = this.responseText;
}
else {
div.innerHTML = "Error: no data";
}
}
};
request.send();
}

We test both the re adySt at e and the st at us in the callback f unct io n; we need to make sure bo th have a
specific value befo re co ntinuing. The && makes sure that bo th expressio ns are true befo re the bo dy o f the if
statement is run. Let's lo o k at what the re adySt at e and st at us tell us.

The ready state that we're mo st interested in is the DONE state; this indicates that the respo nse is co mplete.
So , in the f unct io n, we're testing to see whether the re adyst at e is equal to the DONE value. But what is
t his?

Yo u've actually enco untered t his befo re; in o bject metho ds, we can refer to the o bject who se metho d we're
using as t his. It refers to this object. It's wo rks the same way here. By setting a f unct io n as the value o f the
o nre adyst at e change pro perty o f the re que st o bject, yo u've created an o bject metho d. So here, t his is the
re que st o bject itself—the o bject that co ntains the metho d.

No w that yo u kno w what t his is, yo u can see that t his.re adySt at e is the ready state o f this object—the
request o bject. We're testing to see if it's DONE, where DONE is a pro perty also defined in the request o bject,
so we can co mpare the value o f the re adySt at e to the value o f the DONE to see if they are equal. If they are,
we kno w we go t a respo nse and that it's co mplete.

T esting the Status


But just kno wing that the respo nse is DONE isn't quite eno ugh to be sure that we can pro ceed; we need to
make sure that everything went o kay. Just like when yo u type a URL into a bro wser windo w and send o ff an
HTTP request to get a file, that request co uld fail if say, the server is do wn, o r the file do esn't exist. Yo u might
have seen the infamo us "50 0 Internal Server" erro r, o r the "40 4 No t Fo und" erro r befo re.

We're sending an HTTP request here to o ; the server will respo nd and let us kno w if everything went o kay o r if
so mething went wro ng. This part o f o f the respo nse is put into the request o bject's st at us pro perty. If the
st at us is 20 0 , then we kno w everything went o kay, and we can pro ceed.

Processing the Data


Ah, finally! Yes, it's taken a lo t to get to this po int, but no w we're ready to pro cess the data we go t back fro m
the request. We kno w that we have a respo nse (readyState is DONE), and we kno w that everything went
acco rding to plan (status is 20 0 ), so let's get the JSON data in the respo nse.
OBSERVE:
function getPetData() {
var request = new XMLHttpRequest();
request.open("GET", "pets.json");
request.onreadystatechange = function() {
var div = document.getElementById("pets");
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
div.innerHTML = this.responseText;
}
else {
div.innerHTML = "Error: no data";
}
}
};
request.send();
}

When the respo nse is co mplete, and if everything went as planned, the data is put into the request o bject's
re spo nse T e xt pro perty. Again, we refer to the request o bject using t his, so we can access the data using
t his.re spo nse T e xt .

We want to make sure that the re spo nse T e xt actually has so mething in it, so we co mpare re spo nse T e xt
to null. If it's no t null, we kno w there is so me data present, so we update the inne rHT ML pro perty o f the
" pe t s" <div> with the JSON data that we go t fro m the pe t s.jso n file. No tice that we go t the " pe t s" <div> at
the very to p o f the callback f unct io n. When yo u lo ad the page, yo u see this data in the <div> o n the page.

If there was a pro blem and the "pets.jso n" file is empty, o r the re spo nse T e xt didn't get set co rrectly and is
null, then we can put the erro r message into the " pe t s" <div> instead.

Overview of XMLHttpRequest Request and Response


Wo w, that was a lo t to go thro ugh. Study it and review the explanatio ns abo ve to make sure yo u understand
each step. The re que st o bje ct co ntains both the request and the respo nse. In o ther wo rds, yo u use an
XMLHt t pRe que st o bject to create and send the request to the web server, and also to get the respo nse
back fro m the server (including the ready state, the status, and the actual data). The XMLHt t pRe que st o bject
handles all o f that co mmunicatio n—to and fro m the server.

Step 1:
Step 2:
Step 3:

Asynchronicity Rocks!
As we learned earlier, o ne o f the benefits o f Ajax is that it is asynchronous. Let's take a clo ser lo o k at exactly what that
means:
OBSERVE:
function getPetData() {
var request = new XMLHttpRequest();
request.open("GET", "pets.json");
request.onreadystatechange = function() {
...
};
request.send();
}

In the functio n ge t Pe t Dat a(), where we create the XMLHt t pRe que st o bject and use it to request data fro m the web
server and get the respo nse, we set up the callback f unct io n to handle the respo nse. But we're no t actually calling
this functio n, in fact, we rely o n the bro wser to call that functio n fo r us when the request o bject has received a respo nse.
We set up the functio n, and then se nd the request. After that, the ge t Pe t Dat a() functio n ends and any o ther co de we
might run after calling ge t Pe t Dat a() co ntinues to run.

Mo re impo rtantly, the bro wser is free to respo nd to user interactio n while we're waiting fo r the respo nse to co me back.
So , let's say yo u have a page that has a lo t o f interactive items o n it; yo u may have menus that expand and co llapse
when yo u mo ve yo ur mo use o n and o ff o f them. If yo ur co de uses XMLHttpRequest to go fetch so me data, while it's
o ff fetching that data, yo u can still interact with the page. Yo u can ho ver yo ur mo use o ver a menu and watch it expand
and then co llapse when yo u mo ve yo ur mo use o ff the menu—meaning that the bro wser is running o ther JavaScript
co de while it's waiting fo r the respo nse. The JavaScript engine in the bro wser isn't hung up waiting fo r the respo nse to
yo ur request fo r data; the bro wser will get the respo nse at so me po int and will call yo ur functio n. In the meantime, yo ur
o ther co de is free to co ntinue to wo rk as no rmal.

This asynchro no us benefit o f Ajax do esn't matter much in o ur little Pets example, but imagine yo u're building an
interactive game, and using XMLHttpRequest to send and receive data. In a highly interactive applicatio n, like a game,
it's especially impo rtant that the user be able to co ntinue to interact with the page while the data is being transferred.
That's where the capacity to send and receive data asynchro no usly really co mes in handy.
If Ajax wasn't asynchro no us, whenever yo u made a request to get so me data, all actio n wo uld be suspended until the
respo nse came back. Fo r small applicatio ns like Pets, and small amo unts o f data like pets.jso n, yo u pro bably
wo uldn't no tice, but if yo u were playing a game, o r sending a request to a web service that has to search a huge
database fo r results, yo u'd really no tice a hangup if the data transfer wasn't asynchro no us.

Yo u can make Ajax synchro no us if yo u really want to —that means as so o n as yo u use the se nd() metho d o f the
request o bject to send the request, all o ther wo rk sto ps. There might be a time when yo u need synchro no us behavio r.
If yo u do , add an extra parameter to the o pe n() metho d, like this:

OBSERVE:
request.open("GET", "pets.json", false);

Content T ypes and Headers


There are two mo re pieces o f info rmatio n abo ut the XMLHttpRequest o bject that co me in handy, especially when yo u
are debugging yo ur co de. They are the co nt e nt -t ype o f data in the respo nse, and the text co rrespo nding to the
st at us. Update pe t s.js as sho wn:
CODE TO TYPE:

window.onload = init;

function init() {
getPetData();
}

function getPetData() {
var request = new XMLHttpRequest();
request.open("GET", "pets.json");
request.onreadystatechange = function() {
var div = document.getElementById("pets");
if (this.readyState == this.DONE && this.status == 200) {
var type = request.getResponseHeader("Content-Type");
console.log("Content-type: " + type);
console.log("Status: " + this.statusText);
if (this.responseText != null) {
div.innerHTML = this.responseText;
}
else {
div.innerHTML = "Error: no data";
}
}
};
request.send();
}

Save it, switch o ver to pe t s.ht m l tab, and click ). Yo u see bo th the co ntent-type o f the respo nse and
the status in yo ur co nso le windo w:
Make sure yo u cho o se the Co nso le tab to view the results; if the windo w is narro w, yo u might have to use the dro p-
do wn menu to get to the co nso le. Fo r instance, in Safari 5 and Chro me, it will lo o k like this:
In Safari 6 , yo u access the co nso le by go ing to the Lo g tab in the Web Inspecto r, and clicking o n "Current Lo g":
The results sho w that the co ntent type o f the respo nse data is applicat io n/jso n. The server kno ws it's serving us
JSON data. Typically, yo u'll kno w what co ntent type to expect as a respo nse to a request, but if no t, yo u can use the
co ntent type to determine ho w to handle the respo nse. If yo u're expecting plain text, the co ntent-type will be t e xt /plain;
fo r XML, the co ntent-type will co ntain the letters "xml" (the full co ntent-type can vary). If yo u are using XML, the data fo r
the respo nse will be in the pro perty re spo nse XML, not in respo nseText.

The JavaScript Co nso le and Develo per To o ls in every bro wser tend to change with new bro wser
Note versio ns, so yo ur bro wser to o ls may lo o k different.

Cross-Domain Security Restrictions


Befo re we leave the Pets example behind and mo ve o n to the To Do applicatio n, let's go o ver o ne last impo rtant
aspect o f Ajax.

Yo u can o nly re que st dat a t hat is se rve d f ro m t he do m ain f ro m which t he re que st is co m ing.

So , if yo u're running yo ur web applicatio ns at the URL http://yourusername.o reillystudent.co m/JavaScript2/pets.html,


yo u can o nly requst data that is being served fro m the do main o re illyst ude nt .co m . The web applicatio n and the data
it's requesting must be o n the same do main, o r the request will fail.

This is a security restrictio n built into bro wsers to keep malicio us data o ut o f yo ur web applicatio n. In the pre-mashup
wo rld, this restrictio n was pretty helpful, but no w that we have so many great web services aro und that serve up all
kinds o f interesting data that yo u co uld use to create the next millio n-do llar mashup, this restrictio n co uld be a
hinderance.

Fo rtunately, there is a wo rkaro und: a se rve r-side script , say, a PHP pro gram, that go es and fetches the data yo u
want fro m a remote web service, and then fo rwards it o n to yo ur web applicatio n. This server-side script, o ften called an
"intermediary" script, can verify the data yo u've requested (if necessary) befo re it ever gets to yo ur web applicatio n.
That way, yo u can make the request to the script (which is running o n the same do main), the script requests the data
fro m the web service, verifies the data, and then hands it back to yo ur web applicatio n. This is transparent to yo ur
JavaScript Ajax co de, but it do es mean yo u'll need to write this intermediary pro gram.

We co vered a lo t o f co mplex Ajax details in this lesso n. Take a break, let it all sink in a bit, and then do the pro ject and the quiz.
Then yo u'll be ready to tackle the To Do List applicatio n co ming up next...

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
The To-Do List Application
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

create a JSON file that co ntains to -do items.


create so me HTML and CSS fo r this applicatio n.
add co ntent to yo ur applicatio n.
create and array o f to -do o bjects.
update yo ur page with to -do items.

Yo u've built an Ajax applicatio n that reads JSON data fro m an external file. The ability to retrieve data fro m an external so urce is
o ne key feature o f an Ajax applicatio n; a seco nd feature that's just as impo rtant, is the ability to update yo ur web page (o r web
application) dynamically with that data.

In this lesso n, we'll begin building a To -Do List applicatio n. Eventually this applicatio n will let yo u bo th retrieve and save to -do
list items using a web applicatio n that yo u've built. Fo r this first part o f the To -Do List applicatio n, we'll expand the Pets
applicatio n and actually parse the JSON data, then update yo ur web page using that data.

Yo u'll reco gnize the co de belo w. Feel free to reuse so me o f yo ur pro ject so lutio n, o r, if yo u'd prefer, yo u can start fro m scratch.
We'll assume that yo u're starting o ver, so yo u can create fresh files using the instructio ns belo w.

Create the Files for the T o-Do List Application


Creating the JSON Data File
To begin building the To -Do List applicatio n, yo u'll create a JSON file co ntaining so me to -do items. (Yo u'll
learn ho w to do that thro ugh the applicatio n itself.) Just like with the Pets example, yo u'll create a new file and
write so me data using the JSON fo rmat (o r yo u can reuse the to -do list yo u created in the previo us lesso n's
pro ject):

CODE TO TYPE:
[{"task":"get milk","who":"Scott","dueDate":"today","done":false},
{"task":"get broccoli","who":"Elisabeth","dueDate":"today","done":false}]

Save this file in yo ur /javascript 2 fo lder as t o do .jso n and, in HTML mo de, click . Yo u see
yo ur to -do list JSON data in a web page, it lo o ks like this:
Yo ur web applicatio n will read in these items using Ajax. No tice that there are two to -do items: o ne fo r Sco tt
and o ne fo r Elisabeth. Each item has fo ur pro perties: t ask, who , due Dat e , and do ne . All the pro perty values
are strings, except fo r do ne , which is a Bo o lean.

If yo u had to represent these to -do items as an o bject, what wo uld that o bject lo o k like? Give it so me tho ught
and we'll co me back to that quesio n sho rtly.

Creating the HT ML and CSS


Next, let's create the HTML & CSS fo r this applicatio n. We'll keep it relatively simple, just like we did with pets;

fo r no w, all we need is a heading and a <div> element. Create a new file and type in this HTML and CSS:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>To-Do List</title>
<meta charset="utf-8">
<script src="todo.js"></script>
<style>
body {
font-family: Helvetica, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>My To-Do List</h1>
<div id="todoList">
</div>
</body>
</html>

Save this file in yo ur /javascript 2 fo lder as t o do .ht m l and click . Yo u see this web page:
There's no thing in the web page, because we haven't added any co ntent to it yet.

Adding the Content


Okay, go ahead and create the JavaScript to read in the JSON fro m the "to do .jso n" file, just like we did with
Pets:

CODE TO TYPE:
window.onload = init;

function init() {
getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
var listDiv = document.getElementById("todoList");
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
listDiv.innerHTML = this.responseText;
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

Save it in yo ur /javascript 2 fo lder as t o do .js. Open yo ur t o do .ht m l file (which links to this t o do .js file
with the <script> element at the to p), and click . The JSON data fro m t o do .jso n in yo ur web page,
lo o ks like this:

So far, so go o d. It's the same pro cess we applied to Pets, just revised fo r a To -Do List, and using slightly
different JSON data. Here are the steps we take to get there:

Call the functio n init() o nce the page finishes lo ading.


Call getTo do Data() fro m init(), which is where we use Ajax to retrieve the to do .jso n data.
Create an XMLHttpRequest o bject to make the request fo r the data.
Create an o nreadystatechange handler fo r the request.
Add the data to the page.

So no w that yo u've go t yo ur hands o n the JSON data, let's do so mething better than just spitting it o ut to the
web page as is. What we'd really like to do is make a nice-lo o king list, right?

Create an array of T o-Do Objects


To create a nice to -do list o ut o f the JSON data, we need to parse the data using J SON.parse (). As we learned earlier,
J SON.parse () parses, o r deserializes JSON-fo rmatted data and turns that data into a JavaScript o bject. In o ur case,
that o bject will be a To do o bject.

If yo u were to create a To do o bject in JavaScript, yo u'd write so mething like this:


OBSERVE:
var todoObject = {
task: "Get Milk",
who: "Scott",
dueDate: "today",
done: false
};

Remember, this is a literal object, that is, an o bject yo u typed in literally, rather than created using a co nstructo r. No tice
ho w clo sely this t o do Obje ct resembles the items in the t o do .jso n file yo u created earlier.

When we read in the JSON data and turn the JSON to -do items into JavaScript o bjects, we need so mewhere to put
tho se o bjects; we'll stash them in an array. Fo r this step, we'll take a lo o k at that array in the co nso le. After that, we'll
actually use the array o f o bjects to update the page. Update t o do .js as sho wn:

CODE TO TYPE:
var todos = new Array();

window.onload = init;

function init() {
getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
var listDiv = document.getElementById("todoList");
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
listDiv.innerHTML = this.responseText;
parseTodoItems(this.responseText);
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
console.log("To-do array: ");
console.log(todos);
}

Save it, o pen yo ur t o do .ht m l file, and click . Yo u wo n't see anything in the web page no w, but yo u'll
see so me o utput in yo ur develo per co nso le. Remember, if yo u're in Safari o r Chro me, yo u might need to use the
arro w in the Develo per co nso le windo w to access the co nso le:
In the co nso le, yo u see an array o f yo ur to -do o bjects, like this in Safari o r Chro me:
...and like this in Firefo x (using Firebug):
Let's walk thro ugh the new co de. First, we add an empty array, t o do s, to ho ld all the o bjects that we create as we
parse the JSON data fro m the t o do .jso n file:

OBSERVE:
var todos = new Array();

No te that this is a glo bal variable so we can access it fro m within any functio n.

Next, we update the o nre adyst at e change handler in the ge t T o do Dat a() functio n to call the functio n
parse T o do It e m s() and pass in the re spo nse T e xt , which ho lds the JSON data fro m the t o do .jso n file:

OBSERVE:
parseTodoItems(this.responseText);

And o f co urse, we implement the parse T o do It e m s() functio n:


OBSERVE:
function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
console.log("To-do array: ");
console.log(todos);
}

This functio n is where the magic happens: where we turn JSON fro m a file into real HTML o bjects that dynamically
appear in yo ur web page. Well, it wo n't seem like magic anymo re, because yo u'll kno w ho w to do it.

In parse T o do It e m s, the JSON data is passed in as a variable named t o do J SON. In the functio n we test to make
sure t o do J SON is no t null o r the e m pt y st ring. If the file is empty (that is, there are no to -do s yet), then we have no
JSON to parse and no thing to add to the page. If we try to parse an empty JSON string, we'll get an erro r message. We
can avo id that by testing the string first.

We'll lo o k at strings in mo re detail in a later lesso n, but fo r no w, we're using the t rim () functio n (actually a String
metho d!) o n the string t o do J SON. This gets rid o f extra white space at the beginning o r end o f a string. So if yo u have
a string like this:

OBSERVE:
var myString = " There are a few empty spaces. "

...with empty spaces at the begining and/o r end, after calling m ySt ring.t rim (), yo u'll have a string like this:

OBSERVE:
"There are a few empty spaces."

If the string is empty, then we just return fro m the functio n (that is, the rest o f the functio n isn't executed).

No w let's review the next bit o f co de:

OBSERVE:
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}

Once we kno w the string isn't empty, we can parse it using J SON.parse (). This turns the t o do J SON string into an
o bject. In o ur case, we kno w that we actually have an array of objects because that's ho w we designed the data in the
to do .jso n file (take ano ther lo o k at the co ntents o f the file and no tice the [ and ] surro unding the rest o f the data; that
means array). So instead o f putting the result o f the call to J SON.parse () into an o bject variable, we are putting it into
an array variable, the t o do Array.

So , why do n't we just put the result o f the J SON.parse () into the t o do s array? That's a great questio n! And the
answer is: because later o n, we're go ing to add a fo rm to the page to let yo u add new to -do items to yo ur list. We want
to keep the to -do s that are in the file separate fro m the to -do s managed by the page, so we have this tempo rary array,
t o do Array, which we use to get the to -do items fro m the file. Later we'll add them to the t o do s array.

Befo re we try to add items fro m the t o do Array to the t o do s array, we need to make sure there's so mething in it. What
if yo u made an empty array (Like this: [ ]) in yo ur to do .jso n file? That wo uld be a valid JSON string, but it wo uldn't
co ntain any o bjects. We need to check fo r that, and return fro m the functio n if the array is empty.

Okay, at this po int we have an array with at least o ne o bject in it. Here's the next (and final) piece o f co de fro m the
parse T o do It e m s functio n:

OBSERVE:
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
console.log("To-do array: ");
console.log(todos);

No w, we want to put the o bjects in the t o do Array into the t o do s array. We'll iterate (lo o p) thro ugh all the items in the
t o do Array and co py them into the t o do s array. First we sto re the item fro m the t o do Array[i] in the variable
t o do It e m , and then use the Array metho d push() to add the t o do It e m o nto the end o f the t o do s array. (We do n't
actually need the intermediate t o do It e m variable; can yo u see ho w yo u'd do this witho ut it?)

Finally, to make sure the t o do s array has the right stuff in it, we use co nso le .lo g() to display the array in the co nso le.
Here's ho w it lo o ks in Safari and Chro me:
Update the Page with T o-Do Items
The next step is to get the items fro m the t o do s array into yo ur web page. We're creating a to -do list, so let's use an
HTML list to put the items in. First, change t o do .ht m l so that the "to do List" element is a <ul> instead o f a <div>, like
this:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>To-Do List</title>
<meta charset="utf-8">
<script src="todo.js"></script>
<style>
body {
font-family: Helvetica, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>My To-Do List</h1>
<div id="todoList"><ul id="todoList">
</div></ul>
</body>
</html>

Of co urse, the "to do List" list is empty (it has no <li> elements in it) because we're go ing to use JavaScript to add <li>
elements created using the o bjects sto red in the t o do s array. Update t o do .js as sho wn:
CODE TO TYPE:
var todos = new Array();

window.onload = init;

function init() {
getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
console.log("To-do array: ");
console.log(todos);
}

function addTodosToPage() {
var ul = document.getElementById("todoList");
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
ul.appendChild(li);
}
}

Save it, o pen yo ur t o do .ht m l file, and click . Yo u see the to -do items in yo ur t o do .jso n file displayed
as list items in yo ur web page, like this:
Try adding ano ther to -do item to yo ur t o do .jso n file and relo ad the page. Do yo u see it?

So ho w did we add the array items to the page? Let's review:

OBSERVE:
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}

In the o nre adyst at e change handler in ge t T o do Dat a(), in additio n to calling parse T o do It e m s(), we're calling
addT o do sT o Page (). This gets called o nly after all the JSON has been pro cessed and all the to -do items have been
added to the t o do s array, so we kno w all the data will be in the array at this po int.

This addT o do sT o Page functio n actually adds the items to the page by adding them to the "to do List" <ul>:

OBSERVE:
function addTodosToPage() {
var ul = document.getElementById("todoList");
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
ul.appendChild(li);
}
}

In addT o do sT o Page (), first we ge t t he <ul> element with the id "to do List" fro m the page, so we can add a new list
item to it. Then we lo o p t hro ugh all t he o bje ct s in t he t o do s array and create a tempo rary variable t o do It e m
fo r each item in the array. Then we cre at e a ne w <li> e le m e nt element fo r each item using the
do cum e nt .cre at e Ele m e nt () metho d, and set the HTML co ntents o f that element to a string with data fro m the
t o do It e m o bject. The t o do It e m is an object that was created when we parsed the JSON in the to do .jso n file using
J SON.parse (), an o bject that lo o ks so mething like this:
OBSERVE:
var todoObject = {
task: "Get Milk",
who: "Scott",
dueDate: "today",
done: false
};

Yo u can access each pro perty in the o bject as yo u no rmally wo uld, using do t no tatio n. The string we create to add to
the list item uses the pro perties o f the t o do It e m , like t o do It e m .who , t o do It e m .t ask, and t o do It e m .due Dat e .

Once we've set the co ntent o f the <li> element, we can add it t o t he " t o do List " <ul> e le m e nt using the
appe ndChild() metho d.

As so o n as yo u add the element to the page with appe ndChild(), the page updates to reflect the new element and
yo u see yo ur to -do list item.

Did yo u no tice that we're no t passing the t o do s array into the addT o do sT o Page functio n? That's because it's a
glo bal variable, and we do n't need to —we can access it fro m any functio n. We made the t o do s array a glo bal variable
so we co uld access it in bo th the parse T o do It e m s() and addT o do sT o Page () functio ns.

In this lesso n, yo u've bro ught to gether several things yo u already knew abo ut— JSON, o bjects, arrays, using XMLHttpRequest
to get data fro m a file, and updating the page with new elements—to create an Ajax web applicatio n that lo ads to -do items fro m
a file, to do .jso n, and updates a page with tho se to -do items. All these parts wo rk to gether in yo ur web applicatio n to do
so mething that's really kinda co o l!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Saving Data with Ajax
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

add a fo rm to the HTML page that lets yo u enter new To -Do items and save them in the JSON file.
style yo ur fo rms.
validate yo ur fo rms.
use Ajax to write data to a file.

Adding and Saving a T o-Do List Item


We're o ff to a great start with o ur To -Do List applicatio n; we've go t a simple Ajax web applicatio n that reads To Do data
fro m a JSON file, and updates a page dynamically with that data.

In this lesso n, we co ntinue building the applicatio n. We'll add a fo rm to the HTML page that lets yo u enter new To -Do
items and save them in the JSON file, so that the next time yo u lo ad the page, yo ur new items will be there. We'll do
so me fo rm validatio n to o , which is always go o d practice (and will be go o d review o f JavaScript techniques). Yo u've
used Ajax to read data fro m a file; in this lesso n, yo u'll see ho w to use Ajax to write data to a file (with a little help fro m
a PHP script).

So , let's get to it!

Add a Form to Your Page and Style It


To add new items to o ur to -do list using a web page, we need a fo rm. Add a fo rm to t o do .ht m l as sho wn:
CODE TO TYPE:

<!doctype html>
<html>
<head>
<title>To-Do List</title>
<meta charset="utf-8">
<script src="todo.js"></script>
<style>
body {
font-family: Helvetica, Arial, sans-serif;
}
</style>
<link rel="stylesheet" href="todo.css">
</head>
<body>
<h1>My To-Do List</h1>
<ul id="todoList">
</ul>

<form>
<fieldset>
<legend>Add a new to-do item</legend>
<div class="tableContainer">
<div class="tableRow">
<label for="task">Task: </label>
<input type="text" id="task" size="35" placeholder="get milk">
</div>
<div class="tableRow">
<label for="who">Who should do it: </label>
<input type="text" id="who" placeholder="Scott">
</div>
<div class="tableRow">
<label for="dueDate">Due Date: </label>
<input type="date" id="dueDate">
</div>
<div class="tableRow">
<label for="submit"></label>
<input type="button" id="submit" value="submit">
</div>
</div>
</fieldset>
</form>
</body>
</html>

Save it and click . Yo u see the web page, which lo o ks like this:
When yo u preview, the applicatio n lo ads the to -do items yo u already have in the t o do .jso n file, using the
co de yo u added in t o do .js in the previo us lesso n (yo ur list may lo o k a little different fro m mine if yo u've
added o r changed items).

In this versio n o f t o do .ht m l, we added a fo rm with three data inputs and a submit butto n. Two o f the data
inputs are fo r text—the task, and who sho uld do it—and the third is fo r a date. In many bro wsers, the date field
will lo o k like a text input, but in so me bro wsers yo u may get a po pup date picker.

We've added so me extra <div>s fo r styling the table; yo u'll see ho w this wo rks in the next step.

Style the Form


That HTML do esn't lo o k to o bad, but let's make it lo o k even better with a little CSS. We remo ved the CSS we
had previo usly, and added a link to a file. Go ahead and create a new file, and we'll add the extra CSS we need
to style the fo rm. Create a new file and type in this CSS:
CODE TO TYPE:

body {
font-family: Helvetica, Arial, sans-serif;
}
legend {
font-weight: bold;
}
div.tableContainer {
display: table;
border-spacing: 5px;
}
div.tableRow {
display: table-row;
}
div.tableRow label {
display: table-cell;
text-align: right;
}
div.tableRow input {
display: table-cell;
}

Save this file in yo ur /javascript 2 fo lder as t o do .css. Open t o do .ht m l, which links to this t o do .css file
at the to p (with the <link> element), and click . Yo ur page no w lo o ks like this:

The fields are aligned pro perly in this fo rm which gives it a mo re pro fessio nal appearance than the previo us
o ne.

This CSS uses table display pro perties to lay o ut the <div>s and fo rm co ntro ls, similar to the way an HTML
table is laid o ut. If yo u're no t familiar with table display, do n't wo rry abo ut it; it's just a way o f laying o ut a page.
In fact, CSS table display pro perties are the same pro perties that are used by default in the bro wser to lay o ut
HTML tables. Using table display wo rks really well fo r fo rms, because fo rms o ften have a regular, table-like
structure to them.
No w that yo u've go t the web page ready to go fo r entering new to -do items, it's time to update yo ur JavaScript
so that yo u can do so mething with the items yo u enter.

Process the Form with JavaScript


Yo u can enter data in the fo rm in the web page yo u created in the previo us step, but no thing happens when
yo u click subm it , because we have no act io n in the <fo rm> element, and no JavaScript to capture a click o n
the submit butto n. So , as it is, the fo rm do es no thing.

In this next step, we need to get the data fro m the fo rm and check it. We want to get the fo rm data using o ur
o wn JavaScript co de so we can validate the data and then do so mething with it. So , we'll need a way to
determine when the submit butto n is clicked so we can get the data that was entered into the fo rm. We'll add a
click handle r functio n to the Submit butto n.

Update t o do .js as sho wn:


CODE TO TYPE:
var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;
getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
}

function addTodosToPage() {
var ul = document.getElementById("todoList");
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueD
ate;
ul.appendChild(li);
}
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

console.log("New task: " + task + ", for: " + who + ", by: " + date);
}
function checkInputText(value, msg) {
if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

Save it, o pen t o do .ht m l, and click . Open the develo per co nso le so yo u can see the co nso le
message we're using to display the data. Then, type so me data into the fo rm. After entering the data and
clicking subm it , yo u see the co nso le message sho wing the data yo u entered. It lo o ks like this:

We get the data fro m the fo rm with the functio n ge t Fo rm Dat a(), which we set up as the click handle r fo r the
subm it but t o n:

OBSERVE:
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;

In ge t Fo rm Dat a(), we check to make sure yo u enter values fo r the vario us fields:
OBSERVE:
function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

console.log("New task: " + task + ", for: " + who + ", by: " + date);
}

We get the value o f each field in the fo rm using do cum e nt .ge t Ele m e nt ById() to first get the fo rm co ntro l
element, and then using the element's value () metho d to get the value o f the co ntro l. In o ur fo rm, each
co ntro l will be a string. If any o f the fields is empty, the functio n just re t urns, witho ut displaying anything in the
co nso le. No tice that we're using a helper functio n, che ckInput T e xt () to make sure that each fo rm co ntro l
has a value:

OBSERVE:
function checkInputText(value, msg) {
if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

che ckInput T e xt () has two parameters: value and m sg. The value is the string data that yo u enter into the
fo rm co ntro l, and the m sg is the text to display in the alert if yo u didn't enter anything fo r that co ntro l. We check
to make sure value isn't empty; if it is, we display the m sg and return true. If it is no t empty, we return false.
Lo o k back at ge t Fo rm Dat a() and yo u'll see that if we return false fro m che ckInput T e xt () fo r each fo rm
field, then we display t he co nso le m e ssage sho wing the data that yo u entered into the fo rm.

Create a New T o-Do Object


It's go o d to see the fo rm data we enter in the co nso le, but we really want to see it in the page. We also need to
add the new to -do item to o ur t o do s array, which means we need to create a to -do o bject. Yo u pro bably
remember fro m the previo us lesso n that when we read in the to -do s fro m the file t o do .jso n, each to -do item
sto red in in the t o do s array is an o bje ct .

Ho w can we create a to -do o bject fo r the data that yo u've entered in the fo rm? We need a to -do o bject
co nstructo r. Do yo u remember ho w to create a co nstructo r functio n? Mo dify t o do .js as sho wn:
CODE TO TYPE:
function Todo(task, who, dueDate) {
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;
getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
}

function addTodosToPage() {
var ul = document.getElementById("todoList");
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueD
ate;
ul.appendChild(li);
}
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

console.log("New task: " + task + ", for: " + who + ", by: " + date);
var todoItem = new Todo(task, who, date);
todos.push(todoItem);
}

function checkInputText(value, msg) {


if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

Here, we use the T o do () co nstructo r functio n to create a new T o do o bject, and push that o bject o nto the end
o f the t o do s array. The T o do () co nstructo r takes three arguments: t ask, who , and due Dat e . The
co nstructo r functio n sets the o bject pro perties to the values that are passed into it. In ge t Fo rm Dat a(), we
pass in the values that we go t fro m the fo rm fo r tho se arguments. In additio n, the T o do () co nstructo r sets the
do ne pro perty to f alse . Can yo u guess why?

We'll go back to the do ne pro perty in a later lesso n, but fo r no w assume that all the to -do items are still to be
do ne!

Adding the New T o-Do Item to the Page


No w we'll add the to -do item to the page so we can see the result o f all o f o ur hard wo rk in the web page,
rather than just in the co nso le. Mo dify t o do .js as sho wn:
CODE TO TYPE:
function Todo(task, who, dueDate) {
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;
getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
}

function addTodosToPage() {
var ul = document.getElementById("todoList");
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueD
ate;
ul.appendChild(li);
}
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

console.log("New task: " + task + ", for: " + who + ", by: " + date);
var todoItem = new Todo(task, who, date);
todos.push(todoItem);
addTodoToPage(todoItem);
}

function checkInputText(value, msg) {


if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
ul.appendChild(li);
document.forms[0].reset();
}

Save it, o pen t o do .ht m l and click . Enter the info rmatio n fo r a to -do item in the fo rm and click
subm it , and yo ur new item appears in the page:

To add the to -do item to the page, we use a new functio n, addT o do T o Page (), and call it just after we add
the new T o do o bject to the t o do s array in the functio n ge t Fo rm Dat a().
OBSERVE:
function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
ul.appendChild(li);
document.forms[0].reset();
}

The To do o bject that's passed into addT o do T o Page () gets the parameter name t o do It e m . To add it to
the page, first we get the "to do List" <ul>; then we create a new <li> element to add to this list, and set the
innerHTML o f the <li> to the info rmatio n in the t o do It e m o bject. Then we add the new list item to the
"to do List" list. Finally, we reset the fo rm to make it easier to create ano ther to -do item using the fo rm.

Do n't mix up the two functio ns: addT o do T o Page (), which we just created to add a single to -do item entered
using the fo rm to the page, and the functio n we added earlier (in the previo us lesso n), addT o do sT o Page (),
which adds all the to -do items in the JSON file, t o do .jso n, to the page when the page first lo ads. The two
functio ns lo o k similar (and have similar names), but have slightly different purpo ses. Can yo u see why we
need bo th? Can yo u think o f ho w we might make o ur co de mo re efficient by co mbining so me o f the
functio nality o f the two functio ns? We'll co me back to these and o ther pressing questio ns in the next lesso n!

T he Case of the Disappearing T o-Do Item


Try adding a co uple mo re to -do items to yo ur page. No w, relo ad the page. What happens? Hmm. That's
interesting.

Saving the Data with Ajax


Okay, we've go t the new to -do item to display in the to -do list o n the page, but ho w do we save it in the
t o do .jso n file so that it do esn't disappear when yo u relo ad the page?

We can use Ajax to send the data in the t o do s array (where all o f o ur to do s are sto red) to a PHP script sto red
o n the server (the same server where yo ur t o do .jso n file is sto red, in o ur case, the O'Reilly Scho o l o f
Techno lo gy server) that will write the to -do items into the t o do .jso n file.

Yo u might be wo ndering, "Why can't we write the to -do items directly to the file using JavaScript?" Currently,
JavaScript do es no t allo w yo u to read fro m o r write to files directly o n a filesystem, fo r security purpo ses.
(Imagine if yo u went to a malicio us website that co uld read o r write fro m yo ur hard drive. Bad news!) That's
why we need the help o f a server script.

Wo rk is underway o n a file system library fo r JavaScript that will allo w yo u to read and write files
Note o n yo ur lo cal file system (yo ur o wn co mputer, no t the file system o n the server!), but this is a
new pro ject still in develo pment as o f this writing.

So yo u may think that we read directly fro m the t o do .jso n file. Actually we do n't. We use an HT T P re que st
to get the data, and the web server reads the data fro m the file and returns it to the bro wser, which places the
data in the XMLHt t pRe que st o bject, and o ur JavaScript co de gets it fro m there. Whew!

We need to do so mething similar, o nly in reverse to write the data. We'll use the XMLHt t pRe que st o bject to
send so me data back to the web server. But we need a pro gram o n the web server that kno ws what to do with
the data. We can't just rando mly send data to the web server; we need to send it to a specific pro gram—the
server script—that kno ws ho w to update yo ur t o do .jso n file.

It's really similar to what happens when yo u submit a fo rm that has a server specified in the act io n attribute,
but instead o f using the submit actio n with the fo rm, we'll send the data to the server using the
XMLHt t pRe que st o bject.

Here's ho w it wo rks:
Creating the PHP Server Script
First, we need to create the script we're using to save the to -do items we send it in the t o do .jso n file.

Yo u can switch into PHP mo de to add the PHP file if yo u like; just use the pull-do wn menu to use PHP instead
o f HTML:
CODE TO TYPE:

<?php

if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) {


function strip_slashes($input) {
if (!is_array($input)) {
return stripslashes($input);
}
else {
return array_map('strip_slashes', $input);
}
}
$_GET = strip_slashes($_GET);
$_POST = strip_slashes($_POST);
$_COOKIE = strip_slashes($_COOKIE);
$_REQUEST = strip_slashes($_REQUEST);
}

function customError($errno, $errstr) {


echo "<b>Error:</b> [$errno] $errstr<br>";
echo "Ending Script";
die("Ending Script");
}
set_error_handler("customError");

$myData = $_GET["data"];

$myFile = "todo.json";
$fileHandle = fopen($myFile, "w");

fwrite($fileHandle, $myData);
fclose($fileHandle);

?>

Save it in yo ur /javascript 2 fo lder as save .php.

Do n't wo rry, fo r this co urse, we do n't expect yo u to kno w PHP (altho ugh, if yo u want to learn, there's a great
co urse o n PHP and ho w to write web pages that use PHP scripts: Intro ductio n to PHP. Fo r info rmatio n abo ut
this and all O'Reilly Scho o l o f Techno lo gy co urses, co ntact info @o reillyscho o l.co m.

Anyway, since yo u kno w JavaScript, yo u can pro bably read thro ugh this PHP script and understand so me o f
it. The first part o f the file is respo nsible fo r stripping tho se extra slashes that get added to yo ur string when
yo u send a string that co ntains quo tatio n marks. The next part is an erro r handling functio n (cust o m Erro r),
and belo w that is where the script actually gets the data fro m the request and writes it to the file. Again, do n't
wo rry abo ut these details, just be aware that this script writes all the to -do items yo u send it to the file
t o do .jso n (o verwriting what was there befo re, which will be impo rtant in just a mo ment when we co nsider
what data to send the script fro m o ur JavaScript).

Adding the JavaScript


No w that yo u have the PHP script in place and ready to accept requests fro m yo ur JavaScript, it's time to
update the JavaScript to save yo ur to -do items!
CODE TO TYPE:
function Todo(task, who, dueDate) {
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;
getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
}

function addTodosToPage() {
var ul = document.getElementById("todoList");
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueD
ate;
ul.appendChild(li);
}
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

console.log("New task: " + task + ", for: " + who + ", by: " + date);
var todoItem = new Todo(task, who, date);
todos.push(todoItem);
addTodoToPage(todoItem);
saveTodoData();
}

function checkInputText(value, msg) {


if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}
function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
ul.appendChild(li);
document.forms[0].reset();
}

function saveTodoData() {
var todoJSON = JSON.stringify(todos);
var request = new XMLHttpRequest();
var URL = "save.php?data=" + encodeURI(todoJSON);
request.open("GET", URL);
request.setRequestHeader("Content-Type",
"text/plain;charset=UTF-8");
request.send();
}

Save it, o pen t o do .ht m l, and click . Add o ne o r two to -do items. No w, relo ad the page. They
sho uld still be there. Open yo ur t o do .jso n file; yo u sho uld see all yo ur to -do items there. (Make sure yo u
clo se it again befo re yo u add any mo re to -do items).

Let's go o ver ho w this wo rks, step-by-step. We added a new functio n save T o do Dat a() where all the actio n
happens. We call this functio n fro m ge t Fo rm Dat a(), after we add a t o do It e m to the page. Let's see what
save T o do Dat a() do es:

OBSERVE:
function saveTodoData() {
var todoJSON = JSON.stringify(todos);
var request = new XMLHttpRequest();
var URL = "save.php?data=" + encodeURI(todoJSON);
request.open("GET", URL);
request.setRequestHeader("Content-Type",
"text/plain;charset=UTF-8");
request.send();
}

We do n't pass any arguments to save T o do Dat a(). Why? Because save T o do Dat a() uses the t o do s
array, which is a glo bal variable, so we do n't need to pass it. First, save T o do Dat a() co nverts the t o do s
array to a JSON string using J SON.st ringif y(). The J SON.st ringif y() metho d takes an o bject o r an array
and turns it into a string that we can then send as data to a script and save in a file. We save that JSON string
in the variable t o do J SON. We put every to -do item in the t o do s array in the JSON string (because the
save .php script o verwrites the entire t o do .jso n file each time we call it; yo u'll see mo re o n this in a
mo ment).

Next, we create a new XMLHt t pRe que st o bject, and save it in the variable re que st . Just like when we used
XMLHttpRequest to send a request to get data fro m a server, we will use XMLHttpRequest to send a request
to send data to a server. We send that data alo ng with the request. There are two ways to send data to a
server script using Ajax: GET and POST. (These are the same GET and POST yo u can use with fo rms to send
data to a server). Let's take a quick lo o k at each o f these metho ds o f sending data.

GET

When yo u use GET to send a request with data to a server, yo u create a URL that lo o ks very similar to the
URLs yo u typically use, except yo u add so me data o n the end o f the URL. Yo u append the data yo u want to
send o n the end o f the URL, with a "?" between the URL o f the script and the data. This is the metho d we use
in save T o do Dat a(). We append the to -do items to the end o f the URL fo r the request to save .php. But we
can't just send the data as is, because it co ntains all kinds o f characters that wo uld mess up the URL and
co nfuse the bro wser and the server. So , we have to enco de the URL first so all the characters that wo uld
co nfuse the bro wser and the server get replaced with their enco ded versio ns. Fo r instance, the enco ded
versio n o f a space (" ") is %20 , and the enco ded versio n o f a do uble quo te (") is %22.

In save T o do Dat a(), we create the URL fo r the request like this:

OBSERVE:

var URL = "save.php?data=" + encodeURI(todoJSON);

In the first part o f o ur URL, we specify the name o f the script we want to send the request to : save .php.

This URL is a re lat ive URL, which means we do n't specify the full http://... URL. Rather, we just specify the
name o f the script, save .php, because we kno w it's in the same directo ry fro m which the web applicatio n is
lo aded. This request will be co nverted auto matically to a request fo r the full URL fo r us, which is pretty
co nvenient Yo u co uld use the full URL (kno wn as the abso lut e URL) if yo u wanted, but that's no t required.

After the name o f the script, we add ? to separate the path to the script fro m the data we're sending to the
server.

Next, we type dat a=, which gives a name to the data we're sending. This is just like a variable name, and it
allo ws the PHP script to use that name to GET the data fro m the URL. In the PHP script, we had the line:

OBSERVE:

$myData = $_GET["data"];

The result o f this PHP co de is that it can access the data in the URL and sto re it in the variable $ m yDat a,
which it then uses to write the data to the file, t o do .jso n.

Finally, we append the data in the t o do J SON string to the end o f the URL variable. Remember that
t o do J SON is co mprised o f all the to -do items in o ur t o do s array, co nverted into JSON.

We e nco de the t o do J SON string using the built-in JavaScript functio n, e nco de URI(), which takes a string
and turns it into a fo rm suitable fo r sending as part o f a URL with a GET request. So the actual URL that we
use in the XMLHttpRequest lo o ks so mething like this:

OBSERVE:
save.php?data=%5B%7B%22task%22:%22get%20milk%22,%22who%22:%22Scott%22,%22dueDate
%22:%22today%22,%22done%22:false%7D,%7B%22task%22:%22get%20broccoli%22,%22who%22
:%22Elisabeth%22,%22dueDate%22:%22today%22,%22done%22:false%7D,%7B%22task%22:%22
get%20balsamic%20vinegar%22,%22who%22:%22Georgia%22,%22dueDate%22:%222012-08-04%
22,%22done%22:false%7D%5D

POST

If we want to use POST instead o f GET to send the data to the server we can (altho ugh in this particular
example, we'd have to change the co de in the PHP script to o , so it wo n't wo rk). In general, in o rder to use
POST, instead o f putting the data in the URL itself, we send it using the request o bject. We use a similar fo rmat
fo r the data (an enco ded string), but rather than adding it to the URL string, we send it when we call
re que st .se nd().

Sending the Request


Okay, we've created a URL that co ntains the name o f the script to which we're sending the request, save .php,
and we've go t the data we're sending—the t o do s array co nverted into JSON—enco ded and added to the
URL, so ho w do we send the request?

OBSERVE:

function saveTodoData() {
var todoJSON = JSON.stringify(todos);
var request = new XMLHttpRequest();
var URL = "save.php?data=" + encodeURI(todoJSON);
request.open("GET", URL);
request.setRequestHeader("Content-Type",
"text/plain;charset=UTF-8");
request.send();
}

To send the request, we use the re que st .o pe n() metho d to tell the XMLHt t pRe que st o bject which URL we
want to send the request to , and the kind o f request it is (in this case, it's a GET request). Unlike the request
we sent to retrieve the data in the t o do .jso n file, we do n't have to set up an o nre adyst at e change handler
no w, because we're no t expecting any data back fro m the server (altho ugh in so me situatio ns, yo u might
expect so me data back; it's po ssible to bo th send and receive data in the same request).

So we're almo st ready to send the request, but first we need to tell the server the kind o f data we're sending.
There are many kinds o f data we co uld send in a request: XML, HTML, plain text, binary data (like if we're
sending an image), and so o n. In o ur case, we're just sending plain text: a JSON string.

Any request sent fro m a bro wser to a server co ntains a lo t o f info rmatio n. A request always has a he ade r that
includes info rmatio n like the kind o f request we're making (called the request "metho d," in o ur case, this is
GET), info rmatio n abo ut the enco ding, info rmatio n abo ut the bro wser sending the request, and the URL to
which we're sending the request.

To tell the server the type o f data we're sending with the request, we se t o ne o f t he f ie lds in t he he ade r
named Co nt e nt -T ype , and pro vide the co rrect value fo r plain text. This value co nsists o f a MIME type,
"text/plain" (a string that represents the type o f the file that co nfo rms to the MIME standard o f file types), and a
"charset" that tells the bro wser the character enco ding o f the data. This enco ding is usually "UTF-8 " which is
the current standard fo r enco ding characters (because, amo ng o ther capabilities, it can enco mpass all the
languages in the wo rld).
So no w we're ready to send! We call re que st .se nd() to send the request to the server, which sends all the
data we want to save in t o do .jso n alo ng with the request.

When we send the request, the save .php script is called o n the server, which saves the to -do data into the file
t o do .jso n. If yo u recall, we mentio ned that the script o verwrites anything that's already in this file. That's the
reaso n we send all o f the to -do items each time we make the request. It's no t the mo st efficient way to do it,
but it's a bit less co mplex and a go o d place fo r us to start. Can yo u think o f a better way to do it?

Yo u've updated the applicatio n so that yo u can add new items to yo ur to -do list, and saved tho se items back to the
t o do .jso n file. Yo u can see ho w Ajax is a po werful way to increase the functio nality and flexibility o f yo ur web
applicatio ns. The To -Do List applicatio n is much mo re useful with the ability to save yo ur to -do items, do n't yo u think?

Ho wever, o ur so lutio n isn't perfect by any means. What happens if mo re than o ne perso n uses this web applicatio n?
Fo r instance, if yo u give the URL o f the web applicatio n to a friend, will they be able to see yo ur to -do items? Will they
be able to add items to your to -do list?

Creating a to -do list applicatio n that can suppo rt mo re than o ne user is beyo nd the sco pe o f this co urse, but yo u no w
have a so lid understanding o f what Ajax is, and ho w Ajax wo rks.

So no w yo u kno w that Ajax is a set o f techniques that allo w yo u to create what we call "single-page web
applicatio ns"—web applicatio ns that yo u can use much like a regular deskto p applicatio n. With Ajax techniques, yo u
can update a web page dynamically with new data, yo u can change the page when the user interacts with it, and yo u
can retrieve and save data the user enters into the page, all witho ut having to "lo ad" ano ther web page.

In the next lesso n we'll lo o k at ho w to make JavaScript applicatio ns better by adding so me efficiency to the To -Do List
applicatio n.

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Document Fragments
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

use do cument fragments to add elements to a page.


use do cument fragments in a to -do list applicatio n.
co mbine co mmo n co de.
enhance a to -do list applicatio n.

As yo u no w kno w, a big part o f writing Ajax applicatio ns, and in fact, mo st JavaScript web applications, is the ability to add and
remo ve elements fro m the page dynamically, using JavaScript. In the To -Do List applicatio n, we add elements fro m the
t o do .jso n file to the page when we first lo ad the page, and we add a new to -do item each time the user enters data into the
fo rm and clicks the subm it butto n.

Each time yo u add (o r remo ve) an element fro m a web page, yo u're accessing the DOM tree—the Do cument Object Mo del tree
—that is the internal representatio n o f the web page in the bro wser. Fo r fairly small applicatio ns like this o ne, accessing the
DOM do esn't cause any pro blems because it's no t happening to o frequently. Ho wever, fo r large applicatio ns, frequent access
to the DOM can beco me a pro blem; each time yo u access the DOM, the bro wser has to sto p everything and update the entire
page. If yo u have hundreds o r even tho usands o f elements in a page, and yo u're adding and remo ving elements frequently, all
tho se DOM o peratio ns can really slo w things do wn.

One way we can make adding new elements to a page mo re efficient is to use document fragments.

Using Document Fragments to Add Elements to a Page


A do cument fragment is a fragment o f a DOM tree, a chunk o f tree that's separated fro m the rest o f the DOM. Why is this
useful? Because we can add elements to it before we add them to the DOM, avo iding the mo re expensive DOM
o peratio ns until we've go t o ur new structure created and ready to go . Once the do cument fragment co ntains everything
we want to add to the DOM, we can add it using o ne o peratio n instead o f many.

The best way to understand do cument fragments, o f co urse, is to dive right in and start making them. Let's start by
creating a small page with so me simple JavaScript:

CODE TO TYPE:

<!doctype html>
<html>
<head>
<title>Document Fragments</title>
<meta charset="utf-8">
<script src="frag.js"></script>
<style>
div.box {
position: relative;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div id="container">
</div>
</body>
</html>

Save it in yo ur /javascript 2 fo lder as f rag.ht m l. This page do esn't co ntain much o f anything, so do n't preview until
after yo u've added the JavaScript to create the new elements and add them to the page.

Take no te o f the CSS in the do cument. We'll use this CSS to style the new <div> elements with the class "bo x" that we'll
add to the page using JavaScript. Create a new file and type in this JavaScript:
CODE TO TYPE:
window.onload = init;

function init() {
var colors = [ "red", "blue", "green" ];
var container = document.getElementById("container");
for (var i = 0; i < 3; i++) {
var box = document.createElement("div");
box.setAttribute("class", "box");
box.style.backgroundColor = colors[i];
container.appendChild(box);
}
}

Save it in yo ur /javascript 2 fo lder as f rag.js. Open yo ur f rag.ht m l file, which links to this f rag.js file at the to p
(with the <script> element), and click . Yo u see a page like this:

The JavaScript here is fairly straightfo rward; we use a lo o p to create three new <div> elements, each with the class
"bo x", and add them o ne at a time to the DOM by calling

OBSERVE:
container.appendChild(box);

Because co nt aine r is an element in the DOM, each time yo u call appe ndChild(bo x), yo u add a new <div> element
directly to the DOM:
The first time thro ugh the lo o p, we add the red "bo x" <div>:

Then the blue:


And finally the green:

Fo r o ur small lo o p o f three <div> elements, this is fine, but imagine yo u're adding 10 0 o r 10 0 0 <div> elements, like
yo u might if yo u were, say, initializing the state o f a game applicatio n that has many elements representing game
co mpo nents. In that case, updating the DOM fo r each individual element like this will slo w do wn the initializatio n
pro cess substantially.

We can impro ve the perfo rmance o f this co de by using do cument fragments.

A do cument fragment is an empty no de. It's just like the no des yo u find in a DOM tree—where "no de" just means an
element o bject in the tree—except that it do esn't represent any specific element. It's a co mpletely generic no de that
do esn't mean anything. It's po werful tho ugh, because yo u can add elements to the fragment as children, just like yo u
can with a DOM tree.

Let's update the co de fo r the example to use a do cument fragment. Mo dify f rag.js as sho wn:
CODE TO TYPE:
window.onload = init;

function init() {
var colors = [ "red", "blue", "green" ];
var container = document.getElementById("container");
var fragment = document.createDocumentFragment();
for (var i = 0; i < 3; i++) {
var box = document.createElement("div");
box.setAttribute("class", "box");
box.style.backgroundColor = colors[i];
container.appendChild(box);
fragment.appendChild(box);
}
container.appendChild(fragment);
}

Save it, then o pen yo ur f rag.ht m l file and click . Yo ur page will lo o k exactly the same, but it will be just
a tiny bit mo re efficient. We like that.

Let's walk thro ugh the co de and see ho w it wo rks:

OBSERVE:
function init() {
var colors = [ "red", "blue", "green" ];
var container = document.getElementById("container");
var fragment = document.createDocumentFragment();
for (var i = 0; i < 3; i++) {
var box = document.createElement("div");
box.setAttribute("class", "box");
box.style.backgroundColor = colors[i];
fragment.appendChild(box);
}
container.appendChild(fragment);
}

We still need to get the "co ntainer" <div> fro m the DOM because we'll use it later. After we get that, we cre at e an
e m pt y do cum e nt f ragm e nt using do cum e nt .cre at e Do cum e nt Fragm e nt (). Think o f it like an empty element
just hanging o ut in yo ur pro gram, separate fro m the DOM, except that it do esn't actually represent any specific element;
it's just a generic no de waiting fo r yo u to add things to it.

Next we lo o p, as befo re, o nly this time, each time thro ugh the lo o p, we add a ne w " bo x" <div> to the f ragm e nt
rather than the DOM. We still use the appe ndChild() metho d as usual, because a fragment o bject is just like an
element o bject and as such, it has all the same metho ds.

Once we've co mpleted the lo o p, here's what we've go t: we have a DOM tree with an empty "co ntainer" <div>, and a
do cument fragment sitting o ff separately fro m the DOM with three "bo x" <div>s, waiting to be added to the DOM:
Finally, we add t he e le m e nt s in t he f ragm e nt t o t he DOM by calling appe ndChild() again. No tice that we're
calling appe ndChild() o n t he " co nt aine r" <div> and passing t he f ragm e nt as t he argum e nt .

No w this is where adding a fragment to the DOM differs a little fro m adding an element to the DOM using
appe ndChild(). Instead o f appending the fragment alo ng with everything co ntained in the fragment to the DOM, o nly
the elements co ntained in the fragment are mo ved to the DOM, like this:

...which is perfect, because no w o ur "co ntainer" <div> co ntains the three "bo x" <div>s just like we want! All the new
elements are added to the DOM in one o peratio n, instead o f the three separate o peratio ns that we needed befo re
when we weren't using the fragment. This is a lo t mo re efficient, and we're left with an empty fragment (which yo u can
even use again if yo u want). Can yo u think o f any reaso n yo u wo uldn't want the actual fragment in the DOM?
Using Document Fragments in the T o-Do List Application
No w that yo u kno w ho w to use a do cument fragment, let's see if we can impro ve the To -Do List applicatio n we've
been building. If yo u remember fro m the previo us lesso ns, the To -Do List applicatio n adds to -do items to the page
when the page is first lo aded by reading them fro m a file using XMLHttpRequest, and then adding each item to the page
in a lo o p fro m an array.

Also , recall that we have two very similar functio ns in o ur JavaScript: addT o do sT o Page (), which is the functio n that's
called when the page is lo aded to add the to -do items fro m t o do .jso n to the page, and addT o do T o Page (), which
is the functio n that's called when yo u add a to -do item using the fo rm.

Co mpare the two functio ns:

OBSERVE:
function addTodosToPage() {
var ul = document.getElementById("todoList");
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
ul.appendChild(li);
}
}

...and:

OBSERVE:
function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
ul.appendChild(li);
document.forms[0].reset();
}

The main difference is that addT o do sT o Page () adds to -do items to the page fro m the t o do s array, while
addT o do T o Page () adds the o ne t o do It e m that's passed into the functio n. But o ther than that, they bo th essentially
do the same thing: they each cre at e a ne w <li> e le m e nt , and add it t o t he DOM. addT o do sT o Page () do es this
each time thro ugh the lo o p, while addT o do T o Page () do es it o nce.

We can impro ve this co de in two ways. First, we can co mbine so me o f the co mmo n co de that is used by bo th
functio ns so we aren't repeating o urselves. Seco nd, we can make addT o do sT o Page () mo re efficient by adding all
the to -do items to a do cument fragment befo re adding them to the DOM.

Combining Common Code


Let's take it o ne step at a time. First, we'll co mbine so me co mmo n co de into a new functio n,
cre at e Ne wT o do It e m (). Mo dify t o do .js as sho wn:
CODE TO TYPE:
function Todo(task, who, dueDate) {
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;

getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
}
function addTodosToPage() {
var ul = document.getElementById("todoList");
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = createNewTodo(todoItem);
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueD
ate;
ul.appendChild(li);
}
}
function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = createNewTodo(todoItem);
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
ul.appendChild(li);
document.forms[0].reset();
}

function createNewTodo(todoItem) {
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
return li;
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

var todoItem = new Todo(task, who, date);


todos.push(todoItem);
addTodoToPage(todoItem);
saveTodoData();
}

function checkInputText(value, msg) {


if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

function saveTodoData() {
var todoJSON = JSON.stringify(todos);
var request = new XMLHttpRequest();
var URL = "save.php?data=" + encodeURI(todoJSON);
request.open("GET", URL);
request.setRequestHeader("Content-Type",
"text/plain;charset=UTF-8");
request.send();
}

Save it, then o pen yo ur t o do .ht m l file and click . Yo ur To -Do List applicatio n sho uld wo rk
exactly the same way as it did befo re; that is, it sho uld lo ad any to -do items yo u have in yo ur t o do .jso n file
when yo u lo ad the page, and yo u sho uld be able to add items using the fo rm.
We to o k the co de that is co mmo n to the addT o do sT o Page () and addT o do T o Page () functio ns:

OBSERVE:
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
ul.appendChild(li);

...and put it in a new functio n, cre at e Ne wT o do ():

OBSERVE:
function createNewTodo(todoItem) {
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
return li;
}

No w that the co mmo n co de is in o ne functio n (cre at e Ne wT o do ()) that returns the new <li> element, we can
call this functio n whenever we need to create a new <li> element, and pass in the t o do It e m o bject to use.
So , that's what we did in the addT o do sT o Page () and addT o do T o Page () functio ns. Instead o f having
bo th these functio ns create the <li> element fo r the to -do item, no w they just call cre at e Ne wT o do () to do it
fo r them.

Improving the Code with a Document Fragment


Next, we can impro ve the functio n addT o do sT o Page () by using a do cument fragment. Mo dify t o do .js as
sho wn:
CODE TO TYPE:
function Todo(task, who, dueDate) {
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;

getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
}
function addTodosToPage() {
var ul = document.getElementById("todoList");
var listFragment = document.createDocumentFragment();
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = createNewTodo(todoItem);
ul.appendChild(li);
listFragment.appendChild(li);
}
ul.appendChild(listFragment);
}
function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = createNewTodo(todoItem);
ul.appendChild(li);
document.forms[0].reset();
}
function createNewTodo(todoItem) {
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
return li;
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

var todoItem = new Todo(task, who, date);


todos.push(todoItem);
addTodoToPage(todoItem);
saveTodoData();
}

function checkInputText(value, msg) {


if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

function saveTodoData() {
var todoJSON = JSON.stringify(todos);
var request = new XMLHttpRequest();
var URL = "save.php?data=" + encodeURI(todoJSON);
request.open("GET", URL);
request.setRequestHeader("Content-Type",
"text/plain;charset=UTF-8");
request.send();
}

Save it, o pen yo ur t o do .ht m l file, and click . Yo ur To -Do List applicatio n will behave the
same way as it did befo re.

No w, instead o f adding each to -do item fro m the t o do .jso n file to the DOM o ne at a time, we're adding them
all in o ne chunk by using a do cument fragment:

OBSERVE:
function addTodosToPage() {
var ul = document.getElementById("todoList");
var listFragment = document.createDocumentFragment();
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = createNewTodo(todoItem);
listFragment.appendChild(li);
}
ul.appendChild(listFragment);
}

Just like we did befo re in o ur simple "bo x" example, we cre at e a f ragm e nt where we can add all the new
to -do items, and then add e ach t o -do it e m in t he lo o p t o t he f ragm e nt . Once we've added all the to -
do items in the t o do s array to the fragment, we can add t he ne w list it e m e le m e nt s in t he f ragm e nt t o
t he DOM. Remember, the elements in the fragment are mo ved fro m the fragment to the DOM, and then the
fragment is left empty, still separate fro m the DOM.
Enhancing the T o-Do List Application
We've made so me subtle impro vements to the To -Do List applicatio n. Next, let's enhance the applicatio n by adding a
little mo re structure to each to -do item in the to -do list and make use o f the do ne pro perty. We'll even add so me CSS
style to make it lo o k better. Mo dify t o do .js as sho wn:
CODE TO TYPE:
function Todo(task, who, dueDate) {
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;

getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
}

function addTodosToPage() {
var ul = document.getElementById("todoList");
var listFragment = document.createDocumentFragment();
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = createNewTodo(todoItem);
listFragment.appendChild(li);
}
ul.appendChild(listFragment);
}

function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = createNewTodo(todoItem);
ul.appendChild(li);
document.forms[0].reset();
}

function createNewTodo(todoItem) {
var li = document.createElement("li");
li.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;
var spanTodo = document.createElement("span");
spanTodo.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;

var spanDone = document.createElement("span");


if (!todoItem.done) {
spanDone.setAttribute("class", "notDone");
spanDone.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
}
else {
spanDone.setAttribute("class", "done");
spanDone.innerHTML = "&nbsp;&#10004;&nbsp;";
}

li.appendChild(spanDone);
li.appendChild(spanTodo);
return li;
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

var todoItem = new Todo(task, who, date);


todos.push(todoItem);
addTodoToPage(todoItem);
saveTodoData();
}

function checkInputText(value, msg) {


if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

function saveTodoData() {
var todoJSON = JSON.stringify(todos);
var request = new XMLHttpRequest();
var URL = "save.php?data=" + encodeURI(todoJSON);
request.open("GET", URL);
request.setRequestHeader("Content-Type",
"text/plain;charset=UTF-8");
request.send();
}

If yo u save and run this co de no w, it will run as expected, but wo n't lo o k much different, so yo u might want to wait until
we add the CSS belo w to run it. Befo re we add the CSS, let's step thro ugh the changes in the JavaScript.

All the changes are in the cre at e Ne wT o do () functio n:


OBSERVE:
function createNewTodo(todoItem) {
var li = document.createElement("li");
var spanTodo = document.createElement("span");
spanTodo.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;

var spanDone = document.createElement("span");


if (!todoItem.done) {
spanDone.setAttribute("class", "notDone");
spanDone.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
}
else {
spanDone.setAttribute("class", "done");
spanDone.innerHTML = "&nbsp;&#10004;&nbsp;";
}

li.appendChild(spanDone);
li.appendChild(spanTodo);
return li;
}

First, no tice that we add two <span> elements to the <li> element ho lding each to -do item. Befo re, we simply added
so me text to the inne rHT ML o f the <li> element directly, no w we add two <span> elements: o ne t o ho ld t he t o -do
inf o rm at io n abo ut who needs to do the task, what the task is, and when it's due (the same info rmatio n we added
directly to the <li> element previo usly); and ano ther <span> t o ho ld t he inf o rm at io n abo ut whe t he r an
e le m e nt is do ne o r no t . We're representing the do ne o r no t do ne info rmatio n with a class so we can style the
<span> appro priately (as yo u'll see after yo u add the CSS belo w). If the to -do item is not do ne, we add the class
"no tDo ne" to the spanDo ne <span> element, and if it is do ne, then we add the class "do ne". Also no tice that we're
setting the inne rHT ML o f the "no tDo ne" <span> to five spaces—&nbsp; is the HTML entity fo r a no n-breaking space—
and setting the inne rHT ML o f the "do ne" <span> to two spaces and a check mark—&#10 0 0 4; is the HTML entity fo r
the check mark. Yo u'll see what this lo o ks like in a minute, after yo u add the CSS and preview again.

After creating the <li> element and the two <span> elements, we add t he t wo <span> e le m e nt s t o t he <li>: first
the spanDo ne , so we can sho w if an item is do ne o r no t o n the left part o f the to -do item, and then the spanT o do ,
which co ntains the text o f the to -do item (the same text as we were sho wing previo usly). Then we return the <li>
element, just like we did befo re. Remember that bo th the addT o do sT o Page () and addT o do T o Page () functio ns
call the cre at e Ne wT o do () functio n, so they get the <li> element back and add it to the DOM, so we can see o ur to -do
item.

Okay, we're almo st there! Mo dify yo ur t o do .css file as sho wn so yo u can see the result o f adding the two <span>
elements to the HTML:
CODE TO TYPE:
body {
font-family: Helvetica, Ariel, sans-serif;
}
legend {
font-weight: bold;
}
div.tableContainer {
display: table;
border-spacing: 5px;
}
div.tableRow {
display: table-row;
}
div.tableRow label {
display: table-cell;
text-align: right;
}
div.tableRow input {
display: table-cell;
}
ul#todoList {
list-style-type: none;
margin-left: 0px;
padding-left: 0px;
}
ul#todoList li {
padding: 5px;
margin: 5px;
background-color: #ededed;
border: 2px inset #ededed;
}
ul#todoList li span.notDone {
margin-right: 10px;
background-color: #FF9999;
}
ul#todoList li span.done {
margin-right: 10px;
background-color: #80CC80;
}

Save it, o pen yo ur t o do .ht m l file, and click . No w yo u sho uld see yo ur to -do items lo o king a lo t
snazzier:
To test the "do ne" vs. "no tDo ne" co de, make sure yo u have o ne to -do item in yo ur t o do .jso n file that uses "true" fo r
the do ne pro perty. Yo u can do this by editing yo ur t o do .jso n file and updating the co de. Fo r instance, I updated the
first item in my file, so my JSON lo o ks like this:

OBSERVE:

[{"task":"Get milk","who":"Scott","dueDate":"today","done":true},
{"task":"Get broccoli","who":"Elisabeth","dueDate":"today","done":false},
{"task":"get balsamic vinegar","who":"Georgia","dueDate":"2012-08-04","done":false},
{"task":"Walk Trish's dog","who":"Scott","dueDate":"2012-06-06","done":false}]

Once yo u've made this change (o r a similar change in yo ur t o do .jso n, which likely has different items in it at this
po int!), either relo ad yo ur already-o pen bro wser windo w with yo ur to -do list applicatio n, o r click o n yo ur
t o do .ht m l file again. Yo u'll see a nice-lo o king green bo x with a check mark indicating that yo ur to -do item is do ne!
Take a lo o k at the CSS to see ho w we styled the to -do items and no tice the two classes, "do ne" and "no tDo ne". We're
using them in o ur JavaScript co de to get the lo o k o f a "do ne" item (a green bo x with a check mark) and a "no tDo ne"
item (a red bo x with no check mark).

No w, yo u've pro bably no ticed that the o nly way to mark an item as "do ne" is to edit yo ur t o do .jso n file, which do esn't
seem like a very user-friendly pro cess. Do n't wo rry abo ut that fo r no w, we'll co me back to this issue in a later lesso n.

In this lesso n, mo st o f what yo u learned is behind-the-scenes stuff. Yo u learned ho w to impro ve yo ur co de by reducing


redundant co de, co mbine co mmo n co de into o ne functio n, and make yo ur co de mo re efficient by using do cument fragments.

As a pro grammer, yo u'll o ften disco ver ways to make yo ur co de better by reducing redundant co de o r making it mo re efficient,
especially as yo u begin to wo rk o n bigger pro jects. We call this refactoring yo ur co de. The main go als o f refacto ring are to
reduce the co mplexity o f yo ur co de, impro ve its readability, and make it easier to maintain. Yo u'll want to get into the habit o f
assessing yo ur co de frquently as yo u build it, to see if it needs refacto ring.

Enjo y the quiz and pro ject, and stay tuned fo r the next lesso n, where yo u'll learn a who le new technique fo r sto ring yo ur to -do
items!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Introduction to Local Storage
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

use lo cal sto rage in JavaScript.


find o ut what lo cal sto rage is available in yo ur bro wser.
use the lo calSto rage o bject with JavaScript.
iterate thro ugh lo cal sto rage.
remo ve items fro m lo cal sto rage.

In previo us lesso ns, yo u learned ho w to use XMLHttpRequest in yo ur web applicatio n to co mmunicate with a server, bo th to
retrieve data to update yo ur web page, and to save data so that the next time a web applicatio n is lo aded, the data is still there.

XMLHttpRequest is a great so lutio n fo r saving and retrieving lo ng-term data, o r data that yo u want yo ur web applicatio n to be
available to users o f any bro wsers.

So metimes tho ugh, yo u o nly need to sto re data fo r a sho rt perio d o f time, fo r example, preferences fo r a sho pping sessio n.
Other times yo u might want to save so me data that a user has do wnlo aded so that if they return to yo ur page again, yo ur
applicatio n do esn't have to do wnlo ad it again if it's still saved.

In these cases, yo u can use a technique called Local Storage.

What is Local Storage?


Lo cal Sto rage is, as its name wo uld suggest, sto rage that's local. Lo cal where? In yo ur bro wser! Let's check it o ut.
Open a new file and type the co de as sho wn:

CODE TO TYPE:

<!doctype html>
<html>
<head>
<title>Local Storage</title>
<meta charset="utf-8">
<script src="prefs.js"></script>
</head>
<body>
<h1>Preferences</h1>
<ul id="items">
</ul>
</body>
</html>

Save it in yo ur /javascript 2 fo lder as pre f s.ht m l. The <script> tag includes a link to pre f s.js; let's create that no w:

CODE TO TYPE:
window.onload = init;

function init() {
localStorage.setItem("favGenre", "fiction");
}

Save it in yo ur /javascript 2 fo lder as pre f s.js. Open yo ur pre f s.ht m l file and click . Yo u wo n't see
anything special in the HTML page, so o pen yo ur develo per co nso le and type lo calSt o rage at the JavaScript
co nso le pro mpt:
We created a little bit o f data, with the name " f avGe nre " and the value " f ict io n" , and sto red this data in the o bject
named lo calSt o rage . Befo re we dig into Lo cal Sto rage, mo dify pre f s.js as sho wn:

CODE TO TYPE:
window.onload = init;

function init() {
localStorage.setItem("favGenre", "fiction");
var favoriteGenre = localStorage.getItem("favGenre");

var ul = document.getElementById("items");
var li = document.createElement("li");
li.innerHTML = favoriteGenre;
ul.appendChild(li);
}

Save it. In this versio n o f pre f s.js, we delete the first line which creates the " f avGe nre " bit o f data, and replace that
line with new co de that retrieves that bit o f data, using the same name (" f avGe nre " ) to save it, and then add the data
to the web page by creating a new list item and adding the list item to the "items" list (which already exists in yo ur
HTML).

Befo re yo u preview, make sure yo u close the bro wser windo w yo u had o pen when yo u ran the pro gram befo re. We
want to make sure we're starting fro m scratch. No w, o pen pre f s.ht m l, and click . Do yo u see "fictio n" in
the list in the page? Open the develo per co nso le, and again, type lo calSt o rage at the JavaScript co nso le pro mpt:
So even tho ugh yo u closed the bro wser windo w, when yo u retrieve the data yo u sto red in the lo calSt o rage o bject, it
is still there! So , what's go ing o n? Ho w do es the data in the lo calSt o rage o bject survive, even tho ugh yo u clo sed the
bro wser windo w, and relo aded the pre f s.ht m l page?!

Here's the secret: the lo calSt o rage o bject is a built-in JavaScript o bject that saves data in the browser itself. It wo rks
in all bro wsers. As yo u kno w, the bro wser is a pro gram that yo u use to bro wse the web and run web applicatio ns.
When yo u run yo ur web applicatio n in the bro wser, and use Lo cal St o rage to sto re data, the bro wser reserves so me
space just fo r yo ur web applicatio n's data, and makes it accessible to yo ur web applicatio n thro ugh the lo calSt o rage
o bject.

We use the term "Lo cal Sto rage" to refer to the feature o ffered by bro wsers, and "lo calSto rage" to refer
Note specifically to the lo calSt o rage o bject in JavaScript.

Have yo u ever heard o f browser cookies? Well, Lo cal St o rage is similar to co o kies, except that in many ways, it's
way better. We'll co me back to co o kies later, fo r no w we'll fo cus o n Lo cal Sto rage.)

Exploring Local Storage in the Browser


All mo dern bro wsers include to o ls yo u can use to inspect Lo cal Sto rage. Befo re we go further using JavaScript
pro gramming with Lo cal Sto rage, let's learn ho w to find o ut what's in yo ur bro wser's Lo cal Sto rage.

These next few video s sho w ho w to explo re Lo cal Sto rage using bro wser to o ls:

Firefo x, Chro me, and Opera

Safari 5
Safari 6

If yo u've upgraded to Safari 6 (fro m Safari 5, the current versio n at the time this co urse was written), yo u'll need to
watch this video :

Safari 6

Microsoft Int ernet Explorer 9

Internet Explo rer 9 wo rks much like Firefo x, Chro me, and Opera. Select T o o ls | F12 De ve lo pe r T o o ls (o r press
F12) and select Co nso le . Then, type lo calSt o rage and any o ther co mmands at the >> pro mpt.

After watching the video s, make sure yo u try accessing Lo cal Sto rage fro m at least o ne o r mo re bro wsers. Try adding
new items to Lo cal Sto rage fro m the JavaScript co nso le. Make sure yo u kno w ho w to determine what's in Lo cal
Sto rage; it will co me in handy fo r debugging yo ur pro grams later.

Bro wser Develo per To o ls usually change with each new versio n o f a bro wser. While the functio nality
sho uld be the same o r better with each new versio n, user interfaces so metimes changes dramatically.
Note So if yo ur bro wser Develo per To o ls lo o k different fro m the versio ns sho wn in the video s o r screen
sho ts, do n't wo rry. Yo u'll still be able to do the same things, yo u just might have to wo rk a bit to figure o ut
the differences in the UI.

A Closer Look at the localStorage Object


Let's take a clo ser lo o k at the lo calSt o rage o bject and the se t It e m () metho d first.

OBSERVE:
localStorage.setItem("favGenre", "fiction");

The lo calSt o rage o bject is similar to o bjects yo u create in JavaScript, except that it's built in to JavaScript, and has
access to native bro wser co de that can sto re and retrieve data fro m the bro wser lo cal sto rage area. The se t It e m ()
metho d allo ws yo u to sto re data. Each item sto red in Lo cal Sto rage has a ke y and a value . This type o f co mputer
sto rage is called an associative array; instead o f using a numerical index (and having a specific o rder based o n that
index) like in a regular array, an asso ciative array uses keys as the index values (and has no inherent o rdering).

The ke y yo u use to sto re the data must be a string; in this case, we used the string " f avGe nre " . Each key must be
unique—if yo u use the same key to sto re ano ther piece o f data, yo u'll o verwrite the previo us piece o f data yo u sto red in
the bucket fo r that key.

When yo u want to retrieve a value fro m Lo cal Sto rage, yo u need to use the same ke y:
OBSERVE:
var favoriteGenre = localStorage.getItem("favGenre");

The lo calSt o rage has a ge t It e m () metho d that yo u use to retrieve a value by its ke y. Here, we're using the
" f avGe nre " ke y to retrieve the bit o f data we just sto red. The value re t urne d is the value yo u sto red; we're putting
that value in the variable f avo rit e Ge nre , which yo u can then use in yo ur pro gram. In the pre f s example, we added
that value to a list in the web page.

Using the localStorage Object with JavaScript


Let's expand o ur example a bit, so yo u can see a co uple mo re ways to use the lo calSt o rage o bject with JavaScript.
Mo dify pre f s.js as sho wn:

CODE TO TYPE:
window.onload = init;

function init() {
var favoriteGenre = localStorage.getItem("favGenre");

addItem("favGenre", "fiction");
addItem("favFlavor", "Vanilla Chocolate Chip");
addItem("book", "Head First HTML5 Programming");
addItem("browserWidth", 1280);
}

function addItem(key, value) {


localStorage.setItem(key, value);
addToList(key, value);
}
function getItem(key) {
var value = localStorage.getItem(key);
alert("Item: " + key + ": " + value);
}
function addToList(key, value) {
var ul = document.getElementById("items");
var li = document.createElement("li");
li.innerHTML = favoriteGenre"Key: " + key + ", value: " + value;
ul.appendChild(li);
}

Save it, o pen yo ur pre f s.ht m l file, and click . Yo u see fo ur items in the list, and if yo u check yo ur
bro wser's Lo cal Sto rage, yo u see the same fo ur items there:
Try adding a few mo re items using this pro gram by adding mo re calls, with different values, to the addIt e m () functio n.
Relo ad the page. Do yo u see yo ur new items? Try changing the values. Relo ad. Do yo u no tice anything abo ut the
o rdering o f the items in the list, and the o rdering in Lo cal Sto rage?

In this pro gram, we create an addIt e m () functio n to add a key and a value to Lo cal Sto rage, and then call ano ther
functio n, addT o List (), to add it to the page.

We also create a ge t It e m () functio n to get an item fro m Lo cal Sto rage, but we're no t using it yet. Let's do that next.
No w that yo u have a bunch o f items in Lo cal Sto rage, let's co mment o ut the lines to add the items, and add a call to
ge t It e m () so we can see the values that are in Lo cal Sto rage that way. Mo dify pre f s.js as sho wn:
CODE TO TYPE:
window.onload = init;

function init() {
//addItem("favGenre", "fiction");
//addItem("favFlavor", "Vanilla Chocolate Chip");
//addItem("book", "Head First HTML5 Programming");
//addItem("browserWidth", 1280);
getItem("book");
}

function addItem(key, value) {


localStorage.setItem(key, value);
addToList(key, value);
}
function getItem(key) {
var value = localStorage.getItem(key);
alert("Item: " + key + ": " + value);
}
function addToList(key, value) {
var ul = document.getElementById("items");
var li = document.createElement("li");
li.innerHTML = "Key: " + key + ", value: " + value;
ul.appendChild(li);
}

Save it, o pen yo ur pre f s.ht m l file, and click . Yo u see an alert sho wing the bo o k item yo u go t fro m
Lo cal Sto rage using ge t It e m (). No tice that yo u will not see the items in the list in the web page. Why? Because yo u're
no lo nger calling addT o List () which is called fro m addIt e m (). Try changing "bo o k" to ano ther key, like
"bro wserWidth". Yo u see an alert sho wing yo u the key and the value fo r each key yo u try. What happens if yo u try a key
that do esn't exist in Lo cal Sto rage?

Local Storage Stores Strings


Did yo u no tice that the "bro wserWidth" item that we added has a numeric value, rather than a string value?

Currently, bro wser implementatio ns o f Lo cal Sto rage o nly sto re string values. So ho w is o ur "bro wserWidth"
value wo rking? Make this change in pre f s.js and then see if yo u can guess ho w it's wo rking:

CODE TO TYPE:
window.onload = init;

function init() {
//addItem("favGenre", "fiction");
//addItem("favFlavor", "Vanilla Chocolate Chip");
//addItem("book", "Head First HTML5 Programming");
//addItem("browserWidth", 1280);
getItem("book""browserWidth");
}

function addItem(key, value) {


localStorage.setItem(key, value);
addToList(key, value);
}
function getItem(key) {
var value = localStorage.getItem(key);
alert("Item: " + key + ": " + value + " (" + (typeof value) + ")");
}
function addToList(key, value) {
var ul = document.getElementById("items");
var li = document.createElement("li");
li.innerHTML = "Key: " + key + ", value: " + value;
ul.appendChild(li);
}
Save it, o pen yo ur pre f s.ht m l file, and click .

The lo calSto rage o bject co nverted the number, 1280 , that we used as the value fo r the "bro wserWidth" key, to
a string to sto re it in Lo cal Sto rage. Then, when we get the value fo r "bro wserWidth" using ge t It e m (), the
value returned is a string, no t a number, because that's ho w it was sto red. (No te that we used a built-in
JavaScript o perato r, t ype o f , to get the type o f the primitive value returned fro m the call to ge t It e m ().
t ype o f is a unary o perato r: it takes o ne value, and returns its type. This o perato r wo rks o n all JavaScript
primitive types, including numbers, strings, Bo o leans and undefined).

Try adding an item (using addIt e m ()) that co ntains a Bo o lean value. Fo r instance:

OBSERVE:
addItem("isItCold", false);

and then use ge t It e m () to get the value and alert it. What is the type?

So , Lo cal Sto rage uses strings fo r bo th its keys and its values. That means, if yo u sto re ano ther value, like a
number o r a Bo o lean, yo u need to be prepared to co nvert it back to that type fro m a string when yo u retrieve
the value fro m Lo cal Sto rage later!

Iterating T hrough Local Storage


Have yo u tho ught abo ut this: What if yo u want to write a pro gram to list all the key/value pairs in Lo cal Sto rage? What if
there are hundreds o f keys and values? Wo uldn't it be a pain to have to identify each key individually?!

Indeed it wo uld, and there is a better way. We can iterate thro ugh all the keys in Lo cal Sto rage, witho ut kno wing in
advance what tho se keys are. Mo dify pre f s.js as sho wn:
CODE TO TYPE:
window.onload = init;

function init() {
//addItem("favGenre", "fiction");
//addItem("favFlavor", "Vanilla Chocolate Chip");
//addItem("book", "Head First HTML5 Programming");
//addItem("browserWidth", 1280);
getItem("browserWidth");

addItem("favTea", "English Breakfast");


showAllPrefs();
}

function addItem(key, value) {


localStorage.setItem(key, value);
addToList(key, value);
}
function getItem(key) {
var value = localStorage.getItem(key);
alert("Item: " + key + ": " + value + " (" + (typeof value) + ")");
}
function addToList(key, value) {
var ul = document.getElementById("items");
var li = document.createElement("li");
li.innerHTML = "Key: " + key + ", value: " + value;
ul.appendChild(li);
}
function showAllPrefs() {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var value = localStorage.getItem(key);
addToList(key, value);
}
}

Save it, o pen pre f s.ht m l, and click . Yo u see every item yo u've sto red in Lo cal Sto rage in yo ur list.
We added a new functio n, sho wAllPre f s(), to sho w us every item in Lo cal Sto rage. This functio n lo o ks thro ugh
everything in Lo cal Sto rage and adds it to the web page by calling addT o List (). We call sho wAllPre f s() in init (), so
we no lo nger need to call addT o List () fro m addIt e m (). We just remo ved that call (o therwise, items we add to Lo cal
Sto rage will be sho wn twice o n the page!).

Let's step thro ugh sho wAllPre f s(), because we're using a co uple o f new things abo ut the lo calSt o rage o bject we
haven't talked abo ut yet: the le ngt h pro perty and the ke y() metho d.

OBSERVE:
function showAllPrefs() {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var value = localStorage.getItem(key);
addToList(key, value);
}
}

First, remember that the lo calSt o rage o bject uses an associative array to sto re items in the bro wser's Lo cal Sto rage.
While an asso ciative array do esn't have numerical indices like a regular array do es, it do es have a le ngt h pro perty,
which is just the number o f key/value pairs in lo calSt o rage .

We can lo o p thro ugh all the keys in lo calSt o rage and access each key using the ke y metho d. Even tho ugh
lo calSt o rage do esn't have an index like regular arrays, the ke y metho d makes it act a bit like it do es, because the
ke y metho d takes a number and returns a key.
lo calSt o rage .ke y(i) returns the string value o f a key, like "bo o k" o r "bro wserWidth." Once yo u have that key, stashed
in the variable ke y, yo u can use it to re t rie ve t he value asso ciat e d wit h t hat ke y, using
lo calSt o rage .ge t It e m (ke y).

Finally, we pass bo th the ke y and the value to addT o List () to add them to the page.

Yo u're go ing to find that yo u'll frequently use this technique o f iterating thro ugh all the keys in Lo cal Sto rage to retrieve
the values because yo u wo n't always kno w precisely what keys yo u have o r ho w many yo u have.

Removing Items from Local Storage


Okay, yo u kno w ho w to add items to Lo cal Sto rage, retrieve items fro m Lo cal Sto rage, and even iterate thro ugh all the
items in Lo cal Sto rage. But ho w do yo u remo ve items fro m Lo cal Sto rage using JavaScript? Yo u kno w ho w to do it
with the bro wser develo per to o ls, but there is also a way to do it using co de. Yo u can remo ve o ne item at a time using
lo calSt o rage .re m o ve It e m (), o r yo u can remo ve everything at o nce, using lo calSt o rage .cle ar(). Mo dify pre f s.js
as sho wn:
CODE TO TYPE:
window.onload = init;

function init() {
//addItem("favGenre", "fiction");
//addItem("favFlavor", "Vanilla Chocolate Chip");
//addItem("book", "Head First HTML5 Programming");
//addItem("browserWidth", 1280);
//addItem("favTea", "English Breakfast");

removeItem("favFlavor");

showAllPrefs();
}

function addItem(key, value) {


localStorage.setItem(key, value);
}
function removeItem(key) {
localStorage.removeItem(key);
}
function getItem(key) {
var value = localStorage.getItem(key);
alert("Item: " + key + ": " + value + " (" + (typeof value) + ")");
}
function addToList(key, value) {
var ul = document.getElementById("items");
var li = document.createElement("li");
li.innerHTML = "Key: " + key + ", value: " + value;
ul.appendChild(li);
}
function showAllPrefs() {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var value = localStorage.getItem(key);
addToList(key, value);
}
}

Save it. Befo re yo u preview, check the items in yo ur Lo cal Sto rage using the bro wser develo per to o ls. Then o pen
pre f s.ht m l and click . If yo u check yo ur Lo cal Sto rage, the item with the key "favFlavo r" sho uld be go ne.
Try remo ving a co uple mo re items fro m Lo cal Sto rage by changing the key yo u pass to re m o ve It e m (), and relo ading
the page. What happens if yo u try to remo ve a key that do esn't exist?

Our re m o ve It e m () functio n uses the lo calSt o rage .re m o ve It e m () metho d to remo ve the item fro m Lo cal Sto rage.
The metho d takes o ne argument, the key to be deleted, and remo ves the key and its value fro m Lo cal Sto rage.

To remo ve all the items in Lo cal Sto rage, use the lo calSt o rage .cle ar() metho d:
CODE TO TYPE:
window.onload = init;

function init() {
//addItem("favGenre", "fiction");
//addItem("favFlavor", "Vanilla Chocolate Chip");
//addItem("book", "Head First HTML5 Programming");
//addItem("browserWidth", 1280);
//addItem("favTea", "English Breakfast");

removeItem("favFlavor");
clearAllItems();
showAllPrefs();
}

function addItem(key, value) {


localStorage.setItem(key, value);
}
function removeItem(key) {
localStorage.removeItem(key);
}
function clearAllItems() {
localStorage.clear();
}
function getItem(key) {
var value = localStorage.getItem(key);
alert("Item: " + key + ": " + value + " (" + (typeof value) + ")");
}
function addToList(key, value) {
var ul = document.getElementById("items");
var li = document.createElement("li");
li.innerHTML = "Key: " + key + ", value: " + value;
ul.appendChild(li);
}
function showAllPrefs() {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var value = localStorage.getItem(key);
addToList(key, value);
}
}

Save it, o pen pre f s.ht m l, and click . No w there are no items in yo ur list, and if yo u check yo ur Lo cal
Sto rage using the bro wser's develo per to o l, yo u'll see no items in yo ur Lo cal Sto rage.

Cookies (Not the Kind You Can Eat)


Yo u may have heard o f browser cookies befo re, and yo u may even be familiar eno ugh with them to see that co o kies
and Lo cal Sto rage have so me similarities. Co o kies are ano ther mechanism fo r sto ring data in the bro wser. To create
a co o kie, yo u create a string that lo o ks like this:

Co o kie: favFlavo r=VanillaCho co lateChip; favTea=EnglishBreakfast; bro wserWidth=8 0 0

As yo u can see, a co o kie has o ne o r mo re keys and o ne o r mo re values asso ciated with tho se keys. So in that way, a
co o kie is quite similar to Lo cal Sto rage. Applicatio ns that interact with a web server use co o kies to sto re tempo rary
data o r perso nalize an experience, in much the same way yo u use Lo cal Sto rage to do tho se things.

Ho wever, co o kies have a few o f big disadvantages:

Co o kies are sent back and fo rth to and fro m the server each time yo u make a request! So if yo ur applicatio n
co mmunicates with a web server at o reillystudent.co m, every co o kie asso ciated with o reillystudent.co m will
be sent to the server, and retrieved fro m the server, with each request. This adds o verhead to each request
(slo wing do wn yo ur applicatio n).
Co o kies are limited to 4K o f data. Co mpare this to 5MB o f data that each do main gets to sto re data in Lo cal
Sto rage. That's a huge difference!
The JavaScript interface fo r interacting with co o kies is a lo t mo re difficult to use than the lo calSto rage o bject.
It's o ften mo re difficult fo r users to have visibility into what co o kies are sto red o n their co mputers.

Co o kies are still useful fo r certain types o f applicatio ns, but it's likely that Lo cal Sto rage will take o ver a lo t o f the
functio ns that co o kies have been used fo r until no w.

We ho pe yo u've had so me fun sto ring, retrieving, and remo ving items to and fro m yo ur bro wser's Lo cal Sto rage using the
JavaScript lo calSt o rage o bject.

Two mo re things to kno w abo ut Lo cal Sto rage: first, Lo cal Sto rage is part o f the We b St o rage part o f the HTML specificatio n.
Mo st develo pers, ho wever, just call it "Lo cal Sto rage" rather than "Web Sto rage." But if yo u hear "Web Sto rage," it's likely to be
referring to the Lo cal Sto rage feature.

Alo ng with the lo calSt o rage o bject, there is ano ther built-in o bject yo u can use named se ssio nSt o rage . Sessio n Sto rage is
just like Lo cal Sto rage except that all data that is sto red in the bro wser go es away when yo u clo se the tab o r windo w yo u've
been using to interact with applicatio n. Sessio n Sto rage is useful fo r sto ring very tempo rary items that yo u want to make sure
go away when the user leaves the page.

In the next lesso n, we'll update the To do List Applicatio n to use Lo cal Sto rage.

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Updating the To-Do List Application for Local Storage
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

sto re o bjects in lo cal sto rage.


save to -do items in lo cal sto rage.
add an ID to a to -do item.
get to -do items fro m lo cal sto rage.
use the substring() metho d to take any String and create a new String fro m it.

Storing Objects in Local Storage


In the previo us lesso n, yo u learned the basics o f sto ring data in the bro wser, using Lo cal Sto rage. In this lesso n, we're
go ing update o ur To -Do List applicatio n to sto re to -do items in Lo cal Sto rage, rather than o n the server.

Yo u kno w ho w to sto re simple strings in Lo cal Sto rage, and yo u kno w that yo u can sto re o nly strings in Lo cal Sto rage
(meaning yo u can't sto re numbers, o bjects, o r o ther types in Lo cal Sto rage). A to -do item is an o bject. Here's an
example o f o ne:

OBSERVE:
{
task: "get milk",
who: "Scott",
dueDate: "today",
done: false
}

Kno wing that yo u can o nly sto re strings in Lo cal Sto rage, can yo u think o f ho w we might sto re to -do items? And what
sho uld we use as the keys fo r the to -do items?

We can use JSON to sto re o bjects such as o ur to -do items in Lo cal Sto rage. Recall that JSON allo ws us to represent
JavaScript o bjects as strings. Once we have a string representatio n o f an o bject, we can sto re it in Lo cal Sto rage just
like any o ther string. Similarly, we can use JSON to retrieve the string fro m Lo cal Sto rage and turn it back into an o bject.

Befo re we dive back into the To -Do List applicatio n, let's create a small example to sto re and retrieve JSON o bjects

fro m Lo cal Sto rage. Create a new HTML file as sho wn:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>Store an Object in Local Storage</title>
<meta charset="utf-8">
<script src="storeObj.js"></script>
</head>
<body>

<h1>Store an object in Local Storage</h1>

</body>
</html>

Save this file in yo ur /javascript 2 fo lder as st o re Obj.ht m l. We link to the file st o re Obj.js in the header—that's
where all the real actio n is! No w we need so me HTML so we can preview and lo o k Lo cal Sto rage using the bro wser
develo per co nso le.
Create a new file and add the JavaScript as sho wn:

CODE TO TYPE:

window.onload = init;

function init() {
var myObject = {
name: "Trish",
age: 29,
favColor: "blue"
};
var myObjectJson = JSON.stringify(myObject);
localStorage.setItem("trish", myObjectJson);
var newMyObjectJSON = localStorage.getItem("trish");
var newMyObject = JSON.parse(newMyObjectJSON);
alert(newMyObject.name + " is " + newMyObject.age +
" and her favorite color is " + newMyObject.favColor);
}

Save this file in yo ur /javascript 2 fo lder as st o re Obj.js, o pen yo ur st o re Obj.ht m l file, click . Yo u
see an alert like this:

Use yo ur bro wser's develo per co nso le to inspect Lo cal Sto rage. The Lo cal Sto rage includes the item yo u just sto red
using this pro gram:
Yo u might see additio nal items in Lo cal Sto rage if yo u've added anything to the Lo cal Sto rage at the
Note o reillystudent.co m do main. Just make sure yo u see the "trish" o bject yo u just added!

Let's lo o k at the JavaScript:

OBSERVE:
function init() {
var myObject = {
name: "Trish",
age: 29,
favColor: "blue"
};
var myObjectJson = JSON.stringify(myObject);
localStorage.setItem("trish", myObjectJson);
var newMyObjectJSON = localStorage.getItem("trish");
var newMyObject = JSON.parse(newMyObjectJSON);
alert(newMyObject.name + " is " + newMyObject.age +
" and her favorite color is " + newMyObject.favColor);
}

This init() functio n:

cre at e s an o bje ct nam e d m yObje ct .


co nve rt s t hat o bje ct t o J SON (a st ring) using t he J SON o bje ct 's st ringif y() m e t ho d.
st o re s t he J SON ve rsio n o f t he o bje ct in Lo cal St o rage , wit h t he ke y " t rish" , using t he
lo calSt o rage o bje ct 's se t It e m () m e t ho d.
re t rie ve s a J SON st ring f ro m Lo cal St o rage using t he sam e ke y, " t rish," using t he
lo calSt o rage o bje ct 's ge t It e m () m e t ho d, and saving it in a ne w variable , ne wMyObje ct J SON.
cre at e s ne wMyObje ct f ro m t hat J SON st ring using t he J SON o bje ct 's parse () m e t ho d.
displays t he value s in ne wMyObje ct using do t no t at io n, and ale rt s t he value s so yo u can se e
t he m (ne wMyObje ct has e xact ly t he sam e st ruct ure as o ur o riginal m yObje ct , so we can do
t he m (ne wMyObje ct has e xact ly t he sam e st ruct ure as o ur o riginal m yObje ct , so we can do
t his).

So no w that yo u kno w ho w to sto re and retrieve an o bject fro m Lo cal Sto rage, let's go back to the To -Do List
applicatio n and mo dify it to use Lo cal Sto rage instead o f Ajax to sto re to -do items.

Saving T o-Do Items in Local Storage


Even tho ugh we use keys that relate to the co ntent o f the items we're sto ring (fo r example, "favGenre" and
"bro wserWidth"), it's still difficult to figure o ut which items in Lo cal Sto rage belo ng to which applicatio n. So , unless yo u
use a string in yo ur key that indicates that a particular item belo ngs to a specific applicatio n, ho w will yo ur applicatio n
kno w which items it can use? Remember that mo re than o ne applicatio n can use Lo cal Sto rage at any given do main,
and all these applicatio ns will be mixed to gether in Lo cal Sto rage; we need a way to identify the items that belo ng to the
To -Do List applicatio n.

Fo r no w, let's create a key fo r each to -do item fro m a unique id and a string that represents the applicatio n. We'll create
the unique id fo r each to -do item as that item is created (that is, when yo u submit a new to -do item using the To -Do
List fo rm), sto re that id (alo ng with the o ther to -do list info rmatio n) in the To do o bject, and then use that id when we
create the key to sto re the item in Lo cal Sto rage. Let's update the co de and then we'll go o ver each change.

We'll co ntinue to update the t o do .ht m l and t o do .js files. Yo u might want to make co pies o f the files to
Note save yo ur previo us wo rk.

Mo dify t o do .js as sho wn:


CODE TO TYPE:
function Todo(id, task, who, dueDate) {
this.id = id;
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;

getTodoData();
}

function getTodoData() {
var request = new XMLHttpRequest();
request.open("GET", "todo.json");
request.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText) {
parseTodoItems(this.responseText);
addTodosToPage();
}
else {
console.log("Error: Data is empty");
}
}
};
request.send();
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
}
function addTodosToPage() {
var ul = document.getElementById("todoList");
var listFragment = document.createDocumentFragment();
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = createNewTodo(todoItem);
listFragment.appendChild(li);
}
ul.appendChild(listFragment);
}
function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = createNewTodo(todoItem);
ul.appendChild(li);
document.forms[0].reset();
}
function createNewTodo(todoItem) {
var li = document.createElement("li");
var spanTodo = document.createElement("span");
spanTodo.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;

var spanDone = document.createElement("span");


if (!todoItem.done) {
spanDone.setAttribute("class", "notDone");
spanDone.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
}
else {
spanDone.setAttribute("class", "done");
spanDone.innerHTML = "&nbsp;&#10004;&nbsp;";
}

li.appendChild(spanDone);
li.appendChild(spanTodo);
return li;
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

var id = todos.length;
var todoItem = new Todo(id, task, who, date);
todos.push(todoItem);
addTodoToPage(todoItem);
saveTodoData();
saveTodoItem(todoItem);
}

function checkInputText(value, msg) {


if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

function saveTodoItem(todoItem) {
if (localStorage) {
var key = "todo" + todoItem.id;
var item = JSON.stringify(todoItem);
localStorage.setItem(key, item);
}
else {
console.log("Error: you don't have localStorage!");
}
}

function saveTodoData() {
var todoJSON = JSON.stringify(todos);
var request = new XMLHttpRequest();
var URL = "save.php?data=" + encodeURI(todoJSON);
request.open("GET", URL);
request.setRequestHeader("Content-Type",
"text/plain;charset=UTF-8");
request.send();
}
Save it. We remo ved the functio ns to save and get the to -do items (save T o do Dat a() and (ge t T o do Dat a()) fro m
the server. We replaced the functio n to save the data with a new functio n, save T o do It e m (), which takes o ne to -do
item and saves it to Lo cal Sto rage.

Because we are still using the same JSON fo rmat we used befo re (in the Ajax versio n), we do n't have to change much
o f the o ther co de. That's co nvenient. Befo re we walk thro ugh the details, o pen t o do .ht m l and click . Try
adding a few to -do items using the fo rm:

When yo u lo o k at Lo cal Sto rage using yo ur bro wser, yo u see the new items yo u added. If yo u had o ther items in Lo cal
Sto rage already, the new to -do items will be mixed in with tho se o ther items. Yo u'll be able to tell which items belo ng
to the To -Do List applicatio n because the keys all begin with "to do ."

Right no w we are saving the items we enter using the fo rm in Lo cal Sto rage (we're no t retrieving tho se items when we
first lo ad the page), so yo u'll see new items being added to Lo cal Sto rage and to the page as yo u add them using the
fo rm. If yo u relo ad the page, yo u wo n't see tho se items in yo ur list yet. We'll get to that sho rtly.

Adding an ID to a T odo Item


We had to add an id pro perty to the T o do o bject, so each to -do item co uld sto re a unique id:
OBSERVE:
function Todo(id, task, who, dueDate) {
this.id = id;
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

Once we have this new id pro perty, we need to update the co de where we create the T o do o bject, to pass in
a unique id. But what do we use fo r that unique id?

OBSERVE:
function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

var id = todos.length;
var todoItem = new Todo(id, task, who, date);
todos.push(todoItem);
addTodoToPage(todoItem);
saveTodoItem(todoItem);
}

Each time we create a new T o do o bject representating a to -do item, we're adding that item to the t o do s
array. Each time we add a new item to the array, the length o f the array increases. Because o f that, we can use
the curre nt le ngt h o f t he array as a unique id. We retrieve the length o f the array with t o do s.le ngt h.
Once we have an id, which in this case is just a number, we can use that id when we cre at e a ne w T o do
o bje ct . Next, we add t he ne w T o do it e m t o t he array, and t o t he page , and then save t he ne w it e m
using o ur new functio n save T o do It e m ():

OBSERVE:
function saveTodoItem(todoItem) {
if (localStorage) {
var key = "todo" + todoItem.id;
var item = JSON.stringify(todoItem);
localStorage.setItem(key, item);
}
else {
console.log("Error: you don't have localStorage!");
}
}

The save T o do It e m () functio n takes o ne argument, t o do It e m , which is a T o do o bject with a unique id in


the id pro perty. The functio n che cks t o m ake sure t hat t he use r's bro wse r has a lo calSt o rage
o bje ct , and if so , creates a ke y using the st ring " t o do " co ncat e nat e d wit h t he id o f t he t o do It e m .
So , if we pass in a t o do item with an id o f 1, then the ke y will be "to do 1".

No w the t o do It e m is a T o do o bject, so we have to co nvert it to JSON befo re we can sto re it in Lo cal


Sto rage. We do that using the J SON.st ringif y() metho d. We no w have a key and an item, so we can st o re
t he it e m in Lo cal St o rage using lo calSt o rage .se t It e m ().

If the user's bro wser do esn't have lo calSto rage, we sho w an erro r message in the co nso le. (In a mo re ro bust
applicatio n yo u might want to have ano ther o ptio n.)
Getting T o-Do Items from Local Storage
Okay, we've go t to -do items sto red in Lo cal Sto rage and they're being displayed o n the page as yo u add them. We put
the items in Lo cal Sto rage so they'll be there when yo u relo ad the page (o r want to access yo ur items the next day o r
even a mo nth fro m no w).

We can use lo calSt o rage .ge t It e m () to retrieve items fro m Lo cal Sto rage, but we want to retrieve o nly items with
keys that co ntain the string "to do ." Because we get just the items fro m Lo cal Sto rage when we lo ad the page, we need
to add all the items in Lo cal Sto rage to the list yo u see o n the page as well. We already have a functio n that do es that:
addT o do sT o Page (). It adds all the items in the t o do s array to the page. So , as we retrieve each item fro m Lo cal
Sto rage, we'll add it to the t o do s array, then we can call addT o do sT o Page () to add them to the page.

Mo dify t o do .js as sho wn:


CODE TO TYPE:
function Todo(id, task, who, dueDate) {
this.id = id;
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;

getTodoItems();
}

function getTodoItems() {
if (localStorage) {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (key.substring(0, 4) == "todo") {
var item = localStorage.getItem(key);
var todoItem = JSON.parse(item);
todos.push(todoItem);
}
}
addTodosToPage();
}
else {
console.log("Error: you don't have localStorage!");
}
}

function parseTodoItems(todoJSON) {
if (todoJSON == null || todoJSON.trim() == "") {
return;
}
var todoArray = JSON.parse(todoJSON);
if (todoArray.length == 0) {
console.log("Error: the to-do list array is empty!");
return;
}
for (var i = 0; i < todoArray.length; i++) {
var todoItem = todoArray[i];
todos.push(todoItem);
}
}
function addTodosToPage() {
var ul = document.getElementById("todoList");
var listFragment = document.createDocumentFragment();
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = createNewTodo(todoItem);
listFragment.appendChild(li);
}
ul.appendChild(listFragment);
}
function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = createNewTodo(todoItem);
ul.appendChild(li);
document.forms[0].reset();
}
function createNewTodo(todoItem) {
var li = document.createElement("li");
var spanTodo = document.createElement("span");
spanTodo.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;

var spanDone = document.createElement("span");


if (!todoItem.done) {
spanDone.setAttribute("class", "notDone");
spanDone.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
}
else {
spanDone.setAttribute("class", "done");
spanDone.innerHTML = "&nbsp;&#10004;&nbsp;";
}

li.appendChild(spanDone);
li.appendChild(spanTodo);
return li;
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

var id = todos.length;
var todoItem = new Todo(id, task, who, date);
todos.push(todoItem);
addTodoToPage(todoItem);
saveTodoItem(todoItem);
}

function checkInputText(value, msg) {


if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

function saveTodoItem(todoItem) {
if (localStorage) {
var key = "todo" + todoItem.id;
var item = JSON.stringify(todoItem);
localStorage.setItem(key, item);
}
else {
console.log("Error: you don't have localStorage!");
}
}

Save it, o pen t o do .ht m l, and click . Yo u see all the items that yo u had previo usly added in the page;
yo u'll be able to add new o nes. Give it a try. Remo ve so me items fro m Lo cal Sto rage using the bro wser co nso le to o l,
and then relo ad and make sure that what yo u see in Lo cal Sto rage matches what yo u see in the page.

Yo u may need to refresh the view o f Lo cal Sto rage in yo ur bro wser (using the bro wser to o ls) o r use the
Note JavaScript co nso le to display the lo calSt o rage o bject after yo u add an item to see the updated values.
Let's walk thro ugh the changes we made. We add a functio n ge t T o do It e m s(), and call that functio n fro m the init ()
functio n, so it runs as so o n as the page lo ads and yo u see all the items yo u have sto red, as so o n as yo u lo ad the
page.

OBSERVE:
function getTodoItems() {
if (localStorage) {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (key.substring(0, 4) == "todo") {
var item = localStorage.getItem(key);
var todoItem = JSON.parse(item);
todos.push(todoItem);
}
}
addTodosToPage();
}
else {
console.log("Error: you don't have localStorage!");
}
}

In ge t T o do It e m s(), we check to make sure the lo calSt o rage o bject o bject exists— if it do esn't, display a message
in the co nso le.

Next, we lo o p thro ugh all the items in Lo cal Sto rage using the le ngt h pro perty o f the lo calSt o rage o bject, but this
time, instead o f adding every item to the page, we want to add o nly the items that have the string "to do " in the key. We
che ck t o m ake sure t hat t he ke y st ring co nt ains t he st ring " t o do " , using the String metho d subst ring()
(we'll co me back to this metho d sho rtly). If it do es, then we get the item fro m Lo cal Sto rage, and use J SON t o parse ()
the string back into a To do o bject, which we sto re in the variable t o do It e m . Then we add t he t o do It e m t o t he
t o do s array. Finally, after we've added all o f the to -do items in Lo cal Sto rage to the t o do s array, we call the
addT o do sT o Page () functio n, which adds all the to -do items to the page.

We delete the parse T o do It e m s() functio n fro m the co de. We do n't need this functio n any mo re because we parse
e ach it e m in t he lo o p as we get the items fro m Lo cal Sto rage, and we add e ach it e m t o t he array right there.
Remember, we used parse T o do It e m s() in the Ajax versio n o f the To -Do List applicatio n to parse the JSON we go t
back fro m the XMLHttpRequest.

If yo u have no "to do " items in Lo cal Sto rage, when yo u lo ad yo ur page, yo u'll see:
After adding so me "to do " items, yo u'll see:
T he String substring() method
In the functio n we just added, ge t T o do It e m s(), we used a String metho d, subst ring(). We haven't talked much
abo ut String metho ds yet; we have a who le lesso n devo ted to Strings later in this co urse. Fo r no w, let's take a clo ser
lo o k at the subst ring() metho d to see ho w it wo rks, and ho w to use it to pull o ut o nly to -do items fo r the To -Do List
applicatio n.

Think o f a String as a set o f multiple characters. It's a little bit like an array (but it's definitely NOT an array, so do n't
co nfuse the two ), in that a character in a string ho lds a po sitio n, like this:

So the string "to do 12" has a length o f six; six separate characters that to gether make up the String. We capitalize
St ring here because St ring is a special o bject in JavaScript. It's no t quite like the o bjects yo u create in JavaScript, but
similar in the sense that a String has pro perties (such as le ngt h) and metho ds (such as subst ring()).

The subst ring() metho d allo ws yo u to take any String and create a new String fro m it by using a range o f characters.
The two arguments yo u pass to the subst ring() metho d are a f ro m value and a t o value, where f ro m is the po sitio n
o f the beginning character yo u want and t o is the po sitio n o f the next character after the ending character yo u want. Fo r
example, if yo u have the string "Hello Wo rld!" and yo u want to get the string "lo w", yo ur co de wo uld lo o k like this:

OBSERVE:
var str="Hello world!";
var mySubString = str.substring(3,7);

The variable m ySubSt ring will co ntain "lo w": all the characters beginning at po sitio n 3 and ending at (but not
including the character at) po sitio n 7. (Remember that string po sitio ns start at 0 , so "H" is at 0 , "e" is at 1, and so o n).
Fo r o ur To do applicatio n, we can extract the "to do " po rtio n o f the key items using subst ring(), like this:

Yo u can use the subst ring() metho d o n any string yo u create in JavaScript. So , if we have a variable ke y that
co ntains the string "to do 12," we can call subst ring() o n the ke y variable—ke y.subst ring(0 , 4 )—which returns the
first fo ur characters o f the string (fro m po sitio ns 0 , 1, 2, and 3), unless the length o f the value in the key is less than fo ur
characters. In that case, yo u get all the letters in the string—so if the ke y co ntains the string "to ," yo u get "to " as the
result.

We can co mpare the result o f calling subst ring() o n ke y to a literal string, "to do ," to see if they are equal, using the
== o perato r:

OBSERVE:

if (key.substring(0, 4) == "todo") {
...
}

If they are, we kno w we've fo und a key fo r a to -do item. Because the subst ring() metho d returns a new string, we
haven't changed the value o f ke y at all. In o ur example, ke y still co ntains the string "to do 12."

In this lesso n, we updated the To -Do List applicatio n to use Lo cal Sto rage instead o f Ajax. That means the user's to -do items
are sto red in the bro wser they used to add items to their to -do list, using the To -Do List applicatio n.

We also learned the impo rtance o f cho o sing go o d keys fo r yo ur applicatio ns that use Lo cal Sto rage. In this example, we used
the string "to do " and a unique id to create a key fo r to -do list items. It seems to be wo rking well, but can yo u think o f anything
that might cause o ur id / key scheme to fail? What wo uld happen if yo u deleted a to do item fro m Lo cal Sto rage, and then added
a new item using the fo rm witho ut relo ading the page first? Will the ids always match the po sitio n in the to do s array when yo u
get all the items fro m Lo cal Sto rage, when yo u first lo ad the page, using the functio n ge t T o do It e m s()? If no t, co uld that cause
a pro blem?

Think abo ut tho se questio ns in preparatio n fo r the next lesso n, take a crack at the quiz and pro ject, and we'll see yo u in the next
lesso n!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Deleting To-Do List Items
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

add a delete butto n to a to -do item.


generate an id that is guaranteed to be unique.
delete items fro m lo cal sto rage and to -do lists.
mo ve an ID to a parent element.

We've made a lo t o f pro gress with the To -Do List applicatio n, but wo uldn't it be nice if we co uld delete items as well as add
them? We also need a way to mark them as do ne. We'll add that functio nality in this lesso n and in the pro ject later. Let's get
started.

Adding a Way to Delete a T o-Do Item


To add a delete butto n to a to -do item, we need to mo dify the co de that generates the elements that represent each to -
do item. We'll be mo difying the <li> that co ntains the do ne/no t do ne butto n we added in a previo us lesso n (using a
<span> element) and the text o f the to -do item. We'll add a delete butto n at the right side o f a list item and again, use a
<span> element to simulate a butto n, and use the HTML &#10 0 0 7; entity that lo o ks like an "x" fo r delete. Yo u co uld
also use a small image fo r this if yo u wanted, but we'll keep it simple and just use the HTML entity.

We generate the HTML to represent a to -do item in the web page in the functio n cre at e Ne wT o do (), so we'll update
that co de to add the delete butto n. We'll also add a functio n to handle the click o n the delete butto n. This functio n is a
click handler that will eventually implement the delete functio nality; fo r no w we'll just display so me info rmatio n in the
co nso le abo ut the click.

We'll co ntinue to update the t o do .ht m l and t o do .js files. Yo u might want to make co pies o f the files to
Note save yo ur previo us wo rk.

Ready? Here we go ! Mo dify t o do .js as sho wn:


CODE TO TYPE:
function Todo(id, task, who, dueDate) {
this.id = id;
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}

var todos = new Array();

window.onload = init;

function init() {
var submitButton = document.getElementById("submit");
submitButton.onclick = getFormData;

getTodoItems();
}

function getTodoItems() {
if (localStorage) {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (key.substring(0, 4) == "todo") {
var item = localStorage.getItem(key);
var todoItem = JSON.parse(item);
todos.push(todoItem);
}
}
addTodosToPage();
}
else {
console.log("Error: you don't have localStorage!");
}
}

function addTodosToPage() {
var ul = document.getElementById("todoList");
var listFragment = document.createDocumentFragment();
for (var i = 0; i < todos.length; i++) {
var todoItem = todos[i];
var li = createNewTodo(todoItem);
listFragment.appendChild(li);
}
ul.appendChild(listFragment);
}
function addTodoToPage(todoItem) {
var ul = document.getElementById("todoList");
var li = createNewTodo(todoItem);
ul.appendChild(li);
document.forms[0].reset();
}

function createNewTodo(todoItem) {
var li = document.createElement("li");
var spanTodo = document.createElement("span");
spanTodo.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;

var spanDone = document.createElement("span");


if (!todoItem.done) {
spanDone.setAttribute("class", "notDone");
spanDone.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
}
else {
spanDone.setAttribute("class", "done");
spanDone.innerHTML = "&nbsp;&#10004;&nbsp;";
}

var spanDelete = document.createElement("span");


spanDelete.setAttribute("id", todoItem.id);
spanDelete.setAttribute("class", "delete");
spanDelete.innerHTML = "&nbsp;&#10007;&nbsp;";

spanDelete.onclick = deleteItem;

li.appendChild(spanDone);
li.appendChild(spanTodo);
li.appendChild(spanDelete);

return li;
}

function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

var id = todos.length;
var todoItem = new Todo(id, task, who, date);
todos.push(todoItem);
addTodoToPage(todoItem);
saveTodoItem(todoItem);
}

function checkInputText(value, msg) {


if (value == null || value == "") {
alert(msg);
return true;
}
return false;
}

function saveTodoItem(todoItem) {
if (localStorage) {
var key = "todo" + todoItem.id;
var item = JSON.stringify(todoItem);
localStorage.setItem(key, item);
}
else {
console.log("Error: you don't have localStorage!");
}
}

function deleteItem(e) {
var id = e.target.id;
console.log("delete an item: " + id);
}

Save it, but do n't preview yet; we need to add the CSS. Let's go ahead and do that, and then we'll co me back to the
co de to step thro ugh it in detail.
CODE TO TYPE:
body {
font-family: Helvetica, Ariel, sans-serif;
}
legend {
font-weight: bold;
}
div.tableContainer {
display: table;
border-spacing: 5px;
}
div.tableRow {
display: table-row;
}
div.tableRow label {
display: table-cell;
text-align: right;
}
div.tableRow input {
display: table-cell;
}

ul#todoList {
list-style-type: none;
margin-left: 0px;
padding-left: 0px;
}
ul#todoList li {
position: relative;
padding: 5px;
margin: 5px;
background-color: #ededed;
border: 2px inset #ededed;
}
ul#todoList li span.notDone {
margin-right: 10px;
background-color: #FF9999;
cursor: pointer;
}
ul#todoList li span.done {
margin-right: 10px;
background-color: #80CC80;
cursor: pointer;
}
ul#todoList li span.delete {
display: inline-block;
position: absolute;
right: 5px;
cursor: pointer;
}

Save it, o pen t o do .ht m l, and click . Yo u'll see a delete "butto n" next to each to -do item in the page.
In the JavaScript, we update the cre at e Ne wT o do () functio n to generate HTML to represent the delete "butto n" (which
isn't a butto n in the sense o f a fo rm butto n, but rather an element yo u can click o n to take so me actio n):

OBSERVE:
var spanDelete = document.createElement("span");
spanDelete.setAttribute("id", todoItem.id);
spanDelete.setAttribute("class", "delete");
spanDelete.innerHTML = "&nbsp;&#10007;&nbsp;";

We cre at e ano t he r <span> e le m e nt just like we did fo r the do ne/no t do ne butto n, and use t he e nt it y &# 10 0 0 7 ;
t o ge ne rat e t he charact e r.

As we did fo r the do ne/no t do ne butto n, we se t t he " class" at t ribut e o f t he <span> e le m e nt , in t his case , t o
" de le t e " , so we can style it (we'll get to the style in just a mo ment).

No tice that we also se t t he " id" at t ribut e o f t he <span> e le m e nt ; that's so we kno w which element we've
clicked o n when we implement the de le t e It e m () functio n (yo u'll see ho w this wo rks a bit later). The id o f the item we
want to delete is just the t o do It e m .id pro perty. So if an item has the id 1, the <span> element will lo o k like this:

OBSERVE:
<span id="1" class="delete">&#10007;</span>

No w, the list item representing the to -do item has three <span> elements in it. The generated HTML will lo o k
so mething like this:
OBSERVE:
<li>
<span class="notDone">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span>TEXT OF TO DO ITEM</span>
<span id="1" class="delete">&#10007;</span>
</li>

To style the delete butto n so it sits all the way to the right o f the to -do item, we po sitio n it using CSS po sitio ning:

OBSERVE:
ul#todoList li {
position: relative;
padding: 5px;
margin: 5px;
background-color: #ededed;
border: 2px inset #ededed;
}
ul#todoList li span.notDone {
margin-right: 10px;
background-color: #FF9999;
cursor: pointer;
}
ul#todoList li span.done {
margin-right: 10px;
background-color: #80CC80;
cursor: pointer;
}
ul#todoList li span.delete {
display: inline-block;
position: absolute;
right: 5px;
cursor: pointer;
}

We can po sitio n the delete butto n using abso lut e po sit io ning, which allo ws us to specify the number o f pixels fro m
the right o f the list item that we want the delete butto n to appear. We used 5 px to leave a little space between the
butto n and the edge o f the list item.

But, to po sitio n an element abso lutely relative to ano ther element (rather than the page), we need to po sitio n its parent
element as well. The parent element o f the <span> that represents the delete butto n is the <li> element. We can
po sitio n the <li> element so that it go es with the flo w o f the page using po sit io n: re lat ive . So the <li> element is
po sit io ne d re lat ive to where it wo uld be no rmally, and then the delete <span> is po sit io ne d abso lut e ly within
the <li> element, 5 px fro m the right.

Finally, we added a pro pe rt y that changes the curso r t o a po int e r. No w when yo u mo use o ver the do ne/no t do ne

butto n o r the delete butto n, yo u'll see a hand po inter ( ) rather than the regular mo use po inter, which pro vides visual
feedback that indicates that yo u're ho vering o ver so mething o n which yo u can click.

T he Delete Button Click Handler


No w that yo u kno w ho w the delete butto n is created with HTML and styled with CSS, let's take ano ther lo o k at
the JavaScript co de where we added a click handler fo r the delete butto n:

OBSERVE:
var spanDelete = document.createElement("span");
spanDelete.setAttribute("id", todoItem.id);
spanDelete.setAttribute("class", "delete");
spanDelete.innerHTML = "&nbsp;&#10007;&nbsp;";

spanDelete.onclick = deleteItem;

After creating the <span> element that ho lds the delete butto n, we add a click handle r t o it by setting the
o nclick pro perty to the functio n de le t e It e m (). Fo r no w, the de le t e It e m () functio n is pretty straightfo rward:

OBSERVE:
function deleteItem(e) {
var id = e.target.id;
console.log("delete an item: " + id);
}

The de le t e It e m () functio n has o ne parameter, e , that is the click e ve nt data. When yo u click o n an element
with a click handler, the event data is always passed into the click handler functio n; it co ntains data abo ut the
click, including the t arge t , which is the e le m e nt t hat was clicke d o n. In o ur case, that element is the
"delete" <span>. We kno w that the <span> element has an id attribute (because we added it!) set to the id o f
the to -do item, so we can access that id using the id pro perty o f the <span> element that is sto red in the
t arge t pro perty.

We co uld also access the id attribute using the ge t At t ribut e () metho d, like this:
Note var id = e .t arge t .ge t At t ribut e (" id" );.

Once we have the id o f the to -do item we want to delete, we display the id to the co nso le so we can verify that
it's co rrect.

T ry it !

t o do .ht m l again, o pen the Develo per to o ls fo r yo ur bro wser so yo u can see the JavaScript
co nso le, and try deleting a to -do item. It do esn't go away, because we haven't implemented de le t e It e m ()
yet, but the message in the co nso le will indicate which item was clicked. Try clicking o n several different items.
Make sure the ids yo u see in the co nso le are co rrect by co mparing them with the ids yo u see in Lo cal
Sto rage.

So, what did we do?

First, we added the HTML fo r the delete "butto n" to the HTML, and set the id attribute o f the <span> element to
the id o f the to -do item:
We also added a click handler so that when yo u click o n the delete butto n, the click handler is called. The
target o f the event that's passed to the click handler is the element yo u clicked o n, which is the <span>
representing the delete butto n. Yo u can get the id o f the to -do item to delete fro m the id o f the <span>
element:
That id sho uld match the id o f the element in Lo cal Sto rage:
We're almost ready to implement de le t e It e m ().

A Problem With Our ID Scheme


Befo re we implement the de le t e It e m () functio n, we need to lo o k o ver o ur id scheme; it's go ing to have so me
pro blems o nce we add delete functio nality. Did yo u think abo ut this at the end o f the previo us lesso n? Did yo u figure
o ut why it wo n't wo rk when we can delete items fro m the to -do list?

Remember that we add all to -do items to the t o do s array, when we first lo ad the page (items already in Lo cal
Sto rage), and also as we add new items using the fo rm. We use the length o f the t o do s array to create a unique id fo r
each T o do o bject that we create, like this (in the ge t Fo rm Dat a() functio n):

OBSERVE:
var id = todos.length;

...so that the id o f the T o do matches the index o f the item in the t o do s array:
When we delete an item, we'll delete it fro m three places: Lo cal Sto rage, the web page, and the t o do s array. Let's say
we decide to delete the item with id 1. When we delete it fro m the array, the o ther items in the array shift do wn, so no w
the T o do with id 2 is in the array at index 1, the T o do with id 3 is in the array at index 2, and the length o f the array is
reduced by o ne; in this example, the length is no w 3 (befo re the delete, the length was 4):

The tro uble arises when we go to add a new T o do . Because the length o f the array is 3, the id o f the new item is set to
3, but we already have an item with the id 3. Even so , we add it to the array with no pro blem; when we add it to Lo cal
Sto rage, we create the key to sto re it in Lo cal Sto rage using the T o do item's id and the string "to do ", getting "to do 3" in
this example. Ho wever, there is already a "to do 3" key with a value in Lo cal Sto rage, and yo u kno w when happens
when yo u use the same key to sto re a new value in Lo cal Sto rage. Yup, yo u overwrite the existing value, which means
yo u just lo st a to -do item o n yo ur list!

So , we need ano ther way to create unique ids fo r o ur T o do items so that we have unique keys fo r each item in Lo cal
Sto rage. Can yo u think o f ho w we might do that?

An ID Scheme Using T ime


We need a way to generate an id that is guaranteed to be unique. To truly guarantee uniqueness is a co mplex task; but
we can get clo se eno ugh fo r this applicatio n by using the time expressed in milliseco nds.

Yo u might think o f this as an o dd cho ice fo r creating a unique id, but it turns o ut to wo rk well o n to day's co mputers, and
using JavaScript in particular, because we can get a value fo r time that is t he num be r o f m illise co nds since
0 1/0 1/19 7 0 . Why 19 70 ? That's just a date that was picked a lo ng time ago by co mputer scientists and we must accept
it. The JavaScript Dat e o bject's ge t T im e () metho d still uses it (and this technique is used by many co mputers and
many co mputer languages, so yo u'll see it po p up in o ther types o f applicatio ns to o ).

Let's try an example. Go to the JavaScript co nso le in a web page and type this:

CODE TO TYPE:
var d = new Date();
var m = d.getTime();
console.log(m);

It sho uld lo o k similar to this in yo ur bro wser's JavaScript co nso le:


Of co urse, yo ur big lo ng number representing the number o f milliseco nds will be different because yo u're do ing it later
than when I ran this co de.

The co de ne w Dat e () creates a new Dat e o bject that represents "right no w." (Try typing co nso le .lo g(d) to see). We
co nvert that date into milliseco nds using the ge t T im e () metho d. We'll talk mo re abo ut the Dat e o bject and the Dat e
o bject co nstructo r later in the co urse.

We can co mbine the two lines to create a Dat e o bject and get the time with the ge t T im e () metho d into o ne, like this:

CODE TO TYPE:
(new Date()).getTime();

Try that in yo ur co nso le and make sure it wo rks. Make sure yo u put the parentheses in the right places! The
parentheses ensure that we get the Dat e o bject fo r "right no w" first, and then call ge t T im e () o n that o bject. Yo u'll see
this syntax used o ften in JavaScript, bo th with the Dat e o bject and o ther o bjects to o .

Okay, no w that we have a new scheme fo r o ur ids, let's add it to o ur co de. We just have to change o ne line in the
functio n ge t Fo rm Dat a() in o ur t o do .js file:
CODE TO TYPE:
function getFormData() {
var task = document.getElementById("task").value;
if (checkInputText(task, "Please enter a task")) return;

var who = document.getElementById("who").value;


if (checkInputText(who, "Please enter a person to do the task")) return;

var date = document.getElementById("dueDate").value;


if (checkInputText(date, "Please enter a due date")) return;

var id = todos.length;
var id = (new Date()).getTime();
var todoItem = new Todo(id, task, who, date);
todos.push(todoItem);
addTodoToPage(todoItem);
saveTodoItem(todoItem);
}

Save it, o pen t o do .ht m l, and click . Make sure yo u have yo ur Develo per co nso le o pen and display the
co ntents o f Lo cal Sto rage, either using the JavaScript co nso le, o r using the Reso urces tab (depending o n yo ur
bro wser). If yo u have any existing to -do items, tho se will have the o ld ids. That's fine. Try adding so me new o nes;
refresh yo ur view o f Lo cal Sto rage and make sure they have new ids that use the time in milliseco nds.
Click o n an item that has o ne o f the new ids. In the JavaScript co nso le, yo u'll see the co nso le.lo g message that sho ws
the id o f the item yo u want to delete, as well as the big, lo ng id.
Okay, no w that we have a new id, we're really ready to implement de le t e It e m (), so let's get to it!

Deleting Items from Local Storage and the T o-Do List


No w that we have an id scheme that wo n't cause o ur To -Do List app to fail when we delete items, we're finally ready to
implement delete. When we click o n an item to delete, we need to remo ve the item fro m Lo cal Sto rage, fro m the array
o f to -do items, and fro m the list in the page. Update the de le t e It e m () functio n in t o do .js as sho wn:
CODE TO TYPE:
function deleteItem(e) {
var id = e.target.id;
console.log("delete an item: " + id);

// find and remove the item in localStorage


var key = "todo" + id;
localStorage.removeItem(key);

// find and remove the item in the array


for (var i = 0; i < todos.length; i++) {
if (todos[i].id == id) {
todos.splice(i, 1);
break;
}
}

// find and remove the item in the page


var li = e.target.parentElement;
var ul = document.getElementById("todoList");
ul.removeChild(li);
}

Save it, o pen t o do .ht m l, and click . Try adding and deleting so me items. Check yo ur Lo cal Sto rage
using the develo per co nso le and make sure the items are go ne. Relo ad the page and make sure, again, that they are
go ne.

Let's walk thro ugh the co de. First, we remo ve the to -do item fro m Lo cal Sto rage. We use the lo calSt o rage metho d,
re m o ve It e m () and pass in the ke y t o de le t e . The key is a string created by co m bining " t o do " wit h t he id, just
like we did when we created the key to add the item to Lo cal Sto rage. Remember, we get id fro m the <span> element
that we click o n to delete an item:

OBSERVE:
// find and remove the item in localStorage
var key = "todo" + id;
localStorage.removeItem(key);

Next, we delete the to -do item fro m the t o do s array:

OBSERVE:

// find and remove the item in the array


for (var i = 0; i < todos.length; i++) {
if (todos[i].id == id) {
todos.splice(i, 1);
break;
}
}

To delete a to -do item fro m the t o do s array, we lo o p t hro ugh t he array, lo o king fo r the T o do o bject with the same
id as the id o f the item o n which we clicked. If we f ind t he T o do wit h t hat id, then we can remo ve it fro m the array
using the array metho d, splice (). We pass in the po sit io n o f t he it e m we 're de le t ing (i), and the num be r o f
it e m s t o de le t e (in t his case , just o ne ), then splice () remo ves that item fro m the array, and shifts everything else
in the array do wn by o ne so that there's no empty spo t in the array. Yo u can use splice () to remo ve multiple elements
fro m an array, but in this case, we just need to delete the o ne to -do item with the id that matches the o ne we clicked o n
to delete.

If we've f o und t he it e m t o de le t e , we do n't need to keep lo o ping. It wo uld be a waste o f time to lo o p o ver the rest
o f the elements, because we kno w no ne o f the o ther items will match the id we're seeking. So , we can use the bre ak
statement to break o ut o f the lo o p. When yo u use bre ak, the lo o p sto ps, and the co de fo llo wing the f o r lo o p will run
next.
In this case, that next co de is the co de to remo ve the to -do item fro m the page. We've remo ved it fro m Lo cal Sto rage,
and remo ved it fro m the t o do s array, but the item still appears o n the page:

OBSERVE:

// find and remove the item in the page


var li = e.target.parentElement;
var ul = document.getElementById("todoList");
ul.removeChild(li);

To remo ve the item fro m the page, we need to remo ve the <li> e le m e nt t hat re pre se nt s (and displays) t he t o -
do it e m in t he page fro m the DOM. Remember that the DOM is an internal bro wser structure that represents what
yo u see o n the page. It co ntains all the elements and co ntent o f the page.

When yo u click o n the delete <span>, that <span> is passed into the click handler functio n, de le t e It e m (), in the event
o bject, and we access it with the pro perty t arge t . Because the <span> is a child o f the <li> element we want to remo ve,
we can get the <li> element using the pare nt Ele m e nt pro perty o f the <span>. Once we have the right <li> element,
we can remo ve it fro m the DOM by using the re m o ve Child() metho d o f the "to do List" <ul> element that co ntains that
<li> element. Here's ho w it wo rks:

Calling re m o ve Child() and passing in the <li> element immediately remo ves that list item fro m the page, and then
yo u see the to -do item disappear when yo u click delete.

Moving the ID to the Parent Element


Befo re we end the lesso n, we need to do o ne mo re thing. In the pro ject fo r this lesso n, yo u're go ing to implement the
do ne/no t do ne butto n. ho wever, right no w, the <span> element that represents the do ne/no t do ne butto n has no id; no
way to kno w the to -do item id yo u want to update when yo u click o n the butto n.

We can't just add ano ther id to that <span> element with the same id we're using fo r the delete butto n, because id
attributes must be unique in HTML. Hmm....

A go o d so lutio n in this case is to mo ve the id up to the parent element: the <li> that co ntains both the do ne/no t <span>
element and the delete <span> element. No w that yo u kno w ho w to access an element's parent element, yo u can get
the id o f the list item to mo dify fro m the parent element, the <li>, rather than the <span>, so that single id will wo rk fo r
bo th the delete click handler functio n, and the do ne/no t do ne click handler functio n that yo u're go ing to write in the
pro ject. Here's ho w that will wo rk:
Let's go ahead and mo ve the id fro m the delete <span> up to the <li> element fo r the to -do item when we create that
element, in cre at e Ne wT o do (). We'll also need to update the de le t e It e m () functio n so that it gets the id fro m the
parent element (the <li>) rather than fro m the target element (the <span>). Mo dify t o do .js as sho wn:
CODE TO TYPE:

.
.
.
function createNewTodo(todoItem) {
var li = document.createElement("li");
li.setAttribute("id", todoItem.id);

var spanTodo = document.createElement("span");


spanTodo.innerHTML =
todoItem.who + " needs to " + todoItem.task + " by " + todoItem.dueDate;

var spanDone = document.createElement("span");


if (!todoItem.done) {
spanDone.setAttribute("class", "notDone");
spanDone.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
}
else {
spanDone.setAttribute("class", "done");
spanDone.innerHTML = "&nbsp;&#10004;&nbsp;";
}

//spanDone.onclick = updateDone;

// add the delete link


var spanDelete = document.createElement("span");
spanDelete.setAttribute("id", todoItem.id);
spanDelete.setAttribute("class", "delete");
spanDelete.innerHTML = "&nbsp;&#10007;&nbsp;";

// add the click handler to delete


spanDelete.onclick = deleteItem;

li.appendChild(spanDone);
li.appendChild(spanTodo);
li.appendChild(spanDelete);

return li;
}

function deleteItem(e) {
var id = e.target.id;
var span = e.target;
var id = span.parentElement.id;
console.log("delete an item: " + id);

// find and remove the item in localStorage


var key = "todo" + id;
localStorage.removeItem(key);

// find and remove the item in the array


for (var i = 0; i < todos.length; i++) {
if (todos[i].id == id) {
todos.splice(i, 1);
break;
}
}

// find and remove the item in the page


var li = e.target.parentElement;
var ul = document.getElementById("todoList");
ul.removeChild(li);
}
.
.
.
Save it, o pen t o do .ht m l, and click . Yo ur delete butto n will wo rk just as it did befo re, but no w yo u kno w
that yo u're getting the id to delete fro m the <li> co ntaining the to -do item, rather than fro m the <span> element
co ntaining the delete butto n.

All we did is change the cre at e Ne wT o do () functio n so that instead o f adding the id attribute to the delete <span>, we
add it to the <li> element. Then in de le t e It e m (), we use the <span> element's pare nt Ele m e nt pro perty to get the id
fro m the <li> element—the rest o f the co de stays the same.

Make sure yo u understand ho w this wo rks because yo u're go ing to need to use it to implement a new click handler,
updat e Do ne () in the pro ject.

Wo w, yo u go t thro ugh a lo t in this lesso n! We implemented the delete butto n and figured o ut ho w to make an id scheme using
time in milliseco nds that wo rks well with o ur applicatio n. We intro duced the Dat e o bject, the Dat e () co nstructo r and the
ge t T im e () metho d briefly; we'll be co ming back to Dat e in a later lesso n.

Take a break, and then tackle the pro ject to implement the do ne/no t do ne functio nality. Once yo u've co mpleted that, yo u'll have
a very nice To -Do List Applicatio n yo u can use!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Strings and String Methods
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

build a string search applicatio n.


explo re several o f the different metho ds we have fo r manipulating strings in JavaScript.
use metho ds and chain tho se metho ds to impro ve yo ur searches.
use the substring() and split() metho ds.
use regular expressio ns to create a pattern to match in so me text.

It's time fo r a well-deserved break fro m the To -Do List Applicatio n. In this lesso n, we'll build a string search applicatio n and, in
the pro cess, explo re several o f the different metho ds we have fo r manipulating strings in JavaScript. Yo u've used strings many
times in this co urse, but we haven't do ne much with them except to put them into web pages. There is a lo t mo re yo u can do
with strings, as yo u'll so o n disco ver.

String Basics
Yo u already kno w that to create a string in JavaScript, yo u write so me text in quo tes, like this:

OBSERVE:
var myString = "I'm a string!";

It's fine fo r a string to co ntain a single quo te, as lo ng as yo u're using do uble quo tes to delimit the string, but if yo u need
a do uble quo te in a string, yo u need to escape it, like this:

OBSERVE:

var myQuoteString = "He said, \"Give me the ice cream!\" but I didn't.";

Strings in JavaScript are a bit special. When yo u write a string, yo u're actually creating a St ring o bje ct . Just like o ther
o bjects, String o bjects have metho ds and pro perties. Yo u already kno w abo ut o ne pro perty, le ngt h. Type this into
yo ur bro wser's JavaScript co nso le:

CODE TO TYPE:

var myString = "I'm a string!";


var len = myString.length;
console.log(len);

Try wo rking thro ugh a few o ther strings fo r a little mo re practice.

When yo u want to see the value o f a variable in the JavaScript co nso le, yo u do n't actually have to use
Note co nso le .lo g(); yo u can just type the name o f the variable. Fo r example, if yo u want the value o f le n, yo u
can just type le n at the co nso le pro mpt.

Let's try a String metho d, charAt ():


CODE TO TYPE:
var myString = "I'm a string!";
var c = myString.charAt(4);
c

If "a" is the fifth character in the string, ho w did charAt() get "a"? It retrieved the character at po sitio n (also called inde x)
4. Just like arrays, strings start with po sitio n 0 , so if yo u co unt 0 , 1, 2, 3, 4 characters (including the space!), yo u'll find
"a" in po sitio n 4. But just because yo u can access a character in a String using a po sitio n, o r index, do n't co nfuse it
with an Array because they are two entirely different o bjects.

Basic String Comparision and Searching


No w that yo u kno w so me string basics, let's start writing o ur string search applicatio n. We'll start with so me basic
HTML. Create this file:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>Strings</title>
<meta charset="utf-8">
<script src="strings.js"></script>
<style>
body {
font-family: Arial, sans-serif;
}
textarea {
width: 700px;
height: 400px;
}
label {
vertical-align: top;
}
</style>
</head>
<body>
<form>
<label for="searchTerm">Search for: </label>
<input type="text" id="searchTerm" size="35"
placeholder="search term">
<input type="button" id="searchButton" value="Search"><br><br>
<label for="textToSearch">Search this text:</label>
<textarea id="textToSearch"></textarea>
</form>
</body>
</html>

Save the file in yo ur /javascript 2 fo lder as st rings.ht m l and click . Yo u see this:
Save the file in yo ur /javascript 2 fo lder as st rings.ht m l and click . Yo u see this:

No thing wo rks yet because we haven't written the JavaScript. The plan is that when yo u click the Search butto n, we'll
begin the search pro cess and search fo r the string yo u enter in the to p search area within the string in the textarea at
the bo tto m. No tice that we're linking to the file st rings.js fro m the HTML. That's where we'll add the JavaScript to make
this wo rk; let's do that no w.

Open a new file and enter this JavaScript:


CODE TO TYPE:

window.onload = init;

function init() {
var searchButton = document.getElementById("searchButton");
searchButton.onclick = searchText;
}

function searchText() {
var searchTerm = document.getElementById("searchTerm").value;
var textToSearch = document.getElementById("textToSearch").value;
if (searchTerm == null || searchTerm == "") {
alert("Please enter a string to search for");
return;
}
if (textToSearch == null || textToSearch == "") {
alert("Please enter some text to search");
return;
}
if (searchTerm == textToSearch) {
alert("Found 1 instance of " + searchTerm);
}
else {
alert("No instance of " + searchTerm + " found!");
}
}

Save it as st rings.js, o pen st rings.ht m l, and click . Try entering a search string and so me text to
search. Yo u'll pro bably no tice two things right away:

If either o f the strings yo u enter has extra white space at the beginning o r the end, yo u might find yo ur search
do esn't wo rk, even if it appears that the text in bo th bo xes is exactly the same.
Bo th strings must be exactly the same in o rder fo r the search to wo rk, which isn't particularly useful.

Do n't wo rry, we'll fix bo th o f these pro blems. First things first. Try entering "test" in the search area, and " test " in the
text area:
These strings wo n't match when yo u click the Search butto n:

The co de that tests to see if the two strings are the same:

OBSERVE:
if (searchTerm == textToSearch) {
alert("Found 1 instance of " + searchTerm);
}

The == o perato r checks to see if they are exactly the same, spaces included; when co mparing two strings using ==,
JavaScript co mpares the two strings, character by character, to see if they are the same, and that includes any spaces
in the quo tes. "test" do es no t equal " test ", so the message we get is co rrect.

The t rim () metho d remo ves leading and trailing spaces fro m a string. This functio n is handy; adding extra spaces is a
co mmo n erro r and typically peo ple do n't really want tho se extra spaces when they are co mparing strings. (In so me
situatio ns they might, ho wever, so yo u'll need to take this o n a case-by-case basis).

Mo dify st rings.js as sho wn:


CODE TO TYPE:
window.onload = init;

function init() {
var searchButton = document.getElementById("searchButton");
searchButton.onclick = searchText;
}

function searchText() {
var searchTerm = document.getElementById("searchTerm").value;
var textToSearch = document.getElementById("textToSearch").value;
searchTerm = searchTerm.trim();
textToSearch = textToSearch.trim();
if (searchTerm == null || searchTerm == "") {
alert("Please enter a string to search for");
return;
}
if (textToSearch == null || textToSearch == "") {
alert("Please enter some text to search");
return;
}
if (searchTerm == textToSearch) {
alert("Found 1 instance of " + searchTerm);
}
else {
alert("No instance of " + searchTerm + " found!");
}
}

Save the file (st rings.js)), o pen st rings.ht m l and click . No w two strings will match if they have the
same letters, even if yo u use leading o r trailing spaces. Try it! Try "test" and " test " again.

The built-in trim functio n is o nly available in recent versio ns o f mo st bro wsers: Firefo x 3.5+, Safari 5+, IE9 +, Chro me
5+, and Opera 10 .5+. Fo r bro wsers that do n't suppo rt the built-in functio n, yo u can substitute yo ur o wn implementatio n:

OBSERVE:
function trim(str) {
return str.replace(/^\s+|\s+$/g,"");
}

And then instead o f calling m ySt ring.t rim (), yo u'd write t rim (m ySt ring).

Improving the Search


So far, o ur search is rather underwhelming, but there are lo ts o f things we can do to impro ve it. Let's start by using two
additio nal String metho ds, inde xOf and t o Uppe rCase ().

The inde xOf () metho d takes a string to find in the string it's called o n, and returns the po sitio n o f the first instance it
finds. So , if yo u write:

OBSERVE:
var myString = "I scream, you scream, we all scream for ice cream";
var pos = myString.indexOf("scream");
pos

...yo u'll get the value 2 in the variable po s, because the f irst inst ance o f " scre am " starts at po sitio n 2 in the
variable m ySt ring. inde xOf () can also take an o ptio nal argument, a starting po sitio n, so that yo u can start lo o king
fo r a string at that po sitio n (if yo u do n't supply this argument, the default is to start lo o king at po sitio n 0 , the beginning
o f the string):
OBSERVE:
var myString = "I scream, you scream, we all scream for ice cream";
var pos = myString.indexOf("scream", 3);
pos

No w yo u'll get 14, the po sitio n o f the f irst inst ance o f " scre am " , st art ing at o r af t e r po sit io n 3.

If inde xOf () do esn't find any instances o f the string yo u pass it, then it returns -1.

The functio n t o Uppe rCase () co nverts the characters in a string to upper case:

OBSERVE:
var myString = "I scream, you scream, we all scream for ice cream";
var myStringUpper = myString.toUpperCase();
myStringUpper

Our co de pro duces the string "I SCREAM, YOU SCREAM, WE ALL SCREAM FOR ICE CREAM", in the variable
m ySt ringUppe r. There is an analo go us t o Lo we rCase () metho d also . No tice that these metho ds do no t change the
o riginal string, m ySt ring, rather, the metho ds return a new string that yo u can sto re in a different variable if yo u want.

Let's use these two metho ds to impro ve o ur search. Mo dify st rings.js as sho wn:

CODE TO TYPE:
window.onload = init;

function init() {
var searchButton = document.getElementById("searchButton");
searchButton.onclick = searchText;
}

function searchText() {
var searchTerm = document.getElementById("searchTerm").value;
var textToSearch = document.getElementById("textToSearch").value;
searchTerm = searchTerm.trim();
textToSearch = textToSearch.trim();
if (searchTerm == null || searchTerm == "") {
alert("Please enter a string to search for");
return;
}
if (textToSearch == null || textToSearch == "") {
alert("Please enter some text to search");
return;
}
if (searchTerm == textToSearch) {
alert("Found 1 instance of " + searchTerm);
}
else {
alert("No instance of " + searchTerm + " found!");
}

var pos = 0;
var count = 0;
while (pos >= 0) {
pos = textToSearch.toUpperCase().indexOf(searchTerm.toUpperCase(), pos);
if (pos >= 0) {
count++;
pos++;
}
}
alert("Found " + count + " instances of " + searchTerm);
}

Save the file, o pen st rings.ht m l and click . Try a few test strings. Make sure yo u try:
lo wer and upper case wo rds
wo rds with spaces in them
wo rds with multiple instances in the text yo u're searching

We pasted in so me text fro m The Adventures o f Sherlo ck Ho lmes, and tried so me searches o n the first paragraph
fro m the bo o k.

Ho w many times do es the wo rd "and" appear in the paragraph? Here's what yo u get if yo u try:

OBSERVE:
var pos = 0;
var count = 0;
while (pos >= 0) {
pos = textToSearch.toUpperCase().indexOf(searchTerm.toUpperCase(), pos);
if (pos >= 0) {
count++;
pos++;
}
}
alert("Found " + count + " instances of " + searchTerm);

Let's break it do wn. We use a lo o p so we can find all instances o f the se archT e rm string that appear in the
t e xt T o Se arch string, and keep track o f ho w many we find using the co unt variable. We use the po s variable to keep
track o f the po sitio n where we find each instance o f se archT e rm . As lo ng as po s is gre at e r t han o r e qual t o 0 we
kno w we've fo und ano ther instance (remember, inde xOf () returns -1 when it can't find an instance o f the string fo r
which yo u're searching). We co nvert bo th strings to upper case with t o Uppe rCase () so that we can find "and" as well
as "AND" o r "And" (o r even "aND"). We use inde xOf () with the seco nd, o ptio nal, argument, po s, so that we can start
searching at a po sitio n that is after the po sitio n where we fo und the previo us instance (so we do n't keep finding the
same instance o ver and o ver). As lo ng as we keep finding a new instance o f the se archT e rm , we increment the
po sitio n, and we increment the co unt. When po s is -1, the lo o p sto ps because we've fo und all the instances o f
se archT e rm there are to find in t e xt T o Se arch.
se archT e rm there are to find in t e xt T o Se arch.

Chaining
We used a JavaScript technique in this co de called chaining. Chaining is co mbining multiple metho d calls
to gether in o ne line o f co de. Fo r instance, if yo u write:

OBSERVE:
var myString = "This is the text we're searching to find the word 'and'.";
var anotherString = "AND";
var pos = myString.toUpperCase().indexOf(anotherString);

..it's the same as if yo u wro te:

OBSERVE:
var myString = "This is the text we're searching to find the word 'and'.";
var anotherString = "AND";
var myStringUpper = myString.toUpperCase()
var pos = myStringUpper.indexOf(anotherString);

By chaining expressio ns to gether with the "do t no tatio n," yo u eliminate the intermediate variable yo u'd create
if yo u used two statements instead. Yo u can do this when yo u kno w that the result o f the first metho d will yield
a value yo u can use fo r the seco nd metho d. In this case, m ySt ring.t o Uppe rCase () returns an uppe r-case
st ring, then we can call the metho d inde xOf () o n t hat st ring.

T he Substring() and Split() Methods


Two o ther useful String metho ds yo u'll run into fairly o ften are subst ring() and split ().

subst ring() creates a string fro m a lo nger string, like this:

OBSERVE:
var myString = "I scream, you scream, we all scream for ice cream";
var myStringSub = myString.substring(0, 8);
myStringSub

The value o f m ySt ringSub is "I scream", a string made fro m the characters at po sitio ns 0 -7 o f m ySt ring. No tice that
the character at po sitio n 8 is not included. subst ring() takes two values, f ro m and t o : f ro m is the po sitio n o f the first
character yo u want included in the substring, and t o is one greater than the po sitio n o f the last character yo u want
included in the substring.

The split () metho d is pretty handy. It splits a string into parts using a character as the divider fo r the split. So , let's say
we want to split the text we entered in o ur string search applicatio n into wo rds. We can split the input string using the " "
(space) character. The result o f split () is an array, so in this case, we'd get an array o f all the wo rds in the text. Let's
mo dify o ur string search applicatio n a bit to co unt the number o f wo rds, and then search fo r a wo rd by co mparing the
search text to each wo rd we fo und in the text we searched. Mo dify st rings.js:
CODE TO TYPE:
window.onload = init;

function init() {
var searchButton = document.getElementById("searchButton");
searchButton.onclick = searchText;
}

function searchText() {
var searchTerm = document.getElementById("searchTerm").value;
var textToSearch = document.getElementById("textToSearch").value;
searchTerm = searchTerm.trim();
textToSearch = textToSearch.trim();
if (searchTerm == null || searchTerm == "") {
alert("Please enter a string to search for");
return;
}
if (textToSearch == null || textToSearch == "") {
alert("Please enter some text to search");
return;
}

var pos = 0;
var count = 0;
while (pos >= 0) {
pos = textToSearch.toUpperCase().indexOf(searchTerm.toUpperCase(), pos);
if (pos >= 0) {
count++;
pos++;
}
}
alert("Found " + count + " instances of " + searchTerm);

var results = textToSearch.split(" ");


var count = 0;
for (var i = 0; i < results.length; i++) {
if (searchTerm.toUpperCase() == results[i].toUpperCase()) {
count++;
}
}
alert("Found " + count + " instances of " + searchTerm + " out of a total of " + re
sults.length + " words!");
}

Save it, o pen st rings.ht m l, and click . Try searching fo r a string. No w yo u'll see an alert that displays
the number o f times the string yo u searched fo r was fo und, as well as ho w many to tal wo rds were fo und:

OBSERVE:
var results = textToSearch.split(" ");
var count = 0;
for (var i = 0; i < results.length; i++) {
if (searchTerm.toUpperCase() == results[i].toUpperCase()) {
count++;
}
}

First, we split t he t e xt T o Se arch into wo rds using split (), and sto red the results in an array nam e d re sult s. Then,
we iterate o ver the entire array, and co m pare e ach wo rd in t he array wit h t he wo rd we 're se arching f o r, and
ke e p t rack o f ho w m any t im e s we f o und it .

If yo u're using the text fro m the Sherlo ck Ho lmes example (abo ve), try the wo rd "and," and yo u might see that "and"
appears 8 times. If yo u tried "and" earlier (when we were using inde xOf () rather than split ()) yo u pro bably saw that it
was fo und 9 times in the text. So why 8 this time? If yo u typed in the text to search o n and pressed the "Enter" key o n
yo ur keybo ard between lines (rather than co ntinuing to type and having the lines wrap aro und), then the wo rds at the
end o f each line and the beginning o f the next line are no t separated by a space; rather, they are separated by a
ne wline character. So when yo u use split (), and yo u split the wo rds up into an array using space (" ") as the split
character, these wo rds (the o nes at the end o f line/beginning o f next line) aren't actually split up. So if "and" appears at
the end o r beginning o f a line, it will no t appear in the array as "and," but rather as "and" co ncatenated with ano ther
wo rd. In my example, I pressed return between "gibe" and "and" so o ne o f the wo rds in the array was
"gibe(newline)and." That wo rd didn't match "and" and that's why I go t 8 as the result rather than 9 .

Typically, split () wo rks best when yo u kno w fo r sure that a given character is used to separate text; fo r instance, in a
CSV file, the "," character is used to delimit the co lumns in the file. It's also used to split smaller pieces o f text, fo r
example, a "firstname lastname" entry co uld be split into "firstname" and "lastname" using split ().

Regular Expressions
So far, we've been searching fo r exact matches fo r the search terms we've tried. Fo r instance, we've tried searching fo r
"and" to see ho w many times the wo rd "and" appears in the text yo u enter to be searched. But what if yo u want to find,
say, all the pho ne numbers in a bit o f text, even if there is mo re than o ne and they are different?

Fo r a task like that yo u need Re gular Expre ssio ns. Regular Expressio ns are a po werful way to express patterns to
match. Yo u'll find Regular Expressio ns used frequently whenever text needs to be matched to a pattern; fo r instance,
yo u can use Regular Expressio ns to verify that the pattern o f text a user enters fo r a pho ne number in a fo rm really
do es lo o k like a pho ne number—o r an email address. There are many uses o f Regular Expressio ns; yo u'll see them
in o ther pro gramming languages, as well as in co mmand line co mmands o r shell scripts.

In JavaScript, there are a few ways to use Regular Expressio ns; we'll talk abo ut o ne o f these: m at ch().

Let's update o ur co de to use a Regular Expressio n and then we'll co me back to talk mo re abo ut ho w Regular
Expressio ns wo rk. They're a bit mysterio us at first, so do n't wo rry if it takes yo u a while to get used to them. Mo dify
st rings.js:
CODE TO TYPE:
window.onload = init;

function init() {
var searchButton = document.getElementById("searchButton");
searchButton.onclick = searchText;
}

function searchText() {
var searchTerm = document.getElementById("searchTerm").value;
var textToSearch = document.getElementById("textToSearch").value;
searchTerm = searchTerm.trim();
textToSearch = textToSearch.trim();
if (searchTerm == null || searchTerm == "") {
alert("Please enter a string to search for");
return;
}
if (textToSearch == null || textToSearch == "") {
alert("Please enter some text to search");
return;
}

var results = textToSearch.split(" ");


var count = 0;
for (var i = 0; i < results.length; i++) {
if (searchTerm.toUpperCase() == results[i].toUpperCase()) {
count++;
}
}
alert("Found " + count + " instances of " + searchTerm + " out of a total of " + re
sults.length + " words!");

var re = new RegExp(searchTerm, "ig");


var results = textToSearch.match(re);
if (results == null) {
alert("No match found");
}
else {
alert("Found " + results.length + " instances of " + searchTerm);
// Show the matches in the page
showResults(results);
}

}
function clearResultsList(ul) {
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
}

function showResults(results) {
var ul = document.getElementById("matchResultsList");
clearResultsList(ul);
var frag = document.createDocumentFragment();
for (var i = 0; i < results.length; i++) {
var li = document.createElement("li");
li.innerHTML = results[i];
frag.appendChild(li);
}
ul.appendChild(frag);
}

Save it. Befo re yo u try it tho ugh, we need to update st rings.ht m l, because we're go ing to update the HTML page
with the results o f o ur search match. We're just creating a list o f matching wo rds in the <ul> element with the id
"matchResultsList," so we need to add this to the page:
CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>Strings</title>
<meta charset="utf-8">
<script src="strings.js"></script>
<style>
body {
font-family: Arial, sans-serif;
}
textarea {
width: 700px;
height: 400px;
}
label {
vertical-align: top;
}
</style>
</head>
<body>
<form>
<label for="searchTerm">Search for: </label>
<input type="text" id="searchTerm" size="35"
placeholder="search term">
<input type="button" id="searchButton" value="Search"><br><br>
<label for="textToSearch">Search this text:</label>
<textarea id="textToSearch"></textarea>
</form>
<div>
<h2>Results</h2>
<ul id="matchResultsList">
</ul>
</div>
</body>
</html>

Save the file, and click . Try searching fo r a string. Fo r instance, if yo u enter the text fro m Sherlo ck
Ho lmes again, search fo r "Irene". Yo u'll find two matches; first, yo u'll see an alert that tells yo u ho w many matches yo u
have, and then yo u'll see tho se matches in the results list in the page.
So what? We co uld do that befo re with inde xOf (). Okay, try entering [a-z]+e s\b in the Search fo r field:
No w yo u see a list o f wo rds that end in "es" in yo ur page. That's pretty co o l, right? But yo u're pro bably wo ndering, what
o n earth is [a-z]+e s\b? It's a Regular Expressio n.

Regular Expressions Create a Pattern


Regular Expressio ns are used to create a pattern to match in so me text. The simplest regular expressio n yo u
can create is an exact wo rd match, like we did abo ve with "Irene." With this kind o f regular expressio n, we just
match the pattern "Irene" letter fo r letter; it wo rks likes inde xOf (), except that the metho d we used here,
m at ch(), returns an array o f results, rather than a po sitio n.

But yo u can also pro vide mo re co mplex regular expressio ns, o nes that will match multiple strings. That's
what we did with the regular expressio n "[a-z]+es\b", which matches all wo rds that end in "es".

In o ur co de, we create a Re gular Expre ssio n o bje ct , using the Re gExp() co nstructo r. We pass in the
se archT e rm , which is a pat t e rn—that is, the text yo u typed, like "Irene" o r "[a-z]+es\b"—and a seco nd
argument, "ig." That seco nd argument is a list o f at t ribut e s. There are a few mo difiers yo u can use; "i" and
"g" are co mmo n. "i" says to igno re the case (so we'd match "Irene" to "IRENE" o r "irene" o r "irENe" o r any
co mbo like that), and "g" says to "glo bally" match every instance in the text, rather than just the first o ne:

OBSERVE:
var re = new RegExp(searchTerm, "ig");

This Re gExp o bject can then be used to match strings in so me text. The pattern can be exact, like "Irene", o r it
can be set up to match a variety o f different wo rds, like all wo rds that end in "es", which is what the expressio n
"[a-z]+es\b" do es:
OBSERVE:

var re = new RegExp(searchTerm, "ig");


var results = textToSearch.match(re);

To match the pattern to so me text, we use the St ring m e t ho d, m at ch(). m at ch() takes a regular
expressio n re and matches the pattern to the string, in this case t e xt T o Se arch, that we called m at ch() o n.
In the example abo ve, t e xt T o Se arch co ntains the text fro m Sherlo ck Ho lmes. We match that against the
pattern "[a-z]+es\b" with the attributes "ig" (that is, find all instances in the text, regardless o f their case). The
re sult is an array, which we put into a variable, re sult s.

Once we have the re sult s, we can get the le ngt h o f the array to co unt ho w many matches there were, o r we
can display each result in the page like we have:

OBSERVE:
var re = new RegExp(searchTerm, "ig");
var results = textToSearch.match(re);
if (results == null) {
alert("No match found");
}
else {
alert("Found " + results.length + " instances of " + searchTerm);
// Show the matches in the page
showResults(results);
}

We create a functio n sho wRe sult s() that takes the re sult s array and displays the results in the page. We
also create a functio n cle arRe sult sList () to clear the results list each time yo u submit a new search so yo u
get new results each time. All o f the co de in sho wRe sult s() and cle arRe sult sList () is pro bably familiar to
yo u.

Regular Expression Patterns


Let's take a clo ser lo o k at the attributes and patterns yo u can use to create a RegExp o bject. Remember that in
o ur search applicatio n, o ur Regular Expressio n o bject has the attributes "ig," so fo r all o f these examples,
we'll match regardless o f case, and we'll search o ver all the instances in the text. If yo u want to , try remo ving
o ne o r bo th o f these attributes to see the difference. Experiment!

Matching Characters in a Range [ ]


To match a character in a range, yo u use [ ] to specify the range. Fo r instance, [a-z] says to match o ne letter
between a and z; [0 -9 ] will match o ne digit between 0 and 9 . Try this: create so me numbers in the text to
search, varying the digits. Fo r example, yo u co uld use 12, 333, 59 8 5, 2, and so o n. No w write a pattern that
will match o nly digits 1, 2 and 3, like this: [1-3]. Enter the pattern in the search field. Yo u sho uld see all the
digits yo u typed that match 0 -3, but no tice, yo u're matching o nly single digits.

Matching Multiple Characters with *, +, and { }


SO, what if yo u want to match mo re than o ne character? Yo u can use * to match any number o f characters,
including zero ; use + to match o ne o r mo re characters, and { } to match a specific number o f characters.
Using yo ur search applicatio n, try matching these patterns to the same numbers yo u entered befo re:

OBSERVE:

[0-9]*
[0-9]+
[0-9]{2}

[0 -9 ]* says to match any number o f characters (o r no ne) as lo ng as they are between 0 and 9 . Yo ur results
will sho w all the numbers yo u typed, as well as matches o f no numbers at all fo r the spaces in between the
numbers. Why? Because the * matches zero or more characters. So fo r the first number, say 123, it will match
all tho se digits. Then, fo r the space between 123 and the seco nd number, say, 333, it will match zero digits. It
will include that in the search results, and go o n until it reaches the end o f the numbers yo u input.
[0 -9 ]+ says match o ne o r mo re characters as lo ng as they are between 0 and 9 . Co mpare yo ur results to [0 -
9 ]*. Yo u'll see that yo ur results are limited to the numbers yo u typed o nly; no "zero " results fo r the spaces in
between, right?

[0 -9 ]{2} matches 2 numbers precisely. No tice that it matches the sequences o f two numbers o nly o nce, so if
a number has been matched (like 12 in 123) the same number wo n't be matched again fo r a different
sequence o f two numbers (like 23, where the 2 is fro m the same number, 123).
Matching Characters at a Specific Position
What if yo u want to match o ne o r mo re characters at a specific po sitio n? Try entering so me letters next to a
number, like I did here:
.2 matches any non-white-space character next to the number 2. Try ..2; do yo u see three-character results?
No w yo u're matching two no n-white-space characters next to the number 2.

Yo u can match characters o r sequences o f characters at the end o f a string. Try entering: "I scream, yo u
scream, we all scream fo r ice cream" in the text to search (do n't put a perio d at the end o f the sentence). No w
search fo r:

OBSERVE:
cream$
scream$

cre am $ matches o ne "cream" in that sentence, the o ne at the very end, because $ says match the pattern at
the end o f the string.

scre am $ do esn't match with any results, even tho ugh "scream" appears three times in the sentence,
because "scream" do es no t appear at the end o f the string we're searching.
Yo u can also match o n a wo rd bo undary (rather than the who le string bo undary, like we just did). Using the
same sentence, try it:

OBSERVE:
cream\b

No w yo u see four results. Why? Because the string "cream" appears fo ur times at the end o f a wo rd:
"scream" three times, and "ice cream" o ne time, and in each case, "cream" is at the end o f the wo rd. The \b
characters mean "match a wo rd bo undary" and because we put \b at the end o f "cream", we're saying to
match the end o f the wo rd. Try this instead:

OBSERVE:
rea\b

Yo u see no results, because, while "rea" appears fo ur times in the text, it do esn't appear at the end o f a wo rd.

Yo u can use \b to match the beginning o f a wo rd to o :

OBSERVE:
\bscr

Yo u get three matches because yo u're matching "scr" three times in the three instances o f "scream".

Matching Words that End in "es"


No w, yo u sho uld be able to decipher the term "[a-z]+es\b" that we used earlier to match all wo rds ending in
"es" fro m the Sherlo ck Ho lmes text.

[a-z] says match any letter fro m a to z; [a-z]+ says match tho se letters o ne o r mo re times so we can find
wo rds o f any length. Since we're no t matching spaces (o r o ther delimiters, like "," o r ";"), this lo cates o nly
single wo rds. But we are finding o nly wo rds that end in "es" because o f the "es\b". So [a-z]+es\b says: "Find
all wo rds with o ne o r mo re characters matching a-z, ending in es (that is, the characters "es" are o n a wo rd
bo undary at the end o f the wo rd).

Yo u can do much mo re with Regular Expressio n matching; we've hit so me o f the highlights here, but if yo u
like using Regular Expressio ns (and yo u will as yo u write mo re co de!), yo u sho uld check o ut a go o d Regular
Expressio ns reference bo o k o r o nline page fo r the vario us pattern o ptio ns yo u can use. There are many o f
them o ut there.

Regular Expressio ns can be hard to read and understand. Keep that in mind as yo u learn mo re abo ut Regular
Expressio ns. Because they are so hard to read, using them in yo ur pro grams can then make yo ur pro grams
mo re difficult to maintain. Malfo rmed Regular Expressio ns can cause erro rs, so use them judicio usly, and
keep them simple!

Summary of String Properties and Methods


In this lesso n yo u learned lo ts abo ut JavaScript Strings. No w yo u kno w that strings are String o bjects, with pro perties
and metho ds, like o ther JavaScript o bjects.

Pro pe rt y o r
What It Do e s
Me t ho d
length Pro perty: Returns the number o f characters in the string.
Metho d: Returns the character at a specific po sitio n in the string (remember that strings are 0 -
charAt()
based like arrays).
trim() Metho d: Remo ves leading and trailing spaces (but no t spaces in the middle).
indexOf() Metho d: Returns the po sitio n o f a substring in the o riginal string, o r -1.
to UpperCase() Metho d: Returns a new string that is the upper case versio n o f the o riginal string.
to Lo werCase() Metho d: Returns a new string that is the lo wer case versio n o f the o riginal string.
split() Metho d: Splits a string into substrings based o n a split character, returns an array.
match() Metho d: Matches a Regular Expressio n to a string, returns an array.

These string manipulatio n techniques co me in handy when yo u're pro cessing fo rms o r o ther kinds o f text entered by
the user in a web page.

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Dates and Date Formatting
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

use vario us metho ds to get the date and time.


set the date and time.
co mpare and set dates relative to the present.
co nvert strings to dates.

Earlier, yo u learned ho w to create a unique key fo r yo ur to -do items in Lo cal Sto rage using the Dat e o bject and the ge t T im e ()
metho d, which returns the current date and time represented as the number o f milliseco nds since 19 70 .

It's time we return to the Dat e o bject, and its metho ds, and explo re the po wer o f this o bject further. The Dat e o bject has many
o f metho ds fo r getting and setting the date and/o r the time; we'll fo cus o n a few o f the mo st useful metho ds.

Dates and times are a bit tricky to wo rk with o n the co mputer. If all yo u care abo ut is the date right no w o n yo ur o wn co mputer,
it's fairly straightfo rward, but if yo u're creating a web page o n the internet, then yo u'll have users fro m all aro und the wo rld in
different time zo nes visiting yo ur site. Wo rking with dates and times in co de can be tricky when yo u're co nsidering all time
zo nes o r, say, co nverting the way we write dates in the US to the way peo ple write dates in o ther co untries. Unfo rtunately,
there's no o ne best way to tackle this stuff, so we'll lo o k at the metho ds we have available, and yo u can experiment with yo ur
o wn co de to see what wo rks best fo r yo u.

What's the Date and T ime Right Now?


Let's start with a web page that displays the current date and time when yo u lo ad the page. Create a new HTML file as
sho wn:

CODE TO TYPE:

<!doctype html>
<html>
<head>
<title>Dates</title>
<meta charset="utf-8">
<script src="dates.js"></script>
<style>
body {
font-family: Arial, sans-serif;
}
span {
font-weight: bold;
}
</style>
</head>
<body>
<div>
Right now, the date and time is:
<span id="datetime"></span>
</div>
</body>
</html>

Save it in yo ur /javascript 2 fo lder as dat e s.ht m l. Next, we'll create a new JavaScript file:
CODE TO TYPE:
window.onload = init;

function init() {
var datetime = document.getElementById("datetime");
var now = new Date();
datetime.innerHTML = now;
}

Save it in yo ur /javascript 2 fo lder as dat e s.js. Open dat e s.ht m l again and click . Yo u see a page
with the current date and time. Because the JavaScript is running in the bro wser that's o n yo ur co mputer, yo u'll see the
date and time fo r where yo u are, even tho ugh the file is sto red and served fro m the web server at O'Reilly Scho o l o f
Techno lo gy.

In this co de, we create a new Dat e o bject, and then set the co ntent o f the "datetime" <span> element in the HTML to
that date. By default, the value that yo u get is a string that sho ws yo u bo th the date and time.

OBSERVE:
datetime.innerHTML = now;

Here, we set the value o f a pro perty, inne rHT ML, that expects a String, no t a Date. So JavaScript auto matically calls
the Date metho d t o St ring() o n the no w Dat e o bje ct to co nvert it to a String. Try changing the co de to :

OBSERVE:
datetime.innerHTML = now.toString();

Yo u get exactly the same result.

Other Methods for Getting the Date and T ime


Alo ng with t o St ring(), there are several o ther metho ds fo r getting a string that represents the Date in a
readable fo rmat, including: t o Dat e St ring(), t o Lo cale Dat e St ring(), t o T im e St ring(),
t o Lo cale T im e St ring() and t o Lo cale St ring(). Try each o f these by changing the co de in the JavaScript in
dat e s.js.

t o St ring(): Displays the date and time as a string using the lo cal time zo ne.
t o Dat e St ring(): Displays the date as a string using the lo cal time zo ne.
t o Lo cale Dat e St ring(): Displays the date as a string using the lo cal time zo ne, fo rmatted using
lo cal co nventio ns.
t o T im e St ring(): Displays the time as a string using the lo cal time zo ne.
t o Lo cale T im e St ring(): Displays the time as a string using the lo cal time zo ne, fo rmatted using
lo cal co nventio ns.
t o Lo cale St ring(): Displays the date and time as a string using the lo cal time zo ne, fo rmatted
using lo cal co nventio ns.

No tice the differences in these vario us metho ds o f creating a string fro m the Dat e o bject. Fo r instance,
co mpare the way the date string is displayed using t o Lo cale St ring() with ho w it was displayed using
t o St ring() (abo ve):

Dates and T ime Zones


In the previo us example using t o St ring(), in the date and time info rmatio n displayed in the web page, yo u can see the
time zo ne info rmatio n: GMT -0 7 0 0 (PDT )? I'm in the Pacific time zo ne in the United States, so my time is currently 7
ho urs behind the GMT (Greenwich Mean Time) measured fro m Greenwich, in Lo ndo n, England. GMT is similar to the
Co o rdinated Universal Time (UTC), which is no w the wo rldwide standard fo r measuring time. Fo r mo st purpo ses (and
certainly o ur purpo ses here), GMT and UTC are equivalent.

Yo u can use the Dat e metho ds, ge t T im e zo ne Of f se t () and t o UT CSt ring() to help figure o ut differences in the
lo cal time where yo u are (o r where so meo ne using yo ur web page is), and the UTC time. Let's update dat e s.js to use
these metho ds to sho w ho w many ho urs we are fro m UTC time:

CODE TO TYPE:
window.onload = init;

function init() {
var datetime = document.getElementById("datetime");
var now = new Date();
datetime.innerHTML = now.toUTCString();
var hoursDiff = (now.getTimezoneOffset()) / 60;
datetime.innerHTML += ", " + hoursDiff + " hours difference from me.";
}

Save it, o pen dat e s.ht m l again, and click . No w yo u see the current time expressed in universal time
(o r GMT), and the number o f ho urs difference fro m yo ur lo cal time to GMT.

The ge t T im e zo ne Of f se t () metho d returns the difference in minutes, no t ho urs, so here, we divide by 6 0 to get the
ho urs. Altho ugh yo u may want the time in minutes, because in so me places the time difference fro m GMT do es no t
o ccur o n the ho ur. Fo r instance, so me places will be several ho urs plus a half ho ur different fro m GMT. I'm exactly 7
ho urs behind GMT, so my result is:
Setting a Date and T ime
So far, all we've do ne with Dat e is get the current date, and co nvert it to a String fo r display in a web page. But what if
yo u want to create a specific date?

The Dat e () co nstructo r functio n creates a date that represents now, if yo u do n't pass in any arguments. But yo u can
also create specific dates, by passing in the year, mo nth, day, ho urs, minutes, seco nds, and even milliseco nds o f a
specific date and time yo u want. Let's create the Date that represents New Year's Day, 20 50 . Mo dify dat e s.ht m l as
sho wn:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>Dates</title>
<meta charset="utf-8">
<script src="dates.js"></script>
<style>
body {
font-family: Arial, sans-serif;
}
span {
font-weight: bold;
}
</style>
</head>
<body>
<div>
Right now, tThe date and time is:
<span id="datetime"></span>
</div>
</body>
</html>

Save it, and mo dify dat e s.js as sho wn:


CODE TO TYPE:
window.onload = init;

function init() {
var datetime = document.getElementById("datetime");
var now = new Date();
datetime.innerHTML = now.toUTCString();
var hoursDiff = (now.getTimezoneOffset()) / 60;
datetime.innerHTML += ", " + hoursDiff + " hours difference from me.";
var nyd = new Date(2050, 0, 1);
datetime.innerHTML = nyd.toString();
}

Save it, o pen dat e s.ht m l again, and click .

OBSERVE:
var nyd = new Date(2050, 0, 1);

In this example, we created a Dat e o bject fo r New Year's Day, 20 50 , by passing in the ye ar 20 5 0 , the m o nt h 0 (fo r
January), and the day 1 (fo r the 1st o f January). No tice that the m o nt h is 0 , no t 1. Why? Because in JavaScript, the
mo nths start at 0 and go to 11. That's a little weird, but that's the way it wo rks. Days, ho wever, do start at 1, and go up to
31 depending o n the mo nth.

Because we didn't supply any arguments fo r the time, JavaScript assumed we wanted 12:0 0 AM (midnight) o n January
1, 20 50 (which is perfect fo r celebrating the New Year!). We co uld have supplied time arguments, like this:

OBSERVE:
var nyd = new Date(2050, 0, 1, 0, 1, 0);

...where 0 is the ho ur (midnight), 1 is the minute (1 minute after midnight), and 0 is the seco nds. Try o ther dates and
times. No tice that if yo u want to specify a time, yo u must also specify a date. That is, while all the arguments to Dat e ()
are o ptio nal, yo u must supply them in o rder, and yo u can't skip any. So , if yo u want to supply minutes, yo u must also
supply a year, mo nth, day, and ho ur, but yo u can skip the seco nds and milliseco nds.

Try changing the co de so yo u display the date and time using t o Lo cale St ring() instead. Mo dify dat e s.js as sho wn:
CODE TO TYPE:
window.onload = init;

function init() {
var datetime = document.getElementById("datetime");
var nyd = new Date(2050, 0, 1, 0, 1, 0);
datetime.innerHTML = nyd.toLocaleString();
}

Save it, o pen dat e s.ht m l again, and click . Co mpare yo ur result with the previo us result using
t o St ring(). Which do yo u like better?

Setting Date and T ime Elements Separately with Methods


Using the Dat e co nstructo r, yo u can specify the year, mo nth, day, ho ur, minutes, seco nds, and milliseco nds
all to gether to create a date and time. But what if yo u need to set the vario us values separately?

In that case, yo u can use the Dat e o bject's se t metho ds. Mo dify dat e s.js as sho wn

CODE TO TYPE:
window.onload = init;

function init() {
var datetime = document.getElementById("datetime");
var nyd = new Date(2050, 0, 1, 0, 1, 0);
datetime.innerHTML = nyd.toLocaleString();
var aDate = new Date();
aDate.setFullYear(2020);
aDate.setHours(13);
aDate.setMinutes(3);
aDate.setSeconds(59);
datetime.innerHTML = aDate.toLocaleString();
}

Save it, o pen dat e s.ht m l again, and click . Yo u see yo ur current mo nth and day (that is, the
day yo u are do ing this lesso n), but in the year 20 20 . And the time sho uld be set to 1:0 3pm in yo ur time zo ne.
Here, we created a new Dat e o bject that, by default, represents the present time, and then changed the year to
20 20 , the ho ur to 1pm, and the minute to 3 minutes after 1pm. Everything else stays the same (that is, the
values in place when yo u created the Date representing the present).

There are metho ds fo r setting every aspect o f a Dat e , including:

se t FullYe ar(): sets the year.


se t Mo nt h(): sets the mo nth.
se t Dat e (): sets the day o f the mo nth.
se t Ho urs(): sets the ho ur o f the day.
se t Minut e s(): sets the minutes after the ho ur.
se t Se co nds(): sets the seco nds after the minute.
se t Millise co nds(): sets the milliseco nds after the seco nds.
se t T im e (): takes a date represented as milliseco nds after 19 70 and sets the full date.

Experiment with these o ther metho ds and try using them in yo ur co de to set specific dates and times.

Comparing and Setting Dates Relative to the Present


Two tasks yo u might need to do fairly o ften when wo rking with dates are comparing dates and setting dates
relative to the present. Fo r bo th o f these tasks, wo rking with dates expressed in milliseco nds since 19 70 is
the way to go .

First, let's try co mparing dates. Mo dify dat e s.js as sho wn:

CODE TO TYPE:
window.onload = init;

function init() {
var datetime = document.getElementById("datetime");
var aDate = new Date();
aDate.setFullYear(2020);
aDate.setHours(13);
aDate.setMinutes(3);
aDate.setSeconds(59);
datetime.innerHTML = aDate.toLocaleString();
var now = new Date();
var diff = aDate.getTime() - now.getTime();
var days = diff / 1000 / 60 / 60 / 24;
datetime.innerHTML = aDate.toLocaleString() + ", " + days + " days from now"
;
}

Save it, o pen dat e s.ht m l again, and click . Yo u'll see the number o f days between no w and
the same date in 20 20 (and remember, yo ur date will be different fro m mine because the dates are based o n
now, that is, the date and time yo u are do ing this lesso n).
To co mpute the difference between two dates, we create two Dat e o bjects. We have o ne Dat e o bject fo r the
date in 20 20 , and o ne Dat e o bject fo r no w. Then, we can use the ge t T im e () metho d to get the date and time
in milliseco nds since 19 70 . The number o f milliseco nds to the date in 20 20 will be lo nger than the number o f
milliseco nds to no w (assuming it's still befo re 20 20 o f co urse!). So we subtract the milliseco nds to no w fro m
the milliseco nds to the date in 20 20 to get the difference in milliseco nds. Then, we co nvert fro m milliseco nds
to days by dividing by 10 0 0 (the number o f milliseco nds in a seco nd), then 6 0 (the number o f seco nds in a
minute), then 6 0 again (the number o f minutes in an ho ur), and then 24 (the number o f ho urs in a day). The
result is the number o f days between no w and the date in 20 20 .

That number is kind o f ugly when we display it because o f the precisio n o f the number after the decimal po int.
Let's cut o ff everything after the decimal po int to make it easier to read. We can use the Mat h.f lo o r() metho d
to do this. Mo dify dat e s.js as sho wn:

CODE TO TYPE:
window.onload = init;

function init() {
var datetime = document.getElementById("datetime");
var aDate = new Date();
aDate.setFullYear(2020);
aDate.setHours(13);
aDate.setMinutes(3);
var now = new Date();
var diff = aDate.getTime() - now.getTime();
var days = Math.floor(diff / 1000 / 60 / 60 / 24);
datetime.innerHTML = aDate.toLocaleString() + ", " + days + " days from now"
;
}

Save it, o pen dat e s.ht m l, and click . No w the number o f days is be easier to read:
Remember that Mat h is a built-in JavaScript o bject with lo ts o f handy metho ds yo u can use fo r do ing math
co mputatio ns. Mat h.f lo o r() dro ps all o f the numbers after the decimal po int in a flo ating po int number, so
yo u get a number that is less than (o r equal to , if the number is even) the o riginal. Co mpare that to
Mat h.ce il() which ro unds up and then dro ps the numbers.

No w let's try creating a date that's three days fro m no w. Mo dify dat e s.js as sho wn:

CODE TO TYPE:
window.onload = init;

function init() {
var datetime = document.getElementById("datetime");
var aDate = new Date();
aDate.setFullYear(2020);
aDate.setHours(13);
aDate.setMinutes(3);
var now = new Date();
var diff = aDate.getTime() - now.getTime();
var days = Math.floor(diff / 1000 / 60 / 60 / 24);
datetime.innerHTML = aDate.toLocaleString() + ", " + days + " days from now"
;
var now = new Date();
var threeDays = (24 * 60 * 60 * 1000) * 3;
var threeDaysFromNow = new Date(now.getTime() + threeDays);
datetime.innerHTML = now.toLocaleString() + "; 3 days from now: " + threeDay
sFromNow.toLocaleString();
}

Save it, o pen dat e s.ht m l, and click . Yo u see two dates: no w, and three days fro m no w.
We also use the date expressed as milliseco nds since 19 70 to create a date three days fro m no w. Here, we
create a Dat e representing no w. Then we figure o ut ho w many milliseco nds are in three days, and create
ano ther Dat e that is no w (expressed in milliseco nds) plus the number o f milliseco nds in three days. That
gives us a Dat e three days fro m no w.

Experiment by creating o ther dates. Can yo u create a date that is three days in the past fro m no w?

Converting Strings to Dates


Yo u might have an applicatio n, like the To -Do List applicatio n we've been building in this co urse, that asks the user to
submit a date. In the To -Do List applicatio n, we ask the user to submit a date fo r when the to -do item is due.

When yo u submit a date using a fo rm, whether yo u're using <input type="date"> o r <input type="text">, the value yo u
get when yo u pro cess the input data with JavaScript is a String. But so metimes yo u might need that value as a Dat e
o bject; that's when yo u'll use the techniques we're learning in this lesso n.

The Dat e o bject has a metho d named parse () that yo u can use to parse a string representing a date. Let's see ho w
we can use it in an example. First, we'll update o ur HTML to add a fo rm entry fo r a date, and then update o ur JavaScript
to pro cess the string value we get fro m the fo rm. We'll co nvert the string into a Date, and then display the Date in the
page, in the "datetime" <span>. Mo dify dat e s.ht m l as sho wn:
CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>Dates</title>
<meta charset="utf-8">
<script src="dates.js"></script>
<style>
body {
font-family: Arial, sans-serif;
}
span {
font-weight: bold;
}
form {
margin-bottom: 20px;
}
</style>
</head>
<body>
<form>
<label>Enter a date:</label>
<input type="date" id="aDate">
<input type="button" id="submit" value="Submit">
</form>
<div>
The date and time is:
<span id="datetime"></span>
</div>
</body>
</html>

Save it, and mo dify dat e s.js as sho wn:

CODE TO TYPE:
window.onload = init;

function init() {
var datetime = document.getElementById("datetime");
var now = new Date();
var threeDays = (24 * 60 * 60 * 1000) * 3;
var threeDaysFromNow = new Date(now.getTime() + threeDays);
datetime.innerHTML = now.toLocaleString() + ", 3 days from now: " + threeDaysFromNo
w.toLocaleString();
var submit = document.getElementById("submit");
submit.onclick = getDate;
}
function getDate() {
var aDateString = document.getElementById("aDate").value;
if (aDateString == null || aDateString == "") {
alert("Please enter a date");
return;
}
var aDateMillis = Date.parse(aDateString);
alert(aDateMillis);
var aDate = new Date(aDateMillis);

var datetime = document.getElementById("datetime");


datetime.innerHTML = aDate.toLocaleString();
}

Save it, o pen dat e s.ht m l again, and click . Enter a date in the date input field. Click Subm it . Yo u first
see an alert, and then yo u see a date displayed in the page belo w the fo rm input. Try writing the date in different
fo rmats.
Yo u pro bably see the string "NaN" in the alert, and yo u pro bably get a date that makes no sense in the page. If yo u're
using a bro wser like Safari o r Chro me that displays arro ws next to the "date" input co ntro l, and yo u use these to enter
a date, like "0 7-19 -20 12," yo u'll still see a no nsensical date displayed in the page.

So mething's definitely go ne wro ng, because that date do esn't make sense.

Yo u'll likely find that this co de wo rks pro perly o nly when yo u enter dates in particular fo rmats. One o f the fo rmats yo u
can use is the same fo rmat yo u see when yo u use t o St ring() o r t o Lo cale St ring(), as we've do ne in these
examples. Try entering a date using this fo rmat:

J uly 20 , 20 12 15 :25 PDT

No w it sho uld wo rk. Yo u see an alert with the number o f milliseco nds representing that date, and the date appears
pro perly under the fo rm.
Here are so me o ther fo rmats yo u can try:

July 20 , 20 12
20 12/7/20
20 12.7.20
20 12-7-20
7/20 /20 12
7-20 -20 12
7.20 .20 12

Take no te o f the different fo rmats yo u try, which o nes wo rk, and which o nes do n't.

Let's go thro ugh the co de and see what's happening:

OBSERVE:
function getDate() {
var aDateString = document.getElementById("aDate").value;
if (aDateString == null || aDateString == "") {
alert("Please enter a date");
return;
}
var aDateMillis = Date.parse(aDateString);
alert(aDateMillis);
var aDate = new Date(aDateMillis);

var datetime = document.getElementById("datetime");


datetime.innerHTML = aDate.toLocaleString();
}

First, we ge t t he value o f t he " aDat e " input as a string. We che ck t o m ake sure t he st ring isn't e m pt y; if it is,
we ale rt t he use r and ask them to enter a date, and then return fro m the functio n.

If we get a string, we t ry t o parse it using Dat e .parse (). Dat e .parse () will return the date in milliseco nds fro m the
string yo u pass in, but o nly if the metho d can parse the string. As yo u've disco vered, there are o nly certain string
fo rmats that Dat e .parse () will parse co rrectly. If Dat e .parse () fails, then instead o f returning the milliseco nds (a large
number), it returns "NaN," meaning "No t a Number." That's JavaScript's way o f letting yo u kno w it co uldn't parse this
string into a number. So , if yo u enter a date using the wro ng type o f string fo rmat, yo u see "NaN" in the ale rt .

After getting the date (o r trying to get the date) in milliseco nds using Dat e .parse (), we create a new Dat e o bject fro m
that value. If we're successful, aDat e will be a valid Dat e o bject. If no t, aDat e will be a no nsensical date, because the
Dat e () co nstructo r can't make sense o f the value, "NaN".

Finally, we display the Dat e in the " dat e t im e " <span> using the t o Lo cale St ring() metho d.
We've been using Dat e o bjects by using the Dat e () co nstructo r to create a date o bject and then calling
metho ds o n that o bject. So what is Dat e .parse ()? Dat e .parse () is an example o f a st at ic m e t ho d. In
JavaScript, functio ns are o bjects; so the Dat e () co nstructo r functio n that yo u use to create new date
o bjects is, itself, an o bject. And it so happens that that o bject has a metho d named parse (). Yo u call
Note static metho ds using the name o f the co nstructo r functio n, Dat e , but witho ut the parentheses () that
invo ke the functio n. Do n't wo rry if yo u do n't fully understand this; this to pic is mo re advanced JavaScript
and isn't within the sco pe o f this co urse. Still, yo u're getting a taste o f the kinds o f things yo u can do with
JavaScript when yo u delve into object-oriented programming. But, that's a to pic fo r ano ther co urse...

Dates and HT ML Input T ypes


When yo u enter a date into an <input> element and yo u parse it using the Dat e .parse () metho d, yo u must
enter a date string that the parse () metho d can understand. Fo r bro wsers that suppo rt the "date" <input> type,
and o ffer a date picker fo r entering a date, this beco mes less o f an issue because the date picker usually
creates the date as a string with the co rrect date fo rmat, a fo rmat that can be parsed using Dat e .parse ().
Ho wever, if yo u enter a date using an invalid fo rmat, that can create an erro r in yo ur co de, and po ssibly cause
yo ur web page to malfunctio n.

We already checked to make sure the user is entering so mething into the field (by checking to see if the input
is null o r the empty string); we sho uld also be checking to make sure that the user is entering a valid date.
We'll tackle this using Exce pt io n Handling in the next lesso n.

In this lesso n, we've explo red the JavaScript Dat e o bject. Yo u've learned ho w to create Dates, display Dates, co mpare Dates,
and co nvert strings to Dates. Yo u've also learned a few ins and o uts o f time zo nes. As yo u can see, wo rking with Dates can
so metimes be tricky! But fun to o , right? Take a sho rt break and then yo u'll be ready to tackle mo re Dates in the pro ject befo re
yo u mo ve o n to the next lesso n.

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Handling Exceptions with Try/Catch
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

thro w and catch exceptio ns.


use the finally clause to execute co de regardless o f what happens in the try/catch.

As yo u have realized by no w, there are plenty o f things that can go wro ng when writing JavaScript co de! There are unexpected
events, bugs, and mistyped data submitted by users. So far we've handled these exceptional conditions and errors either by
igno ring them (no t a go o d lo ng-term plan!) o r by testing with if/then/else statements to check fo r certain co nditio ns.

JavaScript includes ano ther way o f handling exceptions: the t ry/cat ch statement. T ry/cat ch lets yo u try so me co de and if
so mething go es wro ng, yo u can catch the erro r and do so mething abo ut it.

In this lesso n yo u'll learn ho w to use t ry/cat ch (and the o ptio nal f inally part o f this statement) to handle exceptio ns.

What Causes an Exception?


An exceptio n is o ften caused by an erro r in yo ur JavaScript that causes yo ur co de to sto p executing; that is, it's an erro r
fro m which the JavaScript intepreter can't easily reco ver. In a previo us lesso n, yo u wro te so me co de to see whether
the result o f calling Dat e .parse () o n an invalid string was equal to NaN. Clearly, if yo u give Dat e .parse () an invalid
string, that's an erro r (in this case, a user erro r fo r using the wro ng date fo rmat), but it's no t a fatal erro r; JavaScript is
happy to set the result to NaN and pro ceed. Of co urse, later o n in yo ur co de, the fact that NaN is no t a real number
might cause an exceptio n, but that's ano ther issue.

So what kind o f co de causes an exceptio n that causes yo ur co de to sto p running? Let's take a lo o k at an example:

INTERACTIVE SESSION:

var myString = null;


myString.length;

Open a bro wser windo w, and o pen the JavaScript co nso le (yo u may have to lo ad a web page to be able to access the
co nso le; any web page will do , including a previo us file yo u've created in the co urse). Type in tho se two lines; yo u see
a JavaScript Erro r like this (in Safari):

...o r like this (in Firefo x):

Erro r messages in vario us bro wsers will differ slightly, but all bro wsers sho uld give yo u an exceptio n fo r
Note this co de, and a similar erro r message. Try different bro wsers to see what yo u get!
An erro r like this in yo ur co de will cause the co de to sto p executing. Let's try making an erro r in co de lo aded with an
HTML page (rather than just at the co nso le), and this time, we'll t ry it and cat ch the erro r with the t ry/cat ch
statements. First, we'll create a super-simple HTML page, and then the JavaScript to create the erro r.

Open a new HTML file and type the co de as sho wn:

CODE TO TYPE:

<!doctype html>
<html>
<head>
<title>Exceptions</title>
<meta charset="utf-8">
<script src="ex.js"></script>
<style>
body {
font-family: Arial, sans-serif;
}
</style>
</head>
<body>
</body>
</html>

Save it in yo ur /javascript 2 fo lder as e x.ht m l. Next, create a new JavaScript file as sho wn:

CODE TO TYPE:
window.onload = init;

function init() {
var myString = null;
try {
var len = myString.length;
console.log("Len: " + len);
}
catch (ex) {
console.log("Error: " + ex.message);
}
}

Save it in yo ur /javascript 2 fo lder as e x.js. Open e x.ht m l again, and click . Open the JavaScript
co nso le and yo u see an erro r message like this (in Safari):

OBSERVE:
TypeError: 'null' is not an object (evaluating 'myString.length')

...o r like this (in Firefo x):

OBSERVE:
TypeError: myString is null

The erro r messages generated fro m yo ur JavaScript co de are the same as tho se yo u saw using the co nso le earlier.

Let's take a clo ser lo o k at the t ry/cat ch statement:


OBSERVE:
function init() {
var myString = null;
try {
var len = myString.length;
console.log("Len: " + len);
}
catch (ex) {
console.log("Error: " + ex.message);
}
}

After setting m ySt ring to null, which we kno w will cause an erro r when we try to access the le ngt h pro perty (because
null do esn't have a length pro perty), we start the t ry/cat ch blo ck. We use { and } to delimit each part o f the statement;
these are required.

JavaScript will try to execute all the co de in the t ry part o f the statement. If it wo rks and no exceptio n is created, then the
t ry ends no rmally, and the cat ch is not executed. So the flo w o f executio n wo uld co ntinue belo w the cat ch; in this
case, that means the functio n simply returns.

But if so mething go es wro ng, and an exceptio n is generated, as we kno w it will in this co de, then as so o n as the
exceptio n is generated, the flo w o f executio n jumps fro m the t ry to the cat ch. In this case, that means we never see
the co nso le .lo g() message that displays the value o f le n.

JavaScript auto matically generates a value f o r t he e xce pt io n and passes it into the cat ch clause (much like
passing an argument to a functio n parameter). Fo r erro rs generated internally by the JavaScript interpreter, like this o ne
is, that value is typically an Erro r o bject. Here, we assume it is such an erro r, and we name that o bject e x, and access
its m e ssage pro perty to display in the co nso le, with info rmatio n abo ut what the erro r was.

So in this co de, we cause, o r raise (as it's o ften called), an exceptio n by attempting to access a pro perty that do esn't
exist, and we catch that exceptio n so that it do esn't cause o ur pro gram to sto p running entirely. This is usually better fo r
the applicatio n; if yo u handle the erro rs that are caused in yo ur pro gram gracefully, then the end user can co ntinue
using yo ur applicatio n, whereas if yo ur JavaScript sto ps running, that might cause yo ur applicatio n to sto p wo rking
alto gether!

T hrowing Exceptions and the Finally Clause


T hrowing Exceptions
Yo u might want to use the t ry/cat ch statement in situatio ns where JavaScript might no t raise an exceptio n
internally, but where yo u are testing fo r erro rs o r exceptio nal co nditio ns in yo ur co de. In that case, yo u can
raise yo ur o wn exceptio ns by using the t hro w statement. Let's expand o ur example just a bit and thro w o ur
o wn exceptio n (and catch it, o f co urse). Mo dify e x.ht m l as sho wn:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>Exceptions</title>
<meta charset="utf-8">
<script src="ex.js"></script>
<style>
body {
font-family: Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Enter a string</h1>
<p id="stringInfo"></p>
<p id="error"></p>
<p id="msg"></p>
</body>
</html>
Save it. No w, update e x.js:

CODE TO TYPE:
window.onload = init;

function init() {
var myString = null;
try {
var len = myString.length;
console.log("Len: " + len);
}
catch (ex) {
console.log("Error: " + ex.message);
}
var myString = prompt("Enter a string:");
try {
var len = myString.length;
if (len == 0) {
throw new Error("You didn't enter anything. Try again.");
}
else {
displayLength(myString, len);
}
}
catch (ex) {
displayError(ex.message);
}
finally {
displayMessage("Thanks for trying!");
}
}

function displayError(e) {
var error = document.getElementById("error");
error.innerHTML = e;
}

function displayLength(myString, len) {


var stringInfo = document.getElementById("stringInfo");
stringInfo.innerHTML = "The string '" + myString + "' has length: " + len;
}

function displayMessage(m) {
var msg = document.getElementById("msg");
msg.innerHTML = m;
}

Save it, o pen e x.ht m l again, and click . Yo u'll be pro mpted to enter a string. Try entering a real
string; see what happens. No w try clicking OK witho ut entering anything, an see what happens.
Let's go thro ugh the co de to see what's go ing o n:

OBSERVE:
function init() {
var myString = prompt("Enter a string:");
try {
var len = myString.length;
if (len == 0) {
throw new Error("You didn't enter anything. Try again.");
}
else {
displayLength(myString, len);
}
}
catch (ex) {
displayError(ex.message);
}
finally {
displayMessage("Thanks for trying!");
}
}

No w, instead o f setting m ySt ring to null, we're pro mpting the user to enter a string. Once we've do ne that, we
t ry to get the length o f the string. When yo u use pro m pt (), even if yo u do n't enter anything, the value returned
is still a string (in that case, it wo uld be an empty string, ""), and getting the length o f an empty string will result
in 0 , rather than an exceptio n. We can che ck t o se e if t he le ngt h is 0 , and if it is, we can t hro w o ur o wn
e xce pt io n. Yo u can thro w any value yo u want; in this case, we are thro wing an Erro r o bject (just like
JavaScript did in the previo us example). As so o n as we t hro w t he Erro r, the co de skips any o ther co de in
the t ry clause, and jumps to the cat ch clause. There, we pass the value o f the m e ssage pro perty fro m the
Erro r o bject to the functio n displayErro r(), which displays that value in the web page. The value o f
m e ssage is the value we passed to the Erro r() co nstructo r when we t hre w t he Erro r.

If t he le ngt h is gre at e r t han 0 , we display the string and the length o f the string by passing the two values
to displayLe ngt h().

T he Finally Clause
We added o n a f inally clause in this example. This clause is executed whether the exceptio n is thro wn o r no t.
In o ther wo rds, if the length is 0 , and we execute the cat ch clause, o nce the cat ch is co mplete, we execute
the f inally clause. If the length is greater than 0 , we execute the co de in the t ry clause, skip the cat ch clause
(because there's no exceptio n), and execute the f inally clause.

Finally allo ws yo u execute so me co de regardless o f what happens in the t ry/cat ch, so it's handy fo r clean-
up co de, fo r example. In this case, all we do is display the same message whether the pro mpt is successful
o r no t.

Using Exceptions and T ry/Catch


In the previo us lesso n, we created a pro gram to parse a string and co nvert it to a JavaScript Dat e , but we weren't
checking to make sure the Dat e .parse () actually wo rked!

This is a go o d example o f where we can use t ry/cat ch and thro w an e xce pt io n instead. Do ing this will make the
co de mo re ro bust; it's a go o d way to handle this type o f erro r. Let's give it a try. Edit dat e s.js as sho wn:

CODE TO TYPE:
window.onload = init;

function init() {
var submit = document.getElementById("submit");
submit.onclick = getDate;
}

function getDate() {
var aDate;
var aDateString = document.getElementById("aDate").value;
if (aDateString == null || aDateString == "") {
alert("Please enter a date");
return;
}
var aDateMillis = Date.parse(aDateString);
alert(aDateMillis);
var aDate = new Date(aDateMillis);
try {
if (isNaN(aDateMillis)) {
throw new Error("Date format error. Please enter the date in the format MM/
DD/YYYY, YYYY/MM/DD, or January 1, 2012");
}
else {
aDate = new Date(aDateMillis);
}
var datetime = document.getElementById("datetime");
datetime.innerHTML = aDate.toLocaleString();
}
catch (ex) {
alert(ex.message);
}
}

Save it, o pen dat e s.ht m l, and click . Try entering a string using a fo rmat the pro gram will reco gnize,
and a string that it will no t reco gnize. Yo u see the same behavio r yo u saw at the end o f the previo us lesso n, but the
way we handle the erro r (that is, the fo rmat that the pro gram can't parse) is different. No w we use t ry/cat ch and thro w
an exceptio n if we can't match the date fo rmat the user has entered.

No tice that as so o n as we thro w the Erro r o bject when we can't match the fo rmat, the rest o f the t ry clause is skipped,
so we do n't have to check to see if an erro r was generated. As so o n as that Erro r is thro wn, the co de jumps directly to
the cat ch clause. This is a co nvenient way to tell yo ur pro gram to , "sto p everything we're do ing and go here!" This
technique can be really useful. It can also make the co de a bit easier to read.

Remember that the t ry clause must always be matched with either a cat ch o r a f inally. Typically, yo u'll see t ry/cat ch, but
yo u'll find f inally will also co me in handy. If yo u want to raise yo ur o wn exceptio ns, yo u can use t hro w and thro w a value that
is caught by the cat ch clause parameter. Yo u can thro w any value yo u want; JavaScript typically thro ws the Erro r o bject, and
yo u can do this to o by creating a ne w Erro r o bject, and passing in the value fo r the m e ssage pro perty. Always check the
JavaScript do cumentatio n in a go o d reference to find o ut exactly which type o f exceptio n to expect fo r circumstances where
JavaScript might no t thro w the Erro r o bject, so yo u kno w which kind o f value to expect in yo ur cat ch clause.

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Geolocation and Google Maps
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :

use the geo lo catio n o bject to get yo ur po sitio n co o rdinates (latitude and lo ngitude).
put yo ur lo catio n into a webpage using geo lo catio n.
handle geo lo catio n erro rs.
add a Go o gle map.
add a marker to yo ur map.

One fairly new feature in JavaScript and web bro wsers is Geo lo catio n. It's been aro und in vario us fo rms fo r a while, but it was
standardized recently thro ugh the W3C in the Geo lo catio n specificatio n. All mo dern bro wsers (including IE9 +) no w suppo rt this
versio n o f Geo lo catio n, which makes it easier to get lo catio n data into yo ur web pages.

Geo lo catio n is especially fun when yo u're using a web applicatio n o n a mo bile bro wser that suppo rts Geo lo catio n, because
yo u're likely to be mo ving aro und, and mo re likely to be using an app where seeing yo ur lo catio n co mes in handy. Fo rtunately,
Geo lo catio n is suppo rted by bo th the iOS bro wser (o n iPho ne and iPad) and the Andro id bro wser (o n a variety o f smart
pho nes).

In this lesso n we'll build a simple "Rando m Tho ughts" applicatio n that allo ws yo u to add rando m tho ughts to a web page. The
app will capture yo ur lo catio n when yo u begin adding yo ur tho ughts and add it to a map. So und like fun? Let's get go ing!

How Geolocation Works


Geo lo catio n in bro wsers is suppo rted with a built-in JavaScript o bject named, yo u guessed it, ge o lo cat io n. The
ge o lo cat io n o bject is a pro perty o f the navigat o r o bject that all bro wsers also have built-in. Let's see ho w to use
the ge o lo cat io n o bject to get yo ur po sitio n co o rdinates (latitude and lo ngitude). We'll start with an empty web page
and fill it o ut in mo re detail as we build o ur Rando m Tho ughts applicatio n. Start a new HTML file as sho wn:

CODE TO TYPE:

<!doctype html>
<html>
<head>
<title>My Random Thoughts</title>
<meta charset="utf-8">
<script src="random.js"></script>
<style>
</style>
</head>
<body>
</body>
</html>

Save it in yo ur /javascript 2 fo lder as rando m .ht m l. Next, create the JavaScript in a new file:
CODE TO TYPE:

window.onload = init;

function init() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getLocation);
}
else {
console.log("Sorry, no Geolocation support!");
}
}
function getLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
alert("My position is: " + latitude + ", " + longitude);
}

Save it in yo ur /javascript 2 fo lder as rando m .js, o pen rando m .ht m l again, and click . Yo u're
pro mpted to co nfirm that yo u're o kay with sharing yo ur lo catio n. This is to pro tect yo ur privacy. Here's what the pro mpt
lo o ks like in Safari:

In Chro me:

In Internet Explo rer:

And in Firefo x:

Once yo u allo w the bro wser to use yo ur lo catio n (assuming yo u're o kay with that, and we'll talk mo re later abo ut what
happens if yo u do n't allo w it), then yo u'll see an alert with yo ur lo catio n:
If yo u do n't see an alert, we'll add so me co de so o n that yo u can use to help tro ublesho o t whatever the pro blem might
be. Assuming yo u're using a mo dern bro wser, yo u'll see a lo catio n, even if yo u're o n a deskto p machine. Ho wever,
so metimes yo ur lo catio n might be based o n yo ur ISP's netwo rk hub rather than yo ur actual lo catio n, so it may no t be
as precise o n a deskto p co mputer as it wo uld be, say, o n a pho ne with GPS. We'll review the vario us ways bro wsers
determine yo ur lo catio n sho rtly.

Fo r no w tho ugh, let's just go thro ugh the co de:

OBSERVE:
function init() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getLocation);
}
else {
console.log("Sorry, no Geolocation support!");
}
}
function getLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
alert("My position is: " + latitude + ", " + longitude);
}

First, in the init () functio n that runs when the page has lo aded, we che ck t o se e if t he navigat o r.ge o lo cat io n
o bject exists. We kno w navigat o r exists (all bro wsers have this o bject), but so me bro wsers might no t have the
ge o lo cat io n o bject.

If the ge o lo cat io n o bject exists, then we call its ge t Curre nt Po sit io n() metho d. The argum e nt we pass to
ge t Curre nt Po sit io n() is a functio n value, ge t Lo cat io n. If this is the first time yo u've seen a functio n passed as an
argument, yo u might be wo ndering, ho w o n earth do es that wo rk?

Well, remember that JavaScript functio ns are values that can be saved in pro perties (like we do when we say
windo w.o nlo ad = init ) o r sto red in variables (like we do when we set an o bject's pro perty name to a functio n value),
o r passed as arguments to o ther functio ns. In this case, we're passing the name o f a callback function, which we've
named ge t Lo cat io n, to ge t Curre nt Po sit io n, so that Geo lo catio n can call that functio n when the bro wser has
successfully retrieved yo ur lo catio n. Here's ho w it wo rks:
Just like o ur windo w.o nlo ad handler functio n, init () is called when the page is lo aded, the ge t Lo cat io n() functio n
is called when the bro wser has retrieved yo ur lo catio n. Remember to pass o nly the name o f the functio n; do not write:

OBSERVE:
navigator.geolocation.getCurrentPosition(getLocation());

Why? Because that wo uld call the functio n and try to pass the value the functio n returns to ge t Curre nt Po sit io n(),
which is no t what we want! We want to pass the functio n value to ge t Curre nt Po sit io n(), so ge t Curre nt Po sit io n()
calls the callback fo r us.
Once the bro wser has retrieved yo ur lo catio n successfully, it tells Geo lo catio n to call yo ur callback functio n,
ge t Lo cat io n(), and then passes yo ur lo catio n to this functio n as a po sit io n o bject.

The po sit io n o bject co ntains ano ther o bject, co o rds, which has two pro perties we're interested in: lat it ude and
lo ngit ude . These two pro perties are the co o rdinates o f yo ur lo catio n. Fo r no w, all we're do ing is saving tho se values
in variables, and using ale rt () to display them.

How the Browser Retrieves Your Location


So , what exactly is a lo catio n, and ho w do es the bro wser get it? If yo u've ever studied a glo be, o r navigated
o n a sailbo at, then yo u're familiar with the lines o n a map o r glo be that represent latitude and lo ngitude.
Lat it ude is the distance no rth o r so uth o f the equato r, and lo ngit ude is the distance east o r west o f
Greenwich, England (Greenwich seems to be a po pular place fro m which to measure), and to gether they
identify a specific lo catio n o n the Earth.

Latitude and lo ngitude are o ften specified in degrees, minutes, and seco nds (which yo u may be familiar with if
yo u're an astro no mer), o r as decimal values. In the Geo lo catio n API, we always deal with the decimal values,
so the values yo u get fro m the po sit io n o bject are the decimal versio ns o f latitude and lo ngitude.

The bro wser retrieves yo ur lo catio n using o ne o f fo ur metho ds:

GPS: this is available o n many smart pho nes and o ther GPS-enabled devices, and is the mo st
accurate way to get yo ur lo catio n. These devices use data fro m satellites to get yo ur lo catio n. In
these devices, the bro wser has access to the GPS info rmatio n (assuming yo u have GPS turned
o n, which is a real battery drainer, so do n't fo rget to turn it o ff when yo u do n't need it!).
Cell Pho ne To wer Triangulatio n: If yo u're o n a cell pho ne witho ut GPS (o r yo u have it turned o ff),
then tho se cell pho nes can still get a ro ugh idea o f yo ur lo catio n by seeing which cell pho ne to wers
can see yo ur pho ne. The mo re to wers yo u're near, the mo re accurate yo ur lo catio n will be.
WiFi: Like cell pho ne to wer triangulatio n, WiFi po sitio ning uses o ne o r mo re WiFi access po ints to
co mpute yo ur lo catio n. This is handy when yo u're indo o rs o n yo ur lapto p.
IP Address: If yo u're co nnected to a wired netwo rk, then this is the metho d yo u'll use (like, o n yo ur
deskto p co mputer). In this case, yo ur IP address is mapped to a lo catio n via a lo catio n database.
This has the advantage o f being able to wo rk anywhere, but it's o ften less accurate, because
so metimes the database maps yo ur IP address to yo ur ISP's lo catio n, o r yo ur neighbo rho o d,
rather than yo ur specific lo catio n.

Whatever metho d yo ur device o r co mputer uses to get yo ur lo catio n, o nce that lo catio n is fo und, the bro wser
can then get that back to yo ur JavaScript co de using the po sit io n o bject, and yo ur callback functio n.

Getting Your Location into a Web Page with Geolocation


Okay, let's do so mething fun with yo ur lo catio n. Mo dify rando m .ht m l as sho wn:
CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>My Random Thoughts</title>
<meta charset="utf-8">
<script src="random.js"></script>
<style>
body {
font-family: Arial, sans-serif;
}
form {
margin-bottom: 20px;
}
</style>
</head>
<body>
<form>
<label>Enter a random thought:</label>
<input type="text" id="aThought">
<input type="button" id="submit" value="Submit">
</form>

<h2> What I'm thinking today </h2>


<ul id="thoughts">
</ul>

<h2> Where I'm thinking today </h2>


<div id="map">
</div>
</body>
</html>

Save it. We added a fo rm to enter a tho ught, added a list, "tho ughts," that we'll use to display the tho ughts in the
page, and a "map" <div>, where we'll display the lo catio n o f the tho ughts. Do n't preview yet; first, mo dify rando m .js as
sho wn:
CODE TO TYPE:
window.onload = init;

function init() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getMyLocation);
}
else {
console.log("Sorry, no Geolocation support!");
}
}
function getMyLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
alert("My position is: " + latitude + ", " + longitude);
}

function Thought(id, text) {


this.id = id;
this.text = text;
}

function init() {
var submit = document.getElementById("submit");
submit.onclick = getThought;
}

function getThought() {
var aThought = document.getElementById("aThought").value;
if (aThought == null || aThought == "") {
alert("Please enter a thought with at least one word");
return;
}
var id = (new Date()).getTime();
var thought = new Thought(id, aThought);

// get the location of the thought


if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getLocation);
}
else {
console.log("Sorry, no Geolocation support!");
return;
}

addThoughtToPage(thought);
}

function addThoughtToPage(thought) {
var ul = document.getElementById("thoughts");
var li = document.createElement("li");
li.setAttribute("id", thought.id);

var spanText = document.createElement("span");


spanText.setAttribute("class", "thoughtText");
spanText.innerHTML = thought.text;

li.appendChild(spanText);
ul.appendChild(li);
}

function getLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
var mapDiv = document.getElementById("map");
mapDiv.innerHTML = "I'm thinking at " + latitude + ", " + longitude;
}
Save it, o pen rando m .ht m l again, and click . Enter a tho ught in the fo rm input text co ntro l, and click
Subm it . Yo u'll be pro mpted to co nfirm that yo u're o kay with sharing yo ur lo catio n. Appro ve the request to share, and
see what happens. Here's what yo u sho uld see if yo ur Geo lo catio n is wo rking well:

Let's check o ut the co de in a little mo re detail:


OBSERVE:
window.onload = init;

function Thought(id, text) {


this.id = id;
this.text = text;
}

function init() {
var submit = document.getElementById("submit");
submit.onclick = getThought;
}

function getThought() {
var aThought = document.getElementById("aThought").value;
if (aThought == null || aThought == "") {
alert("Please enter a thought with at least one word");
return;
}
var id = (new Date()).getTime();
var thought = new Thought(id, aThought);

// get the location of the thought


if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getLocation);
}
else {
console.log("Sorry, no Geolocation support!");
return;
}

addThoughtToPage(thought);
}

function addThoughtToPage(thought) {
var ul = document.getElementById("thoughts");
var li = document.createElement("li");
li.setAttribute("id", thought.id);

var spanText = document.createElement("span");


spanText.setAttribute("class", "thoughtText");
spanText.innerHTML = thought.text;

li.appendChild(spanText);
ul.appendChild(li);
}

function getLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
var mapDiv = document.getElementById("map");
mapDiv.innerHTML = "I'm thinking at " + latitude + ", " + longitude;
}

First, we added an o bject co nstructo r functio n to create T ho ught o bjects. A T ho ught o bject has an id and so me text
that the user types into the fo rm.

When the use r clicks t he Subm it but t o n in the fo rm, we call the ge t T ho ught () functio n and check to make sure
the user really typed so mething in the fo rm. If they did, we create a new T ho ught o bject, by first creating a unique id
(using the time in milliseco nds like we did in the lesso n o n Dates and Date Fo rmatting), and then using the co nstructo r
to create a new T ho ught o bject, passing in the id and the text o f the tho ught.

Next we want to get the user's lo catio n, so we can add the lo catio n po sitio n info rmatio n to the page. To do that, we use
the ge o lo cat io n o bject again, call the ge t Curre nt Po sit io n() metho d, passing in the ge t Lo cat io n() functio n, like
we did befo re.

Yo u can see that ge t Lo cat io n() has o ne parameter, the po sit io n o bject. ge t Lo cat io n() gets yo ur latitude and
lo ngitude fro m po sit io n, just like befo re, and then updates the page with this data.

Lo o king back up at the ge t T ho ught () functio n, after we call the ge t Curre nt Po sit io n() metho d o f ge o lo cat io n,
we call ano ther functio n, addT ho ught T o Page (), passing the tho ught that needs to be added to the page. The
addT ho ught T o Page () functio n adds the tho ught info rmatio n to the page by creating a new <li> element and adding
the data fro m the t ho ught o bject it's been passed: the id and the text o f the tho ught.

Handling Errors
So , what if so mething go es wro ng when the bro wser tries to get yo ur lo catio n? Or, what happens if yo u do n't allo w
yo ur po sitio n to be shared with the bro wser? If yo u haven't been able to see any lo catio n data, the call to
ge t Curre nt Po sit io n() is likely failing and no lo catio n is being retrieved. If yo u have been getting yo ur lo catio n
successfully, try shift-relo ading the page, and deny the request fro m the bro wser to use yo ur lo catio n. What happens?

It wo uld be nice fo r yo ur applicatio n to kno w a little bit mo re abo ut what went wro ng. We can pass a seco nd callback
functio n, an e rro r callback f unct io n, to ge t Curre nt Po sit io n() that will be called if ge t Curre nt Po sit io n() is
unable to retrieve a lo catio n fro m the bro wser. Let's see ho w that wo rks. Mo dify rando m .js as sho wn:
CODE TO TYPE:
window.onload = init;

function Thought(id, text) {


this.id = id;
this.text = text;
}

function init() {
var submit = document.getElementById("submit");
submit.onclick = getThought;
}

function getThought() {
var aThought = document.getElementById("aThought").value;
if (aThought == null || aThought == "") {
alert("Please enter a thought with at least one word");
return;
}
var id = (new Date()).getTime();
var thought = new Thought(id, aThought);

// get the location of the thought


if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getLocation, locationError);
}
else {
console.log("Sorry, no Geolocation support!");
return;
}

addThoughtToPage(thought);
}

function addThoughtToPage(thought) {
var ul = document.getElementById("thoughts");
var li = document.createElement("li");
li.setAttribute("id", thought.id);

var spanText = document.createElement("span");


spanText.setAttribute("class", "thoughtText");
spanText.innerHTML = thought.text;

li.appendChild(spanText);
ul.appendChild(li);
}

function getLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
var mapDiv = document.getElementById("map");
mapDiv.innerHTML = "I'm thinking at " + latitude + ", " + longitude;
}

function locationError(error) {
var errorTypes = {
0: "Unknown error",
1: "Permission denied by user",
2: "Position not available",
3: "Request timed out"
};
var errorMessage = errorTypes[error.code];
if (error.code == 0 || error.code == 2) {
errorMessage += " " + error.message;
}
console.log(errorMessage);
alert(errorMessage);
}

Save it, o pen rando m .ht m l again, and click . If yo u were no t seeing lo catio n info rmatio n befo re, yo u
sho uld see an alert no w with mo re info rmatio n abo ut what went wro ng. If yo u were seeing lo catio n info rmatio n befo re,
go ahead and deny the bro wser's request to use yo ur lo catio n, and again, yo u sho uld see an alert with the erro r
message, like this:

So me bro wsers may pro mpt yo u to Always o r Never allo w the lo catio n info rmatio n, so if yo u deny the
request, yo u may need to go to the appro priate setting in the bro wser to set it back to allo w o r pro mpt fo r
permissio n. In so me bro wsers, yo u can just relo ad the page, o r clo se and restart rando m .ht m l. Others
may require that yo u change the o ptio n back in the To o ls area.

To reset the permissio n in Firefo x, select T o o ls | Page Inf o | Pe rm issio ns and change the
Note permissio n fo r Share Lo cat io n.

To reset the permissio n in Chro me, select Chro m e | Pre f e re nce s | Se t t ings, click o n Advance d
Pe rm issio ns, then Privacy | Co nt e nt Se t t ings | Lo cat io n and change the permissio n to Ask m e
whe n a sit e t rie s t o t rack m y physical lo cat io n. Then click o n Manage Expe ct at io ns to see which
sites are listed to Allo w o r Blo ck auto matically, and remo ve yo ur o reillystudent.co m site if it's listed there,
so the bro wser will pro mpt yo u to Allo w o r Deny the next time yo u try.

Let's take a lo o k at the changes:

OBSERVE:
...
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getLocation, locationError);
}
.
.
.
function locationError(error) {
var errorTypes = {
0: "Unknown error",
1: "Permission denied by user",
2: "Position not available",
3: "Request timed out"
};
var errorMessage = errorTypes[error.code];
if (error.code == 0 || error.code == 2) {
errorMessage += " " + error.message;
}
console.log(errorMessage);
alert(errorMessage);
}

Here we added a seco nd argument to the call to ge t Curre nt Po sit io n(), passing in a seco nd callback functio n,
lo cat io nErro r(). The error callback handler is passed info rmatio n fro m the bro wser when it's called: an e rro r o bject.
The e rro r o bject has a pro perty, co de , that co ntains a number representing the type o f erro r. In lo cat io nErro r(), we
map that co de to an erro r message using an e rro rT ype s o bject literal. If the erro r co de is 0 o r 2, so metimes we can
retrieve mo re info rmatio n abo ut what went wro ng in the e rro r o bject's m e ssage pro perty. We then display the full
erro r message bo th to the co nso le and in an alert().

If yo u cause an erro r by denying the bro wser's request to use yo ur lo catio n, yo u generate an erro r co de o f type 1, and
yo u see the message "Permissio n denied by user." If yo u see o ne o f the o ther erro r messages, the bro wser is unable
to access yo ur po sitio n fo r so me o ther reaso n. It co uld be that yo ur signal isn't stro ng eno ugh o n yo ur cell pho ne (o r
yo u're o ut o f range o f a cell to wer), o r yo ur GPS is turned o ff, o r yo ur ISP isn't mapped to a lo catio n in the lo catio n
database, fo r instance.

If yo u see an erro r message no w, what do es it say? If yo u're appro ving the request to share yo ur lo catio n, but still
getting an erro r, try yo ur pro gram o n ano ther co mputer o r device if yo u have o ne available. Ideally, yo ur pro gram
wo rks successfully at this po int, and yo u wo n't need the erro r handling co de, but it's still go o d to have it in there.

No te that even if lo cat io nErro r() is called, we still add the tho ught to the page, using addT ho ught T o Page (), so the
basic app still wo rks; it just do esn't sho w yo u a lo catio n fo r the tho ught.

Adding a Google Map


Wo uldn't it be a lo t mo re fun if we co uld see the lo catio n o f o ur tho ughts o n a map? Let's make that happen. We'll use
the Go o gle Maps API. We'll need to link to the Go o gle Maps JavaScript library and add a <div> fo r the map to yo ur
HTML. Mo dify rando m .ht m l as sho wn:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<title>My Random Thoughts</title>
<meta charset="utf-8">
<script src="http://maps.google.com/maps/api/js?sensor=true"></script>
<script src="random.js"></script>
<style>
body {
font-family: Arial, sans-serif;
}
form {
margin-bottom: 20px;
}
div#map {
width: 400px;
height: 400px;
}
</style>
</head>
<body>
<form>
<label>Enter a random thought:</label>
<input type="text" id="aThought">
<input type="button" id="submit" value="Submit">
</form>

<h2> What I'm thinking today </h2>


<ul id="thoughts">
</ul>

<h2> Where I'm thinking today </h2>


<div id="map">
</div>
</body>
</html>

Save it. We added the link to the Go o gle Maps API JavaScript in the line:

OBSERVE:
<script src="http://maps.google.com/maps/api/js?sensor=true"></script>
This includes all the JavaScript fo r the Go o gle Maps API, so when yo u use the Go o gle functio ns to create a map, the
bro wser will be able to find the JavaScript. se nso r=t rue o n the end o f the URL is required. It tells the maps API that
we're using the bro wser's Geo lo catio n capabilities to get o ur lo catio n.

Next, we'll use the JavaScript functio ns in the Go o gle Maps API to add a map to o ur page. Mo dify rando m .js as
sho wn:
CODE TO TYPE:
window.onload = init;

var map = null;

function Thought(id, text) {


this.id = id;
this.text = text;
}

function init() {
var submit = document.getElementById("submit");
submit.onclick = getThought;
}

function getThought() {
var aThought = document.getElementById("aThought").value;
if (aThought == null || aThought == "") {
alert("Please enter a thought with at least one word");
return;
}
var id = (new Date()).getTime();
var thought = new Thought(id, aThought);

// get our location


if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getLocation, locationError);
}
else {
console.log("Sorry, no Geolocation support!");
return;
}

addThoughtToPage(thought);
}

function addThoughtToPage(thought) {
var ul = document.getElementById("thoughts");
var li = document.createElement("li");
li.setAttribute("id", thought.id);

var spanText = document.createElement("span");


spanText.setAttribute("class", "thoughtText");
spanText.innerHTML = thought.text;

li.appendChild(spanText);
ul.appendChild(li);
}

function getLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
var mapDiv = document.getElementById("map");
mapDiv.innerHTML = "I'm thinking at " + latitude + ", " + longitude;
if (!map) {
showMap(latitude, longitude);
}
}

function showMap(lat, long) {


var googleLatLong = new google.maps.LatLng(lat, long);
var mapOptions = {
zoom: 12,
center: googleLatLong,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var mapDiv = document.getElementById("map");
map = new google.maps.Map(mapDiv, mapOptions);
map.panTo(googleLatLong);
}

function locationError(error) {
var errorTypes = {
0: "Unknown error",
1: "Permission denied by user",
2: "Position not available",
3: "Request timed out"
};
var errorMessage = errorTypes[error.code];
if (error.code == 0 || error.code == 2) {
errorMessage += " " + error.message;
}
console.log(errorMessage);
alert(errorMessage);
}

Save it, o pen rando m .ht m l again, and click . Enter a tho ught and click Subm it . A map appears!
Let's lo o k at ho w we added the map in a bit mo re detail:
OBSERVE:

var map = null;


.
.
.
function getLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
if (!map) {
showMap(latitude, longitude);
}
}

function showMap(lat, long) {


var googleLatLong = new google.maps.LatLng(lat, long);
var mapOptions = {
zoom: 12,
center: googleLatLong,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var mapDiv = document.getElementById("map");
map = new google.maps.Map(mapDiv, mapOptions);
map.panTo(googleLatLong);
}

First, we create a glo bal m ap variable to ho ld the map. We want o nly o ne m ap o bject, so we're just go ing to create it
o nce, sto re it in this glo bal variable, and check each time we add a new tho ught to make sure we're using that same
map.

When the bro wser calls ge t Lo cat io n() with yo ur po sitio n, we check to see if the m ap o bject has been created yet. If it
hasn't, we call sho wMap() to create the map. We pass the lat it ude and lo ngit ude o bjects we retrieved fro m the
po sit io n o bject to sho wMap() and then use tho se to create the map.

The sho wMap() functio n uses the Go o gle Maps API to create a go o gle .m aps.Lat Lng o bject fro m the latitude and
lo ngitude we passed in to the functio n. We then use that go o gle .m aps.Lat Lng o bject to create a set o f o ptio ns we'll
use to create the actual map; these o ptio ns tell the map things like the zo o m level (ho w zo o med in o r o ut yo u are o n
the map; the higher the number, the clo ser the zo o m), where to center the map, and the type o f map yo u want
(ROADMAP, SATELLITE, TERRAIN, o r HYBRID).

We then get the "map" <div> in o ur HTML and use that, alo ng with the mapOptio ns, to create a go o gle .m aps.Map
o bject, which we sto re in the m ap glo bal variable. Finally, we center the map o n the latitude and lo ngitude, by calling
the m ap's panT o () metho d.

No te that this co de is o nly called the first time yo u add a tho ught because we want to create the map just o nce. The
next time yo u add a tho ught, this co de is skipped.

No w, if yo u're sitting at yo ur desk and no t mo ving aro und, all yo ur tho ughts will have the same lo catio n, so yo ur map
wo n't mo ve. But if yo u are running this app o n yo ur smart device and mo ving aro und, then each tho ught will have a
different lo catio n and yo u'll see the map pan to a different lo catio n each time yo u add a new tho ught fro m a different
po sitio n. If yo u have a device and can go mo bile, give it a try! Just enter the URL o f yo ur applicatio n at
o reillystudent.co m into the mo bile bro wser. It will be so mething like this:

ht t p://yourusername.o re illyst ude nt .co m /javascript 2/rando m .ht m l

Adding a Marker to the Map


This map wo uld be a who le lo t mo re useful if we co uld see the precise lo catio n o f o ur tho ughts, right? So befo re we
end this lesso n, let's add a marker fo r each tho ught to the map. Mo dify rando m .js as sho wn:
CODE TO TYPE:

.
.
.
function getLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
if (!map) {
showMap(latitude, longitude);
}
addMarker(latitude, longitude);
}
.
.
.

function addMarker(lat, long) {


var googleLatLong = new google.maps.LatLng(lat, long);
var markerOptions = {
position: googleLatLong,
map: map,
title: "Where I'm thinking today"
}
var marker = new google.maps.Marker(markerOptions);
}
.
.
.

Save it, o pen rando m .ht m l again, and click . No w when yo u add a new tho ught and it appears o n the
map, yo u see a nice red marker sho wing yo u exactly where yo u were when yo u added the tho ught. Again, if yo u add
multiple tho ughts at yo ur desk, yo u'll see o nly o ne marker, because all the markers will sit right o n to p o f each o ther
because yo u aren't mo ving aro und.
To add a marker, we call a new functio n, addMarke r(), fro m ge t Lo cat io n(), passing in the latitude and lo ngitude:
OBSERVE:

function addMarker(lat, long) {


var googleLatLong = new google.maps.LatLng(lat, long);
var markerOptions = {
position: googleLatLong,
map: map,
title: "Where I'm thinking today"
}
var marker = new google.maps.Marker(markerOptions);
}

addMarke r() creates go o gle Lat Lo ng and go o gle .m aps.Marke r o bjects. In the m arke rOpt io ns, we give the
marker o bject the po sitio n where it sho uld be lo cated, the map to which it sho uld be added, and a title co ntaining so me
text.

Ano ther actio n-packed lesso n! In this lesso n, yo u learned the basics o f the Geo lo catio n and Go o gle Maps APIs, and used
these APIs to create a nice little applicatio n that lets yo u add tho ughts to a web page, and a map. If yo u're able to test the
applicatio n o n a mo bile device, we highly reco mmend it, so yo u can see yo ur tho ughts appear o n the map in different lo catio ns.

Practice yo ur Geo lo catio n and mapping skills a bit befo re mo ving o n to the final lesso n. Hang in there; yo u're almo st do ne!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Dates and Date Formatting

Final Project
In yo ur final pro ject, yo u're go ing to add mo re features to the To -Do List applicatio n based o n what yo u've learned in
the previo us few lesso ns.

Start with the co de fro m yo ur pro ject fro m the Strings lesso n. Yo ur co mpleted To -Do List applicatio n sho uld suppo rt
the fo llo wing features:

Use Lo cal Sto rage (no t Ajax) fo r sto ring to -do items.
Suppo rt deleting to -do items.
Suppo rt marking to -do items as do ne.
Suppo rt a basic text search o ver the "task" and "who " fields (so I can search by perso n o r wo rd in task).
Suppo rt fo r dates, sho wing ho w many days until a task is due, o r ho w many days o verdue a task is.
Use Exceptio n handling fo r po tential erro rs in Date pro cessing.
Suppo rt Geo lo catio n, so a task has a lo catio n asso ciated with it.
Use Mo dernizr to separate Lo cal Sto rage and Geo lo catio n co de fro m the main co de. Yo ur applicatio n
sho uld still functio n pro perly (altho ugh, o bvio usly, with less capability) even if Lo cal Sto rage and
Geo lo catio n are no t suppo rted.

Here's ho w yo ur final To Do List applicatio n might lo o k:


Belo w, we've included co de yo u can begin with if yo u do n't want to use yo ur existing co de. Yo u'll add any new features
listed abo ve to the co de belo w (which is fro m the Strings lab).

Do cument yo ur co de by adding co mments explaining what yo u're do ing and why. Submit all yo ur files o nce yo u have
the applicatio n wo rking, including:

Yo ur HTML file.
Yo ur CSS file.
Yo ur JavaScript files (yo u'll have five files).

See the pro ject instructio ns fo r initial co de to help yo u get started. Go o d luck! As always, be sure to email yo ur
instructo r at learn@o reillyscho o l.co m if yo u need additio nal guidance.
Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Das könnte Ihnen auch gefallen