HTML5

for Masterminds

How to take advantage of HTML5

to create responsive websites

and revolutionary applications

J.D Gauchat

www.jdgauchat.com

Cover Illustration by Patrice
Garden

www.smartcreativz.com

HTML5 for Masterminds

Copyright ©
2017 by John D Gauchat

All Rights
Reserved

No part of
this work may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording, or by any
information storage or retrieval system without the prior written permission of
the copyright’s owner.

Companies,
services, or products’ names used in this book are for identification purposes
only. All trademarks and registered trademarks are the property of their
respective owners.

For
information on translations, please visit www.formasterminds.com.

The
information in this book is distributed without warranty. Although every
precaution has been taken in the preparation of this work, neither the author
nor the publisher shall have any liability to any person or entity concerning
any loss or damage caused or alleged to be caused directly or indirectly by the
information contained in this work.

The source
code for this book is available at www.formasterminds.com

Registration Number: 1086517

1st Edition 2011

1st Edition 2011 Revised Edition

2nd Edition 2012

2nd Edition 2014 Revised Edition

3rd Edition 2017

3rd Edition 2017 Revised Edition

iOS Apps for Masterminds

How to take advantage of Swift to create

insanely great apps for iPhones and iPads

www.formasterminds.com

Table of Contents

Chapter
1 - Web Development

1.1 Websites

Files, Domains and URLs, Hyperlinks, Absolute and Relative URLs

1.2 Languages

HTML, CSS, JavaScript, Server-Side Languages

1.3 Tools

Editors, Domain Registration, Web Hosting, FTP Software, MAMP

Chapter 2 - HTML

2.1 Structure

Document Type, Structural Elements, Global Attributes

2.2 Content

Text, Links, Images, Lists, Tables, Global Attributes

2.3 Forms

Definition, Elements, Submission, Global Attributes

Chapter 3 - CSS

3.1 Styles

Applying Styles, Cascading Style Sheets

3.2 References

Keywords, Id Attribute, Class Attribute, Other Attributes, Pseudo-Classes

3.3 Properties

Text, Colors, Sizes, Background, Borders, Shadows, Gradients, Filters, Transformations, Transitions, Animations

Chapter 4 - Web Design

4.1 Boxes

Display

4.2 Traditional Box
Model

Floating Content, Floating Boxes, Absolute Positioning, Columns, Real-life Application

4.3 Flexible Box
Model

Flex Container, Flexible Elements, Organizing Flexible
Elements, Real-life
Application

Chapter 5 - Responsive Web Design

5.1 Mobile Web

Media Queries, Breakpoints, Viewport, Flexibility, Box-sizing, Fixed and Flexible, Text, Images, Real-life Application

Chapter 6 - JavaScript

6.1 Introduction to
JavaScript

Implementing
JavaScript, Variables, Strings, Booleans, Arrays, Conditionals and Loops, Control Transfer
Instructions

6.2 Functions

Declaring Functions, Scope, Anonymous Functions, Standard Functions

6.3 Objects

Declaring Objects, Methods, The this Keyword, Constructors, The new Operator, Inheritance

6.4 Standard Objects

String Objects, Array Objects, Date Objects, Math Object, Window Object, Document Object, Element Objects, Creating Element Objects

6.5 Events

The
addEventListener() Method, Event Objects

6.6 Debugging

Console, Console Object, Error Event, Exceptions

6.7 APIs

Native Libraries, External Libraries

Chapter 7 - Forms API

7.1 Processing Forms

7.2 Validation

Custom Errors, The invalid Event, The ValidityState Object

7.3 Pseudo-Classes

Valid and Invalid, Optional and Required, In-range and Out-of-range

Chapter 8 - Media

8.1 Video

Video Formats

8.2 Audio

8.3 Media API

Video Player

8.4 Closed
Captioning

8.5 TextTrack API

Reading Tracks, Reading Cues, Adding Tracks

Chapter 9 - Stream API

9.1 Capturing Media

The MediaStreamTrack
object

Chapter 10 - Fullscreen API

10.1 Modern
Applications

Full Screen, Full Screen Styles

Chapter 11 - Canvas API

11.1 Graphics

The Canvas, The Context

11.2 Drawing

Rectangles, Colors, Gradients, Paths, Lines, Text, Shadows, Transformations, Status, The
GlobalCompositeOperation Property

11.3 Images

Patterns, Image Data, Cross-Origin, Extracting Data

11.4 Animations

Simple Animations, Professional Animations

11.5 Video

Real-Life
Application

Chapter 12 - WebGL

12.1 3D Canvas

12.2 Three.js

Renderer, Scene, Camera, Meshes, Geometric Primitives, Materials, Implementation, Transformations, Lights, Textures, UV Mapping, Canvas Textures, Video Textures, 3D Models, 3D Animations

Chapter 13 - Pointer Lock API

13.1 Custom Pointer

Mouse Capture

Chapter 14 - Web Storage API

14.1 Storage Systems

14.2 Session Storage

Storing Data, Reading Data, Deleting Data

14.3 Local Storage

Storage Event

Chapter 15 - IndexedDB API

15.1 Data Structure

Database, Objects and Object Stores, Indexes, Transactions

15.2 Implementation

Opening the Database, Defining Indexes, Adding Objects, Retrieving Objects

15.3 Listing Data

Cursors, Order

15.4 Deleting Data

15.5 Searching Data

Chapter 16 - File API

16.1 Files

Loading Files, Reading Files, Properties, Blobs, Events

Chapter 17 - Drag and Drop API

17.1 Drag and Drop

Validation, Thumbnail, Files

Chapter 18 - Geolocation API

18.1 Locations

Getting the Location, Watching the Location, Google Maps

Chapter 19 - History API

19.1 History

Navigation, URLs, The state Property, Real-Life Application

Chapter 20 - Page Visibility API

20.1 Visibility

State, Full Detection System

Chapter 21 - Ajax Level 2

21.1 The
XMLHttpRequest Object

Properties, Events, Sending Data, Uploading Files, Real-Life Application

Chapter 22 - Web Messaging API

22.1 Cross-Document
Messaging

Posting a Message, Filters and Cross-Origin

Chapter 23 - WebSocket API

23.1 Web Sockets

WebSocket Server, Connecting to the Server

Chapter 24 - WebRTC API

24.1 Web Paradigms

ICE Servers, Peer Connection, ICE Candidate, Offer and Answer, Session Description, Media Streams

Events

24.2 Configuration

Configuring the
Signaling Server, Configuring
the ICE Servers

24.3 Implementing
WebRTC

24.4 Data Channels

Chapter 25 - Web Audio API

25.1 Audio Structure

Audio Context, Audio Sources, Connecting Nodes

25.2
Audio Applications

Loops and Times, Audio Nodes, AudioParam, GainNode, DelayNode, BiquadFilterNode, DynamicsCompressorNode, ConvolverNode, PannerNode and 3D Sound, AnalyserNode

Chapter 26 - Web Workers API

26.1 Background
Processing

Workers, Sending and Receiving
Messages, Errors, Terminating Workers, Synchronous APIs, Importing JavaScript Code, Shared Workers

Introduction

The Internet
is a fundamental part of everyone's life these days, and the Web has become the
centerpiece of every technology involved. From news and entertainment to mobile
applications and video games, everything revolves around the Web. We need a
website to open an account for almost every service we use, to connect our
mobile applications and devices together, to work, or to share the score of our
favorite video game. The Web is the hub of our daily activities, and HTML5 is
what makes it all possible.

It all
started a long time ago with a simple version of a computer language called HTML.
The language, along with identifiers and communication protocols, was developed
to provide the basis for the creation of the Web. The initial purpose of HTML
was to structure text to share documents between remote computers. Over time,
the introduction of better systems and multi-color displays forced the language
to evolve to work with other types of media besides text, such as pictures and
customized fonts. This expansion made it more difficult for developers to
create and maintain large websites using HTML alone. The problem was solved by
the incorporation to browsers of a new language called CSS that helps
developers to prepare the document that will be displayed on the screen.

The association
between HTML and CSS simplified the work for developers, but the ability of
these languages to respond to the user or perform tasks such as playing video or
audio was still very limited. At first, independent companies offered their own
alternatives. Programming languages and plugins like Java and Flash became very
popular, but they were incapable of providing a definitive solution. The tools
produced by these technologies were still disconnected from the content and
only sharing with the document a space on the screen. This weak association
paved the way for the evolution of a language that was already embedded in
browsers and therefore tightly integrated with HTML. This language, called JavaScript,
allowed developers to access and modify the document's content dynamically, request
additional data from the server, process information, and show the results on
the screen, turning websites into small applications. Originally, browsers' performance
was not good enough to execute some of these tasks, but with the incorporation
of better JavaScript engines, developers found ways to take advantage of the
language's capabilities and create useful applications, confirming JavaScript
as the best option to complement HTML and CSS.

With the
combination of HTML, CSS, and JavaScript, the technologies required to build
the Web we enjoy today were ready, but there was still a problem to be solved.
These languages were independently developed and therefore they were following
their own path. The solution was found in the definition of a new specification
called HTML5. HTML5 is the glue that holds everything together,
proposing standards to every aspect of the Web as well as a clear purpose for
each technology involved. Now, HTML defines the document's structure, CSS
prepares that structure and its content to be shown on the screen, and JavaScript
brings the power and functionality necessary to build full web applications.

The
integration of HTML, CSS, and JavaScript under the HTML5 umbrella changed the
Web forever. Innovative companies and new markets were created overnight,
generating a golden era for web development that is still underway. The
opportunities are endless. The Web is here to stay, and you can be part of it.

IMPORTANT:
At
the time of writing, most browsers support HTML5 features. We recommend you to execute
the examples in this book with the latest versions of Google Chrome and Mozilla
Firefox (www.google.com/chrome/ and www.mozilla.org). You can also check the state of the current implementations at www.caniuse.com. To find
examples, resources, links and videos, visit our website at www.formasterminds.com.

Chapter 1 - Web Development

1.1 Websites

Websites are files that users download with their
browsers from remote computers. When users want to access a website, they tell
the browser where it is located, and the program downloads the website's files,
processes their content, and show it on the screen.

Because websites are publicly
accessible and the Internet is a global network, these files must be available
all the time. For this reason, websites are not stored in personal computers
but in specialized computers designed to deliver their files. The computer that
stores the website's files and data is called Server and the computer
that accesses the information is called Client, as shown in Figure 1-1.

Figure 1-1: Clients and Servers

Servers are very similar to personal
computers, but they are connected to the network all the time and run software
that allows them to respond to users' requests. The most widely adopted programs
for servers are Apache, for Linux systems, and IIS (Internet Information Server), created by Microsoft for Windows
systems. Among other processes, these programs are responsible for establishing
the connection between client and server, controlling user access, managing
files, and dispatching the documents to the clients.

Files

Websites are composed of multiple
documents that the browser downloads upon user request. The documents that make
up a website are called pages, and the process of opening new pages is
called navigation (the user navigates through the pages of a website). To
develop a website, we have to create a file for each page we want the website
to contain. Along with these files, we also have to include the files with the
images and any other media we want to show inside the pages (images and other media
are stored in separate files). Figure 1-2 illustrates what the files and
folders of a simple website may look like once they are uploaded to the server.

Figure 1-2: Website's files

The example in Figure 1-2 includes
two folders called images and resources and three files called contact.html,
index.html, and news.html. The folders were created to store the
images we want to show inside the pages and other resources, such as the files
containing the CSS and JavaScript codes, as we will see later. On the other
hand, the files in our example represent three pages we want our website to
contain. The index.html file contains the code and information corresponding to
the initial page (the page the user sees when enters our website for the first
time), the contact.html file contains the code necessary to present a form that
the user can fill to send us a message, and the news.html file contains the
code necessary to display the news we want to share with our users. When a user
accesses our website for the first time, the browser downloads the index.html
file and shows its content on the window. If the user performs an action to see
the news provided by our website, the browser downloads the news.html file and
replaces the content of the index.html with the content of this new file. Every
time the user wants to access a new page, the browser has to download the
corresponding file from the server, process it, and show its content on the
screen.

The files of a website are the same
type of files we may find on our personal computer. They have a custom name and
an extension that reflects the language that was used to program their content
(in our example, the files have the .html extension because they were
programmed in HTML). Although we can assign any name we want to these files,
the file for the initial page have some requirements. Servers like Apache, for
example, designate files by default in case the user does not specify any. The
most commonly used name is index. If a user accesses a server without
specifying a file, the server looks for a file with the name index and
sends that file back to the client. For this reason, the index file is the entry
point to our website and we always have to include it.

IMPORTANT: Servers are flexible regarding file names, but there
are a few rules you should follow to make sure your files are accessible. Avoid
using spaces. If you need to separate words, use the underscore (_) instead. Also,
some characters perform specific functions on the Web, so it is better to avoid
special characters such as ?, %, #, /, and use only lowercase letters and
numbers.

The
Basics: Although
index is the most common name, it is not the only one we can assign to
the file by default. Some servers designate different names like home or default.
They also include different extensions. For example, if instead of programming
our documents directly in the HTML language we use a scripting language on the
server like PHP to generate the document dynamically, we have to name our index
file index.php and the server considers this one to be the file by
default. The server has a list of possible files to look for and keeps looking
until it finds one that matches any on the list. For instance, Apache first looks
for a file with the name index and the extension .html, and then, if it cannot
find it, looks for a file with the name index and the extension .php. We will
learn more about HTML and PHP in this and later chapters.

Domains and URLs

Servers are identified by a value called IP
(Internet Protocol). This IP is unique to each computer, and therefore it works
like an address to find the location of a computer inside a network. When a
browser has to access a server to retrieve the document requested by the user,
it searches for the server through its IP address and then asks the server to
return the document.

IP addresses are composed of integer
numbers between 0 and 255 separated by a period, or numbers and letters
separated by colons, depending on the version (IPv4 or IPv6). For example, the
address 216.58.198.100 corresponds to a server where the Google's website is
stored. If we write this IP in the browser's navigation bar, the Google's initial
page is downloaded and shown on the screen.

In theory, we could access any server
using its IP address, but these values are cryptic and difficult to remember. For
this reason, a system that assigns distinctive names to each server was
implemented. These are simple names, called domains, that a human can
understand and remember, such as google or yahoo, with an
extension that determines the purpose of the website, such as .com (commercial)
or .org (organization). When the user asks the browser to access the website
with the domain www.google.com, the browser accesses a server called DNS
that contains a list of domains and their respective IP addresses. The server
finds the IP 216.58.198.100 associated to the www.google.com domain, returns it
to the browser, and then the browser uses this IP address to access the Google's
website. Because websites' IP addresses are always associated to domains, we do
not have to remember the address of the server to access the website's documents,
all we have to do is to remember the domain and the browser takes care of
finding the server and downloading the files for us.

Websites are composed of several
files. To indicate which file we want to access, its name must be attached to the
domain. This construction is called URL and includes three parts, as described
in Figure 1-3.

Figure 1-3: URLs

The first part of a URL is a string
that represents the communication protocol we want to use to access the
resource (the protocol created for the Web is called HTTP), the next
component is the website's domain, and the last component is the name of the
resource we want to download (this could be a file, as in our example, or a
path that includes the name of the directory where the file is located (e.g.,
http://www.example.com/images/mylogo.jpg). The URL in our example asks the
browser to use the protocol HTTP to access the file contact.html, located at
the server identified with the domain www.example.com.

URLs are used to locate every
document on the website and therefore they are required for navigation. If no
file is specified, the server returns the file by default, but from that point
on, every time the user performs an action to open a different page, the
browser must include in the URL the name of the file that corresponds to the requested
page.

IMPORTANT: Once you have a domain, you can create
subdomains. Subdomains are direct links to folders, and therefore they allow
the construction of multiple websites in the same account. Subdomains are
constructed with the name of the folder plus the domain connected with dot
notation. For example, if you have the domain www.example.com and then create a
subdomain for one of its folders called resources, you will be able to
access the folder directly from the browser by typing the URL http://resources.example.com.

The
Basics: There
are different protocols available that computers use to communicate with each
other and transfer resources and data. The HTTP protocol (HyperText
Transfer Protocol) is the communication protocol used to access web documents. We
always have to include the HTTP prefix in the URL when the resource we are
trying to access belongs to a website, but in practice, this is not necessary
because browsers do it automatically for us. There is another version of the
protocol called HTTPS. The S indicates that the connection is encrypted
by TLS or SSL encryption protocols. Encryption is not necessary for small websites,
but it is recommended for large websites that handle sensitive information.

Hyperlinks

In theory, we can access every document on
the website by writing its URL in the browser's navigation bar. For example, if
we want to access the initial page of the For Masterminds website, we can
insert the URL http://www.formasterminds.com/index.php (since index.php is the
file by default, the file's name may be omitted), or we can insert the URL http://www.formasterminds.com/contact.php
to open the contact page to send a message to the website's owner. Although using
this method we can access every file on the website, it is not practical. First,
users do not know the names developers have chosen for the files, and therefore
they are limited to the names they can guess or only the initial page returned
by default. And second, websites may be composed of dozens or even thousands of
pages (some websites have millions) and most documents would be impossible to
find.

The solution was found in hyperlinks.
Hyperlinks, or links for short, are references to documents inside the
pages of a website. By incorporating these links, a page may contain references
to other pages inside. If the user clicks with the mouse on a link, the browser
follows the reference and the document indicated by the reference's URL is
opened. Because of these connections between pages, users can navigate the
website and access all of its documents by clicking on their links.

The
Basics: Links are the
feature that turns a group of files into a website. To create a website, you
have to program the documents for each page and include links on each one of
them to set a path the user can follow to reach every page available. We will
learn how to incorporate links in our documents in Chapter 2.

Absolute and Relative URLs

Hyperlinks are processed by the browser
before they are used to access a document. For this reason, hyperlinks may be
defined with Absolute or Relative URLs. Absolute URLs are URLs that include all
the information necessary to access the resource (see Figure 1-3), while Relative URLs are URLs
that only declare the part of the path the browser has to add to the current
URL to access the resource. For example, if we have a hyperlink inside a
document that is referencing an image inside the images folder, we can create
the link with the URL http://www.example.com/images/myimage.png, but we also
have the option to declare it as "images/myimage.png" and the browser
will take care of adding this path to the current URL to get the complete URL
and download the image.

Relative URLs not only determine a
path down but also up the hierarchy. For example, if we have a document inside
the resources folder of our example in Figure 1-2 and we want to access a
document in the root folder, we can create a Relative URL using the characters
../ at the beginning of the path. If the document we want to access is
news.html, the Relative URL will be ../news.html. The double dots .. tell the
browser that the document we want to access is inside the parent folder of the
current folder (resources, in our example).

1.2 Languages

As we
mentioned in the introduction, HTML5 provides three features, structure, style,
and functionality, by integrating three independent programming languages,
HTML, CSS, and JavaScript. These languages provide sets of instructions that browsers
can interpret to process and show documents. To create our documents, we have
to learn all the instructions included in these languages and how to organize
them.

HTML

HTML (HyperText Markup Language) is a language composed of a
set of tags defined by a keyword surrounded by angle brackets. The angle brackets delimit
the tag, and the keyword defines the type of content the tag represents. For
example, the <html> tag indicates that its
content is HTML code. Some of these tags are declared individually (e.g., <br>) and others in pairs, with an opening and a closing
tag, as in <html></html> (notice that the closing tag
is identified with a backslash). Opening and single tags may also include attributes to provide
additional information about their content (e.g., <html lang="en">). Single tags and the
combination of
opening and closing tags are called elements. Elements composed of a
single tag are used to modify the content around them or to load additional
content, while elements that include an opening and a closing tag are used to delimit
content, as
illustrated in Figure 1-4.

Figure 1-4: HTML element

Multiple elements are combined to
define an HTML document. They are listed in sequence from top to bottom and may
contain other elements inside. For example, the <html> element shown in Figure 1-4 declares that
its content must be interpreted as HTML code. Therefore, the rest of the elements
that describe the document's content are declared between the <html> and </html> tags, and in turn, they may
also include other elements inside. The following example shows a simple HTML
document with all the elements necessary to describe the document's basic
structure and display the message HELLO WORLD! on the screen.

Listing 1-1: Creating an HTML document

<!DOCTYPE html>

<html lang="en">

<head>

<title>My First HTML
Document</title>

</head>

<body>

<p>HELLO WORLD!</p>

</body>

</html>

In Listing 1-1, we have a simple code but an
already complex structure. In the first line, there is a single tag declaring
the type of the document (<!DOCTYPE
html>)
followed by the opening tag <html lang="en">. Between the <html> and </html> tags there are other elements
representing the head and body of the document (<head> and <body>), which in turn enclose more elements
with their own content (<title> and <p>), demonstrating how an HTML
document is built. Elements are listed one after another and in between other elements,
constructing a tree-like structure, with the <html> element as its root.

The
Basics: In general,
every element can be nested, become a container, or be contained by other
elements. Special structural elements like <html>, <head> and <body> have a
specific place in an HTML document, but the rest are flexible, as we will see
in Chapter 2.

As we mentioned before, individuals
and opening tags can also include attributes. For instance, the opening <html> tag declared in Listing 1-1 is composed
not only of the keyword html and angle brackets but also the string lang="en". This is an attribute with a value. The
name of the attribute is lang, and the value en is assigned to the attribute using the = character. Attributes provide additional information about the
element and its content. In this case, the attribute lang declares the human language of the document (en for English).

The
Basics: Attributes
are always declared inside the opening tag (or individual tags) and may have a
name/value structure, like the lang attribute
of the <html> tag, or represent a
single value, like the html attribute of the <!DOCTYPE> tag. We
will study HTML elements and their attributes in Chapter 2.

CSS

CSS (Cascading Style Sheets) is the
language used to define styles for HTML elements, such as the size, the color,
backgrounds, borders, etc. Although by default each browser grants styles to most
elements, those styles are usually far from what we want for our websites. To
declare custom styles, CSS uses properties and values. This construction is
called declaration and the syntax includes a colon after the name of the
property and a semicolon to close the line.

Figure 1-5: CSS property

In the example in Figure 1-5, the
value #FF0000 is assigned to the color property. If this property is applied later to an HTML
element, the content of that element will be shown in color red (the value #FF0000 represents the color red).

Properties can be grouped together
using braces. A group of one or more properties enclosed by braces is called rule
and it is identified by a name called selector.

Listing 1-2: Declaring CSS rules

body {

width: 100%;

margin: 0px;

background-color: #FF0000;

}

Listing 1-2 declares a rule with
three properties: width, margin and background-color. This rule is identified with
the name body, which means that the properties are
going to be applied to the <body> element. If we include this
rule in a document, the content of the document will extend to the edges of the
browser window and will have a red background.

The
Basics: There are
different techniques to apply CSS styles to HTML elements. We will study CSS
properties and how to include them in an HTML document in Chapter 3 and 4.

JavaScript

Unlike HTML and CSS, JavaScript is a
programming language. To be fair, they all may be considered programming
languages, but in practice, there are some differences in the way they provide
the instructions to the browser. HTML is like a set of markers that browsers
interpret to organize information, CSS may be considered as a list of styles that
help the browser prepare the document to be displayed on the screen (although
the latest specification has turned it into a more dynamic tool), but JavaScript
is a scripting language, comparable to any full programming language such as
C++ or Java. JavaScript is different from the other two languages because it
can perform custom tasks, from storing values to calculating complex algorithms,
including the capacity to interact with the elements in the document and
process their content.

As well as HTML and CSS, JavaScript
is embedded in browsers and therefore it is available for every document, but
we have to indicate to the browser that the code is written in this language. To
declare JavaScript code inside a document, HTML offers the <script> element. The following example
illustrates what this type of code looks like.

Listing 1-3: Declaring JavaScript code

<script>

function changeColor() {

document.body.style.backgroundColor =
"#0000FF";

}

document.addEventListener("click",
changeColor);

</script>

The code in Listing 1-3 changes the
background color of the <body> element to blue when the user
clicks on the document.

The
Basics: Using the <script> element, you
can also load JavaScript code from external files. We will study the <script> element in
Chapter 2 and the JavaScript language in Chapter 6.

Server-Side Languages

Codes programmed in HTML, CSS, and JavaScript are
executed by the browser in the user’s computer (the client). This means that
after the website’s files are uploaded to the server, they remain the same
until they are downloaded into a personal computer and their codes are
executed. Although this allows the creation of useful and interactive websites,
there are times when we need to process information in the server before to
send it to the user. The content produced from this process is called dynamic
content, and it is generated with code that is executed in the server and programmed
in languages designed specifically for this purpose (server-side languages). When
the browser requests a file containing this type of code, the server first executes
the code and then sends the result to the user. These codes are used not only
to generate content and documents in real time but also to process information
received from the browser, store user's data on the server, manage users
accounts, etc.

There are several languages available to
create code that runs on the server, with the most popular being PHP, Ruby, and
Python. The following example shows what PHP code looks like.

Listing 1-4: Declaring server-side
code in PHP

<?php

$name = $_GET['myname'];

print('Your name is: '.$name);

?>

The code in Listing 1-4 receives a value
from the browser, stores that value in memory, and creates a message with it. When
this code is executed, a new document is produced with the final message, the
file is sent to the client, and the browser shows its content on the screen.

IMPORTANT: Server-side languages use a separate technology but
work with HTML5 to keep track of users’ accounts, store data on the server, manage
databases, etc. The topic is beyond the scope of this book. For more
information and to learn how to program code in PHP, Ruby, or Python, visit our
website at www.formasterminds.com and follow the links for this chapter.

1.3 Tools

Creating a website involves several steps.
We have to program the documents using HTML, create the files with the CSS styles
and JavaScript codes, set up the server that will make our website available to
the public, and upload the files to it. Fortunately, there are several tools
available to help us with these tasks. They are very easy to use, and most of
them are free.

IMPORTANT: In this section of the chapter, we
introduce all the tools you need to create a website and make it available to the
users. This includes tools required to program and design your website, but
also others that are necessary for configuration and testing. Most of the
examples in this book do not have to be uploaded to a server to work properly,
and therefore you can ignore some of this information until it is required by
your project.

Editors

HTML documents, as well as CSS and JavaScript
files, are text files, so we can use any text editor included in our computer
to create them, like the Windows’ Notepad or the Text Edit application in
Apple’s computers, but there are special text editors designed for programmers
and web developers with features that can simplify our work. They do things
like highlighting the text with different colors to identify each part of the
code, or listing the files of a project on the side to make it easy for us to
work with multiple documents at the same time. The following is a list of the
most popular editors and IDEs
(Integrated Development Environments) available for PCs and Apple computers.

·
Atom (www.atom.io) is a free editor, simple to use and
highly customizable (recommended).

·
Brackets (www.brackets.io) is a free editor made by Adobe.

·
KompoZer (www.kompozer.net) is a free editor with a preview panel
that makes it easy to find specific parts of the document to modify.

·
Aptana (www.aptana.com) is a free IDE with tools to easily manage
files and entire projects.

·
NetBeans (www.netbeans.org) is a free IDE with tools to manage
projects and files.

·
Sublime (www.sublimetext.com) is a commercial editor with a free
version for evaluation.

·
Komodo (www.activestate.com/komodo-ide) is a commercial IDE that can handle a
very extensive list of programming languages.

·
Dreamweaver (www.adobe.com/products/dreamweaver.html) is a commercial IDE with WYSIWYG
technology built in (What You See Is What You Get) that allows us to see the
results of the execution of our code in real time.

Working with an editor is simple, we
have to create a folder on our hard drive to store the files for our website,
open the editor, and then create inside this folder all the files and
additional folders we need for our project. Figure 1-6 shows what the Atom
editor looks like when it is opened for the first time.

Figure 1-6: Atom editor with an empty
file

This editor has an option in the File
menu at the top of the screen to open a project. The option is shown in Figure
1-7, number 1.

Figure 1-7: Add Project Folder option

If we click on this option and then
select the folder created for our project, the editor opens a new panel on the
left with the list of files inside the folder. Figure 1-8, below, shows a
folder with the name Test created to contain the files of the example in Figure 1-2.

Figure 1-8: Project's files

Do It Yourself: Visit www.atom.io to download the Atom editor.
Once the editor is installed on your computer, create a new folder on your hard
drive to store your website. Open Atom, go to the File menu and click the
option Add Project Folder (Figure 1-7, number 1). Select the folder you have
just created. Go to the File menu and click the option New File. Copy the code in
Listing
1-1
inside and save the file with the name index.html. When the file is selected
from the left panel, you should see something similar to Figure 1-8.

IMPORTANT: Teaching how to work with Atom, or any
other editor, is beyond the scope of this book, but we have included links to
tutorials and videos on our website with additional information. Visit www.formasterminds.com and click on the options Links and
Videos.

Domain Registration

Once our website is ready for the public,
we have to register the domain that users are going to type on their browsers
to access it. As we already mentioned, a domain is a custom name with an extension
that determines the purpose of the website. We have several options available
for the extension, from those with commercial purposes, such as .com or .biz. to
non-profit or personal, such as .org, .net or .info, not to mention regional extensions
that include an additional value to determine the location of the website, such
as .co.uk for websites in the United Kingdom, or.eu for websites in the
European Union.

To get the domain for our website, we
have to open an account with a registrar and order it. Depending on the type of
domain, there is usually an annual fee, but the process is relatively easy, and
there are several companies available that can take care of everything for us. The
most popular is GoDaddy, at www.godaddy.com, but every company that offers
services for web developers usually includes the possibility to register a
domain. The registration process is simple; we have to decide the name and the
extension we want for the domain, perform a search to make sure that it is available,
and then place the order (the companies mentioned above provide all the tools
necessary for this purpose).

After the domain is registered, the
system asks for the Nameservers we want to associate with it. These are strings
composed of a domain and a prefix, usually NS1 and NS2, that determine the
location of our website (Nameservers are provided by the server where our
website is stored). If we do not have the Nameservers yet, we can use those
provided by the company and change them later.

IMPORTANT: The company that registers your domain
assigns Nameservers that they use to provide a temporary destination (also
known as Parking). You can change these values after your server is
ready. Some companies offer domain registration along with server rental, and
therefore they can configure the domain for you if you use their servers.

Web Hosting

Setting up and maintaining a server is difficult
and demands knowledge that not every web developer has. For this reason,
companies offer a service called Web Hosting that allows anyone to rent
a server configured and ready to store and run a website or multiple websites.

There are different types of Web
Hosting available, from those that allow multiple websites to run on one single
server (Shared Hosting) to more professional options that reserve an entire
server for a website (Dedicated Hosting) or distribute a large website in
multiple servers (Cloud Hosting), including various other options in between.

The advantage of having a hosting
account is that they all offer a control panel with options to create and
configure our website. The following are common options that we are going to
find in almost every hosting service.

·
File
Manager is a
web-based tool to manage the website's files. With this tool, we can upload,
download, edit or delete files on the server from the browser, without using
any external software.

·
FTP
Accounts is a
service that allows us to manage the accounts we use to connect to the server
via FTP. FTP (File Transfer Protocol) is a communication protocol designed to
transfer files from one computer to another.

·
MySQL
Databases is a
service that allows us to create databases for our website.

·
phpMyAdmin is an application programmed
in PHP that we can use to manage the databases created for our website.

·
Email
Accounts is a
service that allows us to create email accounts with our website's domain
(e.g., info@mydomain.com).

The most popular control panel in the
market for websites is called cPanel. Figure 1-9 shows the design and
some of the options offered by this panel.

Figure 1-9: Options offered by cPanel

The cost of a hosting account can go
from a few dollars for a shared account to hundreds of dollars per month for a
dedicated server. Once we get an account, the company sends us an email with
the information we need to access the control panel and configure the server.
They usually create all the basic accounts we need, including an FTP account to
upload the files, as we will see next.

The
Basics: There are
hosting services available for free that you can use to practice, but they include
ads or impose restrictions that impede the development of professional
websites. It is recommendable to start from a shared hosting account that may
cost less than 5 dollars per month to learn how a professional web hosting
works and see all the options they have to offer. Multiple companies provide
this kind of hosting, with the most popular being www.godaddy.com and www.hostgator.com.

FTP Software

As we just mentioned, a web hosting
account usually offers a service to manage the website's files from the
browser. This is a web page we can access from the control panel to upload,
download and edit the files on the server. It is a useful tool but only
practical for performing small changes or uploading a few files. The tool takes
advantage of a system integrated with the browser that works with a protocol
called FTP (File Transfer Protocol) to transfer files from one computer
to another on a network. Browsers have this system built in because they need
it to let users download files, but because their purpose is to download and
display websites on the screen, they offer a poor experience when it comes to managing
those files. For this reason, professional developers do not use the browser to
upload files to the server, but rather programs specifically designed to transfer
files from client to server using the FTP protocol.

There are several FTP programs
available, including free and commercial. The most popular free software is
called Filezilla, available at www.filezilla-project.org. The program offers several
panels with information about the computers that participate in the connection
and the connection itself, including two panels side by side listing the local
and remote files to let us transfer the files by dragging them from one panel
to another.

Figure 1-10: Filezilla's interface

When we open a hosting account, the
system automatically creates an FTP account for our website that includes the
user and password required to connect to the server via FTP (if the system does
not set up the account for us, we can do it ourselves from the FTP Accounts option
in the control panel). The values we need to perform the connection are the
host (IP or domain), the username and password, and the port assigned by the
server to connect via FTP (by default, 21). Filezilla offers two ways to insert
this information: a bar at the top with fields for a quick connection (Figure
1-11, number 2), and a button to store multiple connections for frequent access
(Figure 1-11, number 1).

Figure 1-11: Connection's setup

If we press the button to store or
access previously stored connections (number 1), Filezilla opens a window to
manage the list of connections available and offer additional options for
configuration. There are buttons to create, rename, and delete a connection
(New Site, Rename, Delete), fields where we can select the protocol we want to
use (FTP for a normal connection or SFTP for a secured connection), the
encryption used to encode the files, and the type of account required (Anonymous
for anonymous connections or Normal for a connection that requires username and
password). The window also includes additional panels for advanced
configuration, as shown in Figure 1-12.

Figure 1-12: Connection's manager

Normally, to establish the connection
we have to insert the value of the host (the IP or domain of our website),
select the type of account as Normal, insert our user and password, and leave
the rest of the values by default. Once the computers are connected, Filezilla
displays the list of files on the interface. On the left side are the files
contained in the selected folder (we can select any folder on our hard drive we
want), and on the right side is the list of files and folders available in the
root directory of our server, as shown in Figure 1-13.

Figure 1-13: Folders in the root
directory

When a hosting account is set up, the
system creates several folders and files to manage the information required to
provide the service (store emails, monitor users' activity, etc.) The folder where
the files for our website are stored is called public_html. After this
folder is opened, we can start uploading our files by dragging them from the
panel on the left to the panel on the right (see Figure 1-10).

IMPORTANT: Teaching how to work with Filezilla, or
any other FTP program, is beyond the scope of this book, but we have included
links to tutorials and videos on our website with additional information. Visit
www.formasterminds.com and click on the options Links
and Videos.

MAMP

HTML documents may be opened on a personal
computer. For example, if we open the index.html file created from the code in Listing 1-1, the browser window shows the
text HELLO WORLD!, as illustrated in Figure 1-14 below (the rest of the tags in
this document are structural and therefore do not produce any visible result).

Figure 1-14: HTML document in the
browser

The
Basics: To open a
file in the browser, you can select the Open File option from the browser's
menu, or double-click the file from the File Explorer (or Finder on Apple
computers) and the system takes care of opening the browser and loading the
document.

Although most of the examples in this
book can be tested in the browser without uploading the files to a server, testing
an entire website in a personal computer is not always possible. As we will see
in later chapters, some JavaScript codes only work when they are downloaded
from a server, and server-side technologies like PHP require a server to run. There
are two options available to work with these types of documents: we can get a
hosting account right away to upload our website and use it for testing, or we
can install a server on our own computer. The last option will not make our website
visible to the users but allows us to test it and experiment with the code before
uploading the final version to a real server.

There are several packages available
that install all the software we need to turn our personal computer into a
server. These packages include an Apache server (to deliver web files through
the HTTP protocol), a PHP server (to process PHP code), and a MySQL server (to
run a MySQL database we can use to store data on the server). The one we
recommend is called MAMP. This is a free package, available for PCs and Apple
computers, that we can download from www.mamp.info (there is also a more advanced
commercial version called MAMP PRO).

MAMP is very simple to use. Once
downloaded and installed, we just have to open it to get the server running, as
shown in Figure 1-15.

Figure 1-15: MAMP's main screen

By default, MAMP creates a folder
inside its own folder called htdocs where we are supposed to store the
files of our website, but we can assign a different folder from the Preferences
option if we want. The option opens a new window with several tabs for
configuration. The Web Server tab shows the current folder used by the Apache
server and offers a button to select a different one, as illustrated in figure
1-16, number 1.

Figure 1-16: Apache server's folder

Once we select the folder where our
website is located, we can access its files from the browser. Apache creates a
special domain called localhost to reference the server, and therefore
our website is accessible from the URL http://localhost/. If we want to access
a specific file, we have to add its name at the end of the URL, as we do with domains
(e.g., http://localhost/contact.html).

Do It Yourself: Go to www.mamp.info and download the free version
of MAMP for your system (Windows or MacOS). Install the package and open the
application. Select Preferences (Figure 1-15) and replace the htdocs folder with
the folder you created for your website in the previous example (Figure 1-8). Open the browser and insert
the URL http://localhost/. If you created the index.html file as suggested
before, you should see the text HELLO WORLD! on the window (Figure 1-14).

IMPORTANT: Apple systems come with their own version of the Apache server,
and therefore MAMP has to connect Apache to a different port to avoid conflicts.
For this reason, on a Mac computer, you have to specify the port 8888 when
accessing the localhost, as in http://localhost:8888. You can change the port
from MAMP settings to avoid this. Go to Preferences, select the Ports tab, and
click on the button called Set Web & MySQL ports.

The
Basics: Most of the
examples in this book can be tested on a personal computer without installing
any server (we will let you know when that is not the case), but you can
install MAMP if you want to make sure that everything works as it would on a
real server.

Chapter 2 - HTML

2.1 Structure

Despite the
innovations introduced by CSS and JavaScript in recent years, the structure
created from HTML code is still the fundamental part of a document. It defines
the space inside the document where static and dynamic content is allocated,
and it is the basic platform for applications. To create a website or a web
application, we have to start by programming the HTML code that defines the
structure of every page.

IMPORTANT: HTML documents are text files that can be created with any
text editor or the professional text editors introduced in Chapter 1. Most of
these editors provide tools to help you write your documents, but they do not
check the validity of the code. If you make a mistake or miss something there
is nothing that warns you about the error. To check your code, you can use
validation tools available online. The most popular for HTML documents is
available at www.validator.w3.org.

Document Type

Because
browsers are capable of processing different kinds of files, the first step we
have to take in the construction of any HTML document is to indicate its type.
To ensure that our document is interpreted correctly as HTML code, we must
declare its type at the beginning with the <!DOCTYPE> declaration.
This declaration, similar in construction to an HTML tag, is required at the
beginning of every HTML document to help the browser decide how to render the
page. For HTML5 documents, the declaration must include the html attribute, as in the
following example.

Listing 2-1: Including the <!DOCTYPE> element

<!DOCTYPE html>

Do It Yourself: Open Atom, or your favorite
editor, and create a new file called index.html to test the codes of this chapter (you
can also use the file created in the previous chapter).

IMPORTANT: The line with the <!DOCTYPE> declaration must be the
first line in your document, without any spaces or code before. This activates
the standard mode and force browsers to interpret HTML5 when possible or ignore
it otherwise.

The
Basics: Some of the
elements and attributes introduced in HTML5 are not available in older
browsers. To know which browsers have already implemented the new features,
visit www.caniuse.com. This
website offers a list of all the elements, attributes, and CSS properties
included in the specifications, along with the browsers that support them.

Structural Elements

As we mentioned in
Chapter 1, HTML elements conform a tree-like structure with the <html> element as its root.
This structure presents multiple organization levels, with some elements in
charge of outlining general sections of the document and others representing
minor sections or content. The following are the elements in charge of defining
the backbone of the structure and providing the information the browser needs
to show the page on the screen.

<html>—This element
delimits the HTML code. It may include the attribute lang to define
the human language of the document.

<head>—This element
is used to enclose information necessary to render the page, such as the title,
the character encoding, and the external files required by the document.

<body>—This element
delimits the content of the document (the visible part of the page).

After declaring the document's type,
we have to build the HTML tree structure, starting by the <html> element. This element may include the lang attribute to set the human language we are going to use to
write the document's content, as in the following example.

Listing 2-2: Including the <html> element

<!DOCTYPE
html>

<html lang="en">

</html>

The
Basics: There are
several values available for the lang attribute,
like en for English, es for Spanish,
fr for French,
etc. A complete list of codes is available at https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes.

The HTML code inserted between
the <html> tags has to be divided
into two main sections, the head, and the body. The head goes first, of course,
and like other structural elements, it is composed of an opening and a closing
tag.

Listing 2-3: Including the <head> element

<!DOCTYPE
html>

<html lang="en">

<head>

</head>

</html>

Within the <head> tags we will define the
title of our web page, declare the character encoding, provide general
information about the document, and incorporate external files with styles and
codes necessary to render the page. Except for the title and some icons, the
rest of the information incorporated between these tags is not visible to the
user.

The next big section of the main
organization of an HTML document is the body. The body is the visible part of
the document, and it is specified with the <body> element.

Listing 2-4: Including the <body> element

<!DOCTYPE
html>

<html
lang="en">

<head>

</head>

<body>

</body>

</html>

The Basics: As
we already mentioned, the HTML structure can be described as a tree, with the <html> element as its root, but another way to define the
relationship between elements is to name them as parents, children or siblings
according to their position in the structure. For example, in a typical HTML
document the <body> element is child of the <html> element and sibling of the <head> element. Both, <body> and <head>, have the <html> element as their parent.

The basic structure is already in
place. Now we have to build the page, starting with the definition of the head.
The head provides all the information and resources the browser needs to render
the page. The following are the elements available for this purpose.

<title>—This element defines the
page's title.

<base>—This element
defines the URL used by Relative URLs to establish the real location. It
includes the href attribute to declare
the URL. When this element is declared, instead of the current URL, the browser
uses the URL assigned to the href attribute to
complete Relative URLs.

<meta>—This element
represents metadata associated with the document, such as the document's
description, keywords, character encoding, etc. It may include the attributes name to describe
the type of metadata, content to specify
the value, and charset to declare
the character encoding used to render the content.

<link>—This element
specifies the relationship between the document and an external resource
(usually used to load CSS files). It may include the attributes href to declare
the location of the resource, rel to define
the type of relationship, media to specify the media
the resource is associated with (screen, printer, etc.), and type and sizes to declare
the type of the resource and its size (often used for icons).

<style>—This element
is used to declare CSS styles inside the document (studied in Chapter 3).

<script>—This element
is used to load or declare JavaScript code (studied in Chapter 6).

The first thing we have to remember
when declaring the head of the document is to specify its title with the <title> element. This is a text that is shown by
browsers at the top of the window, and it is what users see when searching for
information on our website using search engines like Google or Yahoo.

Listing 2-5: Including the <title> element

<!DOCTYPE
html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

</head>

<body>

</body>

</html>

Do It Yourself: Replace the code in your index.html file with
the code in Listing 2-5 and open the document in your browser (double-click the
file or select the Open File option from the browser's File menu). You should
see the text between the <title> tags at the top of the browser
window.

The
Basics: The <title> element in
the example of Listing 2-5 is displaced to the right. The white space on the
left is used to help us see the location of the element in the document's
hierarchy. This white space is automatically generated by editors like Atom,
but you can do it yourself when necessary pressing the Tab key on your keyboard
(Browsers ignore the white spaces and break lines outside the elements).

Besides the title, we also have to
declare the metadata for the document. This is information about the page that
browsers, as well as search engines, use to render and classify the document.
The values are declared with the <meta> element. The element includes
several attributes, but which ones to use depend on the type of information we
want to declare. For example, the most important value is the character
encoding, which specifies the table of characters used to display the text on
the screen, and it is declared with the charset attribute.

Listing 2-6: Including the <meta> element

<!DOCTYPE
html>

<html lang="en">

<head>

<title>This text is the title of the
document</title>

<meta charset="utf-8">

</head>

<body>

</body>

</html>

The
Basics: The example
of Listing 2-6 defines the set of characters as utf-8, which is
the set recommended because it includes the characters found in most human
languages, but there are others available. For more information, visit our
website and follow the links for this chapter.

Multiple <meta> elements may be added to declare
additional information. For example, two pieces of data that search engines may
consider when scanning our documents are the description of the page and
keywords identifying its content. These <meta> elements require the name attribute with the values "description" and "keywords", and the content attribute with the text we want to assign to the
description and the keywords, as in the following example.

Listing 2-7: Adding more <meta> elements

<!DOCTYPE
html>

<html lang="en">

<head>

<title>This text is the title of the
document</title>

<meta charset="utf-8">

<meta name="description" content="This is an
HTML5 document">

<meta name="keywords" content="HTML, CSS, JavaScript">

</head>

<body>

</body>

</html>

Another important element in the
document's head is <link>. This element is used to incorporate to the document styles,
codes, images, or icons from external files. For example, some browsers show an
icon along with the page's title at the top of the browser window. To load an
icon, we have to include a <link> element with the rel attribute defined as icon, the href attribute with the location of the file containing the
icon, the type attribute specifying the format in which
the icon was created, and the sizes attribute with the icon's
width and height separated by the letter x.

Listing 2-8: Including the document's
icon

<!DOCTYPE
html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta
charset="utf-8">

<meta
name="description" content="This is an HTML5 document">

<meta
name="keywords" content="HTML, CSS, JavaScript">

<link
rel="icon" href="images/favicon.png"
type="image/png" sizes="16x16">

</head>

<body>

</body>

</html>

The browser has little space to show
the icon, so their typical size is 16 pixels by 16 pixels. Figure 2-1 shows
what we see in the browser when we open a document that includes an icon (see
the image with the letter M on the left side of the title).

Figure 2-1: A browser displaying the
document's icon

Do It Yourself: Update the code in your index.html file
with the example of Listing 2-8. The icon is loaded from the file favicon.png
that you have to copy inside the image folder of your project. You can download
this file from our website or provide your own.

The
Basics: The value assigned to the type attribute of the <link> element must be specified as a MIME
type. Every file has a MIME type associated to indicate the format of its
content. For instance, the MIME type of an HTML file is text/html. There are MIME types for every type
of file available, including image/jpeg and image/png for JPEG and PNG images. To get a
complete list, visit our website and follow the links for this chapter.

Another common use of the <link> element is to load CSS files with
styles for the document. For instance, the following example loads the file
mystyles.css. After the file is loaded, all the styles declared inside are
applied to the elements in the document. In this case, we only need the rel attribute to declare the resource's
type (for CSS style sheets we have to assign the value "stylesheet") and the href attribute with the URL that
determines the file's location (we will learn how to create these files and
define CSS styles in Chapter 3).

Listing 2-9: Loading a CSS file with
the <link> element

<!DOCTYPE html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta
charset="utf-8">

<meta
name="description" content="This is an HTML5 document">

<meta
name="keywords" content="HTML, CSS, JavaScript">

<link
rel="stylesheet" href="mystyles.css">

</head>

<body>

</body>

</html>

With the head ready, it is time to
build the body. This structure (the code between <body> tags) is what generates
the visible part of our document (the web page).

HTML always offered different ways of
building and organizing the information in the document's body. The first
element implemented by developers for this purpose was <table>. Tables allowed authors to
arrange data, text, images, and tools into rows and columns of cells. With the
introduction of CSS, the structure generated by tables was no longer practical,
and developers started to implement a more flexible element called <div> (division). But <div>, as well as <table>, does not provide much information about
the parts of the body that the element is representing. Anything from images to
menus, text, links, codes or forms, could go between the opening and
closing <div> tags. In other words, the
name div only specifies a division in
the body, like the cell in a table, but it does not give a clue about what kind
of division that is, what is its purpose, or what is inside. This is why HTML5
introduced new elements with descriptive names that allow developers to
identify each part of the document. These elements not only help developers to
create the document but also inform the browser the purpose of each section.
The following is the list of all the elements available to create the structure
of the body.

<div>—This element defines a
generic division. It is used when no other element applies.

<main>—This element
defines a division that contains the document's main content (the content that
represents the central topic of the document).

<nav>—This element
defines a division that contains navigational aid, like the website's main menu
or major navigation blocks.

<section>—This element
defines a generic section. It is frequently used to separate thematic content
or to generate columns or blocks to organize the main content.

<aside>—This element
defines a division that contains information that is related to the main
content but is not part of it, such as references to articles or links to
previous posts.

<article>—This element
represents an independent item of content, such as a forum post, a magazine
article, a blog entry, a comment, etc.

<header>—This element
defines a header for the body or sections within the body.

<footer>—This element
defines a footer for the body or sections within the body.

These elements have been defined for
the purpose of representing specific sections of a web page. Although they are
flexible and may be applied to different types of layouts, they follow a
pattern currently found on most websites. Figure 2-2, below, illustrates this
type of layout. Despite the fact that every developer creates his or her own
design, in general, we will be able to describe every website considering the
following sections.

Figure 2-2: Visual representation of
a typical layout

The top bar, described as the Header
in Figure 2-2, is where we usually place the logo, name, subtitles and short
descriptions of our website or web page. The Navigation Bar below
is where most developers offer a menu or a list of links for navigation
purposes. The relevant content of the page is located in the middle of the
layout, where we usually find articles or news, as well as links to related
documents or resources. In the example in Figure 2-2, this section was divided
into two columns, Main Information and Side Bar,
but designers adapt it to their needs by inserting more rows or splitting the
columns into smaller blocks. At the bottom of a typical layout, we have one
more bar called the Institutional bar. We named it as such
because this is the area where we usually display general information about the
website, the author, the company, links regarding rules, terms and conditions,
site maps, etc.

As we mentioned before, the HTML5
elements were designed to follow this pattern. Figure 2-3 applies the
structural elements introduced before to the layout defined in Figure 2-2.

Figure 2-3: Visual representation of
a document’s structure with HTML5 elements

Elements are declared in the document
in the same order they are going to be display on the screen, from top to
bottom and left to right (although the order may be changed with CSS styles, as
we will see in Chapter 4). The first element of a typical design is <header>. This element must not be confused with
the <head> element used before to build
the head of the document. As well as <head>, the <header> element is intended to provide
introductory information (such as titles, subtitles or logos), but not for the
entire document, just for the body or sections within the body. In the
following example, this element is used to define the title of the web page.

Listing 2-10: Including the <header> element

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<header>

This
is a title

</header>

</body>

</html>

The insertion of the <header> element represents the
beginning of the body and the visible part of the document. From this point on,
we will be able to see the results of the execution of the code in the browser
window.

Do It Yourself: Replace the code in your index.html file with
the code in Listing 2-10 and open the document in your browser. You should see
the page's title on the screen.

The next section of our example is
the Navigation Bar. This bar defines a section with navigational aid,
and it is represented by the <nav> element.

Listing 2-11: Including the <nav> element

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<header>

This is a title

</header>

<nav>

Home |
Photos | Videos | Contact

</nav>

</body>

</html>

The structure and the order we choose
to follow depends on what our website or application demands. HTML elements are
versatile, they only give us the parameters to work with, but how to use them
depends on us. One example of this versatility is that the <nav> element could be inserted
within the <header> tags or in any other section
of the body. However, we must consider that these elements were created to
provide more information to browsers and to help every new program and device
identify the relevant parts of the document. If we want to keep our HTML code
portable and readable, it is better to follow the standards. The <nav> element is intended to contain
navigation aid like the main menu or major navigation blocks, and we should use
it that way.

Another example of specificity is provided by the
<main>, <section>, and <aside> elements, which were designed to organize
the document's main content. In our design, these elements represent the sections we called Main Information and Side
Bar. Because the Main Information section is more generic, its
content is usually represented by <section> elements (one or more
depending on the layout), and because of the type of information it contains,
the <aside> element is placed on the
sides. Most of the
time, these two elements are enough to represent the main content, but because
they may be used in other areas of the document, the <main> element is used to group them together,
as in the following example.

Listing 2-12: Organizing the main
content

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<header>

This is a title

</header>

<nav>

Home | Photos | Videos | Contact

</nav>

<main>

<section>

Articles

</section>

<aside>

Quote from article one

Quote from article two

</aside>

</main>

</body>

</html>

The <aside> element is only describing the
information that is enclosed, not a place in the structure, so it could be
located in any part of the layout, and it may be used as long as its content is
not considered the main content of the document.

IMPORTANT: The tags that represent every section of
the document are listed in the code one after another, but on the web page some
of these sections will be shown next to each other (the Main Information and Side
Bar columns, for example). If you open the document of Listing 2-12 in
your browser, you will see that the texts are displayed one per line. This is
because HTML5 delegates the presentation of the document to CSS. To show the
elements in the right place on the screen, we have to assign CSS styles to
every element in the document. We will study CSS in Chapter 3.

The layout considered before (Figure 2-2) is the most common and the
basic structure for websites on the Internet today, but it is also
representative of how the relevant content is shown on the screen. Like
articles in a newspaper, web pages usually present relevant information divided
into parts that share similar characteristics. The <article> element is used to identify
each of these parts. In the following example, we implement this element to represent
the posts we want to show in the main section of our website.

Listing 2-13: Including the <article> element

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<header>

This is a title

</header>

<nav>

Home | Photos | Videos | Contact

</nav>

<main>

<section>

<article>

This is the text of my first post

</article>

<article>

This is the text of my second post

</article>

</section>

<aside>

Quote from article one

Quote from article two

</aside>

</main>

</body>

</html>

To finish the construction of the
elemental structure of our document, we only need one more element. We already
have the header of the body, sections with navigation aid and content, and
additional information on the side. The last step is to close the design and
give an end to the document’s body. HTML provides an element for this purpose
called <footer>.

Listing 2-14: Including the <footer> element

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<header>

This is a title

</header>

<nav>

Home | Photos | Videos | Contact

</nav>

<main>

<section>

<article>

This is the text of my first post

</article>

<article>

This is the text of my second post

</article>

</section>

<aside>

Quote from article one

Quote from article two

</aside>

</main>

<footer>

Copyright &copy; 2016

</footer>

</body>

</html>

On the typical layout (Figure 2-2), the section named Institutional
would be defined with the <footer> element. This is because that section represents the end (or
foot) of our document, and it is commonly used to share general information
about the author or the company behind the project, like copyright, terms and
conditions, etc.

The <footer> element is used to represent
the end of the body of our document and has the main purpose described above, however,
this and the <header> elements may also be used
within the body to represent the beginning and the end of a section.

The
Basics: The example of Listing 2-14 includes
the string &copy; in the document's footer, but when
the document is loaded, the browser replaces these characters with the
copyright character (©). These strings are called character entities and
represent special characters that are not available in the keyboard or have a
special meaning in HTML, like the copyright character (©), the registered
trademark character (®), or the angle brackets used by HTML to define its
elements (< and >). When you need to include in your text one of these
characters, you have to write the character entity instead. For example, if you
want to include the characters < and > inside a text, you have to
represent them with the strings &lt; and &gt;. Other common entities are &amp; (&), &quot; ("), &apos; ('), &pound; (£), and &euro; (€). For a complete list, visit our
website and follow the links for this chapter.

Global Attributes

Although most of the structural elements
have an implicit purpose reflected in their name, it does not mean that they
have to be used only once per document. For instance, some elements like <section> and <aside> may be used multiple times to
represent different parts of the structure, and others like <div> are still implemented to separate content
inside sections and contribute with the definition of the structure. For this
reason, HTML defines global attributes that we can use to assign custom
identifiers to each element.

id—This
attribute assigns a unique identifier to an element.

class—This
attribute assigns the same identifier to a group of elements.

The id attribute identifies
independent elements with a unique value, while the value of the class attribute may be
duplicated to associate elements with similar characteristics. For example, if
we have two or more <section> elements that we need to
differentiate from each other, we can assign the id attribute to each one of
them with unique values that declare their purpose.

Listing 2-15: Identifying elements with
the id attribute

<main>

<section id="news">

Long
articles

</section>

<section id="localnews">

Short
articles

</section>

<aside>

Quote from article one

Quote from article two

</aside>

</main>

The example of
Listing 2-15 includes two <section> elements in the
document's main section to separate long and short articles. Because the
content of these two elements will be different, they require different styles,
so we have to identify them with different ids. The first <section> element was identified
with the value "news" and the second element with the value "localnews".

On the other hand,
if we need to identify a group of elements that share similar characteristics,
we can use the class attribute. The following example
separates the content of a section with <div> elements. Because they
all have similar content, they will all share the same styles, and therefore we
should identify them with the same value (they are all of the same class).

Listing 2-16: Identifying elements with
the class attribute

<main>

<section>

<div class="books">Book: IT, Stephen King</div>

<div class="books">Book: Carrie, Stephen King</div>

<div class="books">Book: The Shining, Stephen King</div>

<div class="books">Book: Misery, Stephen King</div>

</section>

<aside>

Quote from article one

Quote from article two

</aside>

</main>

In the code in
Listing 2-16, we have only one <section> element to represent the
document's main content, but multiple divisions were created with <div> elements to organize the
content. Because the elements were all identified with the class attribute and the value "books",
every time we access or modify elements referencing the books class, all these elements
are affected.

IMPORTANT: The values of the id and class attributes are used by some HTML elements
to identify other elements and also by CSS rules and JavaScript code to modify
and access specific elements in the document. We will see some practical
examples in this and later chapters.

The Basics: The
values assigned to these attributes are arbitrary. You can give them any value
you want as long as it does not include any space character (the class attribute uses the space character to assign multiple classes to an
element). Also, to make your website compatible with every browser, you should
use only letters and numbers, always begin with a letter, and avoid special
characters.

2.2 Content

The body of our document is ready. The
basic structure of our website is finished, but we still have to work on the
content. The HTML elements studied so far identify every section of the layout
and assign a purpose to each one of them, but what is crucial for our page is
what is inside those sections. This information is composed of different visual
elements such as titles, texts, images, and videos, among others. HTML defines
various elements to represent it.

Text

The most important type of media included
in a document is text. HTML defines elements to determine the purpose of each
word, sentence, or paragraph in the document. The following is the element used
to represent titles.

<h1>—This element represents a
title (heading). The title is declared between the opening and closing tags.
HTML also includes additional elements for subtitles, up to 6 levels (<h2>, <h3>, <h4>, <h5>, and <h6>).

Every time we have to insert a title
or a subtitle in the document, we have to include it inside <h> tags. For instance, the example we have
created in the previous section of this chapter includes a header with the
title of the page. Because this is the main title, it should be represented by a
<h1> element, as in the following example.

Listing 2-17: Including the <h1> element

<header>

<h1>This is a title</h1>

</header>

Browsers style <h> elements with different font sizes,
depending on their hierarchy level (<h1> is the highest level and <h6> the lowest). Figure 2-4, below, shows
what the text of an <h1> element looks like with the
styles by default.

Figure 2-4: Title represented by an <h1> element with styles by default

Do It Yourself: Replace the <header> element in your index.html file with the
code in Listing 2-17. Open the document in your browser. You should see
something similar to Figure 2-4.

The following are the elements
included by HTML to represent the body of the text.

<p>—This element represents a
paragraph. By default, it is styled by browsers with a margin on top to
separate one paragraph from another.

<pre>—This element
represents a block of preformatted text, such as computer code or a poem that
requires the spaces assigned to each character and the line breaks to be
displayed as they were originally declared.

<span>—This element
may contain a paragraph, a sentence, or a single word. It does not apply any
styles to the text, but it is used to assign custom styles, as we will see in later
chapters.

The <p> element is used to represent paragraphs.
By default, browsers
style these elements with margins and a line break to differentiate one
paragraph from another. Because of these characteristics, we can also use the <p> element to format lines of text.

Listing 2-18: Defining lines of text
with the <p> element

<aside>

<p>Quote from article one</p>

<p>Quote from article two</p>

</aside>

Listing 2-18 presents the quotes
inside the <aside> element of previous examples
with <p> elements. Now the browser displays one
quote per line.

Figure 2-5: Quotes defined inside <p> elements

When a paragraph includes multiple
space characters, the <p> element automatically reduces
the space to only one character and ignores the rest. The element does
something similar to line breaks. Any line break introduced in the document will
not be considered when the text is shown on the screen. If we want these characters
and line breaks to be shown, we have to use the <pre> element.

Listing 2-19: Displaying the text in
the original format

<article>

<pre>

I'm selfish, impatient and a little insecure.

I make mistakes, I am out of control and at times hard to handle.

But if you can't handle me at my worst, then you sure as hell don't
deserve me at my best.

Marilyn Monroe

</pre>

</article>

The example of Listing 2-19 defines
an <article> element with a quote from
Marilyn Monroe. Because we use the <pre> element, line breaks are
considered by the browser and sentences are shown one per line, as defined in
the original text.

Figure 2-6: Text introduced with a <pre> element

Do It Yourself: Replace the <article> element in your index.html file with the
code in Listing 2-19. Open the document in your browser. You should see
something similar to Figure 2-6.

By default, the <pre> element is styled with margins and a font
type that respects the format assigned to the original text, which makes it
suitable to represent programming code and any other kind of preformatted text.
In cases like the one presented in the previous example, where the only thing
we need is to include line breaks in the paragraph, we can implement other
elements that were defined for that purpose.

<br>—This element is used to insert a line
break.

<wbr>—This element suggests the possibility of a line break
to help the browser decide where to cut the text when there is not enough space
to show it all.

These elements are inserted within
the text to generate line breaks. For example, we may write the previous
paragraph in one line and include <br> elements to display one
sentence per line.

Listing 2-20: Adding line breaks to a
paragraph with the <br> element

<article>

<p>I'm selfish, impatient and a little insecure.<br>I make mistakes, I
am out of control and at times hard to handle.<br>But if you can't handle
me at my worst, then you sure as hell don't deserve me at my best.<br>Marilyn
Monroe</p>

</article>

Unlike the <p> and <pre> elements, the <br> and <wbr> elements do not apply any
margins or font types, so the lines are displayed as if they would belong to
the same paragraph and with the font defined by default.

Figure 2-7: Line breaks in a
paragraph defined by <br> elements

Because not every word in a text has
the same emphasis, HTML includes the following elements to declare a special meaning
for words or entire sentences.

<em>—This element is used to indicate emphasis. By default, the text is
shown in italic.

<strong>—This element is used to indicate importance. By default, the text is shown
in bold.

<i>—This element represents an alternate voice or mood, such as a
thought, a technical term, etc. By default, the text is shown in italic.

<u>—This element represents unarticulated text. By default, the text is
underlined.

<b>—This element is used to indicate importance. It should be used only
when there is no other element appropriate for the situation. By default, the
text is shown in bold.

We can use these elements to
highlight titles and labels or to stress words or sentences in a paragraph, as
in the following example.

Listing 2-21: Highlighting text

<article>

<p>I'm selfish, impatient and a <em>little</em> insecure. I
make <i>mistakes</i>, I am <i>out of control</i> and at times <i>hard to handle</i>. But if you can't handle me at my worst, then <strong>you sure as
hell don't deserve me at my best</strong>.<br>Marilyn Monroe</p>

</article>

Unless different styles are specified
with CSS, the text inside these elements is shown with the styles by default,
as illustrated in Figure 2-8.

Figure 2-8: Highlighted text

The specificity of structural
elements is also manifested in some of the elements that define the content.
For instance, HTML includes the following elements to insert text that have a
clearly defined purpose.

<mark>—This element highlights text that is relevant according to the
circumstances (e.g., terms that are searched by the user).

<small>—This element represents small print, like legal print, disclaimers,
etc.

<cite>—This element represents the author or title of a work such as a
book, movie, song, etc.

<address>—This element represents contact information. It is usually
implemented inside footers.

<time>—This element represents time in human-readable form. It includes
the attributes datetime to specify a machine-readable timestamp
and the attribute pubdate, which indicates that the value of the
attribute datetime represents the publication date.

<code>—This element represents computer code. It is often combined with the <pre> element to display the code in the original format.

<data>—This element represents generic data. It
may include the value attribute to specify a machine-readable value (e.g., <data value="32">Thirty
Two</data>).

Because these elements represent specific
information, they usually complement the content of other elements. For
example, we may use the <time> element to declare the date an article was
published and other elements like <mark> and <cite> to give meaning to parts of the text.

Listing 2-22: Complementing the <article> element

<article>

<header>

<h1>Title of post</h1>

<time datetime="2016-10-12" pubdate>posted
12-10-2016</time>

</header>

<p>I'm selfish, <mark>impatient</mark> and a little insecure. I make mistakes, I am out of control
and at times hard to handle. But if you can't handle me at my worst, then you
sure as hell don't deserve me at my best.<br><cite>Marilyn
Monroe</cite></p>

</article>

Listing 2-22 expands the <article> element of previous examples. The element now
includes a <header> element with the article's title and time
of publication, and the text was highlighted with the <mark> and <cite> elements. The <mark> element highlights parts of a text that were not
considered important before but now have become relevant (perhaps because of a
search performed by the user), and the <cite> element highlights the name of the author.
By default, browsers style the text inside the <mark> element with a yellow background and show the content
of a <cite> element in italic.

Figure 2-9: Complementing the <article> element

IMPORTANT: The value of the datetime attribute of the <time> element must be declared as a
machine-readable timestamp. The format of this timestamp must follow the
pattern 2016-10-12T12:10:45, where the T may be replaced with a space and the
less significant components may be ignored (e.g., 2016-10-12).

The Basics: The pubdate attribute is a Boolean
attribute. These types of attributes do not require a value; they represent the
value true when they are present or false otherwise.

The rest of the elements mentioned above are also
combined with other elements to complement their content. For instance, the <address> and <small> elements are usually inserted inside a <footer> element to highlight information about the page or
section.

Listing 2-23: Complementing the <footer> element

<footer>

<address>Toronto, Canada</address>

<small>Copyright &copy; 2016</small>

</footer>

Figure 2-10: Complementing the <footer> element

The <code> element also works with other elements to
present content, but it has a particular relationship with the <pre> element; they are implemented together to
present programming code. The <code> element indicates that the
content is programming code, and the <pre> element formats that content
to be shown on the screen as it was originally declared in the document.

Listing 2-24: Displaying code with the <code> and <pre> elements

<article>

<pre>

<code>

function changeColor() {

document.body.style.backgroundColor = "#0000FF";

}

document.addEventListener("click", changeColor);

</code>

</pre>

</article>

Listing 2-24 defines an <article> element that shows code programmed in JavaScript.
Because this code is not inside a <script> element, it is not executed by
the browser, and because we included the code as the content of a <pre> element, it is presented with the line breaks
and spaces declared in the document.

Figure 2-11: Programming code
presented by the <code> and <pre> elements

The Basics: Sometimes the names of the elements and their content do
not provide enough information for the developer to understand the purpose of
the code. For these situations, HTML allows us to write comments. Comments are
included in the document but are not processed by the browser. To add a
comment, we have to write the text between the special tags <!-- and -->, as in <!-- This is a comment -->.

Links

Connecting documents to other documents
through links is what makes the Web possible. As we mentioned before, a link is content
associated with a URL that indicates the location of a resource. When the user
clicks on the content (text or image), the resource is downloaded by the
browser. HTML includes the following element to create links.

<a>—This element creates a hyperlink. The
text or image representing the link is included between the opening and closing
tags. The element includes the attribute href to specify the link's URL.

Links can be created to connect the
current document to other documents in the same website or external websites.
For example, we may link the options in our web page's menu to other documents
on our server.

Listing 2-25: Linking the document to
other documents with the <a> element

<nav>

<a
href="index.html">Home</a> |

<a href="photos.html">Photos</a> |

<a
href="videos.html">Videos</a> |

<a
href="contact.html">Contact</a>

</nav>

The <nav> element in Listing 2-25 includes four <a> elements for each option of our menu. The
elements include the href attribute to indicate to the
browser the document it has to open when the user clicks on the link. By
default, links are shown underlined and in color blue (or violet if the user
clicked on them before).

Figure 2-12: Hyperlinks

When the user clicks on any of these
links, the browser downloads the document in the URL specified by the href attribute and shows its content on the screen. For
example, if we click on the link created for the Contact option in the code in
Listing 2-25, the browser downloads the contact.html file and displays its
content on the screen, as shown in Figure 2-13 (this example assumes that we
have created a file called contact.html).

Figure 2-13: Navigation between
documents

Do It Yourself: Create a new file called
contact.html. Copy the HTML code in the index.html file to the contact.html
file. Replace the <nav> element in both files with the
code in Listing 2-25. Change the title in the <header> element of the contact.html file to
"Send us a message". Open the index.html file in your browser and
click the Contact link. The browser should open the contact.html file and show
it on the screen, as illustrated in Figure 2-13.

The documents linked in the menu of
Listing 2-25 belong to the same website and that is why we used Relative URLs
to specify their locations, but if we need to create links to documents that
are not stored on our server, we have to use Absolute URLs, as in the following
example.

Listing 2-26: Linking the document to
documents in other websites with the <a> element

<footer>

<address>Toronto, Canada</address>

<small>&copy; 2016 <a
href="http://www.jdgauchat.com">J.D Gauchat</a></small>

</footer>

The code in Listing 2-26 adds a link
to the footer of our example that points to the website www.jdgauchat.com. The
link works like any other, but now the browser has the complete URL to access
the document (the index file of the website located at www.jdgauchat.com).

Figure 2-14: Link to external
documents

The <a> element may include the target attribute to specify the destination where the document
will be opened. The value _self is assigned by default, which
means that the document is going to be opened in the same frame (or window).
Other values are _blank (the document is opened in a
new window), _parent (the document is opened in the
parent frame), and _top (the document is opened in the
current window).

As we will see later, documents may
be opened in frames embedded inside other documents. The values for the target attribute take this hierarchy of frames into account, but
because frames are not frequently used in modern websites, the two most common
values for this attribute are _self, to open the document in the
same window, and _blank, to open the document in a new
window. The following example implements the latter to access the domain
www.jdgauchat.com in a new window, so the user never leaves our website.

Listing 2-27: Opening the link in a new
window

<footer>

<address>Toronto, Canada</address>

<small>&copy; 2016 <a
href="http://www.jdgauchat.com" target="_blank">J.D
Gauchat</a></small>

</footer>

Besides connecting the document to other
documents, links can also be created to elements in the same document. This is
useful when the document generates a long page that the user has to scroll to
see all its content. Using this feature, we can create links to different parts
of the page. When the user wants to see something that is not visible at the time,
it can click one of these links, and the browser will scroll the page to show
the element pointed by the link. The element we want to link has to be identified
with the id attribute. To create the link to an element, we have
to include the value assigned to this attribute preceded by the # character, as
in the following example.

Listing 2-28: Creating links to
elements in the document

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<header id="toptitle">

This
is a title

</header>

<nav>

Home | Photos | Videos | Contact

</nav>

<main>

<section>

<p>Article 1</p>

<p>Article 2</p>

<p>Article 3</p>

<p>Article 4</p>

<p><a href="#toptitle">Go Back</a></p>

</section>

</main>

<footer>

Copyright &copy; 2016

</footer>

</body>

</html>

In the document of Listing 2-28, we use the id
attribute with the value "toptitle"
to identify the <header> element. Using this value and the #
character, we create a link at the end of the content that takes the user back
to the top of the page. When the user clicks on this link, the browser scrolls
the page until the content of the <header> element becomes visible.

Do It Yourself: Update the code in your index.html file with the code in
Listing 2-28. Add additional paragraphs with the <p> element to generate more content inside the <section> element. Open the document in your browser, scroll
the page to the bottom, and click on the Go Back link. If the header is not
visible, the browser will scroll the page back to the top.

The <a> element can also be used to create links
to applications. HTML provides the keywords mailto and tel to specify an email or a telephone number.
When a link like this is clicked, the system opens the program or application
that can manage the response (send an email or make a phone call). The
following example implements the mailto keyword to send an email instead of
opening a website.

Listing 2-29: Sending an email

<footer>

<address>Toronto, Canada</address>

<small>&copy; 2016 <a
href="mailto:info@jdgauchat.com">J.D Gauchat</a></small>

</footer>

Do It Yourself: Replace the <footer> element in your index.html file with the
code in Listing 2-29. Open the document in your browser and click on the link.
The system should open your email program with the form ready to send an email
to the info@jdgauchat.com account.

Documents that are accessed through a link
created with the <a> element are downloaded by the browser and
automatically shown on the screen, but sometimes users do not need the browser
to open the document, they just want the file to be stored on their hard drive
for further use. HTML offers two attributes to provide this feature.

download—This is a
Boolean attribute that, when present, indicates that instead of reading the
file the browser should download it.

ping—This
attribute declares the path of a file to be opened on the server when the user
clicks on the link. The value could be one or more URLs separated by a space.

When the download attribute is present, the file specified
by the href attribute is downloaded and stored on the
user's hard drive. On the contrary, the file pointed by the ping attribute is not downloaded but executed on the server.
This file may be used to execute code that stores information on the server
every time the main file is downloaded or keep track of the times the action
occurs. In the following example, we implement both attributes to let the user
download a PDF file.

Listing 2-30: Applying the ping and download attributes

<article>

<p>I'm selfish, impatient and a little insecure. I make mistakes, I am
out of control and at times hard to handle. But if you can't handle me at my
worst, then you sure as hell don't deserve me at my best.<br>Marilyn
Monroe</p>

<footer>

<a
href="http://www.formasterminds.com/content/myfile.pdf"
ping="http://www.formasterminds.com/control.php" download>Click
here to Download</a>

</footer>

</article>

In Listing 2-30, we added a footer to the article
of previous examples with a link to a PDF file. In normal circumstances, a
modern browser would show the file’s content on the screen, but in this case,
the download attribute forces the browser to download the file.

This example also includes a ping attribute pointing to a file called control.php. As a result, every time the user clicks
on the link, the PDF file is downloaded and the PHP code is executed on the
server, allowing the developer to keep track of the action (storing user’s
information in a database, for example).

Do It Yourself: Replace the <article> element in your index.html file with the
code in Listing 2-30 and open the document in your browser. When you click on
the link, the browser should download the PDF file. Eliminate the download attribute to compare the browser’s
behavior.

IMPORTANT: The purpose of the ping attribute is to execute code on the server. In this example,
we used a PHP code, but you can do the same with any other programming language
that runs on the server, such as Python or Ruby. How to program the file
assigned to a ping attribute depends on what
language you use and what you want to achieve. The topic is beyond the scope of
this book. For more information about PHP, visit our website and follow the
links for this chapter.

Images

Images are the second most important media on the Web
after text. HTML includes the following elements to introduce images in our
documents.

<img>—This element
inserts an image in the document. The element requires the src attribute to
specify the URL where the file with the image is located.

<picture>—This element
inserts an image in the document. It works along with the <source> element to
provide multiple images for different resolutions. It is useful to create
responsive websites, as we will see in Chapter 5.

<figure>—This element
represents content that is relevant to the main content but may be removed,
such as illustrations, pictures, videos, etc.

<figcaption>—This element
introduces a caption for the content of a <figure> element.

To include an image in the document, we have to
declare the <img> element and assign the file's URL to the src attribute.

Listing 2-31: Including an image in the
document

<article>

<p>A
people without the knowledge of their past history, origin and culture is like
a tree without roots.<br>Marcus Garvey</p>

<img
src="myimage.jpg">

</article>

The code in Listing 2-31 loads the image in the
myimage.jpg file and shows it on the screen in its original size, as
illustrated in Figure 2-15.

Figure 2-15: Image on the page

The image is presented in its original size, but
we can define a custom size and some configuration parameters from the
additional attributes provided by the <img> element.

width—This
attribute sets the image's width.

height—This
attribute sets the image's height.

alt—This
attribute specifies a text that will be shown when the image cannot be loaded.

srcset—This
attribute allows us to specify a list of images with different resolutions that
the browser can load for the same element.

sizes—This
attribute specifies a list of Media Queries and sizes to tell the browser the
image to show depending on the screen's resolution. We will study Media Queries
and Responsive Web Design in Chapter 5.

crossorigin—This
attribute sets the credentials for CORS-enabled images. The possible values are
anonymous (no credentials)
and use-credentials (require credentials).
We will study CORS in Chapter 11.

The width and height attributes set new dimensions for the
image, but they do not take into consideration the aspect ratio. If we declare
both values without considering the original aspect ratio, the image will be
stretched or shrunk to adapt to the custom size. To reduce the size of the image
without losing the original proportions, we can specify only one of the
attributes and the browser will calculate the value of the other.

Listing 2-32: Reducing the size of the
image

<article>

<p>A
people without the knowledge of their past history, origin and culture is like
a tree without roots.<br>Marcus Garvey</p>

<img
src="myimage.jpg" width="150" alt="Trees in my
backyard">

</article>

The code in Listing 2-32 adds a width attribute with the value 150 to the <img> element of the previous example. This attribute
reduces the width of the image to 150 pixels, but because the height attribute was not declared, its value is calculated by the browser
according to the image's original ratio. The result is shown in Figure 2-16.

Figure 2-16: Image with a custom size

Do It Yourself: Download the myimage.jpg file from our website or
provide your own. Replace the <article> element in your index.html file with the
code in Listing 2-31 and open the document in your browser. You should see
something similar to Figure 2-15. Repeat the process with the code in Listing
2-32. Now the image should be smaller, as illustrated in Figure 2-16. The <img> element of the code in Listing 2-32 also includes the
alt attribute. To test this attribute, erase the
myimage.jpg file and refresh the document in your browser. Because the browser is
no longer able to find the file, it shows the text assigned to the alt attribute in its place.

Some images like icons are relevant and are required
to give meaning to the content, but others, like the picture from previous
examples, just complement the content and may be removed without affecting or interrupting the document’s
flow. When this type of information is present, the <figure> element may be used to identify it. This element is usually implemented along
with the <figcaption> element to provide a caption for its
content. In the following example, we use these elements to identify our image
and show its title to the user.

Listing 2-33: Identifying a picture
with the <figure> element

<article>

<p>A people without the
knowledge of their past history, origin and culture is like a tree without
roots.<br>Marcus Garvey</p>

<figure>

<img src="myimage.jpg"
width="150" alt="Trees in my backyard">

<figcaption>Trees in my backyard<figcaption>

</figure>

</article>

By default, browsers assign margins to the <figure> element on the sides. The result is shown in Figure
2-17.

Figure 2-17: Image with a caption

Lists

Often, information has to be presented as
a list of items. For instance, our web page may include a list of books,
movies, or terms and descriptions. To create these lists, HTML offers the
following elements.

<ul>—This element
creates an unordered list of items. It is composed of opening and closing tags
to enclose the items (<ul> and </ul>) and it works
along with the <li> element to
define the items of the list.

<ol>—This element
creates an ordered list of items. It is composed of opening and closing tags to
enclose the items (<ol> and </ol>) and it
works along with the <li> element to
define the items of the list. This element may include the attributes reversed to reverse
the order of the indicators, start to determine
the value at which the indicators starts counting, and type to determine
the type of indicator. The possible values for type are 1 (numbers), a (lowercase
letters), A (uppercase letters), i (lowercase
Roman numerals) and I (uppercase Roman
numerals).

<dl>—This element
creates a list of terms and descriptions. The element works along with the <dt> and <dd> elements to
define the items of the list. The <dl> element
defines the list, the <dt> element
defines the terms, and the <dd> element
defines the descriptions.

The element we use to create the list
depends on the characteristics of its content. For instance, if the order of
the items is not relevant, we can use the <ul> element. The items of these
types of lists are declared in between <ul> tags with the <li> element, as in the following example.

Listing 2-34: Creating an unordered
list of items

<aside>

<ul>

<li>IT, Stephen
King</li>

<li>Carrie,
Stephen King</li>

<li>The Shining,
Stephen King</li>

<li>Misery,
Stephen King</li>

</ul>

</aside>

The <ul> element presents the items in the order
they were declared in the code and identified with a single bullet on the left
side.

Figure 2-18: Unordered list

If we need to declare the position of
each item, we can create the list with the <ol> element. This element creates
a list of items in the order they were declared in the code, but instead of a bullet,
it assigns a value to identify their positions. By default, the indicators are
numbers, but we can change their type from the type attribute. In the following example, we use uppercase
letters.

Listing 2-35: Creating an ordered list
of items

<aside>

<ol
type="A">

<li>IT, Stephen
King</li>

<li>Carrie,
Stephen King</li>

<li>The Shining,
Stephen King</li>

<li>Misery,
Stephen King</li>

</ol>

</aside>

Figure 2-19: Ordered list

Although the items are always shown
in the order they were introduced in the code, we can use the attributes
available for the <ol> element to change the order of
the indicators. For example, we can add the reversed attribute to show them in
descending order.

Listing 2-36: Creating a list in
reverse order

<aside>

<ol
type="A" reversed>

<li>IT, Stephen King</li>

<li>Carrie, Stephen King</li>

<li>The Shining, Stephen King</li>

<li>Misery, Stephen King</li>

</ol>

</aside>

Figure 2-20: List in reverse order

The <dl>, <dt>, and <dd> elements work in a similar way than the <ul> element, but their purpose is to show a
list of terms and descriptions. The terms are represented by the <dt> element and the descriptions by the <dd> element. In the following example, we use
them to add a description to the books of our list.

Listing 2-37: Creating a list of terms
and descriptions

<aside>

<dl>

<dt>IT</dt>

<dd>A promise
made twenty-eight years ago calls seven adults to reunite in Derry, Maine,
where as teenagers they battled an evil creature that preyed on the city's
children.</dd>

<dt>Carrie</dt>

<dd>The story of
misfit high-school girl, Carrie White, who gradually discovers that she has
telekinetic powers.</dd>

<dt>The
Shining</dt>

<dd>Danny is only
five years old but in the words of old Mr. Hallorann he is a 'shiner', aglow
with psychic voltage.</dd>

<dt>Misery</dt>

<dd>After a
famous author is rescued from a car crash by a fan of his novels, he comes to
realize that the care he is receiving is only the beginning of a
nightmare.</dd>

</dl>

</aside>

By default, browsers style these types of lists
with margins on the sides to differentiate the terms from the descriptions. The
result is shown in Figure 2-21.

Figure 2-21: List of terms and
descriptions

Do It Yourself: Replace the <aside> element in your index.html file with the
example you want to try and open the document in your browser. Insert the start attribute with the value "3" in the <ol> element in Listing 2-35. The indicators should start
counting from the letter C.

There are two more elements associated
with lists. They were designed for different purposes, but both are frequently
used to list values.

<blockquote>—This element
represents a block of text that is quoted from another part of the document.

<details>—This element
creates a control that expands when it is clicked to reveal additional
information. The visible text is defined with the <summary> element, and
common elements such as <p> may be used
to define its content.

The <blockquote> element is similar to the <p> element, but because it also includes
margins on the sides, it was traditionally used to create lists of values.

Listing 2-38: Creating a simple list
with the <blockquote> element

<aside>

<blockquote>IT</blockquote>

<blockquote>Carrie</blockquote>

<blockquote>The
Shining</blockquote>

<blockquote>Misery</blockquote>

</aside>

The list generated by the <blockquote> element look similar to other lists, but
it does not include bullets or numbers to identify the items.

Figure 2-22: List of items with the <blockquote> element

A tool that reveals additional
information when it is requested by the user is very common in modern websites.
To provide this feature, HTML includes the <details> element. The element displays
a title, specified by the <summary> element, and content that may
be specified with any of the content-specific elements studied before such as <p> or <blockquote>. Because the content is
declared the same way we do with any other content, the <details> element may be used to disclose a list of
values, as in the following example.

Listing 2-39: Disclosing information
with the <details> and the <summary> elements

<details>

<summary>My Books</summary>

<p>IT</p>

<p>Carrie</p>

<p>The Shining</p>

<p>Misery</p>

</details>

Figure 2-23: The <details> element before and after is
clicked

Tables

Tables organize information in rows and
columns. Because of their characteristics, they were used for a long time to
structure HTML documents, but with the introduction of CSS, developers were
able to implement other structural elements to achieve the same purpose and
tables were completely replaced. Although it is no longer recommended to use tables
to define the document's structure, they are still useful to present tabular
data, such as statistics or technical specifications, for example. HTML
includes multiple elements to create a table. The following are the most
frequently used.

<table>—This element
defines a table. It has an opening and a closing tag to enclose the elements
that define the table.

<tr>—This element
defines a row of cells. It has an opening and a closing tag to enclose the
cells.

<td>—This element
defines a cell. It has an opening and a closing tag to enclose the cell's
content and may include the attributes colspan and rowspan to indicate
how many columns and rows are occupied by the cell, respectively.

<th>—This element
defines a cell for the table's header. It has opening and closing tags to
enclose the cell's content and may include the attributes colspan and rowspan to indicate
how many columns and rows are occupied by the cell, respectively.

To include a table in a document, we first
have to declare the <table> element and then describe the
rows one after another with the <tr> and <td> elements, as in the following example.

Listing 2-40: Creating a table

<article>

<table>

<tr>

<td>IT</td>

<td>Stephen King</td>

<td>1986</td>

</tr>

<tr>

<td>Carrie</td>

<td>Stephen King</td>

<td>1974</td>

</tr>

<tr>

<td>The Shining</td>

<td>Stephen King</td>

<td>1977</td>

</tr>

</table>

</article>

Because the document is interpreted
by the browser in sequential order from top to bottom, every time we declare a
row we have to declare all of its cells and their content. Following this
pattern, in the example of Listing 2-40, we create a table to display books,
one per row. The first cell of each row represents the title, the second cell
represents the author, and the third cell represents the year of publication.
When the browser opens this document, it shows the information in the order it
was declared in the code and with a size determined by the content.

Figure 2-24: Table with styles by
default

If we want to include a header to
describe the content of each column, we can create an additional row of cells
represented by the <th> element.

Listing 2-41: Creating a table with a
header

<article>

<table>

<tr>

<th>Title</th>

<th>Author</th>

<th>Year</th>

</tr>

<tr>

<td>IT</td>

<td>Stephen King</td>

<td>1986</td>

</tr>

<tr>

<td>Carrie</td>

<td>Stephen King</td>

<td>1974</td>

</tr>

<tr>

<td>The Shining</td>

<td>Stephen King</td>

<td>1977</td>

</tr>

</table>

</article>

By default, browsers show the headers
with the text in bold and centered.

Figure 2-25: Table's header with
styles by default

Cells may be stretched to occupy more
than one column or row with the colspan and rowspan attributes. For example, we may use only one header to
identify the book's title and author.

Listing 2-42: Stretching cells

<article>

<table>

<tr>

<th colspan="2">Book</th>

<th>Year</th>

</tr>

<tr>

<td>IT</td>

<td>Stephen King</td>

<td>1986</td>

</tr>

<tr>

<td>Carrie</td>

<td>Stephen King</td>

<td>1974</td>

</tr>

<tr>

<td>The Shining</td>

<td>Stephen King</td>

<td>1977</td>

</tr>

</table>

</article>

The example of Listing 2-42 includes
only one cell for the first two columns with the title Book. Because of the
value of the colspan attribute, this cell is
stretched to occupy the space of two cells. The result is shown in Figure 2-26.

Figure 2-26: Multicolumn cells

The Basics: HTML includes the elements <thead>, <tbody>, and <tfoot> to represent the table's header, body,
and footer, respectively, and other elements like <colgroup> to group columns together. For more
information, visit our website and follow the links for this chapter.

Global Attributes

Most current browsers automatically translate content
when a foreign language is detected, but in some cases, there are phrases or
entire paragraphs that should not be modified, such as names, movie titles, etc. To control the translation process, HTML
offers a global attribute called translate. The attribute may take two values: yes or no. By default, the value is yes, unless the parent element was changed to no.
In the following example, we use a <span> element to specify that a part of the text
should not be translated.

Listing 2-43: Using the translate attribute

<p>Mi película favorita es <span translate="no">Dos Caminos</span></p>

The <span> element was designed to present text, but
unlike the <p> element it does not assign any styles to
its content, so the text is presented in the same line and with the current
font and size. Figure 2-27 shows what we see in the browser window after the
text is translated.

Figure 2-27: Text translated by the browser

Do It Yourself: Insert the code in Listing 2-43 in your document's
main section and open the document in your browser. Browsers like Google Chrome
offer an option to translate the document in a contextual menu when you click
the mouse's right button. If the translation is not automatic, select this
option to translate the text. You should see something similar to Figure 2-27.

Another useful attribute we may add to an HTML
element is contenteditable. This is a Boolean attribute that when
present allows the user to edit the content of the element. If the user clicks
on an element that contains this attribute, it can change its content. The
following example implements a <span> element again to allow the user to edit
the name of the movie.

Listing 2-44: Using contenteditable to edit content

<p>My favorite movie is <span
contenteditable>Casablanca</span></p>

Do It Yourself: Replace the paragraph in
Listing 2-43 with the paragraph in Listing 2-44 and open the document in your
browser. Click on the name of the movie to change it.

IMPORTANT:
The
modifications introduced by the user are only available in the user's computer.
If we want the changes to be stored on the server and available to everybody,
we have to upload the information with JavaScript using Ajax, as we will see in
Chapter 21.

2.3 Forms

Forms are sets of tools that may be
included in a document to allow users to insert data, make decisions,
communicate information, and change the application behavior. The main purpose
of forms is to let the user select or insert information and send it to a
server for processing. Figure 2-28 shows some of the tools provided for this
purpose.

Figure 2-28: Form in the browser

Definition

As illustrated in Figure 2-28, forms
may include several tools to let the user interact with the document, including
input fields, checkboxes, drop-down menus, and buttons. Each tool is
represented by an element and the form is defined by the <form> element. This element has an
opening and a closing tag to enclose the rest of the elements and requires a
few attributes to determine how the information is going to be sent to the
server.

name—This
attribute specifies the form's name. The attribute is also available for other
form elements, as we will see later.

method—This
attribute determines the method used to send the information to the server.
There are two possible values: GET and POST. The GET method is
used to send limited public information (the data is included in the URL and must
be no longer than 255 characters). On the other hand, the POST method is
used to send unlimited private information (the data is not visible to the user
and may be as long as we want).

action—This
attribute declares the URL of the file on the server that is going to process
the information sent by the form.

target—This
attribute determines where the response sent from the server will be shown. The
possible values are _blank (new
window), _self (same frame), _parent (parent
frame) and _top (the window containing
the frame). The default value is _self, which means
that the response received from the server is going to be shown in the same
window.

enctype—This
attribute declares the codification to be applied to the data sent by the form.
It takes three values: application/x-www-form-urlencoded (the characters
are encoded), multipart/form-data (the characters
are not encoded), text/plain (only spaces
are encoded). The first value is the one set by default.

accept-charset—This
attribute declares the type of encoding to be applied to the form. The most
common values are UTF-8 and ISO-8859-1. The default
value is the one set for the document by the <meta> tags (see Listing
2-6).

The
following example defines a basic form. The name attribute
identifies the form with the name "myform", the method attribute
determines that the data is going to be included in the URL (GET), and the action attribute
declares process.php as the file to be executed in the server to process the
information and return the result.

Listing 2-45: Defining a
form with the <form> element

<!DOCTYPE html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form
name="myform" method="get"
action="process.php">

</form>

</section>

</body>

</html>

Do It Yourself: Create a new HTML file
in your editor or modify the index.html file created for previous examples.
Write the code in Listing 2-45 inside, and open the document in your browser. You
will not be able to see anything on the screen because the form is empty. We
will develop the form's content next.

Elements

A form may include one or more tools
to let the user select or insert information. HTML includes several elements to
create these tools. The following are the most frequently used.

<input>—This element
creates an input field. It can receive different types of inputs, depending on
the value of its type attribute.

<textarea>—This element
creates an input field to insert multiple lines of text. The size can be
declared in integer numbers using the attributes rows and cols, or in
pixels with CSS styles, as we will see in Chapter 3.

<select>—This element
creates a list of options the user can choose from. It works along with the <option> element to
define each option, and the <optgroup> element to
organize the options in groups.

<button>—This element
creates a button. It includes the type attribute to
define the purpose of the button. The possible values are submit to submit
the form (by default), reset to reset the form, and button to perform
custom tasks.

<output>—This element
represents an output produced by the form. It is implemented with JavaScript to
show the result of an operation to the user.

<meter>—This element
represents a measure or the current value of a range.

<progress>—This element
represents a measure of progress.

<datalist>—This element
creates a list of values available for other controls. It works along with the <option> element to
define the options.

<label>—This element
creates a label to identify a form element.

<fieldset>—This element
groups form elements together. It is used to create sections in large forms.
The element may contain a <legend> element to
define the section's title.

The <input> element is the most versatile of
all. This element generates an input field for the user to select or insert
data, but it can adopt different characteristics and accept multiple types of data
depending on the value of its type attribute. The following are the
values available.

text—This value
generates an input
field to insert generic text.

email—This value
generates an input field to insert emails.

search—This value
generates an input field to insert search terms.

url—This value
generates an input field to insert URLs.

tel—This value
generates an input field to insert phone numbers.

number—This value
generates an input field to insert numbers.

range—This value
generates an input field to insert a range of numbers.

date—This value
generates an input field to insert a date.

datetime-local—This value
generates an input field to insert a date and a time.

week—This value
generates an input field to insert the number of the week.

month—This value
generates an input field to insert the number of the month.

time—This value
generates an input field to insert a time (hours and minutes).

hidden—This value
hides the input field. It is used to send complementary information to the
server.

password—This value
generates an input
field to insert a password. It masks the characters with stars or dots to hide
sensitive information.

color—This value
generates an input field to insert a color.

checkbox—This value
generates a checkbox to activate or deactivate an option.

radio—This value
generates a radio button to select an option. Usually, multiple radio buttons
are combined to provide several options to choose from.

file—This value
generates an input
field to select a file from the user’s computer.

button—This value
generates a button. The button works like the <button> element of
type button. It does not perform any action by default;
the action has to be defined from JavaScript, as we will see in later chapters.

submit—This value
generates a button to submit the form.

reset—This value
generates a button to reset the form.

image—This value
loads an image to use it
as a submit button. It requires the <input> element to
include the src attribute to specify
the image's URL.

To include a form
in our document, we have to declare it with the <form> element, as we did in
the previous example, and then incorporate between the tags all the elements
necessary for the user to insert the information and submit it to the server.
For example, if we want the user to insert his or her name and age, we have to
include two input fields for text and a third one to create the Submit button.

Listing 2-46: Including
tools in a form

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p><input
type="text" name="myname"></p>

<p><input
type="text" name="myage"></p>

<p><input
type="submit"></p>

</form>

</section>

</body>

</html>

The information
inserted in the form is sent to the server for processing. For the server to be
able to identify each value, the elements must include the name attribute. With this
attribute, we can assign a unique name to each element that will identify their
values. In Listing 2-46, we call the input fields "myname" and "myage"
(the <input> element that creates the Submit button
does not require a name because it does not send any data to the server).

Form elements do
not generate line breaks; they are shown on the screen one after another. If we
want the browser to display the elements one per line, we have to modify the
design ourselves. In Listing 2-46, we used <p> elements to separate
the elements in paragraphs, but this design is usually achieved with CSS
styles, as we will see in Chapter 4. The result is shown in Figure 2-29.

Figure 2-29: Form with two input
fields and a Submit button

Another attribute
we may use in this example is value. The input type submit creates a button to submit the
form. By default, browsers give the title Submit to the button, but we can
assign a different name with the value attribute.

Listing 2-47: Assigning a
different name to the Submit button

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p><input type="text"
name="myname"></p>

<p><input type="text"
name="myage"></p>

<p><input
type="submit" value="Send"></p>

</form>

</section>

</body>

</html>

The value attribute may also be
used to declare the initial value of an element. For example, we may insert the
user's age in the field if we already know it.

Listing 2-48: Declaring
initial values

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p><input type="text"
name="myname"></p>

<p><input
type="text" name="myage" value="35"></p>

<p><input type="submit"
value="Send"></p>

</form>

</section>

</body>

</html>

Do It Yourself: Copy the code in Listing 2-48 inside your
HTML file and open the document in your browser. You should see a form similar
to Figure 2-29 but with the number 35 inside the myage field and a Submit button with the title Send.

Forms have to
include descriptions for the elements to let the user know what to insert. For
this reason, HTML includes the <label> element. Because this
element not only provides a hint to the user but also helps browsers identify
each part of the form, they have to be associated with the elements they are
describing. To associate a <label> element to its form
element, we can include the form element inside the <label> element, as in the
following example.

Listing 2-49: Identifying
form elements

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p>

<label>Name:
<input type="text" name="myname"></label>

</p>

<p>

<label>Age:
<input type="text" name="myage"></label>

</p>

<p><input type="submit"
value="Send"></p>

</form>

</section>

</body>

</html>

Another way to
associate a <label> element with its form
element is implementing the for attribute. The for attribute connects the <label> element with a form
element using the value of the element's id attribute, as
illustrated by the following example.

Listing 2-50: Associating
labels with elements using the for attribute

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p>

<label
for="myname">Name: </label>

<input
type="text" name="myname"
id="myname"></label>

</p>

<p>

<label
for="myage">Age: </label>

<input
type="text" name="myage"
id="myage"></label>

</p>

<p><input type="submit"
value="Send"></p>

</form>

</section>

</body>

</html>

The <label> element does not
provide any styles by default, all it does is to associates a label with an element,
and therefore the text is shown on the screen with the standard font and size.

Figure 2-30: Elements identified with
a label

The input fields
used in previous examples were of type text, which means users can
insert any kind of text they want, but this is clearly not what we need. The
first input field expects a name, so it should not allow numbers or long texts,
and the second input field expects the user's age, so it should not accept
other characters than numbers. To determine how many characters are allowed,
the <input> element may include the following
attributes.

maxlength—This
attribute specifies the maximum number of characters allowed in the field.

minlength—This
attribute specifies the minimum number of characters allowed in the field.

The
following example limits the maximum characters allowed for the name to 15.

Listing 2-51: Setting the
maximum number of characters allowed

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p><label>Name: <input type="text"
name="myname" maxlength="15"></label></p>

<p><label>Age: <input
type="text" name="myage"></label></p>

<p><input type="submit"
value="Send"></p>

</form>

</section>

</body>

</html>

The maxlength attribute implemented
in the form in Listing 2-51 limits the number of characters the user is authorized
to insert, but the type of the input fields is still text, which means everything
could be typed into those fields. If the user inserts numbers in the myname field or letters in the
myage field, the browser will
consider the entry valid. To control the entry, we have to declare a different
value for the type attribute. The
following example declares the type number for the myage field to allow only
numbers to be inserted in this field, and includes other input fields for the
user's email, phone number, and website.

Listing 2-52: Requesting
specific types of inputs

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p><label>Name: <input
type="text" name="myname"
maxlength="15"></label></p>

<p><label>Age: <input type="number"
name="myage"></label></p>

<p><label>Email: <input type="email"
name="myemail"></label></p>

<p><label>Phone: <input type="tel"
name="myphone"></label></p>

<p><label>Website: <input type="url"
name="mywebsite"></label></p>

<p><input type="submit"
value="Send"></p>

</form>

</section>

</body>

</html>

The number type assigned to the myage field in Listing 2-52
is telling the element only to accept numbers. The rest of the types
implemented in this example do not impose any restrictions on the characters,
but they indicate to the browser the kind of values expected from the user. For
example, the email type expects an email,
so if no email is inserted, the browser does not allow the form to be submitted
and shows an error. The url type works exactly like
email but for web
addresses. It accepts only Absolute URLs and returns an error if the value is
invalid. Other types like tel do not force any
particular syntax but prompt the browser to suggest values to the user or to
provide a special keyboard in devices that require it. For instance, in mobile devices,
the keyboard shown when we tap on the myphone field
includes only numbers to facilitate the insertion of a phone number.

Although
these input types present their own restrictions or requirements, they all look
the same in the browser window.

Figure 2-31: Input fields of different
type

As illustrated in
Figure 2-31, browsers provide arrows on the right side of an input field of
type number to select a number. To establish restrictions on the possible
numbers the user can select with these arrows or control the numbers typed in
the field, <input> elements of type number may include the
following attributes.

min—The value of
this attribute determines the minimum value allowed for the field.

max—The value of
this attribute determines the maximum value allowed for the field.

step—The value of
this attribute determines the size of the steps by which the value of the field
will be increased or decreased. For example, if we set a step of 5 with a
minimum value of 0 and a maximum of 10, the browser will not let us specify a
value between 0 and 5 or 5 and 10.

The
following example restricts the value inserted in the myage field of our
form to a minimum of 13 and a maximum of 100.

Listing 2-53: Restricting
the numbers

<input
type="number" name="myage" min="13"
max="100">

Another input type
that implements these attributes is range. The range type creates an input
field to select a number from a range. The initial value is set according to
the values assigned to the min and max attributes, but we may declare
a specific value with the value attribute, as in the
following example.

Listing 2-54: Using the range type

<input
type="range" name="myage" min="13"
max="100" value="35">

Browsers display an
input of type range as a slider the user
can move to select a value.

Figure 2-32: Input of type range

Do It Yourself: Replace the <input> element in your HTML file with the element in Listings 2-53 or
2-54 to try these examples. Open the document in your browser and insert or
select a number to see how these types work. Try to submit the form with a
value less than the minimum or greater than the maximum allowed. The browser
should display an error.

The values for the range type implemented in the
previous example are not inserted in a text field but selected from a visual control
generated by the browser. The range type is not the only
one that presents these kinds of tools. For instance, the radio type creates a circular
button that is highlighted when it is selected (see Figure
2-28). This allows us to create a list of values that the user can
select by clicking on the corresponding radio button. To provide the user with
all the options, we have to insert one <input> element per option. The
<input> elements are associated
with each other by the value of their name attribute, and the value of each
option is defined by the value attribute, as in the
following example.

Listing 2-55: Using the radio type

<form
name="myform" method="get" action="process.php">

<p><label>Name: <input type="text"
name="myname" maxlength="15"></label></p>

<p><label><input type="radio" name="myage"
value="15" checked> 15 Years</label></p>

<p><label><input type="radio" name="myage"
value="30"> 30 Years</label></p>

<p><label><input type="radio" name="myage"
value="45"> 45 Years</label></p>

<p><label><input type="radio" name="myage"
value="60"> 60 Years</label></p>

<p><input type="submit" value="Send"></p>

</form>

In the form in
Listing 2-55, we declare four <input> elements of type radio to offer different ages
the user can choose from. Because all the elements have the same name (myage), they are all part of
the same group, and therefore only one of the options can be selected at a
time. Besides the name attribute, we also
implemented a Boolean attribute in this example called checked. This attribute tells
the browser to select the button when the document is loaded, setting the age
of 15 as the default value.

Figure 2-33: Inputs of type radio

As we mentioned,
only one radio button may be selected at a time. The value assigned to the value attribute of that
element is the one sent to the server when the form is submitted. A similar
type of input is provided by the checkbox type. In this case, the
user is allowed to select multiple options by checking a box.

Listing 2-56: Using the checkbox type

<form
name="myform" method="get"
action="process.php">

<p><label>Name: <input type="text"
name="myname" maxlength="15"></label></p>

<p><label><input type="checkbox"
name="age15" value="15" checked> 15
Years</label></p>

<p><label><input type="checkbox"
name="age30" value="30" checked> 30
Years</label></p>

<p><label><input type="checkbox"
name="age45" value="45"> 45
Years</label></p>

<p><label><input
type="checkbox" name="age60" value="60"> 60
Years</label></p>

<p><input type="submit" value="Send"></p>

</form>

The checkbox type is similar to the radio type, but we have to
use different names for each element because multiple options may be selected
by the user. When the user selects one or more options, the values of those
elements are sent to the server. These types of input may also include the checked attribute to select the
options by default. In this example, we checked two: 15 and 30.

Figure 2-34: Inputs of type checkbox

Another
type of input that generates a control is date. This
control helps the user select a date. Some browsers implement this feature with
a calendar that shows up every time the user clicks on an arrow in the field.
The value sent to the server by these types of inputs has the syntax
year-month-day, so if we want to set an initial value for the element or the
browser does not provide a tool to select the date, we have to specify it in
this format.

Listing 2-57: Using the date type

<input
type="date" name="mydate" value="2017-02-05">

The <input> element in
Listing 2-57 creates an input field with the date 2017-02-05 as the default
value. If this element is displayed in a browser that provides a calendar for
selecting the date, like Google Chrome, the first date shown on the calendar is
the one defined by the value attribute.

Figure 2-35: Input of type date

The date type is not
the only one available to insert dates. HTML also offers the datetime-local type to
select date and time, the week type to select a week,
the month type to select a month, and the time type to
select hours and minutes. These types have different purposes, and therefore
they expect values with different syntaxes. The datetime-local type expects
a value that represents a date and time with the syntax year-month-day hours:minutes:seconds
and the letter T separating the date and time, as in 2017-02-05T10:35:00. The week type expects
the syntax 2017-W30, where 2017 is the year, and 30 is the
number of the week. The month type expects the syntax
year-month. And the time type expects a value
that represents hours and minutes with the syntax hours:minutes (the :
character is converted to the value %3A when it is sent to the server).

Do It Yourself: Replace the <input> elements in your HTML file with the
element in Listing 2-57 and open the document in your browser. Click on the
element. In modern browsers like Google Chrome, you should see a calendar
popping up to select a date. Change the type of the <input> element to any of the input types
mentioned above to see what kind of tools the browser provides for inserting
the data. If you press the Send button, the value selected or inserted in the
field is added to the URL, and the browser tries to open the process.php file
on the server to process it. Because the file does not exist, an error is
returned, but you can check the URL to see how the value is sent. We will learn
more about how to submit a form and how to process its values on the server
later in this chapter.

In
addition to the types for dates and times, there is also a type called color that
provides a predefined interface to pick up a value. Usually, the value expected
for this field is an hexadecimal number, such as #00FF00.

Listing 2-58: Using the color type

<input type="color"
name="mycolor" value="#99BB00">

An <input> element of
type color is presented on the screen as a rectangle
painted with the color set by default, selected by the user, or defined by the value attribute.
When the user clicks on the rectangle, the browser opens a tool to select a new
color.

Figure 2-36: Input of type color

The Basics: Colors
are expressed in different formats and using different color systems. The most
common nomenclature is the hexadecimal, which syntax includes the # character
followed by three hexadecimal values that represent the components of the color
(red, green, and blue). We will learn how to define colors in Chapter 3.

So far, we have
been using an <input> element of type submit to create the button
the user presses to submit the form. This input type creates a standard button
identified with a title. If we want to improve the design, we can implement the
input type image that creates a submit
button with an image. These types of inputs require the src attribute to be
declared with the URL of the file containing the image we want to use, and they
can also contain the width and height attributes to define a
custom size for the image.

Listing 2-59: Using the image type to create a submit button

<form
name="myform" method="get"
action="process.php">

<p><label>Name: <input type="text"
name="myname"></label></p>

<p><label>Age: <input type="text"
name="myage"></label></p>

<p><input
type="image" src="buttonsubmit.png"
width="100"></p>

</form>

The form
in Listing 2-59 creates a Submit button with an <input> element of
type image. The element loads the image in the file
buttonsubmit.png and shows it on the screen. When the user clicks on the image,
the form is submitted the same way as before.

Figure 2-37: Input of type image

Do It Yourself: Replace the form in your HTML file with
the form in Listing 2-59. Download the buttonsubmit.png image from our website.
Open the document in your browser. The original image has a width of 150
pixels, but because we declared the width attribute with a value of 100, the
browser reduces its size to 100 pixels. Remove this attribute to see the image
in its original size.

Although the
buttons created from <input> elements are probably
more than enough for most projects, HTML offers a more versatile element to
create buttons called <button>. This element includes
the type attribute to set the type of button we want to create. For
instance, if we want to create a button to submit the form, we have to declare
the type as submit.

Listing 2-60: Using the <button> element to create a
submit button

<form
name="myform" method="get"
action="process.php">

<p><label>Name: <input type="text"
name="myname"></label></p>

<p><label>Age: <input type="text"
name="myage"></label></p>

<p><button
type="submit">Send Form</button></p>

</form>

The Basics: The
<button> element creates a standard button with
the same characteristics of the button created by the <input> element. The difference is that the title for this button is
defined between the element's opening and closing tags, which allows us to use
other HTML elements or even images to declare it. This element is also better
prepared to be customized with CSS styles and to work with JavaScript, as we
will see in later chapters.

The <input> element
allows the user to insert or select different types of values, but the input
fields generated by this element can only accept one line of text. HTML
provides the <textarea> element to
insert multiple lines of text. The element has an opening and a closing tag and
may include the attributes rows and cols to define
the area's width and height in characters.

Listing 2-61: Using the <textarea> element

<form
name="myform" method="get"
action="process.php">

<p><label>Text: <textarea name="mytext"
cols="50"
rows="6"></textarea></label></p>

<p><input type="submit" value="Send"></p>

</form>

In the browser window, the <textarea> element is represented by an empty rectangle of the
size determined by its attributes or the styles set by default. The text
inserted in this area is limited to its width, but it can extend vertically as
long as necessary. If the area is not high enough to display all the text, the
browser shows a scroll bar on the side to scroll the content.

Figure 2-38: The <textarea> element

Do It Yourself: Replace the form in your HTML file with the form in
Listing 2-61 and open the document in your browser. Write multiple lines of
text to see how the text is displayed inside the area.

The Basics: If you want to declare an initial value
for a <textarea> element, you can insert the text between
the opening and closing tags.

Besides the input
types radio and checkbox studied before, HTML
also offers the <select> element to present a
list of values to the user. When the user clicks on this element, the list is
shown as a popup menu, and then the value selected by the user is inserted in
the field. Because the <select> element does not create
an input field, the user cannot insert a custom value, only those on the list
are allowed.

The <select> element works along
with the <option> element to define the options. The <select> element must include
the name attribute to identify the value, and each <option> element has to include
the value attribute to define the value they represent.

Listing 2-62: Using the <select> element

<form
name="myform" method="get"
action="process.php">

<p>

<label
for="bookslist">Books: </label>

<select
name="book" id="bookslist">

<option
value="1">IT</option>

<option
value="2">Carrie</option>

<option
value="3">The Shining</option>

<option
value="4">Misery</option>

</select>

</p>

<p><input type="submit" value="Send"></p>

</form>

In Listing 2-62, we include four options, one for
each book we want to show on the list. The values of the options are defined
from 1 to 4, so every time the user selects a book and submits the form, the
corresponding number is sent to the server with the identifier "book"
(the value of the name attribute).

Figure 2-39: Selecting a value with
the <select> element

Another way to
create a predefined list is with the <datalist> element. This element defines a
list of items that, with the help of the list attribute,
can be used as a suggestion in an input field. As the options for the <select> element, the
options for this element are defined by the <option> element, but
they may include the label attribute to describe
the value. The following example creates a form with an input field to insert a
phone number. The code includes a <datalist> element with
two <option> elements to define the
two values we want to suggest to the user.

Listing 2-63: Suggesting a
list of values with the <datalist> element

<form
name="myform" method="get"
action="process.php">

<datalist
id="mydata">

<option
value="123123123" label="Phone 1">

<option
value="456456456" label="Phone 2">

</datalist>

<p><label>Phone: <input type="tel"
name="myphone" list="mydata"></label></p>

<p><input type="submit" value="Send"></p>

</form>

To connect an input field to a <datalist> element, we have to include the list attribute in the <input> element with the same value we used
to identify the <datalist> element. To this end, in the form in
Listing 2-63 we include the id attribute in the <datalist> element with the value "mydata"
and then assigned that same value to the list attribute of the <input> element. Now, the input field shows
an arrow that displays a list of predefined values the user can choose from to
fill the form.

Figure 2-40: Predefined values with
the <datalist> element

Do It Yourself: Replace the form in your HTML file with
the form in Listing 2-63. Open the document in your browser and click the
arrows on the right side of the field. You should see something similar to
Figure 2-40.

There
are two more elements we may include inside forms: <progress> and <meter>. These
elements are not considered form elements but because they represent
measurements, they come in handy when our form produces this kind of
information.

The <progress> element is
used to report the progress of a task. The element uses two attributes to set
its status and limits. The value attribute indicates how
much of the task has been completed, and the max attribute
declares the value needed to be reached for the task to be over.

Listing 2-64: Using the <progress> element

<progress
value="30" max="100">0%</progress>

Browsers
represent this element with a bar. If the browser cannot recognize the element,
it shows the value between the opening and closing tags. By default, the
portion of the bar that represents progress is shown in color blue.

Figure 2-41: Progress bar

Similar
to <progress>, the <meter> element is
used to show a scale, but not for progression. It represents a known range,
such as the bandwidth usage or storage capacity. This element may include several attributes. The
attributes min
and max set the bounds of the range, value determines the value measured, and low, high and optimum are used to segment the range into
differentiated sections and set the optimum position.

Listing 2-65: Using the <meter> element

<meter
value="60" min="0" max="100" low="40"
high="80" optimum="100">60</meter>

The code
in Listing 2-65 generates a bar on the screen showing a level of 60 on a scale
of 0 to 100 (according to the values declared by the value, min and max attributes).
The color of the bar generated by the element on the screen will depend on the
levels set by the low, high and optimum attributes.
Because the current value in our example was set between the values of the low and high attributes,
the bar is shown in color yellow.

Figure 2-42: Bar created with the <meter> element

Submission

When a form is submitted, the data
introduced in the form is sent using a name/value pair for each element, where name
is the value assigned to the element's name attribute and value is the
value introduced or selected by the user. For example, if we insert the text
"Robert" in an input field identified with the name
"myname", the name/value pair sent to the server will be myname/Robert.
Browsers use two methods to submit this information: GET and POST. The method
is set assigning the value GET or POST to the method attribute of the <form> element, as we did in previous
examples, but which one to use depends on the type of information managed by
the form. As we mentioned in Chapter 1, browsers communicate with servers using
a protocol called HTTP. When the browser wants to access a document, it sends
an HTTP request to the server. An HTTP request is a message that tells the
server which is the resource the browser wants to access and what it wants to
do with it (download the document, process information, etc.). The message is
composed of the resource's URL, some metadata associated with the request
itself, such as the date and the language, and a body with additional
information. The name/value pairs produced by a form are sent to the server
inside these HTTP requests. If the method was set as GET, the name/value pairs are added at
the end of the URL, but if the method was set as POST, the values are included in the
body of the request. This means that the information sent with the GET method
is visible to the user (the user can see the URL with all the name/value pairs
in the browser's navigation bar), but the information sent with the POST method
is hidden inside the request. Therefore, if the information is sensitive or
private, we should use the POST method, but if the information is non-sensitive,
such as a search query, we can use the GET method.

In addition, we have to
consider that the POST method can be used to send unlimited information, but
the GET method has to comply with the limitations presented by URLs. This is
because the length of a URL is limited. If the information inserted in the form
is extensive, it could get lost.

The following example presents
a form with a single input field to illustrate how the process works. The <input> element was identified with the
name "val", and the method used to send the information was set as
GET, which means that the value inserted by the user will be added to the URL.

Listing 2-66: Form with the method GET

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform" method="get"
action="process.php">

<p><input type="text" name="val"></p>

<p><input type="submit" value="Send"></p>

</form>

</section>

</body>

</html>

In Listing 2-66, a file called
process.php was declared as the file in charge of processing the information.
When the user presses the Send button, the browser creates an HTTP request that
includes a URL pointing to this file. Because the method was set as GET, the
name of the input field and the value inserted by the user are appended to the
URL, as shown below.

Figure 2-43: Name/value pairs in the
URL

When the information is sent
with the GET method, the name/value pairs are added at the end of the URL
separated by the = character, and the first pair is preceded by the ?
character. If there are more than one pair of values, the rest are added to the
URL separated by the & character, as in
www.example.com/process.php?val1=10&val2=20.

On the other side, the server
receives this request, reads the URL, extracts the values, and executes the
code in the process.php file. This code must process the information and
produce a response. The way this is done depends on the programming language we
use and what we want to achieve. For example, to read the values sent with a
GET method, PHP offers an array called $_GET. The syntax includes the name of
the value within square brackets.

Listing 2-67: Processing data in the server
with PHP (process.php)

<?php

print('The value is: '.$_GET['val']);

?>

The example of Listing 2-67
shows how to process values with PHP. The <?php and ?> tags indicate to the server that
this is PHP code and it has to be executed. The code may be extensive or just
composed of a few instructions, depending on what we need. Our example includes
only one instruction to illustrate how the information is processed. This
instruction, called print(), takes the values between
parentheses and includes them in the file that is going to be returned to the
browser as a response. In this case, we have added the value received from the
request to the string "The value is: ". If we send the value 10 to
this code, as in the example in Figure 2-43, the server generates a file with
the text "The value is 10" and sends it to the browser.

The Basics: The
PHP code in our example uses the variable $_GET to capture the information received from the browser
because the form method was set as GET, but if we change the method to POST, we
have to use the $_POST variable. We will learn more about instructions and
variables in Chapter 6.

The file produced by the server
from a PHP code is an HTML file. For educational purposes, we did not include
any HTML element in the example of Listing 2-67, but we should always generate
a valid HTML document in response. There are different ways of defining a
document in PHP. The simplest is to create the HTML document inside the PHP
file as we always do, and include the PHP code where we want to show the
result. For instance, the following example inserts the PHP code inside a <p> element. When the server opens the
process.php file with this document, it executes the PHP code, inserts the
result between the <p> tags, and returns the file to the
browser. The result is the same as if we had created a static document with the
text "The value is: 10", but the string was generated dynamically on
the server from the value received from the form.

Listing 2-68: Generating an HTML document
with PHP (process.php)

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Response</title>

</head>

<body>

<section>

<p>

<?php

print('The value is: '.$_GET['val']);

?>

</p>

</section>

</body>

</html>

Do It Yourself: To test this example, you have to upload the files to
a server that can execute PHP code. If you do not have a hosting account with
this capabilities, you can install a server on your computer with a package
like MAMP, as explained in Chapter 1 (if you do not know how to use a server,
do not worry, the procedure is not required for most of the examples in this
book). Create a new HTML file with the code in Listing 2-66, and a file called
process.php with the code in Listing 2-67 or 2-68. Upload the files to the
server and load the HTML file in your browser. Insert a value into the input
field and press the Send button. The browser should display a new page
containing a text that begins with the string "The value is: " and
ends with value you inserted in the form.

IMPORTANT:
This is just an example
of how the information is processed and how the browser transmits the data
entered in the form to the server. We will see more examples in following
chapters, but it is not the purpose of this book to teach you PHP or any other
server-side programming language. To learn more about PHP, go to our website
and visit the sections Links and Videos.

Global Attributes

HTML defines
global attributes that are unique to form elements. The following are the most
frequently used.

disabled—This is a
Boolean attribute that disables the element. When the attribute is present, the
user is not allowed to insert values or interact with the element.

readonly—This
attribute indicates that the value of the element cannot be modified.

placeholder—This
attribute shows a text in the field's background to provide a hint to the user
of what to insert.

autocomplete—This
attribute activates or deactivates the autocomplete feature for the element.
The possible values are on and off.

novalidate—This is a
Boolean attribute for the <form> element that
indicates that the form should not be validated.

formnovalidate—This is a
Boolean attribute for the <button> or <input> elements of
type submit and image that
indicates that the form to which the element belongs to should not be
validated.

required—This is a
Boolean attribute that indicates to the browser that the user must select or
insert the value of the element to validate the form.

multiple—This is a
Boolean attribute that indicates to the browser that multiple values may be
inserted in the field (it applies to <input> elements of
type email and file).

autofocus—This Boolean
attribute asks the browser to move the focus to the element as soon as the
document is loaded.

pattern—This
attribute defines a regular expression that the browser has to use to validate
the value inserted in an input field.

form—This
attribute associates an element with a form. It is used to connect an element with
its form when the element was not defined between <form> tags. The
value assigned to this attribute must be the same value assigned to the id attribute of
the <form> element.

spellcheck—This
attribute asks the browser to check spelling and grammar. The possible values
are true and false.

The disabled and readonly attributes
have a similar purpose, not to allow the user to interact with the element, but
they apply in different situations. The disabled attribute is
usually implemented when we want to show the user that the element may be
available in other circumstances, like when the control does not apply to the
user's country. On the other hand, the readonly attribute is
implemented when there is only one possible value to insert and we do not want
the user to change it. For example, in the following form, the user is not
allowed to enter the age.

Listing 2-69: Using the disabled attribute

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form
name="myform" method="get"
action="process.php">

<p><label>Name: <input type="text"
name="myname"></label></p>

<p><label>Age: <input type="text"
name="myage" disabled></label></p>

<p><input type="submit" value="Send"></p>

</form>

</section>

</body>

</html>

Controls
affected by the disabled and readonly attributes
are shown in lighter colors to warn the user that they are not normal controls.
Another attribute that affects the appearance of an element is placeholder. This
attribute is used with input fields to display a word or phrase that helps the
user produce the right entry. The following example inserts a placeholder in a
search field.

Listing 2-70: Using the placeholder attribute

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form
name="myform" method="get"
action="process.php">

<p><label>Search: <input type="search"
name="mysearch" placeholder="Type your
seach"></label></p>

<p><input type="submit" value="Send"></p>

</form>

</section>

</body>

</html>

The
value of this attribute is shown by browsers inside the field until the user
inserts a value.

Figure 2-44: Input field with a
placeholder

One of
the characteristics of forms is a built-in capability for validation. Every
form is validated unless the novalidate attribute is
declared. This Boolean attribute is specific of <form> elements.
When included, the form is submitted without validation. The presence of this
attribute affects the form permanently, but sometimes the validation process is
required only in some circumstances. For example, when the information inserted
has to be saved to resume the work later. In cases like this, we can implement the
formnovalidate attribute.
When the form is submitted with a button that contains this attribute, the form
is not validated.

Listing 2-71: Sending a
form without validation with the formnovalidate attribute

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p><label>Email: <input
type="email" name="myemail"></label></p>

<p>

<input type="submit"
value="Send">

<input
type="submit" value="Save" formnovalidate>

</p>

</form>

</section>

</body>

</html>

The form in Listing 2-71 will be
validated if it is submitted with the Send button, but we include a second
button with the formnovalidate attribute to provide the
option to send the form without going through the validation process. The Send
button requires the user to insert a valid email, but the Save button does not.

When we use the email type to receive an email
address, as in the previous example, the browser checks whether the entry is a
valid email or not, but it validates the input field when it is empty. This is
because the field is not required. HTML offers the required attribute to change this condition. When
the required attribute is present, the input will only
be valid if a value is inserted and it complies with the requisites of its
type. The following example implements this attribute to force the user to
insert an email.

Listing 2-72: Declaring
the email input as a
required field

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p><label>Email: <input type="email"
name="myemail" required></label></p>

<p>

<input type="submit"
value="Send">

<input type="submit"
value="Save" formnovalidate>

</p>

</form>

</section>

</body>

</html>

Do It Yourself: Create an HTML file with the code in
Listing 2-72. Open the document in your browser and press the Send button. You
should get an error on the myemail field asking you to insert a
value. Press the Save button. Because this button includes the formnovalidate attribute, the error is not shown anymore
and the form is submitted.

Another attribute used for validation
is pattern. Some input types validate specific kinds
of strings, but cannot help when the value is not standard, like a zip code.
There is no predetermined input type for this kind of entry. The pattern attribute lets us create a custom filter using Regular
Expressions.

Regular Expressions are strings
composed of a series of characters that define a matching pattern. For
instance, the characters 0-9 determine that only numbers between 0 to 9 are
accepted. Using this kind of expressions, we can create a custom filter to
validate any value we want. The following form includes an input field that
only accepts a number with five digits.

Listing 2-73: Customizing
types with the pattern attribute

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form
name="myform" method="get"
action="process.php">

<p><label>Postal Code: <input pattern="[0-9]{5}"
name="postalcode" title="Insert your postal
code"></label></p>

<p><input type="submit" value="Send"></p>

</form>

</section>

</body>

</html>

The <input> element in Listing 2-73 includes the pattern attribute with the value [0-9]{5}. This regular expression determines that the value
must have exactly five characters and the characters have to be numbers between
0 and 9. Therefore, we can only insert a number of five digits; any other
characters or length will return an error. Notice that these types of inputs
can also include the title attribute to explain to the user which is
the expected value. This message complements the standard error message
displayed by the browser, as shown in Figure 2-45.

Figure 2-45: Error in an input field
with a custom pattern

IMPORTANT: It is not the purpose of this book to teach Regular
Expressions. For more information on the subject, visit our website and follow
the links for this chapter.

The input types email and file only allow the user to insert
one value at a time. If we want to allow the insertion of multiple values, we
have to include the multiple attribute. When this attribute
is present, we can insert all the values we want separated by commas. The
following example includes an input field that accepts multiple email
addresses.

Listing 2-74: Declaring
the email input as a
multiple field

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form
name="myform" method="get"
action="process.php">

<p><label>Email: <input type="email"
name="myemail" multiple></label></p>

<p><input type="submit" value="Send"></p>

</form>

</section>

</body>

</html>

Besides
validation, there are a few attributes that can help the user decide what to
insert. For instance, the autocomplete attribute
can activate a feature that suggests values to the user based on previous
inputs. The possible values for this attribute are on and off to activate
or deactivate the feature. This attribute can also be implemented in the <form> element to
affect all the elements in the form. The following form disables the
suggestions for a search.

Listing 2-75: Using the autocomplete attribute

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form
name="myform" method="get"
action="process.php">

<p><label>Search: <input type="search"
name="mysearch"
autocomplete="off"></label></p>

<p><input type="submit" value="Send"></p>

</form>

</section>

</body>

</html>

Do It Yourself: Create an HTML file with the code in
Listing 2-75. Open the document in your browser. Insert a value and press the
Send button multiple times. The browser should not suggest any value to insert.
Change the value of the autocomplete attribute to on and repeat the process. Now, every time you try to insert a
value, the browser shows you a list of the values previously inserted that
begin with the same characters.

Another attribute that may help the
user decide what to insert is autofocus. In this case, the attribute
sets the focus on an element when the document is loaded, suggesting the user
what value to insert first. The following example includes two input fields to
insert the user's name and age, but the field for the age includes the autofocus attribute so that the browser positions
the cursor on this field by default.

Listing 2-76: Using the autofocus attribute

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form
name="myform" method="get"
action="process.php">

<p><label>Name: <input type="text"
name="myname"></label></p>

<p><label>Age: <input type="text"
name="myage" autofocus></label></p>

<p><input type="submit" value="Send"></p>

</form>

</section>

</body>

</html>

Another feature that can be activated
automatically by browsers is spell checking. While this is a useful tool and
users expect to have it available most of the time, it can be inappropriate in
some circumstances. Spell checking is activated by default, but we can include
the spellcheck attribute with the value false to deactivate it, as in the following example.

Listing 2-77: Deactivating the spell check feature

<!DOCTYPE html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Forms</title>

</head>

<body>

<section>

<form
name="myform" method="get"
action="process.php">

<p><label>Text: <textarea name="mytext"
cols="50" rows="6"
spellcheck="false"></textarea></label></p>

<p><input type="submit" value="Send"></p>

</form>

</section>

</body>

</html>

Finally, there is a useful attribute we can implement to declare elements for a form
outside the <form> element. For example, if we
have an input field in the navigation area but we want the element to be
submitted with a form defined in the main area, we can connect the element with
the form using the form attribute (The value of the
attribute has to match the value of the id attribute assigned to the <form> element).

Listing 2-78: Declaring
form elements anywhere in the document

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

</head>

<body>

<nav>

<p><input
type="search" name="mysearch"
form="myform"></p>

</nav>

<section>

<form name="myform"
id="myform" method="get" action="process.php">

<p><input type="submit"
value="Send"></p>

</form>

</section>

</body>

</html>

IMPORTANT: Forms are extremely useful in
web development, and are required in almost every website and web application.
The examples in this chapter were created for educational purposes and only
include the basic features to test the elements and controls available. We will
learn how to implement forms in more practical situations and how to expand
them with CSS and JavaScript in later chapters.

Chapter 3 - CSS

3.1 Styles

In the previous chapter, we have discussed
how to create an HTML document, how to organize its structure, and which
elements are more appropriate to represent its content. This information allows
us to define the document, but it does not determine how it is going to be
shown on the screen. Since the introduction of HTML5, that is the
responsibility of CSS. CSS is a language that provides instructions we can use
to assign styles to HTML elements, such as colors, fonts, sizes, etc. Styles
must be defined with CSS and then assigned to the elements until we achieve the
design we want for our document.

For compatibility reasons, browsers automatically
assign styles to some HTML elements. This is why in Chapter 2 some elements had
margins or generate line breaks, but others look the same (same fonts and
colors, for example). Some of these styles are useful and provide a starting
point, but most of them have to be replaced or complemented with custom styles.
In CSS, custom styles are declared with properties. We define a style by
declaring the property's name and its value separated by a colon. For instance,
the following example declares a property that changes the size of the font to
24 pixels (because some properties may include multiple values separated by a
space character, we must indicate the end of the line with a semicolon).

Listing 3-1: Declaring CSS properties

font-size: 24px;

If the property in Listing 3-1 is
applied to an element, the text contained by that element will be shown on the
screen with the font defined by default but with a size of 24 pixels.

The Basics: Although pixels (px) are the units implemented
most of the time, we will see later that in some circumstances, especially when
we create our website using Responsive Web Design, others may be more
appropriate. The most common are px (pixels), pt (points), in (inches), cm (centimeters), mm (millimeters), em (relative to the element's font size), rem (relative to the document's font size), and % (relative to the size of the element's container).

Most CSS properties can modify only
one aspect of the element (the size of the font, in this case). If we want to
change multiple styles at once, we have to declare multiple properties. CSS
defines a syntax that simplifies the process of assigning multiple properties
to an element or several elements at once. The construction is called rule.
A rule is a list of properties declared between braces and identified by a
selector. The selector indicates which elements will be affected by the
properties. For instance, the following rule is identified with the selector p and therefore its properties are going to be applied to all the <p> elements in the document.

Listing 3-2: Declaring CSS rules

p {

color: #FF0000;

font-size: 24px;

}

The rule in Listing 3-2 includes two
properties with their corresponding values surrounded by braces (color and font-size). If we apply this rule to our
document, the text inside every <p> element will be red and have a
size of 24 pixels.

Applying Styles

Properties and rules define the styles we
want to assign to an element or multiple elements, but they are not applied
until we include them in the document. There are three techniques available for
this purpose. We can use Inline Styles, Embedded Styles, or Style Sheets. The
first one, Inline Styles, takes advantage of a global attribute called style to insert the styles directly into the element. This
attribute is available in every HTML element. It takes a property or a list of
properties and applies them to the element it belongs to. If we want to assign
styles using this technique, we have to declare the style attribute in the element we want to modify and assign the
CSS properties to it.

Listing 3-3: Declaring Inline Styles

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

</head>

<body>

<main>

<section>

<p style="font-size: 20px;">My text</p>

</section>

</main>

</body>

</html>

The document of Listing 3-3 includes
a <p> element with the style attribute and the value font-size: 20px;. When the browser reads the
value of the attribute, it assigns a size of 20 pixels to the text inside this <p> element.

Do It Yourself: Create a new HTML file
with the code in Listing 3-3 and open the document in your browser. You should
see the text "My text" with letters of the size defined by the font-size property. Try to change the
property's value to see how different font sizes are display on the screen.

Inline Styles are a good way to test
styles and see how they modify the elements, but it is not recommended for
large projects. The reason is that the style attribute only affects the
element in which it was declared. If we want to assign the same style to other elements,
we have to repeat the code for every one of them, which greatly increases the
document’s size, making it difficult to update and maintain. For instance, if
we later decide that instead of 20 pixels the size of the text in every <p> element should be 24 pixels,
we would have to change every style in every <p> element in the whole document and every
document of our website.

A better alternative is
to embed CSS rules in the head of the document with selectors that determine
the elements that will be affected. For this purpose, HTML includes the <style> element.

Listing 3-4: Including styles in the
head of the document

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<style>

p {

font-size: 20px;

}

</style>

</head>

<body>

<main>

<section>

<p>My text</p>

</section>

</main>

</body>

</html>

The property declared in between <style> tags in the document of Listing 3-4 has
the same function as the one declared in the document of Listing 3-3, but in
this example we do not have to write the style inside every <p> tag we want to modify because
all of them are affected by the rule.

Declaring the styles in the head of the
document simplifies the code, but it requires us to copy the rules in every
document of our website. Because most pages in our website will share the same
design, most rules will be duplicated. The solution is to move all the styles
to an external file and then use the <link> element to insert the file
inside every document that requires it. These types of files are called Style
Sheets, but are no more than text files with the list of CSS rules we want
to apply to the elements in the document.

As we have seen in Chapter 2, the <link> element is used to incorporate external
resources to the document. Depending on the type of resource we want to load,
we have to declare different attributes and values. CSS Style Sheets only need the attributes rel and href. The attribute rel means relationship and specifies the
relationship between the document and the file we are incorporating, so we have
to declare its value as stylesheet to tell the browser that the resource is a CSS file with the
styles required to render the page. The href attribute, on the other hand,
declares the URL of the file to be loaded.

Listing 3-5: Applying CSS styles from an
external file

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<link
rel="stylesheet" href="mystyles.css">

</head>

<body>

<main>

<section>

<p>My text</p>

</section>

</main>

</body>

</html>

The document of Listing 3-5
loads the CSS styles from the mystyles.css file. In this file, we have to
declare the CSS rules we want to apply to the document as we did inside the <style> element before. The following is the
code we have to insert in the mystyles.css file to produce the same effect we
achieved in previous examples.

Listing 3-6: Defining CSS styles in an
external file

p {

font-size: 20px;

}

Including the CSS styles in a
separate file is recommended for websites designed with HTML5, not only because we can
define all our styles in one Style Sheet and then include the file in every
document with the <link> element, but also because
we can replace the entire set of styles by loading a different file, which
allows us to test different designs as well as adapting the website to multiple
devices, as we will see in Chapter 5.

Do It Yourself: Create an HTML file with the code in
Listing 3-5 and a file called mystyles.css with the code in Listing 3-6. Open
the document in your browser. You should see the text inside the <p> element with a size of 20 pixels. Change
the value of the font-size property in the CSS file and
refresh the page to see how the style is applied to the document.

Cascading Style Sheets

An important characteristic of CSS is
that the styles are assigned in cascade (hence the name Cascading Style
Sheets). Elements in lower levels inherit styles that are assigned to an
element in a higher level in the hierarchy. For example, if we assign the rule in
Listing 3-6 to the <section> elements instead of the <p> elements, as shown below, the text
in the <p> element in our document
will be shown with a size of 20 pixels because this element is a child of the <section> element and inherits its styles.

Listing 3-7: Inheriting styles

section {

font-size: 20px;

}

Inherited styles may be replaced with
new styles defined for the elements in a lower hierarchy level. For example, we
may declare an additional rule for <p> elements that overwrite the font-size property defined for <section> elements with a different value.

Listing 3-8: Overwriting styles

section {

font-size: 20px;

}

p {

font-size: 36px;

}

Now, the <p> element in our document will be shown
with a size of 36 pixels, because the value of the font-size property assigned to the <section> elements is overwritten by the rule
assigned to the <p> elements.

Figure 3-1: Cascading styles

Do It Yourself: Replace the styles in your mystyles.css
file with the code in Listing 3-8 and open the document of Listing 3-5 in your browser. You should
see the text inside the <p> element with a size of 36
pixels, as illustrated in Figure 3-1.

3.2 References

Declaring all the styles together in
one external file and then loading that file from every document is very
convenient. However, we need mechanisms to establish the relationship between
the CSS rules in the file and the elements inside the document that will be
affected by them.

There are several methods to select
which HTML elements will be affected by a CSS rule. In previous examples, we
used the element's name, but we can also use the values of the id and the class attributes to reference an element or a
group of elements, or even combine them to build more specific selectors.

Keywords

A rule declared with the keyword of the
element as its selector affects every element of that type in the document. In
previous examples, we used the p keyword to modify <p> elements, but we can change the keyword
to work with any element in the document we want. If we specify the span keyword, for instance, every
text between <span> tags will be modified.

Listing 3-9: Referencing <span> elements by keyword

span {

font-size: 20px;

}

If we want to assign the same styles
to elements of different type, we can declare the keywords separated by commas.
In the following example, the rule affects all the <p> and <span> elements found in the
document.

Listing 3-10: Declaring rules with multiple
selectors

p, span {

font-size: 20px;

}

We can, as well, reference only
elements that are inside a particular element by listing the selectors
separated by a space. These selectors are called descendant selectors
because they affect elements that are inside other elements, regardless of
their place in the hierarchy.

Listing 3-11: Combining selectors

main p {

font-size: 20px;

}

The rule in Listing 3-11 affects only the <p> elements that are inside the <main> element, either as their primary content or embedded
in other elements. For instance, the following document includes a main section
with a header and a section inside. Both, the header and the section, include <p> elements to represent their content. If we apply the
rule in Listing 3-11 to this document, the text in every <p> element will have a size of 20 pixels because all of
them are descendants of the <main> element.

Listing 3-12: Testing selectors

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<main>

<header>

<h1>Title</h1>

<p>This is the introduction</p>

</header>

<section>

<p>Sentence 1</p>

<p>Sentence 2</p>

<p>Sentence 3</p>

<p>Sentence 4</p>

</section>

</main>

</body>

</html>

The rule in Listing 3-11 only affects
<p> elements that are inside the <main> element. If we, for example, add a <footer> element at the end of the document of
Listing 3-12, the <p> elements inside this footer will
not be modified. Figure 3-2 shows what we see when we open this document in the
browser.

Figure 3-2: Descendant selectors

CSS includes additional syntaxes to make
a more accurate selection. For instance, we can use the > character to reference an element that is a direct child
of another element.

Listing 3-13: Applying the > selector

section > p {

font-size: 20px;

}

The > character indicates that the element
affected is the element on the right when it has the element on the left as its
parent. The rule in Listing 3-13 modifies the <p> elements that are children of a <section> element. When we apply this
rule to the document of Listing 3-12, the <p> element inside the <header> element is not affected.

Figure 3-3: Child selector

Another selector provided by CSS uses
the + character. This selector references an
element that is immediately preceded by another element. Both elements must
share the same parent.

Listing 3-14: Applying the + selector

h1 + p {

font-size: 20px;

}

The rule in Listing 3-14 affects the <p> elements that are located
immediately after an <h1> element. If we apply this rule to our document, only the <p> element inside the header will
be modified, because it is the only one that is preceded by an <h1> element.

Figure 3-4: Adjacent Sibling selector

The previous selector affects only
the <p> element that is immediately after an <h1> element. If another element is in between
both elements, the <p> element is not modified. CSS
includes the ~ character to create a selector
that affects every element that follows another element. This selector is
similar to the previous one, but the element affected is not necessarily
immediately preceding the first element. Also, the rule affects all the
elements found, not just the first one.

Listing 3-15: Applying the ~ selector

p ~ p {

font-size: 20px;

}

The rule in Listing 3-15 affects all
the <p> elements that precede another <p> element. In our example, all the <p> elements inside the <section> element will be modified, except for the
first one, because there is no <p> element preceding the first <p> element.

Figure 3-5: General Sibling selector

Do It Yourself: Create a new HTML file with the code in Listing 3-12. Create a CSS file called
mystyles.css and write inside the rule you want to try. Insert other elements
between the <p> elements to see how the rules
work.

Id Attribute

The previous rules affect all the elements
of the type indicated by the selector. To select an HTML element without
considering its type, we can use the id attribute. This attribute is a unique
identification for the element, and therefore we can use it to find a
particular element inside the document. To reference an element using its id attribute, the selector must include the
value of the attribute preceded by the hash character (#).

Listing 3-16: Referencing by the value of
the id attribute

#mytext {

font-size: 20px;

}

The rule in Listing 3-16 only applies
to the HTML element identified by the attribute id with the value "mytext", as the <p> element included in the
following document.

Listing 3-17: Identifying the element <p> by the value of its id attribute

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<main>

<section>

<p>Sentence 1</p>

<p id="mytext">Sentence 2</p>

<p>Sentence 3</p>

<p>Sentence 4</p>

</section>

</main>

</body>

</html>

The benefit of using this procedure is
that every time we create a reference using the identification mytext in our CSS file, the element
with that id value is modified, but other elements are not affected. This is a
very specific way to reference an element and is commonly used for structural
elements, such as <section> or <div>. Because of its characteristics, the id attribute is also used to reference
elements from JavaScript, as we will see in later chapters.

Class Attribute

Rather than using the id attribute, to style multiple elements it is
better to use the class attribute. This attribute is more flexible and can be assigned to
all the elements that have styles in common.

Listing 3-18: Referencing by the value of
the class attribute

.mytext {

font-size: 20px;

}

To reference an element using its class attribute, the selector must
include the value of the attribute preceded by a dot. For instance, the rule in
Listing 3-18 affects all the elements that contain a class attribute with the value "mytext", as in the following example.

Listing 3-19: Assigning styles with the class attribute

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<main>

<section>

<p
class="mytext">Sentence 1</p>

<p class="mytext">Sentence 2</p>

<p>Sentence 3</p>

<p>Sentence 4</p>

</section>

</main>

</body>

</html>

The <p> elements in the first two lines inside
the main section in Listing 3-19 include the attribute class with the value "mytext".
Because the same rule can be applied to different elements in the document,
these first two elements are affected by the rule in Listing 3-18. On the other
hand, the last two <p> elements do not include the class attribute, and therefore they will
be shown with the styles by default.

Rules assigned through the class attribute are called classes. Several classes may be
assigned to the same element. All we have to do is declare the names of the
classes separated by space (e.g., class="text1 text2"). Also, the class may be declared as exclusive for
particular types of elements adding the element's keyword before the period.

Listing 3-20: Declaring a class only for <p> elements

p.mytext {

font-size: 20px;

}

In Listing 3-20, we create a rule
that references the class named mytext but only for the <p> elements. If any other element has the class attribute with this same value,
it will not be modified by this particular rule.

Other Attributes

Although these methods of referencing
elements cover a variety of situations, sometimes they are insufficient to find
the exact element we want to style. For situations in which the id and class attributes are not enough, CSS
allows us to reference an element by any other attribute we need. The syntax to
define the selector includes the element's keyword followed by the name of the
attribute in square brackets.

Listing 3-21: Referencing only <p> elements that have a name attribute

p[name] {

font-size: 20px;

}

The rule in Listing 3-21 modifies
only <p> elements that have an
attribute called name. To reproduce what we were
doing with the attributes id
and class, we can also provide the value
of the attribute.

Listing 3-22: Referencing <p> elements that have a name attribute with the value mytext

p[name="mytext"] {

font-size: 20px;

}

CSS lets us combine the = character with others to make a more detailed selection. The
following are the most frequently used.

Listing 3-23: Applying attribute selectors

p[name~="my"] {

font-size: 20px;

}

p[name^="my"]
{

font-size: 20px;

}

p[name$="my"]
{

font-size: 20px;

}

p[name*="my"]
{

font-size: 20px;

}

The rules in Listing 3-23 include selectors with
the same attribute and value, but they reference different elements, as
described below.

·
The
selector with the characters ~= references any <p> element with a name attribute which value includes
the word "my" (e.g., "my text", "my
car").

·
The
selector with the characters ^= references any <p> element with a name attribute which value begins with
"my" (e.g., "mytext", "mycar").

·
The
selector with the characters $= references any <p> element with a name attribute which value ends with
"my" (e.g., "textmy", "carmy").

·
The selector
with the characters *= references any <p> element with a name attribute which value contains
the string "my" (in this case, the string
could also be in the middle—for example, in textmycar).

In these examples, we use the <p> element, the name attribute and some random text,
but the same technique may be used with any attribute and value we need.

Pseudo-Classes

Pseudo-classes are special features in CSS
that allow us to reference HTML elements by their characteristics, such as the
position in the code or current conditions. The following are the most
frequently used.

:nth-child(value)—This pseudo-class selects an element from a list of siblings that
is at the position specified by the value between parentheses.

:first-child—This
pseudo-class selects the first element on a list of siblings.

:last-child—This
pseudo-class selects the last elements on a list of siblings.

:only-child—This
pseudo-class selects an element when it is the only child of another element.

:first-of-type—This
pseudo-class selects the first element on a list of elements of the same type.

:not(selector)—This pseudo-class selects the elements that do not match the selector
between parentheses.

With these pseudo-classes, we can
perform a more dynamic selection. In the following examples, we are going to
apply a few of them using a simple document.

Listing 3-24: Creating a document to test
pseudo-classes

<!DOCTYPE html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<main>

<section>

<p class="mytext1">Sentence 1</p>

<p class="mytext2">Sentence 2</p>

<p
class="mytext3">Sentence 3</p>

<p class="mytext4">Sentence 4</p>

</section>

</main>

</body>

</html>

The main section of the HTML document
in Listing 3-24 includes four <p> elements that, considering the HTML structure, are siblings and therefore
children of the same <section> element. Using pseudo-classes,
we can reference elements in this document no matter how much we know about
their attributes or values. For instance, with the :nth-child() pseudo-class we can modify every second <p> element found in the document.

Listing 3-25: Implementing the pseudo-class :nth-child()

p:nth-child(2) {

font-size: 20px;

}

The
Basics: Pseudo-classes may be combined with any selector we have
mentioned so far. In the rule in Listing 3-25, we reference <p> elements
with the p keyword, but this rule could have also been
written as .myclass:nth-child(2), for
example, to reference every element that is a child of another element and has
a class attribute with
the value "myclass".

The :nth-child() pseudo-class is indicating
something like "the child at the position…", so the number between
the parentheses will be the number of the position of the child, or index.
Using this type of reference, we can select any child we want by changing the
index number. For example, the following rule will have an impact only on the
last <p> element of our document.

Listing 3-26: Changing the index to affect a
different element

p:nth-child(4) {

font-size: 20px;

}

It is possible to assign styles to all
the elements by creating a rule for every one of them.

Listing 3-27: Creating a list with the
pseudo-class :nth-child()

p:nth-child(1) {

background-color: #999999;

}

p:nth-child(2)
{

background-color: #CCCCCC;

}

p:nth-child(3)
{

background-color: #999999;

}

p:nth-child(4)
{

background-color: #CCCCCC;

}

In Listing 3-27, we use the :nth-child() pseudo-class and the background-color property to generate a list of options
that are differentiated by the color of their background. The value #999999 represents a dark gray color, and the value #CCCCCC represents a light gray color. Therefore, the first
element will have a dark background, the second a light background, and so on.

Figure 3-6: The :nth-child() pseudo-class

Adding more options to the list could
be done by incorporating new <p> elements in the HTML code and new rules with the :nth-child() pseudo-class in the Style Sheet.
However, this approach extends the code unnecessarily and is impossible to
apply to websites with dynamic content. An alternative is to use the values odd and even available for this pseudo-class.

Listing 3-28: Implementing the values odd and even

p:nth-child(odd) {

background-color: #999999;

}

p:nth-child(even)
{

background-color: #CCCCCC;

}

The value odd affects the elements <p> that are children of another element
and have an odd index, and the value even affects those that have an even index.
Now, there are only two rules for the entire list, no matter how long it is.
Even if more options or rows are later incorporated in the document, there is
no need to add additional rules to the CSS file. The styles will be assigned
automatically to every element according to its position.

CSS includes more pseudo-classes for
similar purposes. The most frequently used are :first-child, :last-child and :only-child. The :first-child pseudo-class references only the first
child, :last-child references only the last
child, and :only-child affects an element if it is
the only child of another element. These pseudo-classes, in particular, do not
require keywords or additional parameters and are implemented as in the
following example.

Listing 3-29: Using :last-child to modify the last <p> element of the list

p:last-child {

font-size: 20px;

}

Another important pseudo-class is :not(). With this pseudo-class, we can
select the elements that do not match a selector. For instance, the following
rule assigns a margin of 0 pixels to all the elements which keywords are
different than p.

Listing 3-30: Applying styles to every
element except <p>

:not(p) {

margin: 0px;

}

Instead of the name of the element,
we may use any selector we want. In Listing 3-31, below, every element inside
the <section> element is affected except
those with the value "mytext2" in their class attribute.

Listing 3-31: Defining an exception using
the class attribute

section :not(.mytext2) {

margin: 0px;

}

When we apply this last rule to the
HTML code in Listing 3-24, the browser assigns the styles by
default to the <p> element of class mytext2 and provides a margin of 0 pixels to the rest (because the
<p> element with the class mytext2 is the one that is not affected by the rule). The result is shown in Figure
3-7. The first, third, and fourth <p> elements are presented with no
margin, but the second <p> element is shown with the top
and bottom margins by default.

Figure 3-7: The :not() pseudo-class

3.3 Properties

Properties in CSS define the styles we can
apply to an element. We have introduced some of them in previous examples, but
there are a few hundreds available. To simplify their study, they may be
classified into two types: formatting properties and layout properties.
Formatting properties assign a format to the elements and their content, while
layout properties determine the size and position of the elements on the
screen. In turn, formatting properties may be classified according to the type
of modification they perform. For instance, some properties change the font
used to display text, others generate a border around an element, assign a
background color, etc. In this chapter, we introduce formatting properties
according to this classification.

Text

There are different aspects of the text we
need to control from CSS, such as the type of font used to display it, the
space between lines, how is it going to be aligned, etc. The following are the
properties available to define the text's font, size, and style.

font-family—This
property sets the font family used to render the text. Multiple values may be
declared separated by commas to provide options to the browser if the first
type is not available on the user's computer. Some standard values are Georgia, "Times
New Roman", Arial, Helvetica, "Arial
Black", Gadget, Tahoma, Geneva, Helvetica, Verdana, Geneva, Impact, and sans-serif (names
composed of more than one word must be declared within double quotes).

font-size—This
property sets the size of the font. The value may be declared in pixels (px), percentage
(%), or using
any of the units available such as em, rem, pt, etc. The default
value is usually 16px.

font-weight—This
property determines if the text is going to be bold or not. The possible values
are normal and bold, but we can
also assign the values 100, 200, 300, 400, 500, 600, 700, 800, and 900 to determine
how bold the text will be (only available for some font types).

font-style—This
property sets the style of the font. The possible values are normal, italic, and oblique.

font—This property is a
shorthand property that allows us to declare multiple styles at once. The
values must be separated by a space and declared in a precise order. The style
and weight values must be declared before the size, and the font family must go
last (e.g., font: bold 24px Arial,sans-serif).

These
properties have to be applied to every element (or parent element) which text
we want to modify. For instance, the following document includes a header with
a title and a section with a paragraph. Because we want to assign different
fonts to the title and the text, we have included the id attribute in
each element to be able to identify them from the CSS rules.

Listing 3-32: Testing
formatting properties

<!DOCTYPE
html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta
charset="utf-8">

<link
rel="stylesheet" href="mystyles.css">

</head>

<body>

<header>

<span
id="title">Cascading Style Sheets</span>

</header>

<section>

<p
id="description">Cascading Style Sheets (CSS) are a stylesheet
language used to describe the presentation of a document written in HTML. CSS
describes how elements should be rendered on screen, on paper, in speech, or on
other media.</p>

</section>

<footer>

<a
href="http://www.jdgauchat.com">www.jdgauchat.com</a>

</footer>

</body>

</html>

The
rules must include all the properties required to assign the styles we want.
For example, if we want to assign a font family and a new size to the text, we
have to include the font-family and font-size properties.

Listing 3-33: Changing the
font family and size of the title

#title
{

font-family:
Verdana, sans-serif;

font-size:
36px;

}

Although
we can declare multiple properties in the same rule to modify different aspects
of the element, CSS offers a shorthand property called font to define
the characteristics of the font in one single line of code. The values assigned
to the property must be declared separated by a space character. For instance,
the following rule assigns the style bold, a size of
36 pixels, and the Verdana font family
to the title element.

Listing 3-34: Changing the
font with the font property

#title
{

font: bold 36px Verdana,
sans-serif;

}

When the
rule in Listing 3-34 is applied to the document of Listing 3-32, the title is displayed
with the values defined by the font property and
the paragraph is shown with styles by default.

Figure 3-8: The font property

Do It Yourself: Create a new HTML file with the code in
Listing 3-32. Create or update the mystyles.css file with the rule in Listing
3-34. Open the HTML document in your browser. You should see something similar
to Figure 3-8.

From CSS
we can change not only the font but also other aspects of the text such as the
alignment, indentation, space between lines, etc. The following are some of the
properties available for this purpose.

text-align—This
property aligns the text inside an element. The possible values are left, right, center, and justify.

text-align-last—This
property aligns the last line of a paragraph. The possible values are left, right, center, and justify.

text-indent—This
property defines the amount of indentation of a paragraph (the empty space at
the beginning of the line). The value may be declared in pixels (px), percentage
(%), or using
any of the units available, such as em, rem, pt, etc.

letter-spacing—This
property defines the space between letters. The value may be declared in pixels
(px), percentage
(%), or using
any of the units available, such as em, rem, pt, etc.

word-spacing—This
property defines the space between words. The value may be declared in pixels (px), percentage
(%), or using
any of the units available, such as em, rem, pt, etc.

line-height—This
property defines the space between lines. The value may be declared in pixels (px), percentage
(%), or using
any of the units available, such as em, rem, pt, etc.

vertical-align—This
property aligns elements vertically. It is often used to align text with images
(the property must be applied to the image). The possible values are baseline, sub, super, text-top, text-bottom, middle, top, and bottom.

By
default, browsers align the text to the left, but we can change it with the text-align property.
The following rule aligns the paragraph in our document to the center.

Listing 3-35: Aligning
text with the text-align property

#title
{

font: bold
36px Verdana, sans-serif;

}

#description {

text-align: center;

}

Figure 3-9: The text-align property

The rest
of the properties listed above are self-explanatory. For instance, we can
define the size of the space between words with the word-spacing property.

Listing 3-36: Defining the
space between words with the word-spacing property

#title
{

font: bold
36px Verdana, sans-serif;

}

#description {

text-align:
center;

word-spacing: 20px;

}

Figure 3-10: The word-spacing property

CSS also
offers the following property to decorate the text with a line.

text-decoration—This
property highlights the text with a line. The possible values are underline, overline, line-through, and none.

The text-decoration property is
particularly useful with links. By default, browsers display links with a line
below the text. If we want to remove the line, we can declare the decoration as
none. The
following example adds a rule to our Style Sheet to modify the <a> elements in
our document.

Listing 3-37: Removing the
underline in the links of our document

#title
{

font: bold
36px Verdana, sans-serif;

}

#description {

text-align:
center;

}

a {

text-decoration: none;

}

Figure 3-11: The text-decoration property

So far,
we have been using the font family Verdana (and sans-serif in case Verdana is not
available). This font family is part of a group of fonts commonly known as Safe
Fonts because they are available in most computers and therefore they are
safe to use. The problem with fonts on the Web is that browsers do not download
the fonts from the website's server, they load them from the user's computer,
and users have different fonts installed on their systems. If we use a font
that the user does not have, the design of our website is going to look
different. Using safe fonts we make sure that our website looks the same in every
browser and computer, but unfortunately the group of safe fonts available is
very limited. To provide a solution, CSS includes the @font-face rule. The @font-face rule is a
reserved rule (also called at-rule) that allows designers to provide a
font file to display text on a web page. Using this rule, we can include any
font we want in a website by providing the file for it.

The @font-face rule needs
two properties to declare the font and load the file. The font-family property
specifies the name we want to use to reference that particular font, and the src property
indicates the file's URL with the specifications of the font (The syntax for
the src property requires the use of a function
called url() to include
the URL). In the following example, the name MyNewFont is assigned
to our font, and the font.ttf file is indicated as the source.

Listing 3-38: Loading a
custom font for the title

#title {

font: bold 36px MyNewFont, Verdana, sans-serif;

}

@font-face {

font-family:
"MyNewFont";

src:
url("font.ttf");

}

Once the
font is loaded, we can use it in any element of the document by writing its
name (MyNewFont). In the font property of the #title rule in
Listing 3-38, we specify that the title has to be rendered with the new font or
with the fonts Verdana or sans-serif if the browser cannot load the file.

Figure 3-12: Title with
custom font

Do It
Yourself: Download the file font.ttf from our website. Copy
the file to the folder in which your CSS file is located. Update your Style
Sheet with the code in Listing 3-38 and open the document of Listing 3-32 in
your browser. You
can find more free fonts like the one shown in Figure 3-12 at www.moorstation.org/typoasis/designers/steffmann/.

The
Basics: A function is a piece of code that performs a task and returns
the result. In the case of the url() function,
the task is to load the file indicated between parentheses and return its
content. CSS includes several functions to generate the values of some of its
properties. We will introduce these functions next, and learn more about functions
in Chapter 6.

Colors

There are two
ways to declare a color in CSS; we can use a combination of the three basic
colors, red, green and blue, or define the level of hue, saturation, and
lightness. The final color is produced from the level we assign to each
component. Depending on the type of system we use to define the color, we have to
declare the levels using hexadecimal numbers (from 00 to FF), decimal numbers
(from 0 to 255), or percentages. For example, if we decide to use a combination
of levels of red, green and blue, we can declare the levels with hexadecimal
numbers. In this case, the values of the colors are declared in sequence and
preceded by the hash character, as in #996633 (99 is the level of red, 66 is
the level of green, and 33 is the level of blue). To define colors with other
types of values, CSS provides the following functions.

rgb(red, green, blue)—This
function defines a color from the values specified by the attributes (from 0 to
255). The first value represents the level of red, the second value represents
the level of green, and the last value represents the level of blue (e.g., rgb(153,
102, 51)).

rgba(red, green, blue, alpha)—This
function is similar to the rgb() function,
but it includes an additional component to define opacity. Its value ranges
from 0 to 1, with 0 as fully transparent and 1 as fully opaque.

hsl(hue, saturation, lightness)—This
function defines a color from the values specified by the attributes. The
values are declared in decimal numbers and percentages.

hsla(hue, saturation, lightness, alpha)—This
function is similar to the hsl() function,
but it includes an additional component to define opacity. Its value ranges
from 0 to 1, with 0 as fully transparent and 1 as fully opaque.

As we will
see later, several properties require a value to define a color, but the
following is the most frequently used.

color—This
property declares the color of the content of an element.

The
following rule assigns a light gray to the title of our document using
hexadecimal numbers.

Listing 3-39: Assigning a
color to the title

#title {

font: bold 36px Verdana, sans-serif;

color: #CCCCCC;

}

When the
level of red, green, and blue are the same, as in this case, the final color is
gray.

Figure 3-13: Title with
custom color

Declaring a color
with a function is very similar, we have to replace the hexadecimal value with
the function we want to use. For example, we can define the same tone of gray
with the rgb() function and decimal
values.

Listing 3-40: Assigning a
color with the rgb() function

#title {

font: bold 36px Verdana, sans-serif;

color: rgb(204, 204,
204);

}

The hsl() function is
just another function to generate a color, but it is more intuitive than rgb(). Some
designers find it easier to create a personal set of colors using hsl(). The values
for this function include the level of hue, saturation, and lightness. Hue
represents the color extracted from an imaginary wheel, and it is expressed in
degrees from 0 to 360. Around 0 and 360 are reds, close to 120 are greens and
close to 240 are blues. Saturation is represented as a percentage, from 0%
(gray scale) to 100% (full color or fully saturated), and lightness is also a
percentage value, from 0% (completely black) to 100% (completely white); the
value of 50% is average lighting. For instance, in the following rule, the
saturation was defined as 0% to create a gray scale, and the lightness was set
at 80% to get a light gray.

Listing 3-41: Assigning a
color with the hsl() function

#title {

font: bold 36px Verdana, sans-serif;

color: hsl(0, 0%, 80%);

}

IMPORTANT:
CSS
defines a property to declare opacity called opacity. This
property presents the problem that the opacity value of an element is inherited
by all its children. That problem was solved by the rgba() and hsla() functions.
Using these functions, we can assign an opacity value to the background of an
element without affecting its content.

The
Basics: Finding the right colors for our website by trying a combination
of numbers or values is not practical. There are several tools available for
PCs and Apple computers that allow you to select a color and get the
corresponding values. Also, most of the photo editors and graphic software
available on the market provide a tool that displays a pallet to select a color
and get its hexadecimal and decimal values. You can use these tools or any
other tool available online, such as www.colorhexa.com or www.htmlcolorcodes.com, to create
your colors.

Sizes

By default,
the width of structural elements and other elements like <p> is
determined by the size of the container. The elements expand to occupy the
horizontal space available within the container. On the other hand, the height
is determined by the size of their content. If we want to set a custom size, we
have to assign the following properties to the element.

width—This
property declares the width of an element. The value may be specified in
pixels, percentage, or with the keyword auto (by
default). When the value is specified in percentage, the width is calculated by
the browser from the width of the container, and when it is declared as auto, the element
expands to occupy all the horizontal space available within the container.

height—This
property declares the height of an element. The value may be specified in
pixels, percentage, or with the keyword auto (by
default). When the value is specified in percentage, the height is calculated
by the browser from the height of the container, and when it is declared as auto, the element
adopts the height of its content.

Browsers
generate a box around the elements that determine the area they occupy on the
screen. When we declare a custom size, this box is modified, and the content of
the element is adapted to fit inside the new area, as shown in Figure 3-14.

Figure 3-14: Custom box

For
example, if we declare a width of 200 pixels for the <p> element in
our document, the lines of text will be shorter, but more lines will be added
to show the whole paragraph.

Listing 3-42: Assigning a
custom width

#title {

font: bold 36px Verdana, sans-serif;

}

#description {

width: 200px;

}

Figure 3-15: Main content
with a custom size

The rule
in Listing 3-42 declares a custom width for the <p> element, but
the height is still determined by its content, which means that the box
generated by this element will be taller to be able to accommodate the entire
paragraph, as shown in Figure 3-15. If we also want to restrict the height of
the element, we can use the height property.
The following rule reduces the height of the <p> element in
our document to 100 pixels.

Listing 3-43: Assigning a
custom height

#title {

font: bold 36px Verdana, sans-serif;

}

#description {

width: 200px;

height: 100px;

}

The
problem with elements with custom width and height is that sometimes the
content does not fit in the area. By default, browsers show the rest of the
content outside the box. In consequence, part of the content of our box
overlaps with the content of the element below, as illustrated in Figure 3-16.

Figure 3-16: The content
overflows the element's box

In our
example, the text that is outside the box determined by the <p> element
occupies the space that corresponds to the <footer> element, and
the content of both elements overlap. CSS includes the following properties to
solve this issue.

overflow—This
property specifies how the content that overflows its container box is going to
be shown. The possible values are visible (by
default), hidden (it hides the content that does not fit in
the box), scroll (it shows scroll bars to scroll the content),
auto (it lets the
browser decides what to do).

overflow-x—This
property specifies how the content that overflows its container box
horizontally is going to be shown. It takes the same values as the overflow property.

overflow-y—This
property specifies how the content that overflows its container box vertically
is going to be shown. It takes the same values as the overflow property.

overflow-wrap—This
property indicates whether or not a word should be broken at an arbitrary point
when there is not enough space to show it in the current line. The possible
values are normal (the line will
be broken in natural breakpoints) and break-word (words will
be broken at arbitrary points).

With
these properties, we can determine how the content is going to be shown when
there is no space available. For example, we can hide the text assigning the
value hidden to the overflow property.

Listing 3-44: Hiding the
content that overflows

#title {

font: bold 36px Verdana, sans-serif;

}

#description {

width: 200px;

height: 100px;

overflow: hidden;

}

Figure 3-17: Overflow
hidden

If we
want the user to see the text that was hidden, we can assign the value scroll to the property,
which forces the browser to show scroll bars on the side to scroll the content.

Listing 3-45: Adding
scroll bars

#title {

font: bold 36px Verdana, sans-serif;

}

#description {

width: 200px;

height: 100px;

overflow: scroll;

}

Figure 3-18: Scrollbars

The size
of an element is determined not only by the width and height of its box but
also by its paddings and margins. CSS allows us to designate space around the
box to separate the element from other elements nearby (margin), and also to
include space between the edges of the element's box and its content (padding).
Figure 3-19 illustrates how they are applied to an element.

Figure 3-19: Margins,
paddings, and borders

CSS
includes the following properties to define a custom margin or padding for an
element.

margin—This
property declares the margin of an element. The margin is the space
around the element's box. It accepts up to four parameters with values that
represent the upper margin, the right margin, the bottom margin, and the left
margin, in that order and separated by a space (e.g., margin:
10px 30px 10px 30px;). However, if only one, two or three parameters are declared, the
others take the same values. For example, margin: 10px 30px assigns 10
pixels to the top and bottom margins and 30 pixels to the left and right
margins. The values may be declared independently using the associated
properties margin-top, margin-right, margin-bottom and margin-left
(e.g., margin-left: 10px;). We can also
assign the value auto to this property to let
the browser calculate the margin (this is used to center an element inside its
container).

padding—This property
declares the padding of an element. The padding is the space between the
content of the element and the edges of element's box. The values are
declared the same way as for the margin property. They
can also be declared independently using the associated properties padding-top, padding-right, padding-bottom and padding-left
(e.g., padding-top: 10px;).

The
following rule adds margins and paddings to the header of our document. Because
we assign only one value to each property, the value is used to define all the
margins and paddings of the element (top, right, bottom, and left, in that
order).

Listing 3-46: Adding
margins and paddings

header {

margin: 30px;

padding: 15px;

}

#title {

font: bold 36px Verdana,
sans-serif;

}

The size
of an element and its margins are added to calculate the area occupied by the
element on the page. The same happens with the padding and the border (we will
study borders later). The size of this area is calculated by the formula: size
+ margins + paddings + borders. For instance, if we have an element 200 pixels
wide and with a margin of 10 pixels on each side, the actual width of the
element will be 220 pixels. The total 20 pixels of the margins are added to the
200 pixels of the element, and the final value is represented on the screen.

Figure 3-20: Header with
custom margins and paddings

Do It
Yourself: Replace the rules in your CSS file with the rules in Listing 3-46
and open the document in your browser. You should see something similar to
Figure 3-20. Change the value of the margin property to
see how all the margins are affected.

IMPORTANT:
As
we will see later in this chapter, elements are classified into two main types:
Block and Inline. Block elements may have a custom size, but Inline elements
can only occupy the space determined by their content. The <span> element we
used to define the title for the header is defined as an Inline element and
therefore it cannot have a custom size and margins. This is why we assigned the
margins and paddings to the <header> element
instead (structural elements are all defined as block elements).

Background

Elements may
include a background that is shown behind the element's content and throughout
the area occupied by the content and the padding. Because the background may be
composed of colors and images, CSS defines multiple properties to configure it.

background-color—This property assigns a background color
to an element.

background-image—This
property assigns one or more images to the background of an element. The file's
URL is declared with the url() function
(e.g., url("bricks.jpg")). If more
than one image is provided, the values must be separated by commas.

background-position—This
property declares the starting position of a background image. The values may
be specified in percentage, pixels or using a combination of the following
keywords: center, left, right, top, and bottom.

background-size—This
property declares the size of the background image. The values may be specified
in percentage, pixels, or using the keywords cover and contain. The cover keyword
scales the image until at least its width or its height fits into the element's
box, while contain scales the
image until the whole image fits into the element's box.

background-repeat—This
property determines how a background image will be distributed using four
keywords: repeat, repeat-x, repeat-y and no-repeat. The repeat keyword
repeats the image horizontally and vertically, while repeat-x and repeat-y do so only
for the horizontal axis or the vertical axis, respectively. Finally, no-repeat shows the
background image only once.

background-origin—This
property determines whether the background image will be positioned relative to
the border, the padding, or the content of the box. The possible values are border-box, padding-box, and content-box.

background-clip—This
property declares the area to be painted using the values border-box, padding-box, and content-box. The first
value clips the image to the border of the box, the second clips the image to
the padding of the box, and the third one clips the image to the content of the
box.

background-attachment—This
property determines whether the image is static or scrolls with the rest of the
elements using two values: scroll (by default)
and fixed. The scroll value makes
the image scroll with the page, and the fixed value fixes
the background image in its original location.

background—This property is a
shorthand property that allows us to declare multiple background attributes at
once.

The following rule implements the background-color property to add a gray background to the header of
our document.

Listing 3-47: Adding a
background color

header {

margin: 30px;

padding: 15px;

text-align: center;

background-color:
#CCCCCC;

}

#title {

font: bold 36px Verdana,
sans-serif;

}

Because we defined the margins on the sides of
the header as 30 pixels, the header is centered in the browser window, but the
text inside the <header> element is still aligned to the left (the
alignment by default). In Listing 3-47, besides changing the background, we
also included the text-align property to center the title. The result is
shown in Figure 3-21.

Figure 3-21: Background
color

In addition to colors, we can also assign images to
the background. In this case, we have to declare the background with the background-image property and provide the image's URL with the url() function, as in the following example.

Listing 3-48: Adding a
background image

header {

margin: 30px;

padding: 15px;

text-align: center;

background-image:
url("brickslight.jpg");

}

#title {

font: bold 36px Verdana, sans-serif;

}

The problem with images is that they are usually
not of the same size of the element's box. For this reason, by default,
browsers repeat the image until all the area is covered. The result is shown in
Figure 3-22.

Figure 3-22: Background
image

Do It
Yourself: Replace the rules in your CSS file with the rules in Listing
3-48. Download the brickslight.jpg image from our website. Move the image to
the same folder where your document is stored. Open the document in your
browser. You should see something similar to Figure 3-22.

If we want to determine how the image is distributed
in the box, we can use other background properties. For instance, if we assign
the value repeat-y to the background-repeat property, the browser will only repeat the
image in the vertical axis.

Listing 3-49: Configuring
the background

header {

margin: 30px;

padding: 15px;

text-align: center;

background-image:
url("brickslight.jpg");

background-repeat:
repeat-y;

}

#title {

font: bold 36px Verdana, sans-serif;

}

Figure 3-23: Background
image

When our design requires multiple properties to
set the background, we can use the background property. This is a shorthand property
that allows us to set different aspects of the background in just one line of
code.

Listing 3-50: Configuring
the background with the background property

header {

margin: 30px;

padding: 15px;

text-align: center;

background: #CCCCCC
url("brickslight.jpg") repeat-y;

}

#title {

font: bold 36px Verdana, sans-serif;

}

The rule in Listing 3-50 specifies the same
values as before, but now it combines a background image with a background
color. The result is shown in Figure 3-24.

Figure 3-24: Background
image combined with a background color

Borders

Elements may include a border at the edges of the box.
By default, browsers do not show any border, but we can use the following
properties to define it.

border-width—This property defines the width of the border.
The property accepts up to four values separated by a space to specify the
width of each border (top, right, bottom, and left, in that order). We can also
declare the widths independently with the properties border-top-width, border-bottom-width, border-left-width, and border-right-width.

border-style—This
property defines the style of the border. The property accepts up to four
values separated by a space to specify the style of each border (top, right,
bottom, and left, in that order). The possible values are none, hidden, dotted, dashed, solid, double, groove, ridge, inset, and outset. The default
value is none, which means that the border is not shown
until we assign a different value to this property. We can also declare the
styles independently with the properties border-top-style, border-bottom-style, border-left-style, and border-right-style.

border-color—This
property defines the color of the border. The property accepts up to four values
separated by a space to specify the color of each border (top, right, bottom,
and left, in that order). We can also declare the colors independently with the
properties border-top-color, border-bottom-color, border-left-color, and border-right-color.

border—This is a shorthand property that allows us to define the
border's attributes all at once. We also have the shorthand properties border-top, border-bottom, border-left, and border-right to define
the values of each border.

To
assign a border to an element, all we have to do is to define the style with
the border-style property. Once the
style is set, the browser uses values by default to draw the border. If we do
not want to let the browser set these values, we can use the rest of the
properties to configure the border. The following example adds a solid border
of 2 pixels to the header of our document.

Listing 3-51: Adding a
border to an element

header {

margin: 30px;

padding: 15px;

text-align: center;

border-style: solid;

border-width: 2px;

}

#title {

font: bold 36px Verdana, sans-serif;

}

Figure 3-25: Solid border

As we
did with the background property, we
can declare all the values of the border at once using the border property.
The following example creates a dashed border of 5 pixels in color gray.

Listing 3-52: Defining a
border with the border property

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 5px dashed
#CCCCCC;

}

#title {

font: bold 36px Verdana, sans-serif;

}

Figure 3-26: Dashed
border

The border added with these
properties is drawn around the element's box, which means it is going to
describe a rectangle with square corners. If our design requires round corners,
we can implement the following property.

border-radius—This property defines the radius of the
virtual circle that the browser is going to use to draw the round corners. The
property can take four values to define different radius for each corner
(top-left, top-right, bottom-right, and bottom-left, in that order). We can
also use the properties border-top-left-radius, border-top-right-radius, border-bottom-right-radius, and border-bottom-left-radius to define
the radius of each corner independently.

The following example generates
round corners for our header with a radius of 20 pixels.

Listing 3-53: Generating
round corners

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 2px
solid;

border-radius: 20px;

}

#title {

font: bold
36px Verdana, sans-serif;

}

Figure 3-27: Round
corners

If all
the corners are the same, we can declare only one value, as we did in the
previous example. However, as with the margin and padding properties,
if we want the corners to be different, we have to specify the value of each
one of them.

Listing 3-54: Declaring
different values for each corner

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 2px
solid;

border-radius: 20px 10px
30px 50px;

}

#title {

font: bold
36px Verdana, sans-serif;

}

In
Listing 3-54, the four values assigned to the border-radius property
represent four different locations. The values are always given clockwise
starting from the top-left corner. The order is top-left corner, top-right
corner, bottom-right corner, and bottom-left corner.

Figure 3-28: Different
corners

The
Basics: Like the margin and padding properties, border-radius can also work with just two values. The first value is assigned
to the first and third corners (top-left, bottom-right) and the second to the
second and fourth corners (top-right, bottom-left).

We can
also shape the corners by providing new values separated by a slash. The values
on the left represent the horizontal radius and the values on the right the
vertical radius. The combination of these values generates an ellipse.

Listing 3-55: Generating
elliptic corners

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 2px
solid;

border-radius: 20px /
10px;

}

#title {

font: bold
36px Verdana, sans-serif;

}

Figure 3-29: Elliptic
corners

Do It
Yourself: Copy the styles you want to test into your CSS file and open the
document in your browser. Change the values of each property to see how they
work.

The
borders we have created so far are drawn at the edges of the element's box, but
we can also outline the element with a second border that is rendered away from
the edges. The purpose of this border is to make the element stand out. Some
browsers use it to outline text, but most of them draw a border outside the boundaries
of the element's box. CSS offers the following properties to configure this second
border.

outline-width—This
property defines the width of the outline. The property can take a value in any
of the units available in CSS (px, %, em, etc.) as
well as the keywords thin, medium, and thick.

outline-style—This
property defines the style of the outline. The possible values are none, auto, dotted, dashed, solid, double, groove, ridge, inset, and outset.

outline-color—This property defines the color of the outline.

outline-offset—This
property defines the offset of the outline (how far this second border is from
the actual border of the box). The property can take a value in any of the
units available in CSS (px, %, em, etc.).

outline—This
property is a shorthand property that allows us to specify the width, style,
and color of the outline (the offset must still be defined with the outline-offset property).

By
default, the offset is set to 0, so the outline is drawn right next to the
border. If we want to separate the two borders, we have to define the outline-offset property.

Listing 3-56: Adding an
outline to the header

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid #999999;

outline: 2px dashed
#000000;

outline-offset: 15px;

}

#title {

font: bold
36px Verdana, sans-serif;

}

In
Listing 3-56, we add an outline of 2 pixels with an offset of 15 pixels to the
styles originally assigned to the box of our document. The result is shown in
Figure 3-30.

Figure 3-30: Outline

The
effects achieved by the properties border and outline are limited
to single lines and a few configuration options, but CSS lets us define custom borders
using images to overcome these limitations. The following are the properties
included for this purpose.

border-image-source—This
property determines the image that is going to be used to create the border.
The file's URL is declared with the url() function
(e.g., url("bricks.jpg")).

border-image-width—This
property defines the width of the border. The property can take a value in any
of the units available in CSS (px, %, em, etc.).

border-image-repeat—This property defines
how the middle of the image is used to generate the border. The possible values
are repeat, round, stretch, and space.

border-image-slice—This
property defines how the image is going to be sliced to represent the corners
and the middle of the border. It requires four values to specify four inward
offsets (if only one value is declared, it is used for every offset). The value
can be specified as an integer or a percentage.

border-image-outset—This
property defines the offset of the border (how far it is from the border of the
element's box). The property can take a value in any of the units available in
CSS (px, %, em, etc.).

border-image—This
property is a shorthand property that allows us to specify the attributes of
the border all at once.

These
properties use an image as a pattern. Depending on the values provided, the
image is sliced like a cake to get the pieces and then these pieces are placed
around the element to build the border.

Figure 3-31: Pattern to
build a border

To
accomplish this task, we have to declare three attributes: the location of the
image's file, the size of the pieces we want to get from the pattern, and the
keywords that determine how those pieces will be distributed around the
element.

Listing 3-57: Creating a
custom border for the header

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 29px solid;

border-image-source:
url("diamonds.png");

border-image-slice: 29;

border-image-repeat:
stretch;

}

#title {

font: bold 36px Verdana, sans-serif;

}

In
Listing 3-57, we set a border of 29 pixels for the header and then load the
image diamonds.png to build the border. The value 29 of the border-image-slice property
declares the size of the pieces, and the value stretch, assigned to
the border-image-repeat property, is one of the
methods available to distribute these pieces around the box. There are three
possible values. The keyword repeat repeats the pieces
taken from the image as many times as necessary to cover the side of the
element. In this case, the size of the pieces is preserved, and the image is
only cut if there is not enough space to place it. The keyword round calculates
how long the side is and then stretches the pieces to make sure that none of
them are cut. Finally, the keyword stretch (used in Listing 3-57)
stretches one piece to cover the whole side.

Figure 3-32: Border image
of type stretch

As before, we can declare all the values at once
using the shorthand property.

Listing 3-58: Defining a
border image with the border-image property

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 29px solid;

border-image:
url("diamonds.png") 29 round;

}

#title {

font: bold 36px Verdana, sans-serif;

}

Figure 3-33: Border image
of type round

Shadows

Another
effect we can apply to the elements is shadows. CSS includes the following
properties to generate a shadow for the element's box as well as irregular
shapes like text.

box-shadow—This
property generates a shadow from the element's box. The property accepts up to
6 values. We can declare the horizontal and vertical offsets of the shadow,
blur radius and spread value, the shadow's color, and also include the keyword inset to indicate
that the shadow will be projected inside the box.

text-shadow—This
property generates a shadow from a text. The property accepts up to 4 values.
We can declare the horizontal and vertical offsets of the shadow, blur radius,
and the shadow's color.

The box-shadow property
needs at least three values to determine to color and the offset of the shadow.
The offset may be positive or negative. The values indicate the horizontal and
the vertical distance from the shadow to the element. Negative values position
the shadow to the left and above the element, whereas positive values create a
shadow on the right and below the element. The value 0 positions the shadow
behind the element, offering the possibility to generate a blur effect all
around it. The following example adds a basic shadow to the header of our
document.

Listing 3-59: Adding a
shadow to the header

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px solid;

box-shadow:
rgb(150,150,150) 5px 5px;

}

#title {

font: bold
36px Verdana, sans-serif;

}

Figure 3-34: A basic
shadow

The
shadow we got so far is solid, with no gradient and transparency, but this is
not the way a shadow looks like. A value we can add to make it look more
realistic is the blur distance.

Listing 3-60: Adding the
blur value to the box-shadow property

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 1px solid;

box-shadow:
rgb(150,150,150) 5px 5px 20px;

}

#title {

font: bold 36px Verdana,
sans-serif;

}

Figure 3-35: A real
shadow

Adding
another value in pixels at the end of the property will spread the shadow. This
effect slightly changes the appearance of the shadow by expanding the area that
it covers.

Listing 3-61: Expanding
the shadow

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

box-shadow:
rgb(150,150,150) 10px 10px 20px 10px;

}

#title {

font: bold
36px Verdana, sans-serif;

}

Figure 3-36: A big shadow

The last
possible value for the box-shadow property is not a number but the keyword inset. This
keyword turns the external shadow into an inner shadow that provides a depth
effect to the element.

Listing 3-62: Creating an
inner shadow

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

box-shadow:
rgb(150,150,150) 5px 5px 10px inset;

}

#title {

font: bold
36px Verdana, sans-serif;

}

Figure 3-37: An inner shadow

IMPORTANT: Shadows do not expand
the element nor increase its size, so you have to check carefully that the
space available around the element will allow the shadow to be seen.

The box-shadow property was
designed specifically for boxes. If we try to apply this effect to a <span> element, for
example, the invisible box occupied by this element on the screen will have a
shadow, not the content of the element. CSS defines a property to add a shadow
to a text called text-shadow.

Listing 3-63: Adding a
shadow to the title

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

}

#title {

font: bold
36px Verdana, sans-serif;

text-shadow: rgb(150,
150, 150) 3px 3px 5px;

}

The
values for the text-shadow property are similar to those assigned to the box-shadow property. We
can set the color of the shadow, the horizontal distance from the shadow to the
element, the vertical distance, and the blur radius. In Listing 3-63, a shadow is
added to the title of our header with a distance of only 3 pixels and a blur
radius of 5. The result is shown in Figure 3-38.

Figure 3-38: Text shadow

Gradients

A gradient is
a range of colors that varies continuously with a smooth transition from one
color to another. Gradients are created as images that we have to add to the
element as a background image with the background-image or background properties.
To create the gradient, CSS offers the following functions.

linear-gradient(position, angle, color-stop)—This
function creates a linear gradient. The position attribute determines
the side or corner from which the gradient starts and is declared with the
keywords top, bottom, left and right, the angle attribute defines the gradient's direction and
can be declared in the units deg (degrees), grad (gradians), rad (radians),
or turn (turns), and
the color-stop attribute is the list of colors that participate in the
gradient separated by comma. The values for the color-stop attribute may include a
value in percentage separated by a space character to indicate where the color
ends.

radial-gradient(position, shape, color-stop, extent)—This
function creates a radial gradient. The position attribute indicates the
origin of the gradient and can be declared either in pixels, in percentage, or
with the combination of the keywords center, top, bottom, left and right, the shape
attribute determines the shape of the gradient and is declared with the
keywords circle and ellipse, the color-stop
attribute is a list of the colors that participate in the gradient separated by
comma, and the extent attribute is a keyword that determines the final
shape the gradient is going to take. The possible values are closest-side, closest-corner, farthest-side, and farthest-corner. The values
for the color-stop attribute may include a value in percentage separated
by a space to indicate where the color ends.

As we
mentioned, gradients are set as backgrounds, so we can apply them using the
property background-image or the shorthand
property background, as in the following
example.

Listing 3-64: Adding a
linear gradient to the header

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 1px solid;

background:
-webkit-linear-gradient(top, #FFFFFF, #666666);

}

#title {

font: bold 36px Verdana, sans-serif;

}

Figure 3-39: Linear
gradient

IMPORTANT: Some CSS properties and functions are currently experimental.
For this reason, they have to be declared with a prefix that represents a
rendering engine. For instance, if we want the linear-gradient() function to work in Google Chrome, we
have to declare it as -webkit-linear-gradient(). If you want to use this function
on your website, you have to repeat the background property for every browser
with the corresponding prefix. The prefixes required by the most common
browsers are -moz- for Mozilla Firefox, -webkit- for Safari and Google Chrome, -o- for Opera, and -ms- for Internet Explorer.

In the
previous example, we used the keyword top to set the
gradient's initial position, but two keywords may be combined to start the
gradient from a corner of the element, as in the following example.

Listing 3-65: Setting the
initial position

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

background:
-webkit-linear-gradient(top right, #FFFFFF, #666666);

}

#title {

font: bold
36px Verdana, sans-serif;

}

Figure 3-40: Different
starting position for a linear gradient

For
linear gradients, we may also set the direction with an angle in degrees.

Listing 3-66: Creating a
gradient with a direction of 30 degrees

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

background:
-webkit-linear-gradient(30deg, #FFFFFF, #666666);

}

#title {

font: bold
36px Verdana, sans-serif;

}

Figure 3-41: Linear
gradient with a direction set in degrees

The
previous gradients were created with only two colors, but we may add more
values to generate a multicolor gradient.

Listing 3-67: Creating a
multicolor gradient

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 1px solid;

background:
-webkit-linear-gradient(top, #000000, #FFFFFF, #999999);

}

#title {

font: bold 36px Verdana, sans-serif;

}

Figure 3-42: Multicolor
linear gradient

Using
the keyword transparent instead of a
color, we can make the gradient translucent and merge it with the background
(this effect can also be achieved with the rgba() function discussed
earlier). In the following example, we assign a background image to the <body> element to
change the background of the page and be able to check that the gradient is
translucent.

Listing 3-68: Creating a
translucent gradient

body {

background:
url("brickslight.jpg");

}

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 1px solid;

background:
-webkit-linear-gradient(top, transparent, #666666);

}

#title {

font: bold 36px Verdana, sans-serif;

}

Figure 3-43: Translucent
gradient

The
parameters for the colors are described with the name color-stop. This
is because these attributes can determine the starting and ending point of each
color and customize the gradient by including an additional value in
percentage.

Listing 3-69: Setting
color-stops

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

background:
-webkit-linear-gradient(top, #FFFFFF 50%, #666666 90%);

}

#title {

font: bold
36px Verdana, sans-serif;

}

Figure 3-44: Linear
gradient with color-stop values

Besides
linear gradients, we can also create radial gradients. The syntax for radial
gradients is not that different from the syntax for the linear gradients we
have just studied. The only differences are that we have to use the radial-gradient() function instead
of the linear-gradient() function and include a
parameter for the shape, which can be either circle or ellipse.

Listing 3-70: Creating a
radial gradient

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

background:
-webkit-radial-gradient(center, ellipse, #FFFFFF, #000000);

}

#title {

font: bold
36px Verdana, sans-serif;

}

Figure 3-45: Radial
gradient

Except
for the shape (circle or ellipse), the function works exactly like linear-gradient(). The
position may be customized, and we can use several colors with the second value
for color-stops to determine the limit of each one of them.

Listing 3-71: Creating a
multicolor radial gradient

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 1px solid;

background:
-webkit-radial-gradient(30px 50px, ellipse, #FFFFFF 50%, #666666 70%, #999999
90%);

}

#title {

font: bold 36px Verdana, sans-serif;

}

Figure 3-46: Radial
gradient with color-stops values

Filters

Filters add effects to an element and its
content. CSS includes the filter property to assign the filter
and the following functions to create it.

blur(value)—This function produces
a blur effect. It takes a value in pixels from 1px to 10px.

grayscale(value)—This function turns the
colors of the image into a scale of grays. It takes a decimal number from 0.1 to 1.

drop-shadow(x, y, size, color)—This
function generates a shadow. The x and y attributes
determine the distance the shadow will be from the image, the size attribute
specifies the size of the shadow, and the color attribute
declares its color.

sepia(value)—This function gives a
sepia tone to the colors of the image. It takes a decimal number from 0.1 to 1.

brightness(value)—This function changes
the brightness. It takes a decimal number from 0.1 to 10.

contrast(value)—This function changes
the contrast. It takes a decimal number from 0.1 to 10.

hue-rotate(value)—This function applies a
rotation to the hue. It takes a value in degrees from 1deg to 360deg.

invert(value)—This function inverts
the colors, producing a negative. It takes a decimal number from 0.1 to 1.

saturate(value)—This function saturates
the colors. It takes a decimal number from 0.1 to 10.

opacity(value)—This function changes
the opacity. It takes a decimal number from 0 to 1 (with 0 defining
full transparency and 1 full opacity).

Filters cannot only be applied to images but also
to other elements in the document. The following example applies a blur effect
to the header of our document.

Listing 3-72: Applying a
filter to the header

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 1px solid;

filter: blur(5px);

}

#title {

font: bold 36px Verdana, sans-serif;

}

Figure 3-47: A blurry
header

Do It
Yourself: Replace the rules in your CSS file with the rules in Listing 3-72
and open the document in your browser. Replace the blur() function
with any of the functions available to see how they work.

Transformations

Once created,
HTML elements remain static, but we can modify their position, size, and
appearance with the transform property.
This property performs four basic transformations to an element. We can scale it, rotate it, skew it, and translate
it. The following are the functions defined for this purpose.

scale(x, y)—This function modifies
the scale of the element. There are also two additional functions available
called scaleX() and scaleY() to specify the horizontal and vertical values independently.

rotate(angle)—This function rotates
the element. The attribute represents the rotation angle and may be specified
in the units deg (degrees), grad (gradians), rad (radians),
or turn (turns).

skew(angle)—This function skews the
element. The attribute represents the degrees in which the sides of the element
are displaced. The function may include two values to represent the horizontal
and vertical angles. The values may be provided in any of the units available
for angles: deg (degrees), grad (gradians), rad (radians),
or turn (turns).

translate(x, y)—This function moves the
element to the position determined by the attributes.

The scale() function
receives two parameters, the x value for
horizontal scaling and the y value for vertical
scaling. If only one value is given, the same value is applied to both
parameters.

Listing 3-73: Scaling the
header

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

transform: scale(2);

}

#title {

font: bold
36px Verdana, sans-serif;

}

In
Listing 3-73, we transform the header by scaling the element to double its
size. Integer and decimal values can be provided for the scaling, and it is
calculated using a matrix. Values between 0 and 1 reduce the element, a value
of 1 keeps the original proportions, and values greater than 1 increase the
dimensions of the element linearly.

An
interesting effect possible with this function can be achieved using negative
values.

Listing 3-74: Creating a
mirror image with the scale() function

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

transform: scale(1, -1);

}

#title {

font: bold
36px Verdana, sans-serif;

}

In
Listing 3-74, two parameters are declared to modify the scale of the header.
Both values keep the original proportions, but the second value is negative,
and therefore it inverts the element vertically, producing a mirror effect.

Figure 3-48: Mirror image
with scale()

Besides
scaling the element, we can also rotate it with the rotate() function. In
this case, negative values change the direction in which the element is rotated.
The following example rotates the header 30 degrees clockwise.

Listing 3-75: Rotating the
header

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

transform: rotate(30deg);

}

#title {

font: bold
36px Verdana, sans-serif;

}

Another
function for the transform property is skew(). This function
changes the symmetry of the element by degrees in one or two dimensions.

Listing 3-76: Distorting
the header

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

transform: skew(20deg);

}

#title {

font: bold
36px Verdana, sans-serif;

}

This function has two parameters, but
unlike other functions, each parameter for skew() only affects one dimension (the
parameters are independent of one another). In Listing 3-76, only the first
parameter is declared, so only the horizontal dimension is modified.
Alternatively, we could use the functions skewX() and skewY() to achieve the same purpose.

Figure 3-49: Horizontal
skew

The screen of a device is divided
into rows and columns of pixels (the minimum unit of the screen). With the
purpose of identifying the position of each pixel, computers use a coordinate
system, where the
rows and columns of pixels are counted from the top-left corner to the
bottom-right corner of the grid, starting from 0 (the values increase from left
to right and top to bottom). For example, the first pixel at the top-left
corner of the screen is at the position 0, 0 (column 0, row 0), while a pixel 30
pixels from the left edge of the screen and 10 pixels from the top will be at
the position 30, 10.

Figure 3-50: Coordinate system

The point at the position 0, 0 is
called the origin, and the lines of columns and rows of pixels are
called axes and are identified with the letters x and y (x
for columns and y for rows), as illustrated in Figure 3-50. Using the transform property, we can change the element's
position on this grid. The function we have to assign to the property, in this
case, is translate().

Listing 3-77: Moving the
header to the right

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

transform:
translate(100px);

}

#title {

font: bold
36px Verdana, sans-serif;

}

The translate() function
uses the coordinate system to establish the position of the element, with its current
position used as reference. The top-left corner of the element is considered to
be at the position 0,0 so that negative
values move the element to the left or above the original position, and
positive values move it to the right or below.

In
Listing 3-77, we move the header to the right by 100 pixels from the original
position. Two values may be declared for this function to move the element both
horizontally and vertically, or the translateX() and translateY() functions
can be used to set the values independently.

Sometimes
it may be useful to apply several transformations at once to one single
element. To get a compound transform property, we
have to declare the functions separated by a space.

Listing 3-78: Moving,
scaling and rotating the element with one property

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

transform:
translateY(100px) rotate(45deg) scaleX(0.3);

}

#title {

font: bold
36px Verdana, sans-serif;

}

IMPORTANT: When multiple functions are combined, you have to consider
that the order is important. This is because some functions modify the point of
origin and the center of the element, and therefore they change the parameters
from which the rest of the functions operate.

Besides 2D transformations, we can
also perform 3D transformations on HTML elements. These types of
transformations are performed considering a third axis that represents depth
and is identified with the letter z. The following are the function
provided for this purpose.

scale3d(x, y, z)—This
function assigns a new scale to the element in a 3D space. It takes three
values in decimal numbers to set the scale for the x, y and z axes. As in 2D
transformations, a value of 1 preserves the original scale.

rotate3d(x, y, z,
angle)—This function rotates the element in a specific axis and angle.
The values for the axes must be specified in decimal numbers, and the angle can
be expressed in the units deg (degrees), grad (gradians), rad (radians),
or turn (turns). The
values for the axes determine a vector for the rotation, which is why the
relation between the values is important, not the values themselves. For
example, rotate3d(5, 2, 6, 30deg) will produce
the same effect than rotate3d(50,
20, 60, 30deg), because the resulting vector is the same.

translate3d(x, y, z)—This function moves the
element to a new position in a 3D space. It takes three values in pixels for
the x, y and z axes.

perspective(value)—This function adds
depth to the scene, increasing the size of the side of the element that is
closer to the viewer.

Some 3D
transformations may be applied directly to the element, as we did with 2D
transformations, but some require us to declare the perspective first. For
example, if we rotate the element in the y axis, one side of the element
moves forward and the other backward, but the size of each side remains the same,
and therefore the transformation is not visible to the user. To achieve a more
realistic effect, we also have to declare a perspective with the perspective() function.

Listing 3-79: Adding a
three-dimensional effect to the header

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 1px solid;

transform:
perspective(500px) rotate3d(0, 1, 0, 45deg);

}

#title {

font: bold 36px Verdana, sans-serif;

}

The rule in Listing 3-79 first declares the value
of the perspective and then rotates the element 45 degrees on the y axis
(notice that to select an axis we have to declare the rest of the values as 0).
The browser rotates the element and, because we added perspective to the
transformation, it stretches one side of the element and shrinks the other to
create the impression of perspective.

Figure 3-51: 3D effect
with perspective

CSS also includes a few properties we
can use to get a more realistic effect.

perspective—This is the same as the perspective() function but
works on the parent element. It creates a container box to provide the
perspective effect for the elements inside.

perspective-origin—This property changes
the x and y coordinates
of the viewer. It takes two values in percentage, pixels, or the keywords center, left, right, top and bottom. The values
by default are 50% 50%.

backface-visibility—This property
determines whether the back of an element will be visible or not. It has two
possible values: visible or hidden, with visible set by
default.

Because in our example the header of the document
is a direct child of the body, we have to assign these properties to the <body> element. The following example defines the perspective
for the body and then rotates the header with the transform property, as we did before.

Listing 3-80: Declaring a
different origin for the viewer

body {

perspective: 500px;

perspective-origin: 50%
90%;

}

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 1px solid;

transform: rotate3d(0, 1,
0, 135deg);

}

#title {

font: bold 36px Verdana,
sans-serif;

}

The
result of applying the perspective to the parent element is the same as using
the perspective() function for the child,
but by declaring the perspective this way, we can use the perspective-origin property and
change the coordinates of the viewer as well.

Figure 3-52: 3D effect
using the perspective property

Do It
Yourself: Replace the rules in your CSS file with the rules in Listing 3-80
and open the document in your browser. Try adding the backface-visibility property
with the value hidden to the
header to make it invisible when it is backward.

What we
have done so far modifies the elements in the document, but once the page is
displayed the document remains static. However, we can take advantage of the
combination of transformations and pseudo-classes to turn our document into a
dynamic application.

The
pseudo-class we can implement in this case is called :hover.
Pseudo-classes, as we have seen before, add special effects to rules. In this
case, the :hover rule is applied only when the mouse
pointer is over the element referenced by the rule.

Listing 3-81: Responding
to the mouse

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

}

header:hover {

transform: rotate(5deg);

}

#title {

font: bold
36px Verdana, sans-serif;

}

In
Listing 3-81, the original rule for the header is the same, but now a new rule
identified with the header:hover selector is
added to apply a transformation effect only when the mouse is over the element.
The result is that every time the mouse is over the header, the transform property
rotates the element 5 degrees, and when the pointer is outside the box, it is
rotated back to its previous position. This achieves a basic but useful
animation, using no more than CSS properties.

Transitions

Using the :hover pseudo-class,
we can perform dynamic transformations. However, a real animation requires a
transition between the two steps of the process. For this purpose, CSS offers
the following properties.

transition-property—This property specifies
the properties that participate in the transition. The property includes the
value all to specify that all the properties will
transition to the new values.

transition-duration—This property specifies
the duration of the transition in seconds.

transition-timing-function—This property
determines the function used to calculate the values for the transition. The
possible values are ease, ease-in, ease-out, ease-in-out, linear, step-start, and step-end.

transition-delay—This property specifies the time the
browser will wait to start the animation.

transition—This is a shorthand property to declare
the transition values all at once.

By
implementing these properties, we ask the browser to create all the invisible
steps and generate a smooth transition from the current state of the element to
the new one specified by the properties. The following rules implement the
shorthand property transition to animate
the transformation introduced in the previous example.

Listing 3-82: Creating a
complete animation using the transition property

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

transition: transform 1s
ease-in-out 0s;

}

header:hover {

transform:
rotate(5deg);

}

#title {

font: bold
36px Verdana, sans-serif;

}

The transition property accepts
up to four parameters separated by a space. The first value is the property
that is considered to create the transition (in our example, we use the transform property),
the second parameter sets the time the transition will take from the initial
position to the end (1 second), the third parameter is a keyword that
determines how the transition will take place based on a Bézier curve, and the
last parameter determines how many seconds it takes for the animation to start.

If the
transition has to consider the values of more than one property, we have to
declare the names of the properties separated by commas. When all the
properties that are being modified have to be considered, we can assign the all keyword.

Do It
Yourself: Replace the rules in your CSS file with the rules in Listing 3-82
and open the document in your browser. Move the mouse over the header to see
the animation.

Animations

The transition property creates a basic
animation, but only two states are involved in the process, the initial state
determined by the current values of the properties and the final state,
determined by the new values. To create a real animation, we need to declare
more than two states, like the frames of a movie. CSS includes the following
properties to compose more complex animations.

animation-name—This property specifies the name used to
identify the animation's frames. It can be used to configure several animations
at once declaring the names separated by commas.

animation-duration—This property
determines the duration of each cycle of the animation. The value must be
specified in seconds (e.g., 1s).

animation-timing-function—This property
determines how the animation process will take place based on a Bézier curve
declared by the keywords ease, linear, ease-in, ease-out and ease-in-out.

animation-delay—This property specifies the time the
browser will wait to start the animation.

animation-iteration-count—This property declares
the number of times the animation will be executed. It takes an integer number as
a value or the keyword infinite, which will
run the animation indefinitely. The default value is 1.

animation-direction—This property sets the
direction of the animation. It can take four values: normal (by
default), reverse, alternate, and alternate-reverse. The value reverse inverts the
direction of the animation, showing the frames in the opposite direction in
which they were declared. The value alternate mixes the
cycles of the animation, playing those that have an odd index in the normal
direction and the rest in reverse direction. Finally, the value alternate-reverse does the
same as alternate, but in reverse.

animation-fill-mode—This property defines
how the animation interacts with the styles of the element. It can take the
values none (by default), forwards, backwards, and both. The value forwards keeps the
element styled with the properties applied in the last frame of the animation,
whereas backwards applies the styles of
the first frame to the element as soon as the animation is defined (before it is
executed). Finally, the value both produces
both effects.

animation—This is a shorthand property to define the values of an animation
all at once.

These properties configure the
animation, but the frames are declared with the @keyframes rule. The rule must be identified with
the name used to set up the animation, and it has to include the list of the
properties we want to modify in each frame. The position of each frame in the
animation is determined by a value in percentage, where 0% is the first frame
or the start of the animation and 100% is the end.

Listing 3-83: Creating a
complex animation

header
{

margin: 30px;

padding:
15px;

text-align:
center;

border: 1px
solid;

animation: myanimation 1s
ease-in-out 0s infinite normal none;

}

@keyframes myanimation {

0% {

background: #FFFFFF;

}

50% {

background: #FF0000;

}

100% {

background: #FFFFFF;

}

}

#title {

font: bold
36px Verdana, sans-serif;

}

The
rules in Listing 3-83 create an animation that changes the colors of the
header's background from white to red and back to white. The animation was
configured by the animation property with a
duration of 1 second, and it was set to repeat again and again with the infinite keyword. The
property also assigns the name myanimation to the
animation to later be able to configure the frames with the @keyframes rule.

The
properties in each frame indicate how the element will be styled in that step
of the animation process. In this case, three frames were set, one at 0%,
another at 50%, and a third at 100%. When the animation begins, the browser
assigns to the element the styles defined in the frame at 0% and then animates
the properties until it gets to the values set in the frame at 50%. The process
is repeated from this value to the final value assigned to the property at the
end (100%).

In this example, we defined the
initial state of the animation with a frame at 0% and the final state with a
frame at 100%, but we can start the animation whenever we want and declare all
the frames we need, as in the following example.

Listing 3-84: Declaring
more frames for our animation

header {

margin: 30px;

padding: 15px;

text-align: center;

border: 1px solid;

animation: myanimation 1s ease-in-out 0s infinite
normal none;

}

@keyframes myanimation {

20% {

background: #FFFFFF;

}

35% {

transform: scale(0.5);

background: #FFFF00;

}

50% {

transform: scale(1.5);

background: #FF0000;

}

65% {

transform: scale(0.5);

background: #FFFF00;

}

80% {

background: #FFFFFF;

}

}

#title {

font: bold 36px Verdana, sans-serif;

}

In
Listing 3-84, the animation begins at 20% and ends at 80%, with a total of 5
frames involved. Each frame of the animation modifies two properties that increase
the size of the element and change the color of its background, except for the
first and last frame that only change the color to produce a bouncing effect.

Chapter 4 - Web Design

4.1 Boxes

Browsers create a virtual box around each
element to determine the area they occupy. To organize these boxes on the
screen, elements are classified into two basic types: Block and Inline. The main
differences between these two types of elements is that Block elements have a
custom size and insert line breaks, while Inline elements are of the size of
their content and do not insert line breaks. Because of their characteristics,
Block elements are positioned one per line, and Inline elements are positioned
next to each other on the same line unless there is not enough horizontal space
available, as illustrated in Figure 4-1.

Figure 4-1: Block and Inline elements

Because their size can be customized, Block
elements are suitable to create columns and sections on a web page, and because
they do not insert line breaks, Inline elements are appropriate to represent
content. That is why elements that define the design of our document, like <section>, <nav>, <header>, <footer>, or <div>, are declared as Block elements by
default, and others like <span>, <strong>, or <em>, that represent the content of those
elements are declared by browsers as Inline elements.

Display

Whether an element is of type Block or Inline is
determined by the browser, but we can change their condition from CSS with the
following property.

display—This property defines the type of box used to render an element.
There are several values available for this property. The most frequently used
are none (removes an element), block (shows elements
in a new line with custom size), inline (shows elements
in the same line), and inline-block (shows elements
in the same line with custom size).

Structural elements are set by
default with the value block, while elements that represent
content are usually set as inline. If we want to modify the
type, we have to assign the display property to the element. For
example, old browsers do not recognize the new HTML5 elements and consider them
as Inline elements by default. If we want to make sure that these elements are
interpreted as Block elements in every browser, we can declare the following
rule in our Style Sheets.

Listing 4-1: Defining HTML5 elements as
block elements

header, section, main, footer, aside, nav, article, figure, figcaption
{

display:
block;

}

There are other useful values
available for the display property besides block and inline. For instance, the value none hides the element. When this value is assigned to an element,
the document is rendered as if the element did not exist. It is useful when we
want to change the document dynamically from JavaScript or when we are using
Responsive Web Design to design our website, as we will see in later chapters.

The Basics: The none value for the display property removes the element from the document. If you
want to make the element invisible, you can use another CSS property called visibility. This property can take the values visible and hidden. When the value hidden is assigned to the property, the element and its content
are rendered (they occupy space on the screen) but are not shown to the user.

Another important value for the display property is inline-block. Block elements present two
characteristics, one is that they add a line break, so the next element is displayed
in a new line, and the other is that they can adopt a custom size. This is why
the width and height properties discussed earlier
only work with Block elements. If we assign these properties to an Inline
element like <span>, nothing happens. But the display property offers the inline-block value to define an Inline
element that can have a custom size. This means that Inline-Block elements will
be positioned next to each other on the same row, but with the size we choose.

Figure 4-2: Inline-Block elements

Inline-Block elements allow us to
create sections on our web page of the size we want and place them on the same
line if necessary. For example, if we have two Block elements that should be
displayed side by side, like the <section> and <aside> elements of the document we developed in
Chapter 2, we can declare them as Inline-Block.

Although it may be tempting to use
Inline-Block elements to design all the columns and sections of our web page,
CSS includes better properties for this purpose. They are part of what it is
called a Box Model; a set of rules that determine how the boxes are
going to be displayed, the space they occupy, and how they are organized on the
page considering the space available.

Currently, there are a few Box Models
available, with the Traditional Box Model and the Flexible Box Model considered
as the standards. To learn how to design our web pages, we have to understand
how these Box Models work.

4.2 Traditional Box Model

As we mentioned, Block elements are positioned one over
another and Inline elements from left to right on the same line. The
Traditional Box Model establishes that elements can float to each side of the
window and share space on the same line with other elements, regardless of
their type. For instance, if we have two Block elements that represent columns
in the design, we can position one column on the left and the other column on
the right by making the elements float to the side we want. The following are
the properties provided by CSS for this purpose.

float—This property allows an element to float to one side or another
of its container. The element is moved to the side to occupy the space
available, sharing the line with other elements. The possible values are none (it does not
float), left (it floats to the left), and right (it floats
to the right).

clear—This property recovers the document's normal flow, not allowing
the element to keep floating to the left, right, or both sides. The possible
values are none, left, right, and both.

The float property makes the element float to one
side or another in the available space. When this property is applied, the
elements do not follow the normal flow of the document, they are moved to the
left or to the right of the available space in their container, as far as
possible, responding to the value of float and until we specify the opposite with
the clear property.

Floating Content

The float and clear properties were originally used to make content float
around an element. For example, if we want text to be displayed on one side of
an image, we can float the image to the left or the right and the text will
share with the image the space available on the line.

Listing 4-2: Testing the float property

<!DOCTYPE html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<section>

<img src="myimage.jpg" width="120">

<p>The World Wide Web's markup language has always been HTML. HTML was
primarily designed as a language for semantically describing scientific
documents, although its general design and adaptations over the years have
enabled it to be used to describe a number of other types of
documents.</p>

</section>

<footer>

<p>Published by W3C</p>

</footer>

</body>

</html>

The document of Listing 4-2 includes
a section with an image and a paragraph. If we open this document using the
styles by default, the <p> element is displayed below the
<img> element.

Figure 4-3: Image and paragraph with
styles by default

If we want to display the text next
to the image, we can make the <img> element float to the left or
the right.

Listing 4-3: Floating the image to the left

section img {

float:
left;

margin: 0px 10px;

}

When an element floats to one side,
the following elements float around it to occupy the space available.

Figure 4-4: Image floats to the left

If instead of left we assign the value right to the float property, the image floats to the right, and the text
follows the image, occupying the space available on the left side.

Figure 4-5: Image floats to the right

Do It
Yourself: Create a new HTML file with the document of Listing 4-2 and a CSS
file with the name mystyles.css and the rule in Listing 4-3. Remember to
include the image myimage.jpg in the same folder (the image is available on our
website). Open the document in your browser. You should see something similar
to Figure 4-4. Assign the value right to the float property and
refresh the document in your browser. This should move the image to the right,
as shown in Figure 4-5.

Browsers cannot calculate the size of
a container from floating elements, so if the element affected by the float property is taller than the rest of the elements on the
line, it will overflow its container. For instance, the following figure
illustrates what happens if we remove the width attribute from the <img> element in our example and let the
browser display the image in its original size (we assigned a gray background
to the <section> element to be able to see it).

Figure 4-6: The image is taller than
its container

The browser calculates the size of
the container from the size of the text, and therefore the image is drawn
outside of its container. Because the image floats to the left and it extends
outside of the <section> element, the content of the
next element keeps floating to occupy the space left by the image and we get the
result shown in Figure 4-6 (the <footer> element is displayed on the
right side of the image instead of below).

One way to ensure that the container
is tall enough to contain the floating elements is to assign to the container
the overflow property with the value auto. This forces the browser to calculate the size of the
container considering all the elements inside.

Listing 4-4: Recovering the normal flow
with the overflow property

section {

background-color: #CCCCCC;

overflow: auto;

}

section
img {

float: left;

margin: 0px 10px;

}

The overflow property does not let content to overflow
its container, so it either extends the size of the container to fit all the
content available or includes scroll bars to let the user scroll the content if
the size of the container cannot be changed.

Figure 4-7: The image no longer
overflows its container

Do It
Yourself: Remove the width attribute of the <img> element in
the HTML document of Listing 4-2. Replace the rules in your CSS file with the
code in Listing 4-4. Open the document in your browser. You should see
something similar to Figure 4-7.

Another alternative to normalizing
the flow of the document is to use the clear property in the element that
follows the floating element inside the container. Because we not always have a
sibling after a floating element that we can use to stop elements from floating
around, this technique requires us to add an empty element to the document. For
example, we may include a <div> element below the <p> element inside the <section> element to recover the normal flow of the
document.

Listing 4-5: Adding an empty element to
apply the clear property

<!DOCTYPE html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<section>

<img src="myimage.jpg">

<p>The World Wide Web's markup language has always been HTML. HTML was
primarily designed as a language for semantically describing scientific
documents, although its general design and adaptations over the years have
enabled it to be used to describe a number of other types of
documents.</p>

<div class="clearfloat"></div>

</section>

<footer>

<p>Published by W3C</p>

</footer>

</body>

</html>

In this case, we have to assign the clear property to the additional element. The property provides
values to clear a side or both. Because we usually want to stop the elements
from floating altogether, the preferred value is both.

Listing 4-6: Recovering the normal flow
with the clear property

section {

background-color: #CCCCCC;

}

section
img {

float: left;

margin: 0px 10px;

}

.clearfloat
{

clear:
both;

}

With the rules in Listing 4-6, the <div> element restores the normal flow of the
document, allowing the browser to calculate the right size for the container,
getting the same result we see in Figure 4-7.

Do It
Yourself: Update your HTML file with the document of Listing 4-5 and
replace the rules in your CSS file with the code in Listing 4-6. Open the document
in your browser. You should see something similar to Figure 4-7.

Floating Boxes

The clear property does not affect other
aspects of the elements and it does not add scroll bars like the overflow property, which is why it is the one
implemented in the Traditional Box Model to organize the structure of a
document. With the float and clear properties, we can control precisely where the elements
are displayed on the screen. For example, the following document includes a <section> element with four <div> elements inside that we have to float to
one side to turn them into columns on the page, and one last <div> element at the end that we will use to
restore the document's flow.

Listing 4-7: Creating a document for
testing the float property

<!DOCTYPE html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<section id="parentbox">

<div id="box-1">Box 1</div>

<div id="box-2">Box 2</div>

<div id="box-3">Box 3</div>

<div id="box-4">Box 4</div>

<div class="clearfloat"></div>

</section>

</body>

</html>

As it happens with other documents
before, if we open this one without assigning custom styles, the content of its
elements is displayed from top to bottom, following the flow set for the
document by default.

Figure 4-8: Document's normal flow

Because we want these boxes to be
side by side, representing the columns of our web page, we have to give them a fixed
size and make them float to one side or another. The size is determined by the width and height properties, and how they float
is determined by the float property, but the value
assigned to this last property depends on what we want to achieve. If we make
the boxes float to the left, they will line up from left to right, and if we
make them float to the right they will do the same but from right to left. For
example, if we make them float to the left, box-1 will be positioned on the
left-hand side of the parent box, and then the rest of the boxes will be placed
on its right in the order they were declared in the code (they float to the
left until they collide with the edge of the container or the previous box).

Listing 4-8: Floating boxes to the left

#parentbox {

width: 600px;

border: 1px solid;

}

#box-1
{

float:
left;

width: 140px;

height: 50px;

margin: 5px;

background-color: #AAAAAA;

}

#box-2
{

float:
left;

width: 140px;

height: 50px;

margin: 5px;

background-color: #CCCCCC;

}

#box-3
{

float:
left;

width: 140px;

height: 50px;

margin: 5px;

background-color: #AAAAAA;

}

#box-4
{

float:
left;

width: 140px;

height: 50px;

margin: 5px;

background-color: #CCCCCC;

}

.clearfloat
{

clear:
both;

}

In Listing 4-8, we assign a width of
600 pixels to the <section> element and a size of 140
pixels by 50 pixels to each box. Following the normal flow, they would be stacked
on the page, but because we assign the float property with the value left to each one of them, they float to the left until they
find the edge of their container or another box, filling the space available.

Figure 4-9: Boxes organized with the float property

As we mentioned before, when an
element floats, the browser cannot calculate the height of its parent box. This
is why, every time we have elements that float, we have to clear the next
element.

Do It Yourself: Create a new HTML file with
the code in Listing 4-7 and a new CSS file called mystyles.css with the rules in
Listing 4-8. Open the document in your browser. You should see something
similar to Figure 4-9.

In the last example, we made sure
that the parent box is wide enough to contain all the boxes, so they are all
displayed on the same line, but if the container is not big enough, the
elements that do not fit in the space available will move to a new line. The
following rule reduces the size of the <section> element to 500 pixels to test
this possibility.

Listing 4-9: Reducing the size of the
container

#parentbox {

width:
500px;

border: 1px solid;

}

Once we apply this rule to our
document, the last box does not find room on the right side of the third box,
and therefore it floats to the left edge of the container in a new line.

Figure 4-10: Boxes automatically fill
the space available

On the other hand, if we have more room available
in the container than the one required by the boxes, the remaining space is
located on the sides (left or right, depending on the value of the float property). If we want to keep the remaining space in between the
boxes, we have to float some boxes to the left and others to the right. For
example, the following rules assign a size of 120 pixels to each box, living an
empty space in the container of 80 pixels, but because the last two boxes float
to the right instead of the left, the free space is placed in the middle.

Listing 4-10: Floating boxes to the left and
right

#parentbox {

width: 600px;

border: 1px solid;

}

#box-1
{

float: left;

width: 120px;

height: 50px;

margin: 5px;

background-color: #AAAAAA;

}

#box-2
{

float: left;

width: 120px;

height: 50px;

margin: 5px;

background-color: #CCCCCC;

}

#box-3
{

float:
right;

width: 120px;

height: 50px;

margin: 5px;

background-color: #AAAAAA;

}

#box-4
{

float:
right;

width: 120px;

height: 50px;

margin: 5px;

background-color: #CCCCCC;

}

.clearfloat
{

clear: both;

}

The elements box-1 and box-2 float to the left, which means that box-1 will be on the left side of the container and box-2 will be on the right side of box-1, but box-3 and box-4 float to the right, so they are going to
be located on the right side of the container, leaving a space in the middle.

Figure 4-11: Space between boxes

Notice that box-4 is on the left side of box-3. This is due to the position of the elements in the code. The browser
processes the elements in the way they were declared in the document.
Therefore, when the browser processes the box-3 element, it moves it to the right until it
reaches the right edge of the container, but when it processes the box-4 element, it cannot move it to the side of the container because the box-3 element is already there, so it places this box on the left side of box-3. If we want the boxes to be in order, we have to modify the document
of Listing
4-7 and move the <div> element identified with the id box-4 over the <div> element identified with the id box-3.

Absolute Positioning

The position of an element may be relative or
absolute. With relative positioning, the boxes are positioned one after another
in the space designated by their container. If there is no space available or
the elements are not floating around other elements, they are positioned on a
new line. This is the positioning mode by default, but there is another mode to
position the elements called Absolute Positioning. Absolute positioning
allows us to set the exact coordinates where we want to position each element.
The following are the properties available for this purpose.

position—This property defines the type of positioning used to position
the element. The possible values are static (according
to the document's flow), relative (relative to
the element's original position), absolute (absolute
position relative to the element's container), and fixed (absolute position
relative to the browser window).

top—This property specifies the distance between the element's top
margin and the top margin of its container.

bottom—This property specifies the distance between the element's bottom
margin and the bottom margin of its container.

left—This property specifies the distance between the element's left
margin and the left margin of its container.

right—This property specifies the distance between the element's right
margin and the right margin of its container.

The top, bottom, left, and right properties apply to both types of
positioning relative or absolute, but they work in different ways. When the
element is positioned with relative positioning, it is displaced, but the
layout is not modified. With absolute positioning, the element is removed from
the document's layout, so the rest of the elements are also displaced to occupy
the space left by the element if necessary.

The following example uses relative positioning
to move the first box of our example 25 pixels down.

Listing 4-11: Setting the relative position
of an element

#box-1 {

position: relative;

top:
25px;

float: left;

width: 140px;

height: 50px;

margin: 5px;

background-color: #AAAAAA;

}

Figure 4-12: Relative position

Do It
Yourself: Replace the rule box-1 in your CSS
Style Sheet with the rule in Listing 4-11. In this example, we only move the
first box, so the rest of the rules remain the same. Add other properties like left or right to see how
they affect the position of the element.

Another alternative is to use absolute
positioning. In this case, the element is removed from the layout, so the rest
of the elements are also affected by the rule. When using absolute positioning,
we also have to consider that the element will be positioned relative to the
browser window unless its parent's position is also set. Therefore, if we want
to set the absolute position of an element relative to the position of its
parent box, we have to declare the position property for the parent as well. In the
following example, we declare a relative position for the parentbox element and an absolute position of 25 pixels from
the top for box-1, which will make the box appear lower than
the rest of the boxes.

Listing 4-12: Setting the absolute position
of an element

#parentbox {

position: relative;

width: 600px;

border: 1px solid;

}

#box-1
{

position: absolute;

top: 25px;

float: left;

width: 140px;

height: 50px;

margin: 5px;

background-color: #AAAAAA;

}

Because absolute positioning removes
the element from the document's layout, the rest of the boxes move to the left
to occupy the empty space left by box-1.

Figure 4-13: Absolute position

Do It
Yourself: Replace the #parentbox and #box-1 rules in
your CSS Style Sheet with the rules in Listing 4-12 and open the document in
your browser. You should see something similar to Figure 4-13.

The order in the code determines not only the
location of the boxes on the page but also which box will be on top when they
overlap. Because box-1 is the first one declared in the code, it
is drawn over box-2, but CSS offers the following property to
change this behavior.

z-index—This property defines an index that determines the position of
the element in the z axis. An element with a higher index will be drawn
over an element with a lower index.

For example, we can move the box-1 element below the box-2 element and over the parentbox element by assigning negative indexes to box-1 and parentbox.

Listing 4-13: Setting the z index

#parentbox {

position: relative;

width: 600px;

border: 1px solid;

z-index:
-2;

}

#box-1
{

position: absolute;

top: 25px;

z-index:
-1;

float: left;

width: 140px;

height: 50px;

margin: 5px;

background-color: #AAAAAA;

}

Negative indexes are considered lower than the
indexes assigned by default. In Listing 4-13, we assign the value -1 to the box-1 element and the value -2 to the parentbox element. This moves box-1 below box-2, but it keeps box-1 on top of parentbox, because its index is higher.

Figure 4-14: Custom z index

When we use relative and absolute positioning,
the document's layout is modified. For this reason, this technique is not used
to organize the elements on the screen but to create visual effects, like drop-down
menus or slides that reveal additional information. For instance, we can show a
box with the title of an image when the user moves the mouse over it.

Listing 4-14: Displaying a slide with
additional information

<!DOCTYPE html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<section id="parentbox">

<img src="myimage.jpg" width="365"
height="274">

<div id="container">

<div id="topcontainer"></div>

<div id="bottomcontainer">

<span><strong>This is my backyard</strong></span>

</div>

</div>

</section>

</body>

</html>

The document of Listing 4-14 includes a section
containing an image. Below the <img> element there is a <div> element identified with the name container and inside this element there are two more <div> elements, one empty and another with a title. The
purpose of this code is to have an image covered by a box that when it is moved
up reveals another box at the bottom with the image's title. Figure 4-15
illustrates the structure generated by these elements.

Figure 4-15: Structure to present a
slide with additional information

The image and the container are both inside the parentbox element, but the container has to be positioned on
top of the image, so it can be moved up to reveal the bottomcontainer element with the title when the user moves the mouse
over the image. For this reason, we have to assign an absolute position to this
element.

Listing 4-15: Styling the boxes

#parentbox {

position: relative;

width: 365px;

height: 274px;

overflow: hidden;

}

#container
{

position: absolute;

top:
0px;

width: 365px;

height: 324px;

}

#topcontainer
{

width: 365px;

height: 274px;

}

#bottomcontainer
{

width: 365px;

height: 35px;

padding-top: 15px;

background-color: rgba(200, 200, 200, 0.8);

text-align: center;

}

#container:hover
{

top:
-50px;

}

The image we are using in this
example is 365 pixels wide by 274 pixels tall, so we have to set this same size
for the parentbox element. The container, on the
other hand, has to be taller because it has to contain the topcontainer element and the bottomcontainer element that is revealed when the
container moves up. Because the box with the title is 50 pixels tall (35 pixels
tall and 15 pixels of padding), we give the container box a height of 324
pixels (274 + 50).

The reason why we placed a container on top of
the image is because we have to react when the user moves the mouse over the image.
The only way CSS allows us to do that is with the :hover pseudo-class, as we have seen before (see Listings 3-81 and 3-82). The
problem with this pseudo-class is that it only allows us to modify the element
to which it was applied. Using this pseudo-class with the image, we will only
be able to modify the image itself, but applying it to the container we can change
the value of its top property and move it up to reveal the bottomcontainer element. The rule at the end of Listing 4-15 performs
this task. When the user moves the mouse over the image, the #container:hover rule assigns a value of -50 pixels to the top property of the container element, moving the element and its
content up, revealing the image's title.

Figure 4-16: Slide over an image

The bottomcontainer element is displayed as soon as we move
the mouse over the image. This is because there is no transition set for the top property. The values go from 0px to -50px instantly, so we do not see
any transition in the process. To set intermediate states and create an
animation, we have to add the transition property to the container element.

Listing 4-16: Animating the slide

#container {

position: absolute;

top: 0px;

width: 365px;

height: 324px;

transition: top 0.5s ease-in-out 0s;

}

Do It
Yourself: Create a new HTML file with the code in Listing 4-14 and a CSS
file called mystyles.css with the codes in Listing 4-15. Download the image
myimage.jpg from our website. Open the document in your browser and move the
mouse over the image. You should see the slide with the image's title appear
and disappear at the bottom (Figure 4-16). Replace the #container rule in your
Style Sheet with the rule in Listing 4-16. Now the slide should be animated.

Columns

Besides the
properties we have studied to organize the boxes on the screen, CSS also
includes a group of properties to facilitate the creation of columns.

column-count—This property specifies the number of
columns the browser has to generate to distribute the content of the element.

column-width—This property declares the width of the
columns. The values can be auto (by default) or a
length in any valid CSS unit, such as pixels or percentages.

column-span—This property is applied to elements
inside a box to determine if the element will be placed inside one column or
spanned across multiple columns. The possible values are all, meaning
"all the columns", and none (by
default).

column-fill—This property determines how the content
will be divided into columns. The possible values are auto (the columns
will be filled sequentially) and balance (the content
is equally divided into columns).

column-gap—This property defines the size of the
space between columns. It can take a value in any of the units available in
CSS, such as pixels or a percentage.

columns—This is a shorthand property to set the values of the column-count and column-width properties
all at once.

The
element, which content we want to split into columns, is a standard element,
and its content is declared the same way we always do. The browser takes care
of creating the columns and dividing the content for us. The following example
includes an <article> element with
a long text that we are going to present in two columns.

Listing 4-17: Splitting articles into
columns

<!DOCTYPE html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<section>

<article id="newsarticle">

<span>The World Wide Web's markup language has always been HTML. HTML was
primarily designed as a language for semantically describing scientific
documents, although its general design and adaptations over the years have
enabled it to be used to describe a number of other types of documents. The
main area that has not been adequately addressed by HTML is a vague subject
referred to as Web Applications. This specification attempts to rectify this,
while at the same time updating the HTML specifications to address issues
raised in the past few years.</span>

</article>

</section>

</body>

</html>

The
properties to generate the columns must be applied to the container. The
following rule includes the column-count property to
divide the content of the <article> element in
two columns.

Listing 4-18: Defining the columns

#newsarticle {

width: 580px;

padding: 10px;

border: 1px solid;

column-count: 2;

column-gap: 20px;

}

The rule in Listing 4-18 assigns a
size of 580 pixels to the <article> element and identifies the
area occupied by its content with a solid border. The rule also includes a
padding of 10 pixels to separate the border from the text. The rest of the
properties divide the content into two columns with a gap of 20 pixels in
between. The result is shown in Figure 4-17.

Figure 4-17: Content in two columns

The column-gap property
defines the size of the space between the columns. This gap is just empty
space, but CSS provides the following properties for generating a rule that lets
the user see the division.

column-rule-style—This property defines
the style of the line used to represent the division. The possible values are hidden (by
default), dotted, dashed, solid, double, groove, ridge, inset, and outset.

column-rule-color—This property specifies
the color of the line used to represent the division.

column-rule-width—This property specifies
the width of the line used to represent the division.

column-rule—This is a shorthand property that allows
us to define all the values for the line at once.

To draw
a line between the columns, we have to specify at least the style of the line.
The following example implements the column-rule property to
create a solid black rule of 1 pixel.

Listing 4-19: Adding a rule between the
columns

#newsarticle {

width: 580px;

padding: 10px;

border: 1px solid;

column-count: 2;

column-gap: 20px;

column-rule: 1px solid #000000;

}

Figure 4-18: Columns separated by a rule

Do It Yourself: Create a new HTML file with the code in Listing 4-17
and a CSS file called mystyles.css with the code in Listing 4-18. Open the
document in your browser. You should see the text divided into two columns.
Replace the #newsarticle rule with the rule in Listing 4-19 and
refresh the page in your browser. You should see the rule dividing the columns
as in Figure 4-18.

Real-life Application

The purpose of the Traditional Box Model
is to organize the visual structure of a page, but because of its characteristics
and limitations, the documents must be modified to work with this model.

As explained in Chapter 2, websites
follow a pattern and HTML elements were designed to represent this standard
layout, but they cannot predict every possible scenario. To get the elements to
represent the visual areas of a traditional web page, we have to combine them
with other elements. A common technique is to wrap elements with container
elements to be able to move them around. For instance, if we want the header of
our document to be as wide as the browser window but its content centered on
the screen, we can wrap the content in a <div> element and then center this
element and its content on the page. The following are the adaptations we have
to make to the document introduced in Chapter 2 to reproduce this type of
design.

Listing 4-20: Defining a document for the
Traditional Box Model

<!DOCTYPE html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta charset="utf-8">

<meta name="description" content="This is an HTML5
document">

<meta name="keywords" content="HTML, CSS, JavaScript">

<link rel="stylesheet" href="mystyles.css">

</head>

<body>

<header id="logoheader">

<div>

<h1>This is a title</h1>

</div>

</header>

<nav id="mainmenu">

<div>

<ul>

<li><a href="index.html">Home</a></li>

<li><a href="photos.html">Photos</a></li>

<li><a href="videos.html">Videos</a></li>

<li><a href="contact.html">Contact</a></li>

</ul>

</div>

</nav>

<main>

<div>

<section id="mainarticles">

<article>

<h1>Title First Post</h1>

<time datetime="2016-12-23" pubdate>

<div class="daynumber">23</div>

<div class="dayname">Friday</div>

</time>

<p>This is the text of my first post</p>

<figure>

<img src="myimage.jpg">

</figure>

</article>

<article>

<h1>Title Second Post</h1>

<time datetime="2016-12-7" pubdate>

<div class="daynumber">7</div>

<div class="dayname">Wednesday</div>

</time>

<p>This is the text of my second post</p>

<figure>

<img src="myimage.jpg">

</figure>

</article>

</section>

<aside id="asideinfo">

<h1>Personal Info</h1>

<p>Quote from post one</p>

<p>Quote from post two</p>

</aside>

<div class="clearfloat"></div>

</div>

</main>

<footer id="logofooter">

<div>

<section class="footersection">

<h1>Website</h1>

<p><a href="index.html">Home</a></p>

<p><a href="photos.html">Photos</a></p>

<p><a href="videos.html">Videos</a></p>

</section>

<section class="footersection">

<h1>Help</h1>

<p><a href="contact.html">Contact</a></p>

</section>

<section class="footersection">

<address>Toronto, Canada</address>

<small>Copyright &copy; 2016</small>

</section>

<div class="clearfloat"></div>

</div>

</footer>

</body>

</html>

In this example, we have wrapped the content of
some of the structural elements with an additional <div> element. Now, we can assign independent sizes and
alignment to every section of the document.

Do It Yourself: Create a new HTML file with the document of Listing
4-20 and a CSS file called mystyles.css to include all the CSS code we are
going to present next. Open the document in your browser to see how the
elements are organized by default.

With the HTML document ready, it is time to
develop our Style Sheet. In this regard, the first thing we have to consider is
what we are going to do with the styles assigned by default. In most cases,
these styles are not only different from what we need, but they can also
negatively affect our design. For instance, browsers assign margins to elements
we frequently used in our documents like the <p> element. The <body> element itself creates a margin around
its content, which makes impossible to extend other elements to the edges of
the browser window. In addition, the way elements are styled by default differs
from one browser to another, especially when we consider old browsers that are
still in use. To create a design that is consistent wherever our website is
displayed, we have to reset some or all the styles by default. One way to do it
is using a CSS selector called Universal Selector. This is a selector
represented by the * character that references every element in the document.
For example, the following rule sets a margin and a padding of 0 pixels to all the
elements of our document.

Listing 4-21: Implementing a reset rule

* {

margin: 0px;

padding: 0px;

}

The first rule of our CSS file introduced in
Listing 4-21 ensures that every element will have a margin and a padding of 0
pixels. From this point on, we will only have to modify the margins of the elements
that we want to be greater than zero.

Do It Yourself: To create the Style Sheet for the document of Listing
4-20, you have to include all the rules presented from Listing 4-21, one over
another in the same file (mystyles.css).

The Basics: What we have done with the rule in Listing 4-21 is to reset the
styles of our document. This is common practice, and it frequently requires
more than modifying the margins and paddings, as we did in this example.
Because the styles we have to modify are usually the same for every project, developers
have created Style Sheets that already include these rules. These Style Sheets
are called Reset Style Sheets, and there are several available. You can
find an example at www.meyerweb.com/eric/tools/css/reset/.

With this simple rule, we get all the
elements aligned to the left side of the browser window, with no margins around
them, and with the same distance between each other.

Figure 4-19: Document with basic rules

The next step is to design the
header. In this case, we want the <header> element to extend to the edges
of the browser window and its content to be centered and contained in an area
no bigger than 960 pixels (a standard size for the wide screens present in
desktop computers and laptops). The following are the rules required for this
purpose.

Listing 4-22: Assigning styles to the header

#logoheader {

width:
96%;

height: 150px;

padding:
0% 2%;

background-color: #0F76A0;

}

#logoheader
> div {

width: 960px;

margin:
0px auto;

padding-top: 45px;

}

#logoheader
h1 {

font: bold 54px Arial, sans-serif;

color: #FFFFFF;

}

Because we want the header to be as wide as the
window, we have to declare its size in percentage. When the size of an element
is declared in percentage, the browser takes care of calculating the real size
in pixels from the current size of its container (in this case, the browser
window). For our document, we want the header to be as wide as the window but
to have a padding on the sides, so its content is separated from the edge. To
that end, we assign a value of 96% to the width property and declare a padding of 2% on
the sides. If the window has a width of 1000 pixels, for example, the <header> element will be 960 pixels wide and will have a
padding of 20 pixels on the left and right sides.

The Basics: If you want to assign a fixed margin or padding to an element but
at the same time adjust its width to fill the space available, you can assign
the value auto to the width property. This value asks the browser to calculate the
width of the element from the width of its container, but considering the
values of the element's margins, paddings, and borders. For example, if the
window is 1000 pixels wide and we assign to the header a padding of 50 pixels
on each side, the browser will assign a width of 900 pixels to the header.

The second rule in the example of
Listing 4-22 affects the <div> elements that are direct
descendants of the <header> element. Since we only have
one <div> element that we use to wrap the header's
content, that is the element to be modified. The properties assign a width of
960 pixels, a value of 0 pixels for the top and bottom margins, and the value auto for the left and right margins. The value auto asks the browser to calculate the margins according to the
size of the element and the space available in its container. This makes the
browser center the <div> element, and therefore its content,
when the container is wider than 960 pixels.

Finally, we declare the rule #logoheader h1 to change the font and color of the <h1> elements inside the header. The result is
shown in Figure 4-20.

Figure 4-20: Header

IMPORTANT: With the introduction of mobile devices, the limitations
and requirements have changed. Websites are not developed with fixed layouts
anymore. We will learn how to build flexible layouts in the next section of
this chapter and how to adapt our websites to mobile devices using Responsive
Web Design in Chapter 5.

The Basics: The limit of 960 pixels is the standard used to declare the size
of web pages for the wide screens found in desktop computers and laptops. The
value was established considering the human capacity to read long texts. For a
text to be readable, it is usually recommended to have no more than 50 to 75
characters per line. If we extend all the content to the edges of the browser
window, our website will be unreadable in wide screens, and therefore it is
recommended to keep the number of characters on a line of text within these
values.

Similar to the header, the menu in
our design has to extend to the edges of the window, but the options have to be
centered in a space no bigger than 960 pixels.

Listing 4-23: Assigning styles to the
navigation area

#mainmenu {

width: 96%;

height: 50px;

padding: 0% 2%;

background-color: #9FC8D9;

border-top: 1px solid #094660;

border-bottom: 1px solid #094660;

}

#mainmenu
> div {

width: 960px;

margin: 0px auto;

}

These styles position the <nav> element and its content, but the <ul> and <li> elements that conform the menu
still have styles by default that create a vertical list of elements and what
we need is to have the options side by side. There are different ways of
organizing the element horizontally, but the best alternative, in this case, is
to declare the <li> elements as inline-block elements. By declaring the elements as inline-block we can position them on the same line and
give them a custom size.

Listing 4-24: Assigning styles to the
options of the menu

#mainmenu li {

display:
inline-block;

height: 35px;

padding: 15px 10px 0px 10px;

margin-right: 5px;

}

#mainmenu
li:hover {

background-color: #6FACC6;

}

#mainmenu
a {

font: bold 18px Arial, sans-serif;

color: #333333;

text-decoration: none;

}

The first rule in Listing 4-24
declares the <li> elements inside the <nav> element as inline-block elements and give them a height of 35
pixels, with a padding at the top of 15 pixels and 10 pixels on the sides. This
example also includes a rule with the :hover pseudo-class to change the
background color of the <li> element every time the mouse
is over an option. In the last rule, the <a> elements are also styled with
a different font and color. The result is shown in Figure 4-21.

Figure 4-21: Menu

The Basics: By default, a list of items created with the <ul> and <li> elements has a display mode
called list-item. This display mode creates a
vertical list of items with graphics on the left to identify each item. These
graphics are usually called bullets. When you declare a different value
for the display property, the bullets are
automatically removed. To modify or remove these indicators when the display
mode is list-item, CSS offers the list-style property (a shorthand property for the
properties list-style-image, list-style-position, and list-style-type). CSS offers several values we
can assign to this property to determine the type of graphic we want to show.
The most frequently used are none, square, circle, and decimal. The property also lets us declare the position of the
bullet (inside or outside) and even a custom image (e.g., list-style: url("mybullet.jpg");). We will show an example of
this property in Listing 4-32.

Next, we have the main section of our
web page. This section was identified with the <main> element and it contains two elements, <section> and <aside>, that we need to turn into
columns. To follow the same design pattern used for the header and the menu, we
have to extend the <main> element to the sides of the
window and center its content.

Listing 4-25: Assigning styles to the main
content

main {

width: 96%;

padding: 2%;

background-image: url("background.png");

}

main
> div {

width: 960px;

margin: 0px auto;

}

The rules in Listing 4-25 assign a
background image to the <main> element to differentiate the
main area from the rest of the page. We also add a 2% padding for to the top
and bottom, so the main area's content is separated from the menu bar and
footer.

The
Basics: Backgrounds
cover the area occupied by the element and its padding. The margins are
considered to be outside the element, and therefore they are not affected by
the background property. If you want
the background to be shown in the whole area occupied by the element, you have
to avoid the use of margins and assign paddings to the element instead, as we
did in Listing 4-25.

The next step is to create the two
columns that present the main content of our web page.

Listing 4-26: Creating the columns for the
main content

#mainarticles {

float:
left;

width: 620px;

padding-top: 30px;

background-color: #FFFFFF;

border-radius: 10px;

}

#asideinfo
{

float:
right;

width: 280px;

padding: 20px;

background-color: #E7F1F5;

border-radius: 10px;

}

#asideinfo
h1 {

font: bold 18px Arial, sans-serif;

color: #333333;

margin-bottom: 15px;

}

.clearfloat
{

clear:
both;

}

In this case, we use the float property to move the elements that represent each column
to the sides. The <section> element was moved to the left,
and the <aside> element was moved to the
right, leaving a space in between of 20 pixels.

Figure 4-22: Main content

The
Basics: Every time
the elements are positioned with the float property, we
have to remember to clear the next element to recover the normal flow of the
document. In this example, we have added an empty <div> below the <aside> element for
this purpose.

Now that the columns are ready, we
have to take care of their content. The code in Listing 4-26 already includes a
rule that styles the content of the <aside> element, but we still have to
style the <article> elements in the first column.

Each <article> element includes a <time> element to represent the date on which
the article was posted. For our design, we have decided to display this date in
a box on the left side of the article, so we have to give it an absolute
position.

Listing 4-27: Setting the position and
styles of the <time> element

article {

position: relative;

padding: 0px 40px 20px 40px;

}

article
time {

display: block;

position: absolute;

top:
-5px;

left:
-70px;

width: 80px;

padding: 15px 5px;

background-color: #094660;

box-shadow: 3px 3px 5px rgba(100, 100, 100, 0.7);

border-radius: 5px;

}

.daynumber
{

font: bold 36px Verdana, sans-serif;

color: #FFFFFF;

text-align: center;

}

.dayname
{

font: 12px Verdana, sans-serif;

color: #FFFFFF;

text-align: center;

}

To assign an absolute position to the
<time> element, we have to declare the position
of its parent as relative. This is done by the first
rule in Listing 4-27. The second rule defines the position and size of the <time> element, which will be located 5 pixels
from the top and 110 pixels from the left side of the <article> element.

Figure 4-23: Absolute position for the <time> element

The rest of the rules for the content
have to assign simple styles to the remaining elements inside the <article> elements.

Listing 4-28: Assigning styles to the
articles

article h1 {

margin-bottom: 5px;

font: bold 30px Georgia, sans-serif;

}

article
p {

font: 18px Georgia, sans-serif;

}

figure
{

margin: 10px 0px;

}

figure
img {

padding: 5px;

border: 1px solid;

}

Figure 4-24: Articles

Finally, we have to add a few styles
for the footer. The <footer> element, as well as the rest
of the structural elements, has to extend to the edge of the window, but in
this case, we have divided the content with three <section> elements to display the information in
columns.

Listing 4-29: Assigning styles to the footer

#logofooter {

width: 96%;

padding: 2%;

background-color: #0F76A0;

}

#logofooter
> div {

width: 960px;

margin: 0px auto;

background-color: #9FC8D9;

border-radius: 10px;

}

.footersection
{

float:
left;

width:
270px;

padding:
25px;

}

.footersection
h1 {

font: bold 20px Arial, sans-serif;

}

.footersection
p {

margin-top: 5px;

}

.footersection
a {

font: bold 16px Arial, sans-serif;

color: #666666;

text-decoration: none;

}

The rules in Listing 4-29 define three sections
of 276 pixels wide, all floating to the left to create the three columns
required by our design.

Figure 4-25: Footer

In cases like this, where all the sections are of
the same size, instead of declaring their sizes in pixels, it is better to do
it in percentages. The percentage indicates how much space the element is going
to take from the space available in its container (100%). For instance, we can
declare the width of the sections in the footer of our example with a value of
27.33% and complete the 100% with the padding.

Listing 4-30: Calculating the size of the
sections with percentages

.footersection {

float: left;

width:
27.33%;

padding:
3%;

}

When values are declared in percentages, the
browser takes care of calculating how many pixels it has to assign to each
element according to the space available in the container (e.g., 960 x 27.33 /
100 = 262). If we apply the rule in Listing 4-30 to our document, the footer
will look like Figure 4-25, but the sizes of the sections will be calculated by
the browser before rendering the page.

IMPORTANT: Percentage values can also be used to build flexible layouts, as we
will see in Chapter 5, but there is a better model for this purpose called
Flexible Box Model. We will study the Flexible Box Model in the next section of
this chapter.

The design is finished, but this is
just one of the pages of our website. This document represents the initial
page, usually stored in the file by default (e.g., index.html), but we have to
create the rest of the documents to represent each available page. Fortunately,
this is not a complicated task. The same design we have developed for the
initial page is usually shared throughout the entire website, we just have to
change the structure of the main content area, but the rest, such as the
header, footer, and general structure of the document remains the same,
including the Style Sheet, that is usually shared by most documents.

Do It Yourself: If you haven't done it yet,
assign the name index.html to the document of Listing 4-20. Create the files photos.html,
videos.html and contact.html with a copy of this document. Replace the content
of the <section> and <aside> elements in the main area with the
content corresponding to each page. If necessary, add new rules to the
mystyles.css file to adjust the design. Although it is recommended to keep all
the styles in one file, you can also create different Style Sheets for every
document if necessary. Open the index.html file in your browser and click on
the links to navigate through the pages.

If we need to define other areas on
the page or divide the main area into smaller sections, we can organize the
boxes with the float property, as we did with the
sections inside the footer. Although we can also design some areas with
Relative and Absolute positioning, these modes are reserved for positioning
non-relevant content, like the dates of the articles in our example or to show
hidden content after receiving feedback from the user. For instance, we can
create a drop-down menu that shows a submenu for each option. The following
example illustrates how to add a submenu to the Photos option of our document.

Listing 4-31: Adding submenus

<ul id="menulist">

<li><a href="index.html">Home</a></li>

<li><a href="photos.html">Photos</a>

<ul>

<li><a href="family.html">Family</a></li>

<li><a
href="vacations.html">Vacations</a></li>

</ul>

</li>

<li><a href="videos.html">Videos</a></li>

<li><a href="contact.html">Contact</a></li>

</ul>

The styles for a menu that includes
submenus differs a little bit from the previous example. We have to list the
options of the submenu vertically instead of horizontally, position the list
below the menu bar with absolute values, and only show it when the user moves
the mouse over the parent option. For this purpose, in the HTML code in Listing
4-31, we have added the identifier menulist to the <ul> element that represents the main menu.
Now, we can easily differentiate this element from the <ul> elements that represent the submenus
(only one in our example). The following rules use this identifier to assign
styles to all the menus and their options.

Listing 4-32: Assigning styles to submenus

#menulist > li {

position: relative;

display: inline-block;

height: 35px;

padding: 15px 10px 0px 10px;

margin-right: 5px;

}

#menulist
li > ul {

display:
none;

position: absolute;

top:
50px;

left:
0px;

background-color: #9FC8D9;

box-shadow: 3px 3px 5px rgba(100, 100, 100, 0.7);

border-radius: 0px 0px 5px 5px;

list-style: none;

z-index:
1000;

}

#menulist
li > ul > li {

width: 120px;

height: 35px;

padding-top: 15px;

padding-left: 10px;

}

#menulist
li:hover ul {

display:
block;

}

#mainmenu
a {

font: bold 18px Arial, sans-serif;

color: #333333;

text-decoration: none;

}

#mainmenu
li:hover {

background-color: #6FACC6;

}

The first two rules configure the
main options and their submenus. We assign a relative position to the <li> elements that represent the main options
and then give the submenus an absolute position 50 pixels down from the top.
This value positions the submenus below the menu bar. To make the submenus
invisible, we declare the display property with the value none. Other properties we had to include in this rule are list-style, to remove the bullets shown by default
on the left side of the options, and the z-index property to make sure that the
submenu is shown over the rest of the elements in the area.

As we have done before, to respond to
the mouse, we have to implement the :hover pseudo-class, but this case is
different. We have to show the <ul> element representing the
submenu every time the user moves the mouse over any of the main options, but
the main options are represented by <li> elements. The solution, in
this case, is to declare the :hover pseudo-class for the <li> elements but add the ul keyword at the end of the selector to assign the styles to the <ul> elements that are inside the <li> element affected by the pseudo-class (#menulist li:hover ul). When the user moves the mouse over
a <li> element that represents an option of
the main menu, the value block is assigned to the display property of the <ul> element and the submenu
is shown on the screen.

Figure 4-26: Submenus

Do It Yourself: Update your document with the
code in Listing 4-31 and the rules in your Style Sheet with the code in Listing
4-32. Open the index.html file in your browser and move the mouse to the Photos
option. You should see something similar to Figure 4-26.

4.3 Flexible Box Model

The main purpose of a box model is to
provide a mechanism to divide the window’s space into boxes and create the rows
and columns that are part of the design of a web page. However, the tools
provided by the Traditional Box Model fail in this regard. Defining how boxes
are distributed and specifying their horizontal and vertical size, for example,
cannot be done efficiently with that model.

The difficulty of implementing common
design patterns, like expanding columns to fill the space available, center the
content of a box vertically, or extend a column from top to bottom
independently of its content, forced the industry to search for other
alternatives. Several box models were developed, but none received more
attention than the Flexible Box Model.

IMPORTANT: Although the Flexible Box Model has advantages over
the Traditional Box Model, it is still experimental, and some browsers cannot
process its properties. This is why in this chapter we have discussed the use
of the Traditional Box Model as well. You should consider these issues before
choosing what mode to apply. To determine where you can use any of the features
included in HTML5, visit www.caniuse.com.

Flex Container

The Flexible Box Model solves the
Traditional Box Model’s problems in an elegant manner. It takes advantage of
tools used by the Traditional Box Model, such as absolute positioning and
columns, but instead of floating the elements it organizes the boxes using
flexible containers. A flexible container is an element that allows its content
to be flexible. In
this new model, every set of boxes must have a parent box that configures the
characteristics of all the boxes, as in the following example.

Listing 4-33: Organizing
boxes with a flexible container

<!DOCTYPE
html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta
charset="utf-8">

<meta
name="description" content="This is an HTML5 document">

<meta
name="keywords" content="HTML, CSS, JavaScript">

<link
rel="stylesheet" href="mystyles.css">

</head>

<body>

<section
id="parentbox">

<div
id="box-1">Box 1</div>

<div
id="box-2">Box 2</div>

<div
id="box-3">Box 3</div>

<div
id="box-4">Box 4</div>

</section>

</body>

</html>

The
document of Listing 4-33 is very similar to previous examples. We have a <section> element that
acts as a container for other elements. The difference is in how the elements
are configured from CSS. To make the boxes inside the <section> element
flexible (their sizes change according to the space available), we have to
declare this element as a flexible container. For this purpose, CSS offers the
values flex and inline-flex for the display property.
The value flex defines a flexible Block element, and the
value inline-flex defines a flexible
Inline element.

Listing 4-34:
Declaring the element parentbox as a
flexible container

#parentbox {

display: flex;

}

Do It
Yourself: Create a new HTML file with the code in Listing 4-33 and a CSS
file called mystyles.css with the rule in Listing 4-34. Open the document in
your browser. You should see the boxes inside the <section> element
displayed next to each other on the same line.

Flexible Elements

For an element
inside a flexible container to become flexible it has to be declared as such.
The following are the properties available to configure flexible elements.

flex-grow—This property indicates
how much an element is going to expand. The size is determined considering the
values assigned to the rest of the elements in the box (the siblings).

flex-shrink—This
property indicates how much an element is going to shrink. The size is
determined from the values assigned to the rest of the elements in the box (the
siblings).

flex-basis—This
property declares an initial size for the element.

flex—This is a
shorthand property that allows us to set the values for the flex-grow, flex-shrink, and flex-basis properties
all at once.

Flexible
boxes will be expanded or reduced to fill the extra space inside their parent
box. The distribution of the space depends on the properties of the rest of the
boxes. If all the boxes are set as flexible, the size of each one of them will depend
on the size of their parent box and the value of the flex property.

Listing 4-35:
Making the boxes flexible with the flex property

#parentbox
{

display: flex;

width: 600px;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1;

}

#box-2 {

flex: 1;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

In
Listing 4-35, we declare only the flex-grow value for
the flex property to determine how the boxes will
expand. The size of each box is calculated by multiplying the value of the size
of the parent box by the value of its flex property
divided by the sum of the flex-grow values of all the boxes. For example, the formula for the element
box-1 is 600 × 1 / 4 = 150. The value 600
is the size of the parent box, 1 is the value of the flex property assigned
to the box-1 element, and 4 is the sum of the values of the flex property
assigned to each box. Because each box in our example has the same value of 1
for the flex property, the size of each box will be 150 pixels minus the
margins (we have assigned a margin of 5 pixels to the <div> elements).

Figure 4-27: Same values
for the flex property
distribute the space equally

Do It
Yourself:
Replace
the rules in your CSS file with the code in Listing 4-35 and open the document
of Listing 4-33 in your browser. You should see something similar to Figure
4-27.

The
potential of this property is evident when we assign different values to each
element.

Listing 4-36: Creating an
uneven distribution

#parentbox
{

display:
flex;

width: 600px;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 2;

}

#box-2 {

flex: 1;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

In
Listing 4-36, we change the value of the flex property for
the box-1 element to 2. Now the formula to calculate the size of
this box is 600 × 2 / 5 = 240. Because we have
not change the size of the parent box, the first value of the formula is the
same, but the second value is now 2 (the new value of the flex property of box-1), and the sum
of the values of all the flex properties is 5
(2 for box-1 and 1 for each of the other three boxes). Applying the
same formula for the rest of the boxes, we get their sizes: 600 × 1 / 5 = 120.

Figure 4-28: Uneven
distribution with flex

Comparing
the results, we can see how the space is distributed. The available space is
divided into portions according to the sum of the values of the flex property of
each box (a total of 5 in our example). Then, the portions are
distributed among the boxes. The element box-1 gets two
portions because the value of its flex property is 2 and the rest
of the elements get one portion each because the value of their flex property is 1. The
advantage of using this system is that every time we add a new element we do
not have to calculate its size; the sizes of all the boxes are recalculated
automatically.

This is useful,
but there are other possible scenarios. For example, when one of the boxes is
inflexible and has an explicit size, the other boxes will flex to share the
rest of the space available.

Listing 4-37: Combining inflexible and flexible boxes

#parentbox
{

display:
flex;

width: 600px;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

width: 300px;

}

#box-2 {

flex: 1;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

The
first box of the example of Listing 4-37 has a size of 300 pixels, so the
available space to distribute between the rest of the boxes is 300 pixels (600
– 300 = 300). The browser calculates the size of every flexible box with
the same formula we used before: 300 × 1 / 3 = 100.

Figure 4-29: Only the
free space is distributed

Just as
we could have one box of an explicit size, we may have several. The principle
is the same; only the remaining space is distributed among the flexible boxes.

There is
a possible scenario where we could have to declare a size for the element but
keep it flexible. To do this, we can use the rest of the parameters available
for the flex property (flex-shrink and flex-basis).

Listing 4-38: Controlling
how the elements shrink

#parentbox
{

display:
flex;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1 1 200px;

}

#box-2 {

flex: 1 5 100px;

}

#box-3 {

flex: 1 5 100px;

}

#box-4 {

flex: 1 5 100px;

}

This
time, three parameters were provided for the flex property of
each box. All the boxes have the value 1 for the
first parameter (flex-grow), declaring
the same expansion ratio. The difference is determined by the addition of the values
for the flex-shrink and flex-basis parameters.
The flex-shrink parameter works like flex-grow but
determines the rate at which the boxes will be reduced to fit into the space
available. In our example, the value of this parameter is 1 for the box-1 element and 5 for the rest
of the boxes, which will assign more space to box-1. The flex-basis parameter,
on the other hand, establishes an initial value for the element. As a result,
the value of the flex-basis parameter is
considered to calculate how much a flexible element is going to be reduced or
expanded. When this value is 0 or is not declared, the value taken into account
is the size of the elements’ content.

Do It
Yourself: Replace the rules in your CSS file with the code in Listing 4-38.
In this example, we did not declare the size of the parentbox element, so
when the browser window is expanded, the parent element is also expanded and
all the boxes inside this element grow at the same rate. On the contrary, when
the window’s size is reduced, the box-1 element is
reduced at a different rate due to the value specified for flex-shrink (1 instead
of 5).

IMPORTANT: The value 0
for the parameters flex-grow or flex-shrink does not
allow the element to be expanded or reduced, respectively. To declare flexibility,
the values of these parameters must be 1 or higher.

We can
also assign the keyword auto to the flex-basis parameter to
ask the browser to use the value of the width property as
a reference.

Listing 4-39: Defining
flexible boxes with a preferred size

#parentbox
{

display:
flex;

width: 600px;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

width: 200px;

flex: 1 1
auto;

}

#box-2 {

width: 100px;

flex: 1 1
auto;

}

#box-3 {

width: 100px;

flex: 1 1
auto;

}

#box-4 {

width: 100px;

flex: 1 1
auto;

}

In
Listing 4-39, every box has a preferred width value, but
after all the boxes are positioned there is a remaining space of 100 pixels.
This extra space will be divided among the flexible boxes. To calculate the
portion of space assigned to each box, we use the same formula as before: 100
× 1 / 4 = 25. This means
that 25 pixels of extra width are added to the preferred size of each box
(minus the margins).

Figure 4-30: Empty space
distributed among the boxes

The flex-basis property allows us to set an
initial size to influence how the browser distributes the space available between
the boxes, but the boxes still expand or shrink beyond that value. CSS offers
the following properties to establish limitations in how much the size of a box
can change.

max-width—This property specifies
the maximum width allowed for the element. It accepts values in
any of the units available in CSS, such as pixels or percentages.

min-width—This property specifies
the minimum width allowed for the element. It accepts values in
any of the units available in CSS, such as pixels or percentages.

max-height—This property specifies
the maximum height allowed for the element. It accepts values in
any of the units available in CSS, such as pixels or percentages.

min-height—This property specifies
the minimum height allowed for the element. It accepts values in
any of the units available in CSS, such as pixels or percentages.

The
following example declares all the boxes as flexible, but assigns a maximum
width of 50 pixels to the box-1 element. No matter how
much space is available, box-1 will never be wider
than 50 pixels.

Listing 4-40: Declaring a
maximum size

#parentbox
{

display:
flex;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

max-width: 50px;

flex: 1;

}

#box-2 {

flex: 1;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

Figure 4-31: Boxes with a
maximum size

Organizing Flexible Elements

By default,
elements inside a flexible container are displayed horizontally in the same
line, but they are not organized with a standard orientation. A flexible
container uses axes to describe the orientation of its content. The
specification declares two axes that are independent of the orientation: the
main axis and the cross axis. The main axis is the one on which the content is
laid out (usually horizontal orientation), and the cross axis is the one
perpendicular to the main axis (usually vertical orientation). If the
orientation changes, the axes are displaced with the content.

Figure 4-32: Axes of
flexible containers

The
properties for this model work with these axes, organizing elements from the ends
of each axis: main-start, main-end, cross-start, and cross-end. This is similar
to left-to-right and top-to-bottom used to describe horizontal and vertical
directions, but in this model that relationship is inverted when the
orientation changes. When one of the ends, like main-start, is mentioned in the
description of a property, we have to remember that its meaning could be left
or top, depending on the current orientation of the container (in the
diagram on the left of Figure 4-32, for example, main-start is referencing the
left side of the container, while in the diagram on the right the same term is
referencing the top of the container).

Now that
we know how the model works, we can modify the organization of the boxes. CSS
offers the following properties for this purpose.

flex-direction—This property defines the
order
and orientation of the boxes in a flexible container. The possible values are
row, row-reverse, column and column-reverse, with the row value set by
default.

order—This
property specifies the order of the boxes. Its value is an integer that
determines the location of each box.

justify-content—This
property determines how the free space is going to be distributed. The possible
values are flex-start, flex-end, center, space-between, and space-around.

align-items—This
property aligns the boxes along the cross-axis. The possible values are flex-start, flex-end, center, baseline, and stretch.

align-self—This
property aligns a box along the cross-axis. It works like align-items but it
affects independent boxes. The possible values are auto, flex-start, flex-end, center, baseline, and stretch.

flex-wrap—This
property determines whether multiple lines of boxes are allowed. The possible
values are nowrap, wrap, and wrap-reverse.

align-content—This
property aligns lines of boxes in the vertical axis. The possible values are flex-start, flex-end, center, space-between, space-around, and stretch.

If we
need to set the direction of the boxes, we can use the flex-direction property.
This property is assigned to the container with a value corresponding to the
order we want to give to its content. The row value sets the orientation
according to the orientation of the text (usually horizontal) and orders the
boxes from main-start to main-end (usually left-to-right). The row-reverse value sets
the same orientation as row, but inverts
the order of the elements from main-end to main-start (usually right to left).
The column value sets the orientation according to
the orientation in which blocks of texts are laid out (usually vertical) and
orders the boxes from main-start to main-end (usually top to bottom). And
finally, the column-reverse value sets
the same orientation as column, but inverts
the order of the elements from main-end to main-start (usually bottom to top).
The following example reverses the natural order of a line of boxes.

Listing 4-41:
Reversing the orientation of the boxes

#parentbox
{

display:
flex;

flex-direction:
row-reverse;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1;

}

#box-2 {

flex: 1;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

Figure 4-33: Boxes in
reverse order

The
Basics: CSS offers a
property called writing-mode that sets
the orientation of the lines of text (horizontal or vertical), and this is the
reason why the result of the Flexible Box Model’s properties always depends on
the orientation previously set for the text. To learn more about this property,
visit our website and follow the links for this chapter.

The
order of the boxes may also be customized. The order property
lets us declare a location for each box.

Listing 4-42:
Defining the location of each box

#parentbox
{

display:
flex;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1;

order: 2;

}

#box-2 {

flex: 1;

order: 4;

}

#box-3 {

flex: 1;

order: 3;

}

#box-4 {

flex: 1;

order: 1;

}

Figure 4-34: New locations
for each box defined by the order property

An
important feature of the Flexible Box Model is the capacity to distribute empty
space. When the boxes do not fill the entire container, there will be free
space that has to be located somewhere in the design. For instance, in the
following example, the size of the flexible container is set as 600 pixels, and
each box is 100 pixels wide. Therefore, there is a remaining space of 200
pixels (minus margins).

Listing 4-43:
Distributing empty space in a flexible container

#parentbox
{

display:
flex;

width: 600px;

border: 1px
solid;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

width: 100px;

}

#box-2 {

width: 100px;

}

#box-3 {

width: 100px;

}

#box-4 {

width: 100px;

}

The
example of Listing 4-43 adds a border to the container to be able to identify
the remaining space. By default, the boxes are ordered from main-start to
main-end (usually left to right), leaving an empty space at the end.

Figure 4-35: Boxes and
empty space inside a flexible container

This behavior
may be modified with the justify-content property.
The value by default assigned to this property is flex-start, which
orders the boxes as shown in Figure 4-35, but we can assign a different value
to customize the way the boxes and the extra space are distributed. For
example, the flex-end value moves
the space to the beginning and the boxes to the end.

Listing 4-44:
Distributing empty space with justify-content

#parentbox
{

display:
flex;

width: 600px;

border: 1px
solid;

justify-content:
flex-end;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

width: 100px;

}

#box-2 {

width: 100px;

}

#box-3 {

width: 100px;

}

#box-4 {

width: 100px;

}

Figure 4-36: Empty space
distributed with justify-content: flex-end

The
following figures show the effect produced by the rest of the values available
for this property.

Figure 4-37: Empty space
distributed with justify-content: center

Figure 4-38: Empty space
distributed with justify-content: space-between

Figure 4-39: Empty space
distributed with justify-content: space-around

Another
property that can help us allocate space is align-items. This
property works like justify-content but aligns
the boxes in the cross-axis. This quality makes the property appropriate for
vertical alignment.

Listing 4-45: Distributing
vertical space

#parentbox
{

display:
flex;

width: 600px;

height: 200px;

border: 1px
solid;

align-items: center;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1;

}

#box-2 {

flex: 1;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

In
Listing 4-45, we define the height of the container, leaving an empty space of
55 pixels. Because we assigned the value center to the align-items property,
this space is distributed to the top and bottom, as shown in Figure 4-40.

Figure 4-40: Vertical
alignment with align-items: center

The
possible values for the align-items property are flex-start, flex-end, center, baseline and stretch. The last value stretches the boxes from top to bottom to adjust
them to the space available. This characteristic is so important that the value
stretch is set by
default when a flexible container is created. The effect produced by this value
is that when the height of the boxes is not declared they are automatically resized
to the height of their parent.

Listing 4-46: Stretching
the boxes to occupy the vertical space available

#parentbox
{

display:
flex;

width: 600px;

height:
200px;

border: 1px
solid;

align-items: stretch;

}

#parentbox >
div {

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1;

}

#box-2 {

flex: 1;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

Figure 4-41: Stretching
the boxes with align-items: stretch

This is
useful when we have columns in our design with different amounts of content. If
we let the content determine their heights, one column will be shorter than the
other. By assigning the stretch value to the
align-items property, the shorter
columns will match the size of the longest one.

The
property also offers the flex-start value to align the boxes at the start of the line, which is
determined by the orientation of the container (usually left or top).

Listing 4-47: Aligning the
boxes to the top

#parentbox
{

display:
flex;

width: 600px;

height:
200px;

border: 1px
solid;

align-items: flex-start;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1;

}

#box-2 {

flex: 1;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

Figure 4-42: Boxes
aligned with align-items: flex-start

The flex-end value aligns
the boxes to the end of the container (usually right or bottom).

Figure 4-43: Boxes
aligned with align-items: flex-end

Finally, the baseline value aligns the boxes by the
baseline of the first line of content. The following example assigns a different font to the content of the box-2 element to test this value.

Listing 4-48: Aligning the
boxes by the baseline

#parentbox
{

display:
flex;

width: 600px;

height:
200px;

border: 1px
solid;

align-items: baseline;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1;

}

#box-2 {

flex: 1;

font-size: 36px;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

Figure 4-44: Boxes
aligned with align-items: baseline

Sometimes
it may be useful to align boxes independently of the alignment set by their
parent box. This may be done by assigning the align-self property to
the box we want to modify.

Listing 4-49: Changing the
alignment for the box-2 element

#parentbox
{

display:
flex;

width: 600px;

height:
200px;

border: 1px
solid;

align-items:
flex-end;

}

#parentbox >
div {

height:
145px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1;

}

#box-2 {

flex: 1;

align-self: center;

}

#box-3 {

flex: 1;

}

#box-4 {

flex: 1;

}

The
rules in Listing 4-49 align the elements to the bottom of the container, except
for the box-2 element, which is aligned to the center by
the align-self property.

Figure 4-45: Box aligned
with align-self

A
flexible container can organize the boxes in one or multiple lines. The flex-wrap property
declares this condition using three possible values: nowrap, wrap and wrap-reverse. The value nowrap sets the
flexible container as a single-line container (no lines will be wrapped), the
value wrap declares the container as multi-line, and
it orders the lines from cross-start to cross-end (left to right or top to
bottom), and the wrap-reverse value
generates multiple lines in reverse order.

Listing 4-50: Creating two
lines of boxes with the flex-wrap property

#parentbox
{

display:
flex;

width: 600px;

border: 1px
solid;

justify-content: center;

flex-wrap: wrap;

}

#parentbox >
div {

height: 40px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

width: 100px;

}

#box-2 {

width: 100px;

}

#box-3 {

width: 100px;

}

#box-4 {

flex: 1 1
400px;

}

In
Listing 4-50, the first three boxes have a size of 100 pixels, enough to fit in
one single line of a 600 pixel-wide container, but the last box is declared as
flexible with an initial size of 400 pixels (flex-basis). There is
not enough space in the container to lay out all the boxes in one line. The
browser has two options: reduce the size of the flexible box to make it fit
into the available space or generate a new line. Since the flex-wrap property was
declared with the value wrap, a new line is created,
as shown in Figure 4-46.

Figure 4-46: Multiple
lines in a flexible container

The box-4 element was
declared as flexible by its flex property, so
it is not only placed in a second line but also expanded to occupy the total
space available (the 400 pixels declared by the flex-basis parameter is
the suggested width, not a declaration of size). In addition, the first three
boxes are aligned to the center due to the newly available space in the first
line and the value of the justify-content property.

The
order of the lines can be inverted with the value wrap-reverse, as shown below.

Figure 4-47: New line
order using flex-wrap:
wrap-reverse

When we
have containers with multiple lines, there is a chance we will need to align
them. CSS provides the align-content property to
align lines of boxes in a flexible container.

Listing 4-51: Aligning
multiple lines with the align-content property

#parentbox {

display: flex;

width: 600px;

height: 200px;

border: 1px solid;

flex-wrap: wrap;

align-content:
flex-start;

}

#parentbox > div {

height: 50px;

margin: 5px;

background-color: #CCCCCC;

}

#box-1 {

flex: 1 1 100px;

}

#box-2 {

flex: 1 1 100px;

}

#box-3 {

flex: 1 1 100px;

}

#box-4 {

flex: 1 1 400px;

}

The align-content property can
take six values: flex-start, flex-end, center, space-between, space-around
and stretch. The value stretch is set by
default, which expands the lines to fill the space available unless a fixed
size was declared for the elements.

In
Listing 4-51, each box is declared as flexible with an initial width and a
height of 50 pixels, and the parentbox element is
defined as a multi-line container with the flex-wrap property.
This creates a flexible container with two lines of boxes similar to the
previous example, but with enough vertical space to move them around.

Figure 4-48: Lines
aligned with align-content:
flex-start

The following figures show the effect produced by
the rest of the values available for the align-content property.

Figure 4-49: Lines
aligned with align-content:
flex-end

Figure 4-50: Lines
aligned with align-content: center

Figure 4-51: Lines
aligned with align-content:
space-between

Figure 4-52: Lines
aligned with align-content:
space-around

Figure 4-53: Lines
aligned with align-content:
stretch

Real-life Application

There is no much difference between a
document designed with the Traditional Box Model and the one we present using
the Flexible Box Model. For instance, to design a web page with the Flexible
Box Model from the document introduced in Listing 4-20, all we have to do is to
remove the <div> elements that were used to
clear the floating elements. The rest of the document remains the same.

Listing 4-52: Defining a document for the Flexible Box
Model

<!DOCTYPE
html>

<html lang="en">

<head>

<title>This text is the title of the
document</title>

<meta charset="utf-8">

<meta name="description"
content="This is an HTML5 document">

<meta name="keywords"
content="HTML, CSS, JavaScript">

<link rel="stylesheet"
href="mystyles.css">

</head>

<body>

<header id="logoheader">

<div>

<h1>This is a title</h1>

</div>

</header>

<nav id="mainmenu">

<div>

<ul>

<li><a
href="index.html">Home</a></li>

<li><a
href="photos.html">Photos</a></li>

<li><a
href="videos.html">Videos</a></li>

<li><a
href="contact.html">Contact</a></li>

</ul>

</div>

</nav>

<main>

<div>

<section id="mainarticles">

<article>

<h1>Title First Post</h1>

<time datetime="2016-12-23"
pubdate>

<div
class="daynumber">23</div>

<div
class="dayname">Friday</div>

</time>

<p>This is the text of my first
post</p>

<figure>

<img src="myimage.jpg">

</figure>

</article>

<article>

<h1>Title Second Post</h1>

<time datetime="2016-12-7"
pubdate>

<div
class="daynumber">7</div>

<div
class="dayname">Wednesday</div>

</time>

<p>This is the text of my second
post</p>

<figure>

<img src="myimage.jpg">

</figure>

</article>

</section>

<aside id="asideinfo">

<h1>Personal Info</h1>

<p>Quote from post one</p>

<p>Quote from post two</p>

</aside>

</div>

</main>

<footer id="logofooter">

<div>

<section
class="footersection">

<h1>Website</h1>

<p><a
href="index.html">Home</a></p>

<p><a
href="photos.html">Photos</a></p>

<p><a
href="videos.html">Videos</a></p>

</section>

<section
class="footersection">

<h1>Help</h1>

<p><a
href="contact.html">Contact</a></p>

</section>

<section
class="footersection">

<address>Toronto, Canada</address>

<small>Copyright &copy;
2016</small>

</section>

</div>

</footer>

</body>

</html>

Because the organization of the
structural elements is the same, the CSS rules we have to apply to the document
are also very similar. We have to turn the structural elements into flexible
containers and make their content flexible when necessary. For instance, the
following rules assign the display mode flex to the <header> element and declare the <div> inside this element as flexible, so the
header and its content expand to occupy the space available in the window.

Listing 4-53: Turning the header into a flexible
container

* {

margin: 0px;

padding: 0px;

}

#logoheader {

display: flex;

justify-content: center;

width: 96%;

height: 150px;

padding: 0% 2%;

background-color: #0F76A0;

}

#logoheader > div {

flex: 1;

max-width: 960px;

padding-top: 45px;

}

#logoheader h1 {

font: bold 54px Arial, sans-serif;

color: #FFFFFF;

}

Because we want the header's content
to be centered on the screen, we have assigned the value center to the justify-content property. The <div> element was declared as flexible, which
means that it is going to expand or shrink according to the space available,
but, as we explained before, when the page is presented on devices with wide
screens we have to make sure it is not too wide so that it is uncomfortable to
read for the user. This is solved by the max-width property. Because of this
property, the <div> element does not expand more
than 960 pixels.

The same procedure applies to our
menu, although the rest of the elements inside the <nav> element use the same properties and
values than before.

Listing 4-54: Turning the
menu into a flexible container

#mainmenu {

display: flex;

justify-content: center;

width: 96%;

height: 50px;

padding: 0% 2%;

background-color: #9FC8D9;

border-top: 1px solid #094660;

border-bottom: 1px solid #094660;

}

#mainmenu > div {

flex: 1;

max-width: 960px;

}

#mainmenu li {

display: inline-block;

height: 35px;

padding: 15px 10px 0px 10px;

margin-right: 5px;

}

#mainmenu li:hover {

background-color: #6FACC6;

}

#mainmenu a {

font: bold 18px Arial, sans-serif;

color: #333333;

text-decoration: none;

}

The rules for the main content are
also the same, but this time we have two flexible containers, one represented
by the <main> element responsible for
centering the content on the page, and another represented by the <div> element in charge of configuring the two
columns created by the <section> and <aside> elements. For this reason, the rule that
affects the <div> element requires both
properties: display to declare the elements as a
flexible container and flex to declare the container
itself as flexible.

Listing 4-55: Turning the
columns into flexible containers

main {

display: flex;

justify-content: center;

width: 96%;

padding: 2%;

background-image:
url("background.png");

}

main > div {

display: flex;

flex: 1;

max-width: 960px;

}

#mainarticles {

flex: 1;

margin-right: 20px;

padding-top: 30px;

background-color: #FFFFFF;

border-radius: 10px;

}

#asideinfo {

width: 280px;

padding: 20px;

background-color: #E7F1F5;

border-radius: 10px;

}

#asideinfo h1 {

font: bold 18px Arial, sans-serif;

color: #333333;

margin-bottom: 15px;

}

The <aside> element was declared with a fixed width,
which means that only the left column represented by the <section> element is going to expand or shrink to
occupy the rest of the space available.

The properties for the content of the
<section> element (the left column), are
exactly the same than before.

Listing 4-56: Configuring
the content

article {

position: relative;

padding: 0px 40px 20px 40px;

}

article time {

display: block;

position: absolute;

top: -5px;

left: -70px;

width: 80px;

padding: 15px 5px;

background-color: #094660;

box-shadow: 3px 3px 5px rgba(100, 100, 100, 0.7);

border-radius: 5px;

}

.daynumber {

font: bold 36px Verdana, sans-serif;

color: #FFFFFF;

text-align: center;

}

.dayname {

font: 12px Verdana, sans-serif;

color: #FFFFFF;

text-align: center;

}

article h1 {

margin-bottom: 5px;

font: bold 30px Georgia, sans-serif;

}

article p {

font: 18px Georgia, sans-serif;

}

figure {

margin: 10px 0px;

}

figure img {

max-width: 98%;

padding: 1%;

border: 1px solid;

}

The footer presents a similar
challenge than the main content. We have to turn the <footer> element into a flexible box to be able to
make its content flexible, but we also have to make flexible the three columns
created inside to present the information.

Listing 4-57: Turning the
footer into a flexible container

#logofooter {

display: flex;

justify-content: center;

width: 96%;

padding: 2%;

background-color: #0F76A0;

}

#logofooter > div {

display: flex;

flex: 1;

max-width: 960px;

background-color: #9FC8D9;

border-radius: 10px;

}

.footersection {

flex: 1;

padding: 25px;

}

.footersection h1 {

font: bold 20px Arial, sans-serif;

}

.footersection p {

margin-top: 5px;

}

.footersection a {

font: bold 16px Arial, sans-serif;

color: #666666;

text-decoration: none;

}

Do It
Yourself: Create a new HTML file with the code in Listing
4-52 and a CSS file called mystyles.css with the rules introduced from
Listing
4-53. Remember to include the myimage.jpg file in the same folder.
Open the document in your browser. You should see the same page created before
with the Traditional Box Model, with the exception that this time the sections
on the page are flexible (their sizes change as you expand or reduce the size
of the window) and the column generated by the <aside> element
extends to the bottom of the main area.

Chapter 5 - Responsive Web Design

5.1 Mobile Web

The introduction of the iPhone to the market in 2007 changed the world
forever. Up to that point, cellphones had small screens and were not powerful
enough to load and display most of the websites available at the time. If we
wanted to browse the Web, we had to do it on a computer. These conditions simplified
the work for developers. There was only one type of screen, and it was wide
enough to include everything they needed. The most popular resolutions at that
time included at least 1024 pixels horizontally, which allowed developers to
design their websites with a standard size (like the one introduced in the
previous chapter). But when the iPhone came out, websites with fixed layouts
were difficult to read on such a small screen. The first solution was to
develop two separate websites, one for computers and another for iPhones, but
the introduction of new devices to the market, such as the iPad in 2010, forced
developers to look for better alternatives. Making the elements flexible was
one of them, but it was not enough. Pages with multiple columns still looked
awful on small screens. The final solution had to include flexibility and the
possibility to adapt the design to every device. This is how Responsive Web
Design was born.

Responsive Web Design is a technique
that combines flexible layouts with a tool provided by CSS called Media
Queries that lets us detect the size of the screen and perform the
necessary changes to adapt the design to every situation.

Media Queries

A Media Query is a reserved rule in CSS
that was incorporated to allow developers to detect the media in which the
document is being shown. For instance, using Media Queries, we can determine
whether the document is being displayed on a monitor or sent to a printer and
assign the appropriate styles for each case. For this purpose, Media Queries
offer the following keywords.

all—The properties are applied to all media.

print—The properties are applied when the page is sent to a printer.

screen—The properties are applied when the page is shown on a color screen.

speech—The properties are applied when the page is processed by a speech
synthesizer.

What makes Media Queries useful for
web design is that they can also detect the size of the media. With Media Queries,
we can do things like detecting the size, resolution and orientation of the viewport
(the part of the browser window where our pages are displayed), and from this
information define different CSS rules for each device. There are several
keywords available to detect these characteristics. The following are the most
frequently used in Responsive Web Design.

width—This keyword determines the width at which the properties will be
applied.

height—This keyword determines the height at which the properties will
be applied.

min-width—This keyword determines the minimum width from which the
properties will be applied.

max-width—This keyword determines the maximum width to which the properties
will be applied.

aspect-ratio—This keyword determines the aspect ratio
at which the properties will be applied.

orientation—This keyword determines the orientation at
which the properties will be applied. The possible values are portrait and landscape.

resolution—This keyword determines the pixel density
at which the properties will be applied. It can take values in dots per inch
(dpi), dots per centimeter (dpcm) or by pixel ratio (dppx). For instance, to
detect a screen with a Retina display of a scale of 2, we can use the value 2dppx.

Using these keywords, we can detect certain
aspects of the media and the viewport where the page will be displayed, and
modify the properties to adjust the design accordingly. For example, if we
define the Media Query with the width keyword and the value 768px, the properties will only be applied when the page is
displayed in regular iPads in portrait mode, since that is the width of the
viewport on that device and condition.

To define a Media Query, we can
declare only the keywords and values we need. The keywords that describe a
feature, such as the width of the viewport, have to be declared between
parentheses. If more than one keyword is present, we can associate them with
the logical operators and or or. For example, the Media Query all and
(max-width: 480px) assigns the properties to the document in all medias but only
when the width of the viewport is 480 pixels or less.

There are two ways to declare Media
Queries, we can do it from the document, using the media attribute of the <link> element, or from the Style
Sheet with the @media at-rule. When we use the <link> element, we can select the CSS file with
the Style Sheet we want to load for a specific configuration. For example, the
following document loads two CSS files, one with generic styles for every
situation and media and another with the styles required to present the page in
a device with a small screen (480 pixels wide or less).

Listing 5-1: Loading styles with Media Queries

<!DOCTYPE
html>

<html lang="en">

<head>

<title>This text is the title of the
document</title>

<meta charset="utf-8">

<meta name="description"
content="This is an HTML5 document">

<meta name="keywords"
content="HTML, CSS, JavaScript">

<link
rel="stylesheet" href="responsiveall.css">

<link
rel="stylesheet" media="(max-width: 480px)"
href="responsivephones.css">

</head>

<body>

<section>

<div>

<article>

<h1>Title First Post</h1>

<img src="myimage.jpg">

</article>

</div>

</section>

<aside>

<div>

<h1>Personal Info</h1>

<p>Quote from post one</p>

<p>Quote from post two</p>

</div>

</aside>

<div
class="clearfloat"></div>

</body>

</html>

When the browser reads this document,
it first loads the responsiveall.css file and then, if the width of the
viewport is 480 pixels wide or less, it loads the responsivephones.css file and
applies the styles to the document.

CSS stands for Cascading Style
Sheets, which means that the properties are processed in cascade, and therefore
new properties replace old ones. When we want to modify an element for a particular
viewport, we have to declare the property that we want to change, but the
values of the rest of the properties defined before remain the same. For
instance, we can assign a background color to the body of our document in the
responsiveall.css file and then change it in the responsivephones.css file, but
this new value will only be applied when the document is presented on a small
screen. The following is the rule for the responsiveall.css file.

Listing 5-2: Defining the
styles by default (responsiveall.css)

body {

margin: 0px;

padding: 0px;

background-color: #990000;

}

The styles defined in the
responsivephones.css file have to modify only the values of the properties we
want to change, in this case, the background-color property, but the rest of the
values remain as they were declared in the responsiveall.css file.

Listing 5-3: Defining the
styles for small devices (responsivephones.css)

body {

background-color:
#3333FF;

}

Do It Yourself: Create a new HTML file with the
document of Listing 5-1. Create a CSS file called responsiveall.css with the
rule in Listing 5-2 and a CSS file called responsivephones.css with the rule in
Listing 5-3. Open the document in your browser. You should see a red
background. Drag one side of the window to reduce its size. When the width of
the viewport gets to 480 pixels or less, you should see the background color
changing to blue.

With the <link> element and the media attribute we can load different Style Sheets for every
situation, but when just a few properties are changed, we can include them all
in the same Style Sheet and define the Media Queries with the @media at-rule, as in the following example.

Listing 5-4: Declaring
Media Queries with the @media at-rule (responsiveall.css)

body {

margin: 0px;

padding: 0px;

background-color: #990000;

}

@media (max-width: 480px) {

body {

background-color:
#3333FF;

}

}

The Media Query is declared with the @media at-rule followed by the keywords and values that define
the characteristics of the media and the properties between braces. When the
browser reads this Style Sheet, it assigns the properties of the body rule to the <body> element, and then, if the
viewport's width is 480 pixels or less, it changes the background color of the <body> element to #3333FF (blue).

Do It Yourself: Replace the rules in your
responsiveall.css file with the code in Listing 5-4 and remove the second <link> element from the document of Listing
5-1 (now you
only need one CSS file for all your styles). Open the document in your browser
and reduce the size of the window to see how CSS applies the rule defined by
the Media Query when the viewport's width is equal or smaller than 480 pixels.

Breakpoints

In the previous example, we have declared
a Media Query that detects whether the viewport's width is equal or smaller
than 480 pixels and applies the properties if the condition is satisfied. This
is common practice. In today's market, there are several types of devices
available with a broad range of screen sizes. If we use the width keyword to target a device, some devices we do not know or
new devices entering the market may not be affected, and therefore they will
display our website incorrectly. For instance, the Media Query width: 768px targets only regular iPads in portrait
mode because those devices in that orientation have that size. An iPad in
landscape mode, or an iPad Pro, which presents a different resolution, or any
other device with a screen size smaller or larger than 768 pixels, will not be
affected by these properties, even if the size varies in just one or two
pixels. To avoid mistakes, we do not select specific sizes. Instead, we set
maximum or minimum points where the design has to change, and rely on the box
model to adapt the size of the elements to the space available between those
points. These maximum and minimum points are declared with the max-width and min-width keywords and are called Breakpoints.

Breakpoints are the points at which
the design of a web page has to go through significant changes to adapt to the
size of the screen. These are the widths at which flexibility is insufficient to
adjust the boxes to the space available, and we have to move the elements
around to keep our website usable. There are no standards that we can follow to
set these points; everything depends on our design. For instance, we may decide
that when the viewport's width is equal or smaller than 480 pixels, we have to
use only one column to present the information, but that depends on whether in
our original design we have multiple columns or not. Common values are 320,
480, 768, and 1024.

To set a breakpoint, we have to
declare a Media Query with the max-width or min-width keywords, so the properties will be valid
until the size of the viewport exceeds one of these limits. Which keyword to
apply depends on the direction we want to follow. If we want to target small
devices first, we may use the min-width keyword, but if we want to
provide a generic design for large screens and then modify the properties as
the screen gets smaller, we will be better with the max-width keyword. For instance, the following Style Sheet
assigns a background color by default to the body, but as the screen gets
smaller, the color is changed.

Listing 5-5: Adding
multiple breakpoints

body {

margin: 0px;

padding: 0px;

background-color: #990000;

}

@media (max-width: 1024px)
{

body {

background-color:
#3333FF;

}

}

@media (max-width: 768px) {

body {

background-color:
#FF33FF;

}

}

@media (max-width: 480px) {

body {

background-color:
#339933;

}

}

@media (max-width: 320px) {

body {

background-color:
#CCCCCC;

}

}

When the styles introduced in Listing
5-5 are assigned to the document of Listing 5-1, the browser presents the page
with a red background in a viewport bigger than 1024 pixels, but it changes the
color every time the width goes under any of the limits set by the Media
Queries.

Do It Yourself: Replace the rules in the
responsiveall.css file with the code in Listing 5-5. Open the document in your
browser and change the size of the window. If the viewport is bigger than 1024
pixels, the body is shown with a red background, but if the size is reduced to
1024 or less, it changes to blue. At 768 pixels or less, it changes to pink,
480 pixels or less it changes to green, and 320 pixels or less it changes to
gray.

Viewport

The viewport is the part of the browser
window where our web page is displayed. Because of the relationship between the
window and the viewport, in mobile devices, their sizes are the same, but this
is not always the case. By default, some smartphones consider the viewport to
be 980 pixels wide, regardless of the real size of the screen. This means that
the Media Queries in our Style Sheets will see a viewport of 980 pixels wide
when the actual size is smaller. To normalize this situation and force the
browser to match the size of the viewport with the size of the screen, we have
to declare a <meta> tag in the head of our
documents with the name viewport and values that determine the
width and scale we want to see. The two values required to set the screen's
size and scale are width and initial-scale. The following example illustrates how we
have to configure the <meta> tag to ask the browser to
return the real size and scale of the device's screen.

Listing 5-6: Adding the viewport <meta> tag

<!DOCTYPE
html>

<html lang="en">

<head>

<title>This text is the title of the
document</title>

<meta charset="utf-8">

<meta name="description"
content="This is an HTML5 document">

<meta name="keywords"
content="HTML, CSS, JavaScript">

<meta
name="viewport" content="width=device-width, initial-scale=1">

<link rel="stylesheet"
href="responsiveall.css">

</head>

<body>

<section>

<div>

<article>

<h1>Title First Post</h1>

<img src="myimage.jpg">

</article>

</div>

</section>

<aside>

<div>

<h1>Personal Info</h1>

<p>Quote from post one</p>

<p>Quote from post two</p>

</div>

</aside>

<div
class="clearfloat"></div>

</body>

</html>

The Basics:
The
viewport <meta> tag may include other
values to set attributes like the minimum and maximum scales (minimum-scale and maximum-scale), or if you
want to allow users to zoom in and out (user-scalable). For more
information, visit our website and follow the links for this chapter.

Flexibility

Flexibility is a requirement in Responsive Web Design. We have to make
our web pages flexible, so the elements adapt to the space available between
one breakpoint and another. With the Flexible Box Model, this is simple, we
have to create flexible containers and declare their boxes as flexible with the
flex property (see Chapter 4), but this box model is not recognized by some
browsers, and therefore most developers still use the Traditional Box Model.
Although the Traditional Box Model was not designed to work with flexible
elements, we can turn the elements flexible by declaring their sizes in
percentage. When we declare the size of an element in percentage, the browser
calculates the real size in pixels from the size of its container. For example,
if we assign a width of 80% to an element that is inside a container with a
width of 1000 pixels, the width of that element will be 800 pixels (80% of
1000). Because the size of the elements changes every time the size of their
container changes, they become flexible.

The following example creates two columns with the <section> and <aside> elements of the document of Listing 5-6 and declares
their widths with percentages to make them flexible.

Listing 5-7: Creating
flexible elements with percentages

* {

margin: 0px;

padding: 0px;

}

section {

float: left;

width: 70%;

background-color: #999999;

}

aside {

float: left;

width: 30%;

background-color: #CCCCCC;

}

.clearfloat {

clear: both;

}

The rules in Listing 5-7 declare the sizes of the <section> and <aside> elements as 70% and 30% of the size of their
container, respectively. If the size of the window changes, the size of the
elements is recalculated by the browser and therefore they expand or shrink
according to the space available.

Figure 5-1: Flexible elements
with the Traditional Box Model

Every time we declare the width of the elements in percentages, we
have to make sure that the total sum of the values does not exceed 100%.
Otherwise, the elements will not fit in the space available and will be moved
to a new line. In Listing 5-7, this was simple to do because we only had two
elements with no margins, paddings or borders, but as soon as we add an extra
value, like the padding, we have to remember to subtract that value from the
width of the element, or the total will be more than 100%. For example, the
following rules reduce the width of the <section> and <aside> elements to add a padding of 2% on each side and a
margin of 5% in the middle.

Listing 5-8: Adding
margins and paddings to flexible elements

* {

margin: 0px;

padding: 0px;

}

section {

float: left;

width: 61%;

padding: 2%;

margin-right: 5%;

background-color: #999999;

}

aside {

float: left;

width: 26%;

padding: 2%;

background-color: #CCCCCC;

}

.clearfloat {

clear: both;

}

The rules in Listing 5-8 assign to the <section> element a width of 61%, a padding of 2% at the top, right, bottom, and
left side, and a margin of 5% on the right side, and to the <aside>
element a width of 26% and a padding of 2% at the top, right, bottom, and left
side. Because the sum of these values does not exceed 100% (61 + 2 + 2 + 5 + 26
+ 2 + 2 = 100), the elements are positioned side by side on the same line.

Figure 5-2: Flexible
elements with paddings and margins

Do It Yourself: Replace the rules in the
responsiveall.css file with the code in Listing 5-8. Include the image
myimage.jpg in the document's folder. Open the document in your browser. You
should see something similar to Figure 5-2.

Box-sizing

Every time the total area occupied by an
element is calculated, the browser gets the final number with the formula: size + margin + padding + borders. Therefore, if we set the width property to 100 pixels, margin to 20 pixels, padding to 10 pixels and border to 1 pixel, the total
horizontal area occupied by the element will be 100 + 40 + 20 + 2 = 162 pixels (notice that we had to double the
values of the margin, padding and border in the formula because we assumed the
same values were assigned to the left and right side of the box). This means
that every time we declare the size of an element with the width property, we have to remember that
the area occupied by the element on the page may be larger. This is
particularly troublesome when the sizes are defined in percentage, or we try to
combine percentage with other types of units like pixels. CSS includes the
following property to modify this behavior.

box-sizing—This property allows us to change how the
size of an element is calculated and force browsers to include the padding and
border in the size declared by the width property.
The possible values are content-box and border-box.

By default, the value of the box-sizing property is set to content-box, which means the browser will
add the values of the padding and border properties to the size specified by width. If we assign to the property the value border-box, the browser includes the padding and the
border as part of the width, so we can do things like combining widths defined
in percentage with paddings in pixels, as in the following example.

Listing 5-9: Including padding and border in
the size of the element

* {

margin: 0px;

padding: 0px;

}

section {

float: left;

width: 65%;

padding: 20px;

margin-right:
5%;

background-color: #999999;

box-sizing: border-box;

}

aside {

float: left;

width: 30%;

padding: 20px;

background-color: #CCCCCC;

box-sizing: border-box;

}

.clearfloat {

clear: both;

}

In Listing 5-9, we declare the box-sizing property with the value border-box for both elements, <section> and <aside>. Because of this, we no longer
have to consider the padding to calculate the size of the elements (65 + 5 + 30
= 100).

Do It Yourself: Replace the rules in the
responsiveall.css file with the code in Listing 5-9. Open the document in your
browser. You should see something similar to Figure 5-2, but this time the
paddings do not change when you resize the window because they were declared with
a fixed value of 20 pixels.

Fixed and Flexible

In the previous example, we have seen how to define flexible boxes with
fixed paddings, but there are times when we have to combine entire columns with
fixed and flexible values. Again, this is very easy to do with the Flexible Box
Model. We have to create a flexible container, declare the size of the elements
we want to be flexible with the flex property and those we want to have a fixed size with
the width property (see Chapter 4, Listing 4-37). But the Traditional Box Model was not prepared to
deal with these kinds of tasks. Fortunately, there are a few techniques we can
implement to achieve this purpose. The most popular alternative requires us to
declare a padding on the flexible column to make room for the fixed column and
a negative margin to displace the fixed column to the space left by the
padding. The following are the rules we need to declare a flexible width for
the <section> element and a fixed width for the <aside>
element of our document.

Listing 5-10: Declaring flexible and fixed
columns

* {

margin: 0px;

padding: 0px;

}

section {

float: left;

width: 100%;

padding-right: 260px;

margin-right: -240px;

box-sizing:
border-box;

}

section >
div {

padding:
20px;

background-color: #999999;

}

aside {

float:
left;

width: 240px;

padding:
20px;

background-color: #CCCCCC;

box-sizing:
border-box;

}

.clearfloat {

clear: both;

}

The rules in Listing 5-10 assign a
width of 100% and a right padding of 260 pixels to the <section> element. Because we use the box-sizing property to include the padding in the
value of width, the element's content will occupy only
the left side of the element, leaving an empty space on the right where we can
place our fixed column. Finally, to move the column created by the <aside> element over this place, we declare a
negative margin of 240 pixels on the right side of the <section> element.

The Basics:
In
this example, we float both columns to the left, which means that they are
going to be placed side by side on the same line. In cases like this, we can
create a gap between the columns by increasing the value of the padding property. In
the example of Listing 5-10, we declared a padding of 260 pixels and a margin
of 240 pixels, which means that the fixed column will move only 240 pixels to
the left, leaving a space of 20 pixels in between.

Because we are using the padding to
make room for the right column, we have to assign the normal padding and
backgrounds to the <div> element inside the <section> element that represents the column. This
not only allows us to add padding to the content but it also assigns the
background color only to the area occupied by the content, differentiating the
two columns on the screen.

Figure 5-3: Flexible and
Fixed columns

Do It Yourself: Replace the rules in the
responsiveall.css file with the code in Listing 5-10. Open the document in your
browser. Drag one side of the window to change its size. You should see the
left column expanding or shrinking and the right column always of the same size
(240 pixels).

If we want to place the fixed column
on the left side instead of the right side of the page, the trick is the same,
but we have to declare the left column below the right column in the code (as
declared in Listing 5-6) and then set their positions with the float property, as in the following example.

Listing 5-11: Moving the fixed column to the
left

* {

margin: 0px;

padding: 0px;

}

section {

float: right;

width: 100%;

padding-left: 260px;

margin-left: -240px;

box-sizing:
border-box;

}

section >
div {

padding:
20px;

background-color: #999999;

}

aside {

float: left;

width: 240px;

padding:
20px;

background-color: #CCCCCC;

box-sizing:
border-box;

}

.clearfloat {

clear: both;

}

The <section> element is declared first in our
document, but because we assign the value right to its float property, it is displayed on the right side of the screen.
The position of the element in the code makes sure that it is not going to be
drawn on top of the <aside> element, and the value of the float property moves it to the side we choose. The rest of the
code is similar to the previous example, except that this time we have to
modify the padding and margin of the <section> element on the left side
instead of the right.

Figure 5-4: Fixed column
on the left

The Basics:
When
you move an element to a new position by assigning a negative margin to the
next element, the browser draws the first element behind the second element,
and therefore the first element does not receive any event, such as the user
clicking on a link. To make sure that the fixed column remains visible and
accessible, we have to declare it in the code below the flexible column. This
is why, to move the column created by the <aside> element to
the left, we did not modify our document. The column is put into place by the
value of the float property.

If we want to declare two fixed
columns on the sides and a flexible column in the middle using this same trick,
we have to group the columns inside a container and then apply the properties
to the elements twice, inside and outside the container. In the following document,
we group two <section> elements inside a <div> element identified with the name container to contain the left and center columns.
The two columns are identified with the names columnleft and columncenter.

Listing 5-12: Creating a flexible document
with two fixed columns

<!DOCTYPE
html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta
charset="utf-8">

<meta
name="description" content="This is an HTML5 document">

<meta
name="keywords" content="HTML, CSS, JavaScript">

<meta
name="viewport" content="width=device-width,
initial-scale=1">

<link
rel="stylesheet" href="mystyles.css">

</head>

<body>

<div
id="container">

<section
id="columncenter">

<div>

<article>

<h1>Title
First Post</h1>

<img
src="myimage.jpg">

</article>

</div>

</section>

<section
id="columnleft">

<div>

<h1>Options</h1>

<p>Option
1</p>

<p>Option
2</p>

</div>

</section>

<div
class="clearfloat"></div>

</div>

<aside>

<div>

<h1>Personal Info</h1>

<p>Quote from post one</p>

<p>Quote from post two</p>

</div>

</aside>

</body>

</html>

The CSS code for this document is
simple. The container and columncenter elements have to be declared as flexible,
so we have to define the right padding and margin of the container to make room
for the <aside> element on the right, and then
define the padding and margin for the columncenter element to make room for the
fixed column on the left.

Listing 5-13: Defining two fixed columns on
the sides

* {

margin: 0px;

padding: 0px;

}

#container {

float: left;

width: 100%;

padding-right: 260px;

margin-right: -240px;

box-sizing:
border-box;

}

aside {

float: left;

width: 240px;

padding:
20px;

background-color: #CCCCCC;

box-sizing:
border-box;

}

#columncenter {

float: right;

width: 100%;

padding-left: 220px;

margin-left: -200px;

box-sizing:
border-box;

}

#columncenter
> div {

padding:
20px;

background-color: #999999;

}

#columnleft {

float: left;

width: 200px;

padding:
20px;

background-color: #CCCCCC;

box-sizing:
border-box;

}

.clearfloat {

clear: both;

}

Notice that the columnleft element was declared after the columncenter element in the code. This is to make sure
that columnleft is drawn over columncenter and it is accessible. As we did before,
the positions of these elements are defined by the float property. In consequence, the columnleft element is positioned on the left side of
the page with a fixed size of 200 pixels, the columncenter element is positioned at the center with
a flexible width, and the <aside> element is positioned on the
right with a fixed width of 240 pixels.

Figure 5-5: Fixed
columns on both sides

Do It Yourself: Create a new HTML file with the
document of Listing 5-12 and a CSS file called mystyles.css with the code in
Listing 5-13. Include the myimage.jpg file in the same folder. Open the document
in your browser and change the size of the window. You should see something
similar to Figure 5-5, with the columns on the sides always at the same size.

Text

Another aspect of the design we have to adapt to different devices is
the text. When we declare a font with a fixed size, like 14 pixels, the text is
always shown at that size, regardless of the device. On a desktop computer it
looks fine, but on small screens, a text of that size could be unreadable. To
adjust the size of the font, we can use the standard font size. This is a size
determined by the browser according to the device on which it is running. For
example, on desktop computers, the size of the font by default is usually 16
pixels. To make sure that the text on our web page is readable, we can define
its size relative to this value. CSS offers the following units for this
purpose.

em—This unit represents a measure relative to the size of the font assigned
to the element. It takes decimal numbers. A value of 1 is equal to the font's
current size.

rem—This unit represents a measure relative to the size of the font
assigned to the root element (the <body> element). It
takes decimal numbers. A value of 1 is equal to the font's current size.

Fonts
are defined the same way we did before, but the px unit
must be replaced with the em unit to declare the size of the font relative to the
current size. For
example, the following Style Sheet defines different font sizes for the <h1> and <p> elements in our document using
em units (the rules were defined considering
the document of Listing 5-6).

Listing 5-14: Declaring relative font sizes

* {

margin: 0px;

padding: 0px;

}

section {

float: left;

width: 61%;

padding: 2%;

margin-right:
5%;

background-color: #999999;

}

aside {

float: left;

width: 26%;

padding: 2%;

background-color: #CCCCCC;

}

.clearfloat {

clear: both;

}

article h1 {

font: bold 2em Georgia,
sans-serif;

}

aside h1 {

font: bold 1.2em Georgia,
sans-serif;

}

aside p {

font: 1em Georgia,
sans-serif;

}

The em units replace the px units, and therefore the
browser is responsible for calculating the size of the text in pixels from the size
of the font currently assigned to the element. To calculate this value, the
browser multiplies the number of em by the current size of the
standard font. For example, the rule article h1
in Listing 5-14 assigns a size of 2em to the <h1> elements inside the <article> elements. This means that on a desktop computer the text in these
elements will have a size of 32 pixels (16 * 2).

Figure 5-6: Relative
font size

Do It Yourself: The example of Listing 5-14 assumes
that you are working with the document of Listing
5-6. Replace
the rules in the responsiveall.css file created for this document with the code
in Listing 5-14 and open the document in your browser. You should see something
similar to Figure 5-6.

The em unit defines the size relative to the current size of
the font in the element. This is because elements inherit properties from their
containers and therefore the size of their font may be different from the
standard set by the browser. This means that if we modify the size of the font
in the element's container, it will affect the element's font size. For
example, the following rules assign a font size to the <article> element and another for the <h1> elements inside.

Listing 5-15: Declaring relative sizes for
elements and containers

article
{

font: bold
1.5em Georgia, sans-serif;

}

article h1 {

font: bold
2em Georgia, sans-serif;

}

The first rule assigns a font size of 1.5em to
the <article> element. After the property is applied, the content
of the <article> element, including the content of the <h1>
element inside, will have a size 1.5 times bigger than the standard size (24
pixels in a desktop computer). The <h1> element inherits this value, so when the next rule is
applied, the size of the font is not calculated from the standard size but from
the size established by the container (16 * 1.5 * 2 = 48). The result is shown
in Figure 5-7.

Figure 5-7: Font size
relative to the container

If we want to change this behavior and force the browser to
calculate the size of the font always from the standard value, we can use rem units.
These units are relative to the root element instead of the current element,
and therefore the values are always multiplied against the same base value.

Listing 5-16: Declaring relative sizes for
the elements with rem units

article
{

font: bold 1.5rem
Georgia, sans-serif;

}

article h1 {

font: bold 2rem Georgia,
sans-serif;

}

If we apply the rules in Listing 5-16 to the example of Listing
5-14, the browser calculates the size of the font from the standard value and
shows the article's title only two times bigger, regardless of the size
declared for the container, as illustrated in Figure 5-6.

The Basics:
You
may also use em units instead of
percentages to set the size of the elements. When you use em units, the
size is determined by the size of the font instead of the container. This
technique is used to create Elastic Layouts. For more information, visit our
website and follow the links for this chapter.

Images

By default, images are shown in their original size, which means that
they do not adapt to the space available unless we declare it explicitly.
Turning a fixed image into a flexible image is very simple; we have to declare
its size in percentage. For instance, the following rule sets the width of the <img>
elements inside the <article> elements of our document to 100%, so the images will
always be as wide as their container.

Listing 5-17: Adapting the size of the images

article
img {

width: 100%;

}

Do It Yourself: Add the rule in Listing 5-17 to the
Style Sheet created for the previous example and open the document in your
browser. You should see the image expanding or shrinking to fit the column as
you change the size of the window.

Assigning a percentage to the image's width forces the browser to
calculate the size of the image according to the size of its container (in our
document, this is the <article> element). We can declare lower values than 100%, but
the size of the image will always be proportional to the size of its container,
which means that if the container is expanded, the size of the image could be
bigger than the original. If we want to establish limits to how much the image
can expand or shrink, we can use the max-width and min-width properties. We have seen these properties
before applied to the Flexible Box Model, but they are also used in the
Traditional Box Model to declare a limit for a flexible element. For example,
if we want the image to be expanded until
it reaches its original size, we can use the max-width
property.

Listing 5-18: Setting a maximum size for the
images

article
img {

max-width: 100%;

}

The rule in Listing 5-18 lets the image expand until it reaches
its original size. The image will be as wide as its container unless the
container is bigger than the image's original size.

Figure 5-8: Image with a
maximum width

Do It Yourself: Replace the rule in Listing 5-17 in
your Style Sheet with the rule in Listing 5-18. Open the document in your
browser and change the size of the window. You should see the image expanding
until it reaches its original size, as shown in Figure 5-8.

Besides adapting the image to the space available, a responsive
website also requires the images to be replaced when the conditions change.
There are at least two situations in which this becomes necessary; either there
is not enough space available for the original image to be shown, like when a
logo has to be replaced with a simplified version, or the pixel density of the
screen is different and we need to show an image with more or less resolution.
No matter the reason, HTML offers the following elements to select the image to
be shown.

<picture>—This element is a container that allows us
to specify multiple sources for the same <img> element.

<source>—This element defines a possible source for
the <img> element. It may include
the attributes media, to specify the Media
Query the resource is associated with, and srcset, to specify
the path of the images we want to declare as resources.

The <picture> element is a container that must include one or more <source> elements to specify the possible sources for the image and an <img>
element at the end to load and display the selected image on the screen. The
following document illustrates how to replace the logo of a website with a
simpler version on devices with small screens using these elements.

Listing 5-19: Selecting the image with the <picture> element

<!DOCTYPE
html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta
charset="utf-8">

<meta
name="description" content="This is an HTML5 document">

<meta name="keywords"
content="HTML, CSS, JavaScript">

<meta
name="viewport" content="width=device-width,
initial-scale=1">

</head>

<body>

<header>

<picture>

<source
media="(max-width: 480px)" srcset="logosmall.jpg">

<img
src="logo.jpg">

</picture>

</header>

</body>

</html>

In the document of Listing 5-19, we declare only one resource and
associate it with the Media Query max-width:
480px. When the browser reads this
document, it first processes the <source> element and then assigns the resource to the <img>
element if the viewport complies with the Media Query. If no match is found,
the image specified by the src attribute of the <img> element is displayed. This means that every time the
viewport is bigger than 480 pixels the logo.jpg image is loaded and displayed.

Figure 5-9: Original
logo

If the viewport's width is less or equal than 480 pixels, the
browser assigns the image specified by the <source> element to the <img> element and the logosmall.jpg image is displayed.

Figure 5-10: Logo for
small screens

Do It Yourself: Create a new HTML file with the
document of Listing 5-19. Download the images logo.jpg and logosmall.jpg from
our website and move them to the document's folder. Open the document in your
browser and change the size of the window. Depending on the current size, you
should see something similar to Figure 5-9 or Figure 5-10.

Although this is probably the most common scenario in which an
image has to be replaced with another of different size or content, there is
one more condition we must consider when delivering images to the users. Since
the introduction of Retina displays by Apple in 2010, screens with higher pixel
density became available. Higher density means more pixels per inch, which
translates into sharper images. But if we want to take advantage of this
feature, we have to provide images with higher resolution for these screens.
For instance, if we have an image of 300 by 200 pixels it will look fine in
screens with normal density, but devices with higher density will have to
stretch the image to occupy all the pixels available in the same area. If we
want this image to look right in Retina displays of a scale of 2 (twice the number
of pixels per area), we have to replace it with the same image with twice the
resolution (600 x 400 pixels).

Media Queries already consider this situation with the resolution
keyword. The keyword requires the value to be declared as an integer in dppx units.
A value of 1 means a standard resolution. Currently, Retina displays come in
scales of 2 and 3. The following document declares a source for a display with
a scale of 2.

Listing 5-20: Selecting images for different
resolutions

<!DOCTYPE
html>

<html lang="en">

<head>

<title>This text is the title of the document</title>

<meta
charset="utf-8">

<meta
name="description" content="This is an HTML5 document">

<meta
name="keywords" content="HTML, CSS, JavaScript">

<meta
name="viewport" content="width=device-width,
initial-scale=1">

<link
rel="stylesheet" href="mystyles.css">

</head>

<body>

<section>

<div>

<article>

<h1>Title First Post</h1>

<picture>

<source
media="(resolution: 2dppx)" srcset="myimage@2x.jpg">

<img
src="myimage.jpg">

</picture>

</article>

</div>

</section>

<aside>

<div>

<h1>Personal Info</h1>

<p>Quote from post one</p>

<p>Quote from post two</p>

</div>

</aside>

<div
class="clearfloat"></div>

</body>

</html>

If this document is opened on a screen with a standard density of
pixels, the browser shows the image myimage.jpg, but when the device has a
Retina display with a scale of 2, the browser assigns the image myimage@2x.jpg
to the <img> element and shows it on the screen.

As illustrated by the example of Listing 5-20, the process is
simple. We have to create two images, one with normal resolution and another
with higher resolution for devices with Retina displays, and then declare the
sources available with <source> elements. But there is a problem. If we do not
specify the size of the images, they are shown in their original size, and
therefore the image with high resolution will be twice the size of the image
with normal resolution. For this reason, every time we work with low and high-resolution
images, we have to declare their sizes explicitly. For instance, the following
rules set the width of the image to be 100% the size of its container, which
will assign the same size to both images.

Listing 5-21: Declaring the size of an image
with high resolution

* {

margin: 0px;

padding: 0px;

}

section {

float: left;

width: 61%;

padding: 2%;

margin-right:
5%;

background-color: #999999;

}

aside {

float: left;

width: 26%;

padding: 2%;

background-color: #CCCCCC;

}

.clearfloat {

clear: both;

}

article img {

width: 100%;

}

This is the same we have done before,
but this time the browser displays images of different resolution depending on
the device. Figure 5-11 shows what the
document looks like on a Retina display (we have added the text "2X"
at the bottom of the myimage@2x.jpg file to differentiate the high-resolution
image from the low-resolution image).

Figure 5-11: High-resolution
image shown in Retina displays

As we did in a previous example, we can use the max-width
property to limit the width of the image to its original size (see Listing
5-18), but because this time
we are working with two images of different sizes, we cannot declare the value
of this property in percentage. Instead, we have to use the exact width we want
to be the maximum. In our example, the image in the myimage.jpg file has a
width of 365 pixels, so this is the value we have to use.

Listing 5-22: Declaring the maximum size of
an image with high resolution

article
img {

width: 100%;

max-width: 365px;

}

Now, independently of the image selected by the browser, they are always
shown with a maximum size of 365 pixels.

Figure 5-12: Limitations
for high-resolution images

Do It Yourself: Create a new HTML file with the
document of Listing 5-20 and a CSS file called mystyles.css with the code in
Listing 5-21. Download the images myimage.jpg and myimage@2x.jpg from our
website and move them to the document's folder. Open the document in your
browser. If your computer includes a Retina display, you should see the image
with the text "2X" at the bottom, as illustrated in Figure 5-11.
Update the Style Sheet with the rule in Listing 5-22 and refresh the page. You
should see the image expanding until it reaches its original size (365 pixels).

The Basics:
By
convention, a file containing the high-resolution image must use the same name
than the file with the image in low resolution, but followed by the @
character, the scale value, and the letter x, as in @2x or @3x. This is why we assigned
the name myimage@2x.jpg to the high-resolution image of our example.

As well as the images introduced with the <img>
element, background images can also be replaced, but in this case, we do not
need to use an HTML element because we can do everything from CSS using the background
or the background-image properties. Therefore, the image is not defined in
the document but in the Style Sheet. The document only has to include the
element to which we want to assign the background.

Listing 5-23: Including the element to show
the background image

<!DOCTYPE
html>

<html
lang="en">

<head>

<title>This text is the title of the document</title>

<meta
charset="utf-8">

<meta
name="description" content="This is an HTML5 document">

<meta
name="keywords" content="HTML, CSS, JavaScript">

<meta
name="viewport" content="width=device-width,
initial-scale=1">

<link
rel="stylesheet" href="mystyles.css">

</head>

<body>

<header>

<div
id="logo"></div>

</header>

</body>

</html>

The header of this document contains a <div>
element identified with the name logo that we are going to use to show the logo of our
website.  As we did before, to change the value of a property in different
conditions, we have to define a value by default and then declare Media Queries
to change it according to the circumstances.

Listing 5-24: Updating the background image

* {

margin: 0px;

padding: 0px;

}

#logo {

width: 275px;

height: 60px;

background:
url("logo.jpg") no-repeat;

}

@media (max-width: 480px) {

#logo {

width: 60px;

background:
url("logosmall.jpg") no-repeat;

}

}

The Style Sheet of Listing 5-24 defines the size of the <div>
element to be equal to the size of the image we want to show and then assigns
the logo.jpg image as its background (see Figure 5-9). If the viewport's width is 480 pixels, the Media
Query adjusts the size of the <div> element to fit the size of the small logo and assigns
the logosmall.jpg image to the background (see Figure 5-10). The effect produced by this code is exactly the
same achieved with the <picture> element in the document of Listing 5-19, except this time the image is not presented with the
<img> element but assigned as the background of the
element.

Do It Yourself: Create a new HTML file with the
document of Listing 5-23 and a CSS file called mystyles.css with the code in
Listing 5-24. Download the images logo.jpg and logosmall.jpg from our website
and move them to the document's folder. Open the document in your browser and
change the size of the window. Depending on the current size, you should see
something similar to Figure 5-9 or Figure
5-10.

Real-life Application

Creating a responsive website demands the combination of all the
techniques we have just studied. Some have to be applied several times, and usually,
more than one Media Query has to be declared to adapt the design to multiple
devices. But where the breakpoints are established and how the techniques are
implemented depend on our website's design and the box model we use. To
illustrate how to create a website with Responsive Web Design and the
Traditional Box Model, we are going to use the document introduced in Chapter
4, Listing 4-20. The basic structure of this document includes a header, a navigation
bar, two columns created with a <section> and an <aside> element, and a footer. The first thing we have to do
is to define the styles by default and make these elements flexible.

As mentioned before, to turn the elements flexible in the
Traditional Box Model, we have to define their widths with values in
percentage. The following are the styles by default required by the document of
Chapter 4, Listing 4-20. Unlike what we did in Chapter 4, this time the sizes are declared in percentage,
and we use the max-width property to specify a maximum width for the content
of the page.

Listing 5-25: Defining flexible elements with
the Traditional Box Model

* {

margin: 0px;

padding: 0px;

}

#logoheader {

width: 96%;

height:
150px;

padding: 0%
2%;

background-color: #0F76A0;

}

#logoheader
> div {

max-width: 960px;

margin: 0px
auto;

padding-top:
45px;

}

#logoheader h1
{

font: bold
54px Arial, sans-serif;

color:
#FFFFFF;

}

#mainmenu {

width: 96%;

height: 50px;

padding: 0%
2%;

background-color: #9FC8D9;

border-top:
1px solid #094660;

border-bottom: 1px solid #094660;

}

#mainmenu >
div {

max-width: 960px;

margin: 0px
auto;

}

#mainmenu li {

display:
inline-block;

height: 35px;

padding: 15px
10px 0px 10px;

margin-right:
5px;

}

#mainmenu
li:hover {

background-color: #6FACC6;

}

#mainmenu a {

font: bold
18px Arial, sans-serif;

color:
#333333;

text-decoration: none;

}

main {

width: 96%;

padding: 2%;

background-image: url("background.png");

}

main > div {

max-width: 960px;

margin: 0px
auto;

}

#mainarticles {

float: left;

width: 65%;

padding-top:
30px;

background-color: #FFFFFF;

border-radius: 10px;

}

#asideinfo {

float: right;

width: 29%;

padding: 2%;

background-color: #E7F1F5;

border-radius: 10px;

}

#asideinfo h1 {

font: bold
18px Arial, sans-serif;

color:
#333333;

margin-bottom: 15px;

}

.clearfloat {

clear: both;

}

article {

position:
relative;

padding: 0px
40px 20px 40px;

}

article time {

display:
block;

position:
absolute;

top: -5px;

left: -70px;

width: 80px;

padding: 15px
5px;

background-color: #094660;

box-shadow:
3px 3px 5px rgba(100, 100, 100, 0.7);

border-radius: 5px;

}

.daynumber {

font: bold
36px Verdana, sans-serif;

color:
#FFFFFF;

text-align:
center;

}

.dayname {

font: 12px
Verdana, sans-serif;

color:
#FFFFFF;

text-align:
center;

}

article h1 {

margin-bottom: 5px;

font: bold
30px Georgia, sans-serif;

}

article p {

font: 18px
Georgia, sans-serif;

}

figure {

margin: 10px
0px;

}

figure img {

max-width: 98%;

padding: 1%;

border: 1px
solid;

}

#logofooter {

width: 96%;

padding: 2%;

background-color:
#0F76A0;

}

#logofooter
> div {

max-width: 960px;

margin: 0px
auto;

background-color: #9FC8D9;

border-radius: 10px;

}

.footersection
{

float: left;

width: 27.33%;

padding: 3%;

}

.footersection
h1 {

font: bold
20px Arial, sans-serif;

}

.footersection
p {

margin-top:
5px;

}

.footersection
a {

font: bold
16px Arial, sans-serif;

color:
#666666;

text-decoration: none;

}

The rules in Listing 5-25 turn the structural elements flexible,
so the website adapts to the space available, but when the screen is too small,
the design breaks, some elements are partially displayed, and the content
becomes illegible, as shown in Figure 5-13.

Figure 5-13: Responsive
website with only flexible elements

Do It Yourself: Create a new HTML file with the
document of Listing 4-20. Create a CSS file called
mystyles.css and include the rules in Listing 5-25. Open the document in your
browser. At this point, the elements expand or shrink according to the space
available, but the page looks awful when the viewport is too small (see Figure
5-13). Next, we will add the Media Queries to the Style Sheet to correct this
situation.

Changes in the design have to be introduced gradually. For
example, in our design, when the size of the viewport is 1120 pixels or less,
the <time> element that we use to represent the date the article
was posted falls outside the window. This tells us that our design needs a
breakpoint at 1120 pixels to move this element to a different position or
reorganize its content. In this case, we have decided to move the date back
inside the area occupied by the <article> element.

Listing 5-26: Repositioning the <time> element

@media
(max-width: 1120px) {

article time
{

position: static;

width:
100%;

padding:
0px;

margin-bottom: 10px;

background-color: #FFFFFF;

box-shadow:
0px 0px 0px;

border-radius: 0px;

}

.daynumber {

display: inline-block;

font: bold
14px Verdana, sans-serif;

color:
#999999;

padding-right: 5px;

}

.dayname {

display: inline-block;

font: bold
14px Verdana, sans-serif;

color:
#999999;

}

article h1 {

margin-bottom: 0px;

}

}

The first thing we have to do to reposition the date is to restore
the positioning mode for the <time> element. The rules by default introduced to the Style
Sheet in Listing 5-25 give the element an absolute position to move it to the
right of the area occupied by the <article> element, but when the screen is not big enough to
show it in that position, we have to move it back to its natural place in the
document, below the <h1> element. This is done by assigning the value static to
the position property (static is the positioning mode by default). Now, the <time>
element is positioned below the article's title, but we still have to allocate
the day's number and name on the same line. In this example, we have decided to
turn the elements into inline-block elements, so they will be located next to each other on the
same line. The result is shown in Figure 5-14.

Figure 5-14: The date is
displaced below the article's title with Media Queries

Do It Yourself: Add the Media Query defined in
Listing 5-26 at the end of your Style Sheet and refresh the page in your
browser. Reduce the size of the window to see how the <time> element is modified.

Another point where the design should be modified is when the two
columns become too small to display their content properly. Depending on the
characteristics of the content, we can choose to hide it, replace it with
something else, or rearrange the columns. In this case, we have decided to turn
the two-column design into a one-column design by moving the <aside>
element below the <section> element. There are a few different ways to achieve
this. For instance, we can assign the value none to the float property to prevent the elements from floating to the
sides, or define the width of the elements as 100% or auto and
let the browser take care of placing them one per line. For our example, we
have decided to move to a one-column design when the viewport is 720 pixels or
less using the second option.

Listing 5-27: Rearranging the columns

@media
(max-width: 720px) {

#mainarticles
{

width: 100%;

}

#asideinfo {

width: 90%;

padding: 5%;

margin-top:
20px;

}

}

Every time the document is displayed on a screen of a size of 720
pixels or less, the user will see the content organized in just one column.

Figure 5-15: One-column
design

Do It Yourself: Add the Media Query defined in
Listing 5-27 at the end of your Style Sheet and refresh the page in your
browser. When the viewport's width is 720 pixels or less, you should see the
content in only one column, as illustrated in Figure 5-15.

At this point, the document looks good on computers and tablets,
but there are a few more changes we have to make to adapt it to the small
screens provided by smartphones. When this page is shown in a viewport of 480
pixels or less, the options in the menu may not have enough space to be
displayed on a single line, and the footer may look crowded. One way to solve
these issues is to list the items inside these sections one per line.

Listing 5-28: Creating a mobile menu

@media
(max-width: 480px) {

#logoheader
> div {

text-align: center;

}

#logoheader
h1 {

font: bold
46px Arial, sans-serif;

}

#mainmenu {

width:
100%;

height: 100%;

padding:
0%;

}

#mainmenu li
{

display: block;

margin-right: 0px;

text-align: center;

}

.footersection {

width: 94%;

text-align: center;

}

}

With the rules in Listing 5-28, we
modify the elements to force the browser to show them one per line and center
their content. The first two rules center the content of the logoheader element and assign a new size to the
page's title to make it look better on small screens. Next, we define the size
of the mainmenu element (the container of the
menu) to have the maximum width possible and a height determined by its content
(height: 100%). We also declare the <li> elements inside the mainmenu element as Block elements to display the
options of the menu one per line. Finally, the widths of the three sections
inside the footer are extended to force the browser to show them one per line.
Figure 5-16 illustrates how these changes affect some of the elements.

Figure 5-16: Mobile menu

Do It Yourself: Add the Media Query defined in
Listing 5-28 at the end of your Style Sheet and refresh the page in your
browser. Reduce the size of the window to see how the options in the menu and
the sections in the footer adapt to the space available.

After these rules are applied, the footer looks fine, but the
options of the menu at the top of the screen displace the relevant content down
the page, forcing the user to scroll the page to see it. A better alternative,
implemented by most developers nowadays, is to replace the menu with a button
and show the options only after the button is pressed. For this purpose, we
have to add an image to the document that will take the place of the menu when
the viewport's width is 480 pixels or less.

Listing 5-29: Adding a menu button for small
screens

<nav
id="menuicon">

<img
src="iconmenu.png" width="35" height="28"
id="menu-img">

</nav>

<nav
id="mainmenu">

<div>

<ul>

<li><a
href="index.html">Home</a></li>

<li><a
href="photos.html">Photos</a></li>

<li><a
href="videos.html">Videos</a></li>

<li><a
href="contact.html">Contact</a></li>

</ul>

</div>

</nav>

The first modification we have to introduce to our Style Sheet is
a rule that hides the menuicon element because we only want to show it on small
screens. There are two ways to do it; we can define the visibility
property with the value hidden or declare the display mode as none with
the display property. The first option does not show the element,
but the element still occupies an area on the page, while the second option
tells the browser to display the page as if the element was not included in the
document, so this is the one we have to implement for our menu.

Listing 5-30: Hiding the menu button

#menuicon {

display: none;

width: 95%;

height: 38px;

padding: 12px 2% 0px 3%;

background-color:
#9FC8D9;

border-top: 1px solid
#094660;

border-bottom: 1px solid
#094660;

}

The next step is to show the button and hide the menu when the
viewport's width is 480 pixels or less. The following are the modifications we
need to introduce at this breakpoint.

Listing 5-31: Replacing the menu with the
button

@media
(max-width: 480px) {

#mainmenu {

display: none;

width:
100%;

height:
100%;

padding:
0%;

}

#mainmenu li
{

display:
block;

margin-right: 0px;

text-align:
center;

}

#menuicon {

display: block;

}

.footersection {

width: 94%;

text-align:
center;

}

#logoheader
> div {

text-align:
center;

}

}

By assigning the value none to the display property of the mainmenu element we make the menu disappear. If the viewport's
width is 480 pixels or less, the menuicon element and its content are shown instead.

Figure 5-17: Menu button

Do It Yourself: Add a <nav> element identified with the name menuicon on top of the existent <nav> element in your document, as shown
in Listing 5-29. Add the rule in Listing 5-30 at the top of your Style Sheet.
Update the Media Query for the 480px breakpoint with the code in Listing 5-31.
Open the document in your browser and reduce the size of the window. You should
see the menu button replacing the menu when the viewport's width is 480 pixels
or less.

Now, we have a menu that adapts to the space available, but the
button is not responsive. To show the menu when the user presses or clicks the
button, we have to program our document to detect the action. These actions are
called events and are managed by JavaScript code. We will study JavaScript
and events in Chapter 6, but the process we have to perform in this example is fairly
straightforward. We have to turn the mainmenu element visible when the button is pressed by the
user. The following is a possible implementation.

Listing 5-32: Displaying the menu when the
button is pressed

<script>

var visible =
false;

function
initiate() {

var elem =
document.getElementById("menu-img");

elem.addEventListener("click", showMenu);

}

function
showMenu() {

var elem =
document.getElementById("mainmenu");

if
(!visible) {

elem.style.display =
"block";

visible =
true;

} else {

elem.style.display =
"none";

visible =
false;

}

}

window.addEventListener("load", initiate);

</script>

As we will see later, one way to insert JavaScript code into an
HTML document is with the <script> element. This element is usually placed inside the
document's head (the <head> element), but it can also go anywhere we want in the
document.

JavaScript code, like any other programming code, is composed of a
series of instructions that are processed sequentially. The code in Listing
5-32 gets a reference to the menu-img element and assigns a handler for the click
event to this element. Then, when the element is clicked, it changes the value
of the display property of the mainmenu element depending on the current condition. If the
menu is not visible, it makes it visible, and vice versa (We will explain how
this code works in Chapter 6). Figure 5-18 shows what we see when the button is
clicked.

Figure 5-18: Menu displayed
with JavaScript code

Do It Yourself: Add the code in Listing 5-32 inside
the <head> element and after the <link> element in your document. Refresh
the page in your browser. Click on the button to open the menu. You should see
something similar to Figure 5-18.

Up to now, we have been working with the Traditional Box Model.
Although this is currently the preferred model because of its compatibility
with old browsers, we also have the option to implement Responsive Web Design
with the Flexible Box Model. To illustrate how to develop a responsive website
with this model, we are going to use the document introduced in Chapter 4, Listing
4-52.

This example assumes that we have included in the document the <nav>
element added in Listing 5-29 and the <script> element with the JavaScript code introduced in
Listing 5-32, but this code has to be modified to make it work with this model.
The value assigned to the display property to make the menu appear when the button is
clicked has to be flex instead of block because now we are working with flexible containers.

Listing 5-33: Displaying the menu as a
flexible container

<script>

var visible =
false;

function
initiate() {

var elem =
document.getElementById("menu-img");

elem.addEventListener("click", showMenu);

}

function
showMenu() {

var elem =
document.getElementById("mainmenu");

if
(!visible) {

elem.style.display =
"flex";

visible =
true;

} else {

elem.style.display = "none";

visible =
false;

}

}

window.addEventListener("load", initiate);

</script>

The Style Sheet is very similar than the one we have created for
the Traditional Box Model; the only difference is that we have to build
flexible containers and declare flexibility with the flex
property. The Following are the styles by default for the whole document.

Listing 5-34: Designing a responsive document
with the Flexible Box Model

*
{

margin: 0px;

padding: 0px;

}

#logoheader {

display: flex;

justify-content: center;

width: 96%;

height:
150px;

padding: 0%
2%;

background-color: #0F76A0;

}

#logoheader
> div {

flex: 1;

max-width: 960px;

padding-top:
45px;

}

#logoheader h1
{

font: bold
54px Arial, sans-serif;

color:
#FFFFFF;

}

#mainmenu {

display: flex;

justify-content: center;

width: 96%;

height: 50px;

padding: 0%
2%;

background-color: #9FC8D9;

border-top:
1px solid #094660;

border-bottom: 1px solid #094660;

}

#mainmenu >
div {

flex: 1;

max-width: 960px;

}

#mainmenu li {

display:
inline-block;

height: 35px;

padding: 15px
10px 0px 10px;

margin-right:
5px;

}

#mainmenu
li:hover {

background-color: #6FACC6;

}

#mainmenu a {

font: bold
18px Arial, sans-serif;

color:
#333333;

text-decoration: none;

}

#menuicon {

display:
none;

width: 95%;

height: 38px;

padding: 12px
2% 0px 3%;

background-color: #9FC8D9;

border-top:
1px solid #094660;

border-bottom: 1px solid #094660;

}

main {

display: flex;

justify-content: center;

width: 96%;

padding: 2%;

background-image:
url("background.png");

}

main > div {

display: flex;

flex: 1;

max-width: 960px;

}

#mainarticles {

flex: 1;

margin-right:
20px;

padding-top:
30px;

background-color: #FFFFFF;

border-radius: 10px;

}

#asideinfo {

flex: 1;

max-width: 280px;

padding: 2%;

background-color: #E7F1F5;

border-radius: 10px;

}

#asideinfo h1 {

font: bold
18px Arial, sans-serif;

color:
#333333;

margin-bottom: 15px;

}

article {

position:
relative;

padding: 0px
40px 20px 40px;

}

article time {

display:
block;

position:
absolute;

top: -5px;

left: -70px;

width: 80px;

padding: 15px
5px;

background-color: #094660;

box-shadow:
3px 3px 5px rgba(100, 100, 100, 0.7);

border-radius: 5px;

}

.daynumber {

font: bold
36px Verdana, sans-serif;

color:
#FFFFFF;

text-align:
center;

}

.dayname {

font: 12px
Verdana, sans-serif;

color:
#FFFFFF;

text-align:
center;

}

article h1 {

margin-bottom: 5px;

font: bold
30px Georgia, sans-serif;

}

article p {

font: 18px
Georgia, sans-serif;

}

figure {

margin: 10px
0px;

}

figure img {

max-width:
98%;

padding: 1%;

border: 1px
solid;

}

#logofooter {

display: flex;

justify-content: center;

width: 96%;

padding: 2%;

background-color: #0F76A0;

}

#logofooter
> div {

display: flex;

flex: 1;

max-width: 960px;

background-color: #9FC8D9;

border-radius: 10px;

}

.footersection
{

flex: 1;

padding: 3%;

}

.footersection
h1 {

font: bold
20px Arial, sans-serif;

}

.footersection
p {

margin-top:
5px;

}

.footersection
a {

font: bold
16px Arial, sans-serif;

color:
#666666;

text-decoration: none;

}

The graphic design for this document is the same we have created
before, so we have to use the same breakpoints. Again, when the viewport's
width is 1120 pixels or less, we have to move the <time>
element below the article's title. Because in both models the <time>
element is positioned with absolute values, this Media Query does not change.

Listing 5-35: Moving the <time> element

@media
(max-width: 1120px) {

article time
{

position:
static;

width:
100%;

padding:
0px;

margin-bottom: 10px;

background-color: #FFFFFF;

box-shadow:
0px 0px 0px;

border-radius: 0px;

}

.daynumber {

display:
inline-block;

font: bold
14px Verdana, sans-serif;

color:
#999999;

padding-right: 5px;

}

.dayname {

display:
inline-block;

font: bold
14px Verdana, sans-serif;

color:
#999999;

}

article h1 {

margin-bottom: 0px;

}

}

Now, we have to turn the two-column design into a one-column
design when the viewport's width is 720 pixels or less. Because we no longer
want the columns to share the same line but to be placed one on top of another,
we have to declare the container as a Block element. Once this is done,
extending the elements to the sides is easy, we have to give them a size of
100% (notice that the <aside> element has a maximum width of 280 pixels by default,
so we also have to declare the value of the max-width property as 100% to extend the elements to the edges
of the container).

Listing 5-36: Moving from a two-column to a one-column
design

@media
(max-width: 720px) {

main > div
{

display: block;

}

#mainarticles
{

width:
100%;

margin-right: 0px;

}

#asideinfo {

width: 90%;

max-width: 100%;

padding:
5%;

margin-top:
20px;

}

}

At the last breakpoint, we have to modify the menu bar to show the
menu button instead of the options and declare the container in the footer as a
Block element to move one section on top of another.

Listing 5-37: Adapting the menu and the
footer

@media
(max-width: 480px) {

#logoheader
> div {

text-align:
center;

}

#logoheader
h1 {

font: bold
46px Arial, sans-serif;

}

#mainmenu {

display:
none;

width:
100%;

height:
100%;

padding:
0%;

}

#mainmenu li
{

display:
block;

margin-right: 0px;

text-align:
center;

}

#menuicon {

display:
block;

}

#logofooter
> div {

display: block;

}

.footersection {

width: 94%;

text-align:
center;

}

}

With the code in Listing 5-37,
the Style Sheet is ready. The final design is exactly the same as the one
achieved with the Traditional Box Model, but this time using the Flexible Box
Model. The Flexible Box Model is a vast
improvement over the Traditional Box Model and can simplify the creation of
responsive websites, allowing us to modify the order in which the elements are
presented and facilitating the combination of flexible and fixed elements, but
it is not supported by all the browsers in the market. Some developers are
already using this model or implementing some of its properties, but most
websites are still developed with the Traditional Box Model.

Do It Yourself: Create a new HTML file with the
document of Listing 4-52 (see Flexible Box Model in Chapter
4). Add the <nav> element introduced in Listing
5-29 and the
<script> element in Listing
5-33 to the
document as explained in the previous example. Create a new CSS file called
mystyles.css and copy inside the codes in Listings 5-34, 5-35, 5-36 and 5-37.
Download the files myimage.jpg and iconmenu.png from our website and move them
to the document's folder. Open the document in your browser and change the size
of the window to see how the elements adapt to the space available.

Chapter 6 - JavaScript

6.1 Introduction to JavaScript

HTML and CSS are languages that
provide instructions to tell the browser how to organize and visualize a
document and its content, but the interaction with the user and the system is
limited to a small set of predefined responses. We can create a form with input
fields and buttons, but HTML only provides functionality to send the
information entered by the user to the server or clear the form. Something
similar happens with CSS; we can build instructions (rules) with pseudo-classes
like :hover to apply a different set of
properties when the user moves the mouse over an element, but if we want to
perform a custom task, such as modifying the styles of multiple elements at
once, we have to load a new Style Sheet that already presents those changes. With
the purpose of modifying elements dynamically, performing custom operations, or
responding to user interaction, browsers include a third language called JavaScript.

JavaScript is a programming
language used to process information and manipulate documents. Like any other
programming language, JavaScript provides instructions that are executed in
sequential order to tell the system what to do (perform an arithmetic
operation, assign a new value to an element, etc.). When the browser finds this
type of code in our document, it executes the instructions at once, and any
changes performed to the document are shown on the screen.

Implementing JavaScript

Following the same approach than CSS, JavaScript
code may be incorporated into the document with three different techniques; the
code may be inserted into an element (Inline), embedded in the document, or
loaded from external files. The Inline technique takes advantage of special attributes that
describe an event, such as the click of the mouse. To make an element respond to an event
using this technique, all we have to do is to add the corresponding attribute
with the code to handle it.

Listing 6-1: Defining inline JavaScript

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>JavaScript</title>

</head>

<body>

<section>

<p
onclick="alert('You clicked me!')">Click Me</p>

<p>You Can't Click Me</p>

</section>

</body>

</html>

The onclick attribute added to the <p> element in Listing 6-1 is saying
something like "when someone clicks on this element execute this code",
and the code is (in this case) the instruction alert(). This is a predefined JavaScript
instruction called function. What this function does is to ask the
browser to display a popup window with the value provided between parentheses.
When the user clicks on the area occupied by the <p> element, the browser executes the alert() function and shows a popup window
on the screen with the message "You clicked me!".

Figure 6-1: Popup window
generated by the alert() function

Do It Yourself: Create a new HTML file with the
code in Listing 6-1. Open the document in your browser and click on the text
"Click Me" (the onclick attribute affects the entire
element, not only the text, so you can also click on the rest of the area
occupied by the element to execute the code). You should see a popup window
with the message "You clicked me!", as shown in Figure 6-1.

The Basics:
JavaScript includes
multiple predefined functions, and you can also create your own. We will learn
more about functions and predefined functions later in this chapter.

The onclick attribute is one of a series of
attributes provided by HTML to respond to events. The list of attributes
available is extensive, but they may be organized into groups depending on
their purpose. For instance, the following are the most frequently used
attributes associated with the mouse.

onclick—This
attribute handles the click event. This event is
fired when the user clicks the mouse's left button. HTML offers two more
attributes called ondblclick (the user
double clicks the mouse's left button) and oncontextmenu (the user
clicks the mouse's right button).

onmousedown—This attribute
handles the mousedown event. This
event is fired when the user pushes down the mouse's left or right buttons.

onmouseup—This
attribute handles the mouseup event. This
event is fired when the user releases the mouse's left button.

onmouseenter—This attribute
handles the mouseenter event. This
event is fired when the mouse enters the area occupied by the element.

onmouseleave—This
attribute handles the mouseleave event. This
event is fired when the mouse leaves the area occupied by the element.

onmouseover—This
attribute handles the mouseover event. This
event is fired when the mouse moves over the element or any of its children.

onmouseout—This
attribute handles the mouseout event. This
event is fired when the mouse moves off the element or off any of its children.

onmousemove—This
attribute handles the mousemove event. This
event is fired every time the mouse is over the element and it moves.

onwheel—This
attribute handles the wheel event. This event is fired
every time the mouse's wheel is rotated.

The following are the
attributes available to respond to events coming from the keyboard. They apply
to elements that receive input from the user, such as the <input> and <textarea> elements.

onkeypress—This
attribute handles the keypress event. This
event is fired when the element is focused, and a key is pressed.

onkeydown—This
attribute handles the keydown event. This
event is fired when the element is focused, and a key is pressed down.

onkeyup—This
attribute handles the keyup event. This event is
fired when the element is focused, and a key is released.

There are also two important
attributes associated with the document.

onload—This
attribute handles the load event. This event is
fired when the document or a resource has finished loading.

onunload—This
attribute handles the unload event. This
event is fired when a resource has been unloaded.

Event attributes are included
in an element depending on when we want the code to be executed. If we want to
respond to the click of the mouse, we have to include the onclick attribute, as we did in Listing
6-1, but if we want to perform a task when the mouse passes over an element, we
have to include the onmouseover or onmousemove attributes. Because multiple events
may occur, in some cases even at the same time, we can declare more than one
attribute per element. For instance, the following document includes a <p> element with two attributes, onclick and onmouseout, that include their own JavaScript
code. If the user clicks on the element, a popup window is displayed with the
message "You clicked me!", but if the user moves the mouse outside
the area occupied by the element a different popup window is shown with the
message "Do not leave me!"

Listing 6-2: Implementing multiple event
attributes

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>JavaScript</title>

</head>

<body>

<section>

<p
onclick="alert('You clicked me!')" onmouseout="alert('Do not
leave me!')">Click Me</p>

</section>

</body>

</html>

Do It Yourself: Update your HTML file with the code
in Listing 6-2. Open the document in your browser and move the mouse over the
area occupied by the <p> element. If you move the mouse out
of the area, you should see a popup window with the message "Do not leave
me!".

Events are produced not only by
the user but also by the browser. A useful event fired by the browser is load. This event is fired when resources
have finished loading, and therefore it is frequently used to execute JavaScript
code after the document and its content have been loaded.

Listing 6-3: Responding to the load event

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>JavaScript</title>

</head>

<body
onload="alert('Welcome!')">

<section>

<h1>My Website</h1>

<p>Welcome to my website</p>

</section>

</body>

</html>

The document of Listing 6-3
shows a popup window to welcome the user after it is completely loaded. The
browser first loads the content of the document, and when it is over, it calls
the alert()
function and shows the message on the screen.

IMPORTANT:
Events are critical in
web development. Besides those studied in this chapter, there are dozens more
available to control a variety of processes, from playing a video to checking
the progress of a task. We will learn more about events later in this chapter,
and study the rest of the events available in more practical situations in later
chapters.

The Basics:
When you test the code in
Listing 6-3 in your browser, you will see that the popup window is shown before
the content of the document appears on the screen. This is because the document
is loaded in an internal structure of objects called DOM and later reconstructed
on the screen from these objects. We will learn more about the DOM and how to
access HTML elements from JavaScript later in this chapter.

Event
attributes are useful when we want to test code or implement a feature quickly,
but they are not suitable for large applications. To work with extensive codes
and customized functions, we have to group the codes together with the <script> element. The
<script> element acts exactly like the <style> element for
CSS, organizing the code in one place and affecting the rest of the elements in
the document using references.

Listing 6-4: Embedding JavaScript in the
document

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>JavaScript</title>

<script>

alert('Welcome!');

</script>

</head>

<body>

<section>

<p>Hello</p>

</section>

</body>

</html>

The <script> element and
its content may be located anywhere in the document, but it is usually placed
inside the header, as in this example. This way, when the browser loads the
file, it reads the content of the <script> element, it
executes the code right away, and then continues processing the rest of the
document.

Do It Yourself: Update your HTML file
with the code in Listing 6-4 and open the document in your browser. You should
see a popup window with the message "Welcome!" as soon as the
document is loaded. Because the alert() function stops
the execution of the code, the content of the document is not shown on the
screen until we press the OK button.

Embedding
JavaScript in the document with the <script> element may
be practical when we have a small set of instructions, but JavaScript code can
grow very fast. If we use the same code in more than one document, we will have
to maintain different versions of the same program and the browsers will have
to download the same code again with each document requested by the user. A
better alternative is to include the JavaScript code in a single file and then
load that file from the documents that require it. This way, only the documents
that need that set of instructions will include the file, and the browser will
have to download the file only once (browsers keep the files in a cache in case
they are later requested by other documents from the same website). For this
purpose, the <script> element
includes the src attribute. With this attribute,
we can declare the path to the JavaScript file and write all our code inside that
file. The <script> element in the following example
loads the JavaScript code from an external file called mycode.js.

Listing 6-5: Introducing JavaScript code
from an external file

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>JavaScript</title>

<script src="mycode.js"></script>

</head>

<body>

<section>

<p>Hello</p>

</section>

</body>

</html>

JavaScript files, like HTML and
CSS files, are text files and therefore they are created with a text editor or
the professional editors we have recommended in Chapter 1. We may assign to
these files any name we want, but by convention, they have to include the .js
extension. Inside, the code must be declared exactly as we would do between the
<script> tags. For instance, the following is
the code we have to introduce in the mycode.js file to reproduce the previous
example.

Listing 6-6: Creating a JavaScript file
(mycode.js)

alert("Welcome!");

Do It Yourself: Update your HTML file with the code
in Listing 6-5. Create a new file with the name mycode.js and the JavaScript
code in Listing 6-6. Open the document in your browser. You should see a popup
window with the message "Welcome!" as soon as the document is loaded.

IMPORTANT:
Besides the src attribute, there are two more
attributes available for the <script> element: async and defer. These are Boolean attributes that
indicate how and when the code will be executed. If async is declared, the code is executed
asynchronously (while the document is being processed). If the defer attribute is declared, the code is
executed after the document has been processed.

The Basics: It
is recommended to finish each line of code with a semicolon to make sure that
the browser does not have any trouble identifying the end of the instruction.

Variables

Of course, JavaScript is more than popup
windows showing messages to alert the user. The language can perform numerous
tasks, from calculating complex algorithms to processing the content of a
document. Each one of these tasks involves the manipulation of values, and this
is why the most important feature of JavaScript, as in any other programming
language, is the capacity to store data in memory.

The memory of a computer or mobile
device is like a honeycomb with millions and millions of consecutive cells
available to store information. These cells have limited space, and therefore the
combination of several cells is necessary to store large amounts of data.
Because of the complexity of this structure, programming languages incorporate
the concept of variables to facilitate the identification of each value
stored in memory. Variables are names assigned to a cell or a group of cells
where the data is going to be stored. For example, if we want to store the
value 5 in memory, we need to know where that number was stored to be able to
retrieve it later. Creating a variable allows us to identify that space in
memory with a name and use that name later to read the value or store a new
one.

Variables in JavaScript are declared
with the var keyword followed by the name we want to
give to the variable. If we want to store a value in the space of memory
assigned by the system to the variable, we have to include the character =
(equal) followed by the value, as in the following example.

Listing 6-7: Declaring a
variable in JavaScript

var mynumber =
2;

The
instruction in Listing 6-7 creates the variable mynumber and stores
the value 2 in the space of memory reserved by the
system for this variable. When this code is executed, the browser reserves a
space in memory, stores the number 2 inside,
creates a reference to that space, and finally assigns that reference to the
name mynumber.

After
the value is assigned to the variable, every time the variable is referenced
(the name mynumber is mentioned), the
system reads the memory and returns the number 2, as illustrated by the
following example.

Listing 6-8: Using the
content of a variable

var mynumber = 2;

alert(mynumber);

As we already mentioned, the
instructions in a JavaScript program are executed by the browser one by one in
sequential order. So when the browser reads the code in Listing 6-8, it executes
the instructions from top to bottom. The first instruction asks the browser to
create a variable called mynumber and assign the value 2 to it. After this task is completed, the browser executes the
next instruction on the list. This instruction asks the browser to display a
popup window with the current value stored in the mynumber variable.

Figure 6-2: Popup window
displaying the value of a variable

Do It Yourself: Update your mycode.js file with the
code in Listing 6-8. Open the document of Listing
6-5 in your
browser. You should see a popup window with the value 2.

Variables are called variables
because they are not constant. We can change their values any time we want, and
that is, in fact, their most important characteristic.

Listing 6-9: Assigning a
new value to a variable

var mynumber = 2;

mynumber = 3;

alert(mynumber);

In Listing 6-9, after the variable is
declared, a new value is assigned to it. Now the alert() function shows the number 3 (the value 2 was replaced with the value 3 by the second instruction). There is no need for the use of the var keyword this second time, just the name of the variable
and the = sign are required to assign the new
value.

The value stored in a variable may be
assigned to another variable. For instance, the following code creates two
variables called mynumber and yournumber and assigns the value stored in the mynumber variable to the yournumber variable. The value shown by the popup
window is the number 2.

Listing 6-10: Assigning
the value of a variable to another variable

var
mynumber = 2;

var yournumber = mynumber;

alert(yournumber);

In a more practical situation, we
would probably use the value of the variable to perform an operation and assign
the result back to the same variable.

Listing 6-11: Performing
an operation with the value stored in a variable

var mynumber = 2;

mynumber = mynumber +
1;  // 3

alert(mynumber);

In this example, a value of 1 is added to the current value of mynumber and the result is assigned to the same
variable. This is the same as performing 2 + 1, with the difference that when
using a variable instead of a number its value may change at any time.

The Basics: The characters at the end of the
second instruction are considered to be a comment and therefore they are not
processed as part of the instruction. Comments are added to the code as
references or reminders for the developer. They may be declared using two
slashes for single-line comments (// comment) or combining a slash and a star
character for multi-line comments (/* comment */). Everything after the two slashes
or in between the /* and */ characters is ignored by the
browser.

Besides the + operator, JavaScript also includes the operators - (subtraction), * (multiplication), / (division), and % (remainder). These operators
may be used in a single operation between two values or combined with multiple
values to perform more complex arithmetic operations.

Listing 6-12: Performing
complex operations

var
mynumber = 2;

mynumber = mynumber * 25 +
3;  // 53

alert(mynumber);

Arithmetic operations are executed
following an order of precedence determined by the operators. Multiplication
and division have precedence over addition and subtraction. This means that the
multiplications and divisions are performed before the additions and
subtractions. In Listing 6-12, the current value of the mynumber variable is multiplied by 25, and then
the value 3 is added to the result. If we want to control the precedence, we
can isolate the operations between parentheses. For example, the following code
performs the addition and then the multiplication, producing a different
result.

Listing 6-13: Controlling
precedence in the operation

var
mynumber = 2;

mynumber = mynumber * (25 +
3);  // 56

alert(mynumber);

Performing an operation on the
variable's current value and assigning the result back to the same variable is
very common in computer programming. JavaScript offers the following operators
to simplify this task.

·
++ is shorthand for variable = variable + 1.

·
-- is shorthand for variable = variable - 1.

·
+= is shorthand for variable = variable + number.

·
-= is shorthand for variable = variable - number.

·
*= is shorthand for variable = variable * number.

·
/= is shorthand for variable = variable / number.

With these operators, we can easily
perform operations on the value of a variable and assign the result back to the
same variable. A common use of these operators is to create counters by
incrementing or decrementing the value of a variable in one or more units. For
example, the ++ operator adds the value 1 to
the variable's current value every time the instruction is executed.

Listing 6-14: Incrementing
the value of a variable

var
mynumber = 0;

mynumber++;

alert(mynumber);
// 1

If the value of the variable must be incremented
by more than one unit, we can use the += operator. This operator adds the value
specified in the instruction to the variable's current value and stores the
result back in the variable.

Listing 6-15: Incrementing
the value of a variable by a specific value

var
mynumber = 0;

mynumber += 5;

alert(mynumber);
// 5

The process generated by the code in Listing 6-15
is straightforward. After the value 0 is assigned to the mynumber variable, the system reads the second instruction,
gets the variable's current value, adds 5 to that value, and stores the result
back in mynumber.

An interesting operator is the
remainder operator. This operator returns the amount left by a division between
two numbers.

Listing 6-16: Calculating
the remainder

var mynumber =
11 % 3;  // 2

alert(mynumber);

The operation assigned to the mynumber variable in Listing 6-16 produces the
result 2. The system divides 11 by 3 and finds a quotient of 3. Then, to get
the remainder, it calculates 11 minus the multiplication of 3 times the
quotient (11 - (3 * 3) = 2).

The remainder operator is used
frequently to determine whether a value is odd or even. If we calculate the remainder
of an integer number divided by 2, we get a result according to its parity (odd
or even). If the number is even, the remainder is 0, but if the number is odd,
the remainder is 1 (or -1 for negative numbers).

Listing 6-17: Determining
the parity of a number

var mynumber =
11;

alert(mynumber % 2);   // 1

The code in Listing 6-17 calculates
the remainder of the current value of the mynumber variable. The value returned
is 1, which means that the value is odd. Notice that this time we perform the
operation between the parentheses of the alert() function. Every time an
operation is included inside an instruction, the browser first performs the
operation and then executes the instruction with the result, so an operation
may be provided every time a value is required.

Do It Yourself: Update your mycode.js with the
example you want to try and open the document in your browser. Change the
numbers assigned to the variables and perform more complex operations to see
the different results produced by JavaScript and get familiar with the
language's operators.

Strings

In previous examples, we stored numbers,
but variables can also store other types of values, including text. To assign
text to a variable, we have to enclose the characters in quotation marks (single
or double quotes).

Listing 6-18: Assigning a
string of characters to a variable

var mytext = "Hello World!";

alert(mytext);

The code in Listing 6-18 creates a
variable called mytext and stores a string of
characters in it. When the first instruction is executed, the system reserves a
space in memory long enough to store the string, creates the variable, and
stores the text. Every time we read the mytext variable, we get back the text
"Hello World!" (without the quotes).

Figure 6-3: Popup window
displaying a string stored in a variable

The space reserved in memory by the
system to store the string of characters depends on the size of the text (how
many characters it contains), but the system is prepared to extend that space
if longer values are assigned later to the variable. A common situation in
which longer texts are assigned to a variable is when we add more characters at
the beginning or the end of the variable's current value. The texts may be
concatenated with the + operator.

Listing 6-19:
Concatenating text

var
mytext = "My name is ";

mytext = mytext +
"John";

alert(mytext);

The code in Listing 6-19 adds the
text "John" at the end of the text "My name is ". The final
value of the mytext variable is "My name is John".
If we want to add the text at the beginning, we have to invert the operation.

Listing 6-20: Adding text
at the beginning of the value

var
mytext = "John";

mytext = "My name is
" + mytext;

alert(mytext);

If instead of text we try to
concatenate a string of text and a number, the number is converted into a
string and added to the current value. The following code produces the string
"The number is 3".

Listing 6-21:
Concatenating strings and numbers

var
mytext = "The number is " + 3;

alert(mytext);

This is important when we have a
string that contains a number, and we want to add another number to it. Because
JavaScript considers the current value to be a string, the number is turned
into a string, and the values are not added but concatenated.

Listing 6-22:
Concatenating numbers

var mytext =
"20" + 3;

alert(mytext);
// "203"

The Basics:
The
result of the code in Listing 6-22 is not 23 but the string "203".
The system converts the number 3 into a string and concatenates the strings
instead of adding the numbers. Later, we will learn how to extract numbers from
strings to perform operations with their values.

Strings may contain any character we
want, and this includes single and double quotes. If the quotes included in the
text are different from those used to define the string, the quotes are treated
like any other character, but if the quotes are the same, the system does not
know where the string ends. To solve this problem, JavaScript provides the
escape character \. For example, if the string
was declared with single quotes, we have to escape the single quotes inside the
text.

Listing 6-23: Escaping
characters

var mytext =
'They don\'t make it anymore';

alert(mytext);
// "They don't make it anymore"

JavaScript offers several escape
characters for different purposes. The most frequently used are \n to generate a new line and \r to return the cursor to the
beginning of the line. These two characters are usually implemented together to
divide the text into multiple lines, as in the following example.

Listing 6-24: Generating
new lines

var mytext =
"The good life is one inspired by love\r\n";

mytext = mytext + "and
guided by knowledge"

alert(mytext);

The code in Listing 6-24 begins by
assigning a string to the mytext variable that includes the New
Line and Carriage Return characters. In the next instruction, we add a second
text at the end of the variable's current value, but because of the escape
characters, the strings are displayed in the popup window in two lines.

The Basics:
Strings
are declared as objects in JavaScript, and therefore they include methods to
perform operations on their characters. We will study objects, String objects, and
how to implement their methods later in this chapter.

Booleans

Another
type of values we can store in variables are Booleans. There are only two Boolean
values available: true and false. They are
useful when we only need to determine the current state of a condition. For
example, if our application needs to know whether a value inserted in a form is
valid or not, we can keep track of this condition with a Boolean variable.

Listing 6-25: Declaring a Boolean variable

var valid = true;

alert(valid);

The purpose of these
variables is to simplify the process of identifying the state of a condition.
If we use an integer value to indicate a state, we have to remember which
numbers we decided to use for the valid and the invalid states. Using Boolean
values, we only have to check whether the value is equal to true or false.

IMPORTANT:
Booleans become useful
when we use them with instructions that allow us to perform a task or
repetitive tasks depending on a condition. We will study Conditionals and Loops
later in this chapter.

Arrays

Variables can also store
several values at once in a structure called arrays. Arrays may be
created using a simple notation that includes the values separated by commas
and between square brackets. The values are later identified by an index,
starting from 0.

Listing 6-26: Creating
arrays

var myarray =
["red", "green", "blue"];

alert(myarray[0]);  //
"red"

In Listing 6-26, we create an array called myarray with three values, the strings "red", "green" and "blue".
JavaScript automatically assigns the index 0 to the first value, 1
to the second value, and 2 to the third value. To retrieve this
information, we have to mention the index every time we want to read each value
in the array. This is done by writing the index number within square brackets and
after the name of the variable. For example, to get the first value of myarray,
we have to write the
instruction myarray[0], as we did in our example.

Notice that the alert() function can display not only single values but also entire arrays. If
we want to see all the values included in an array, we just have to specify the
array's name.

Listing 6-27: Displaying
the array's values

var
myarray = ["red", "green", "blue"];

alert(myarray);  //
"red,green,blue"

Arrays, like common variables, can take any type
of value we want. For example, we can create an array like the one in Listing
6-27 combining numbers and strings.

Listing 6-28: Using
different types of values

var
myarray = ["red", 32, "HTML5 is awesome!"];

alert(myarray[1]);  //
32

Do It Yourself: Replace the code in your mycode.js
file with the code in Listing 6-28 and open the document of Listing
6-5 in your
browser. Change the index provided to the alert() function to see every value of the
array (remember that the indexes start from 0).

If we try to read a value in an index
that was not yet defined, JavaScript returns the value undefined. This value is used by the system to
report that the value we are trying to access does not exist, but we can also
assign it to an array to declare that we do not have the value for that
position.

Listing 6-29: Declaring a
value as undefined

var myarray =
["red", undefined, 32];

alert(myarray[1]);  //
undefined

A better way to tell the system that
there is no value available in a certain position of the array is using another
special value called null. The difference between the values undefined and null is that undefined indicates that the variable was declared but no value
was assigned to it, while null indicates
that there is a value but it is null.

Listing 6-30: Declaring a null value

var myarray =
["red", 32, null];

alert(myarray[2]);  //
null

Of course, we can also perform
operations on the values of an array and store the result, as we did before
with single variables.

Listing 6-31: Working with
values in arrays

var
myarray = [64, 32];

myarray[1] = myarray[1] +
10;

alert("The
new value is " + myarray[1]);  // "The new value is
42"

Arrays operate exactly like single
variables, with the exception that we have to mention the index every time we
want to use them. With the instruction myarray[1]
= myarray[1] + 10 we
tell the JavaScript interpreter to read the current value of myarray at index 1 (32), add 10 to that value,
and store the result in the same array at the same index. In the end, the value
of myarray[1] is 42.

Arrays can take any type of values,
so it is
possible to declare arrays of arrays. These types of arrays are called multidimensional
arrays.

Listing 6-32: Defining
multidimensional arrays

var myarray =
[[2, 45, 31], [5, 10], [81, 12]];

The code
in Listing 6-32 creates an array of arrays of integers (notice that the values
are declared inside another array). To access these values, we have to declare
the indexes of each level between square brackets, one after another. The
following example returns the first value (index 0) of the second array (index
1). The instruction reads the array at index 1 and then reads the number in
that array at index 0.

Listing 6-33: Accessing
multidimensional arrays

var myarray =
[[2, 45, 31], [5, 10], [81, 12]];

alert(myarray[1][0]);  // 5

If we want to erase one of the
values, we can declare it as undefined or null, as we did before, or declare it as an empty array
assigning square
brackets with no values inside.

Listing 6-34: Assigning an
empty array as the value of another array

var
myarray = [[2, 45, 31], [5, 10], [81, 12]];

myarray[1] = []

alert(myarray[1][0]);
// undefined

Now, the value shown in the popup
window is undefined, because there is no element
at the positions [1][0]. Of course, this may also be
used to empty any type of array.

Listing 6-35: Assigning an
empty array to a variable

var
myarray = [2, 45, 31];

myarray = []

alert(myarray[1]);
// undefined

The Basics:
Like
strings, Arrays are also declared as objects in JavaScript, and therefore they
include methods to perform operations on their values. We will study objects,
Array objects, and how to implement their methods later in this chapter.

Conditionals and Loops

Up to this point, we have been writing
instructions in a sequence, one after another. In this kind of program, the
system executes each instruction once, and in the order they were presented. It
begins with the first one and goes on until it reaches the end of the list. The
purpose of Conditionals and Loops is to break this sequential flow.
Conditionals allow us to execute one or more instructions only when a certain
condition is met, and Loops let us execute a block of code (a group of
instructions) over and over again until a condition is met. JavaScript provides a total of
four instructions to process code according to conditions determined by the
programmer: if, switch, for and while.

The simplest way to check a condition
is with the if instruction. The if instruction checks an expression and processes a set of instructions
if the condition set by the expression is true. The instruction requires the
keyword if followed by the condition between
parentheses and the instructions we want to execute if the condition is true
between braces.

Listing 6-36: Checking a
condition with if

var
myvariable = 9;

if (myvariable < 10) {

alert("The number is less than 10");

}

In the code in Listing 6-36, the
value 9 is assigned to myvariable, and then using the if instruction we compare the variable with the number 10. If the value of the variable is less than 10, the alert() function shows a message on
the screen.

The operator used in this example to
compare the value of the variable against 10 is called comparison operator.
The following are the comparison operators available in JavaScript.

·
== checks whether the value on the left is
equal to the value on the right.

·
!= checks whether the value on the left is
different than the value on the right.

·
> checks whether the value on the left is
greater than the value on the right.

·
< checks whether the value on the left is
less than the value on the right.

·
>= checks whether the value on the left is
greater or equal than the value on the right.

·
<= checks whether the value on the left is
less or equal than the value on the right.

After a condition is evaluated, it
returns a logical value true or false. This allows us to work with conditions
as values and combine them to create complex conditions. JavaScript offers the
following logical operators for this purpose.

·
! (logical NOT) toggles the state of the
condition. If the condition is true, it returns false, and vice versa.

·
&& (logical AND) checks
two conditions and returns true if both are true.

·
|| (logical OR) checks two conditions and
returns true if one or both are true.

The logical operator ! inverts the state of the condition. If the condition evaluates to
true, the final state will be false, and the instructions between braces will not
be executed.

Listing 6-37: Inverting
the result of the condition

var
myvariable = 9;

if (!(myvariable < 10))
{

alert("The number is
less than 10");

}

The code in Listing 6-37 does not
show any message on the screen. The value 9 is still less than 10, but because
we alter the condition with the ! operator, the final result is false,
and the alert() function is not executed.
Notice that we put the condition between parentheses to work with the state
returned by the condition and not the values the condition is comparing.
Because of the parentheses, the condition is first evaluated, and then the
state returned is inverted by the ! operator.

The && (AND) and || (OR) operators work a little bit different. They calculate the
final result based on the results of the conditions involved. The && (AND) operator returns true only if the
conditions on both sides are true, and the || (OR) operator returns true
if one or both conditions are true. For example, the following code executes
the alert() function only when the age is less than
21 and the value of the smart variable is equal to
"YES" (because we use the && operator, both conditions
have to be true for the overall condition to be true).

Listing 6-38: Checking
multiple conditions with logical operators

var
smart = "YES";

var age = 19;

if (age < 21 &&
smart == "YES") {

alert("John is
allowed");

}

If we assume that our example only
considers two values for the smart variable, "YES" and "NO",
we can turn it into a Boolean variable. Because Booleans are already logical
values, we do not need to compare them to anything. The following code reproduces
the previous example using a Boolean variable.

Listing 6-39: Using
Boolean values as conditions

var
smart = true;

var age = 19;

if (age < 21 && smart) {

alert("John is allowed");

}

JavaScript is very flexible regarding
conditions. The language is capable of determining a condition based on the
value of any variable. For example, a variable with an integer number will
return false if the value is 0 or true if the value is different than 0.

Listing 6-40: Using
integer numbers to set a condition

var
age = 0;

if (age) {

alert("John is allowed");

}

The code in the if instruction of Listing 6-40 is not executed because the value of
the age variable is 0 and therefore the state of
the condition is considered to be false. If we store a different value into
this variable, the condition will be true, and the message will be shown on the
screen.

Variables with empty strings also
return false. The following example checks whether a string was assigned to a variable
and only shows its value if the string is not empty.

Listing 6-41: Using
strings to set a condition

var
name = "John";

if (name) {

alert(name + " is allowed");

}

Do It Yourself: Replace the code in your mycode.js
file with the code in Listing 6-41 and open the document of Listing
6-5 in your
browser. The popup window should show the message "John is allowed".
Assign an empty string to the name variable. The if instruction now considers the
condition to be false and no message is shown on the screen.

There
are times when instructions have to be executed for either state of the
condition (true or false). JavaScript includes the if
else instruction to help in this situation. The instructions to be
executed are declared in two blocks of code delimited by braces. The block
preceded by if is executed when the
condition is true and the block preceded by else is executed
otherwise.

Listing 6-42: Checking two
conditions with if else

var
myvariable = 21;

if (myvariable < 10) {

alert("The number is less than 10");

} else {

alert("The number is 10 or larger");

}

In this example, the code considers
two conditions: when the number is less than 10 and when the number is equal to or greater than 10. If we need to check multiple conditions, instead of the if else instructions we can use the switch instruction. This instruction evaluates an expression
(usually just a variable), compares the result with multiple values, and
executes the instructions corresponding to the value that matches the
expression. The syntax includes the switch keyword followed by the expression
between parentheses. The possible values are listed using a keyword called case, as in the following example.

Listing 6-43: Checking a
value with the switch instruction

var
myvariable = 8;

switch(myvariable) {

case 5:

alert("The number is five");

break;

case 8:

alert("The number is eight");

break;

case 10:

alert("The number is ten");

break;

default:

alert("The number is " + myvariable);

}

In Listing 6-43, the switch instruction evaluates the variable myvariable, gets its value, and then compares it
with the value in every case. If the value is 5, for example, the control is transferred to the first case, and the alert() function shows the text
"The number is five" on the screen. If this first case does not match the value of the variable, then the next
case is evaluated, and so on. If no case matches the value, the instructions in
the case labeled default are executed.

In JavaScript, once a match is found,
the instructions in the case are executed along with the instructions in the
following cases. This is the default behavior but usually not what we need in
our code. For this reason, JavaScript provides the break instruction. To stop the system from executing the
instructions of every case after the match, we have to include the break instruction at the end.

The switch and if instructions are useful but
perform a simple task; they evaluate an expression, execute a block of
instructions according to the result, and return the control to the main code afterward.
There are certain situations in which this is not sufficient. We could have to
execute the instructions several times for the same condition or evaluate the
condition again after a process is over. There are two instructions we can use
in these situations: for and while.

The for instruction executes the code between braces while a
condition is true. It uses the syntax for(initialization;
condition; increment). The first parameter establishes the initial values for the loop;
the second parameter is the condition to be checked, and the last parameter is
an instruction that determines how the initial values evolve in every cycle.

Listing 6-44: Creating a
loop with the for instruction

var
total = 0;

for (var f = 0; f < 5; f++) {

total += 10;

}

alert("The
total is: " + total);  // "The total is: 50"

In the code in Listing 6-44, we
declare a variable called f to control the loop and assign
the number 0 as its initial value. The
condition in this example checks whether the value of the variable f is less than 5. If this is true, the code between braces is
executed. After that, the JavaScript interpreter executes the last parameter of
the for instruction, which adds 1 to the current
value of f (f++), and then checks the
condition again (in every cycle f is incremented by 1). If the
condition is still true, the instructions are executed one more time. This
process continues until f reaches a value of 5, which makes the condition false (5 is not less than 5), and the
loop is interrupted.

Inside the for loop in Listing 6-44, we add the value 10 to the current
value of the total variable. Loops are frequently
used in this manner, to make the value of a variable evolve according to
previous results. For example, we can use a for loop to add all the values of an array.

Listing 6-45: Iterating
over the values of an array

var
total = 0;

var list = [23,
109, 2, 9];

for (var f = 0;
f < 4; f++) {

total += list[f];

}

alert("The
total is: " + total);  // "The total is: 143"

To read all the values of an array,
we have to create a loop that goes from the index of the initial value to a
value that matches the index of the last value in the array. In this case, the list array contains four elements, and therefore the
corresponding indexes go from 0 to 3. The loop reads the value at index 0, adds
it to the current value of the total variable, and then moves to
the next value in the array until the value of f is equal to 4 (there is no value at index 4). In the end, all the
values of the array are added to the total variable, and the result is
shown on the screen.

The Basics:
In
the example of Listing 6-45, we are able to set up the loop because we know the
number of values inside the array, but this is not always the case. Sometimes
you will not have this information during development, either because the array
is created when the page is loaded or with values inserted or selected by the
user. To work with dynamic arrays, JavaScript offers the length property.
The property returns the number of values in an array. We will study this property
and Array objects later in this chapter.

The for instruction is useful when we can determine certain
prerequisites, such as the initial values for the loop or how those values will
evolve. When this information is unclear, we can use the while instruction. The while instruction only requires the
declaration of the condition in parentheses and the code to be executed between
braces. The loop runs until the condition is false.

Listing 6-46: Using the while instruction

var
counter = 0;

while(counter < 100) {

counter++;

}

alert("The
value is: " + counter);  // "The value is: 100"

The example of Listing 6-46 is straightforward.
The instruction between braces is executed while the value of the counter variable is less than 100. This means that the loop will be executed
100 times (when the value of counter is 99, the instruction is executed one
more time and therefore the variable's final value is 100).

If the first evaluation of the
condition returns false (e.g., the value of counter is already greater than 99) the code between braces is
never executed. If we want the instructions to be executed at least once, regardless
of the result of the condition, we can use a different implementation of the while loop called do while. The do while instruction first executes the
instructions between braces and then checks the condition, which guarantees
that the instructions are going to be executed at least once. The syntax is similar;
we have to precede the braces with the do keyword and place the while keyword with the condition at the end.

Listing 6-47: Using the do while instruction

var
counter = 150;

do {

counter++;

} while(counter < 100);

alert("The
value is: " + counter);  // "The value is: 151"

In Listing 6-47, the value of the counter variable is already greater than 99, but because we are
using the do while loop, the instruction between
braces is executed once, and therefore the final value of counter is 151 (150 + 1 = 151).

Control Transfer Instructions

Loops sometimes must be interrupted, regardless
of the state of the condition. JavaScript offers multiple instructions to break
the execution of loops and conditionals. The following are the most frequently
used.

continue—This
instruction interrupts the current cycle and moves to the next. The system
ignores the rest of the instructions in the loop after this instruction is
executed.

break—This
instruction interrupts the loop. All the instructions and any pending cycles
are ignored after this instruction is executed.

The continue instruction is applied when we do not
want to execute the rest of the instructions between braces, but we want to
keep the loop running.

Listing 6-48: Jumping to the next cycle of
the loop

var list = [2,
4, 6, 8];

var total = 0;

for (var f = 0; f < 4;
f++) {

var number = list[f];

if (number == 6) {

continue;

}

total += number;

}

alert("The total is:
" + total);  // "The total is: 14"

The if instruction inside the for loop in Listing 6-48
compares the value of number with the value 6. If the
number currently produced by the loop is 6, the continue instruction is executed, the last
instruction inside the loop is ignored, and the loop moves on to the next value
in the list array. In consequence, all the values of
the array are added to the total variable except the number
6.

Unlike continue, the break instruction interrupts the loop completely, delegating
the control to the instruction after the loop.

Listing 6-49: Interrupting the loop

var list = [2,
4, 6, 8];

var total = 0;

for (var f = 0; f < 4;
f++) {

var number = list[f];

if (number == 6) {

break;

}

total += number;

}

alert("The total is:
" + total);  // "The total is: 6"

Again, the if instruction in Listing 6-49 compares the value of number with the value 6, but this time it executes the break instruction when a match is found. If the number
currently produced by the loop is 6, the break instruction is executed, and
the loop is over, no matter how many values are left in the array. In
consequence, only the values located before the number 6 are added to the value
of the total variable.

6.2 Functions

Functions are blocks of code identified
with a name. The difference between functions and the blocks of code used with
loops and conditionals discussed earlier is that there is no condition to
satisfy; the instructions inside a function are executed every time the
function is called. Functions are called (executed) by writing the name
followed by parentheses. This call may be performed from anywhere in the code and
every time necessary, which completely breaks the sequential processing of a
program. Once a function is called, the execution of the program continues with
the instructions inside the function and only returns back to the section of
the code that called the function when the execution of the function is over.

Declaring Functions

Functions are declared using the keyword function, the name followed by parentheses, and
the code between braces. To call a function (execute it), we have to declare
its name with a pair of parentheses at the end, as shown below.

Listing 6-50: Declaring
and executing functions

function
showMessage() {

alert("I
am a function");

}

showMessage();

Functions
are declared first and then executed. The code in Listing 6-50 declares a
function called showMessage() and then
calls it one time. As with variables, the JavaScript interpreter reads the
function, stores its content in memory, and assigns a reference to the name of
the function. When we call the function by its name, the interpreter checks the
reference and reads the function from memory. This allows us to call the
function as many times as we want, as in the following example.

Listing 6-51: Processing
data with functions

var
total = 5;

function calculateValues(){

total = total
* 2;

}

for(var f = 0; f < 10; f++){

calculateValues();

}

alert("The
total is: " + total);  // "The total is: 5120"

The code in Listing 6-51 combines
different instructions already studied. First, it declares a variable and
assigns the value 5 to it. Then, a function called
calculateValues() is declared (but not
executed). Next, a for instruction is used to create
a loop that will run as long as the value of the variable f is less than 10. The instruction inside the loop calls the calculateValues() function, so the function is executed in
every cycle. Every time the function is executed, the value of total is multiplied by 2, doubling the current value.

Scope

In JavaScript, the instructions that are
outside of a function are considered to be in a global namespace. This is the
space where we write the instructions until a function, or another data
structure is defined. Variables defined in the global namespace have global scope,
and therefore they can be used anywhere in the code, but those declared inside
functions have local scope, which means that they can only be used inside the function
in which they were declared. This is another advantage of functions; they are
special places where we can store information that will not be accessible from
other parts of the code. This segregation helps us avoid duplicates that may
lead to mistakes, such as overwriting the value of a variable when the old
value was still required by the application.

The
following example illustrates how the scopes are defined and what we should
expect when we access the variables of one scope from another.

Listing 6-52: Declaring
global and local variables

var
globalVariable = 5;

function myfunction(){

var localVariable =
"The value is ";

alert(localVariable + globalVariable);  // "The value is 5"

}

myfunction();

alert(localVariable);

The code in Listing 6-52 declares a
function called myfunction() and two variable, one in the global namespace called globalVariable and another inside the function called localVariable. When the function is executed, the code
concatenates the variables localVariable and globalVariable and shows the final string on the screen.
Because globalVariable is a global variable, it is
accessible from inside the function and therefore its value is added to the string.
But when we try to display the value of localVariable outside the function, an error
is returned (the popup window is not shown). This is because localVariable was defined inside the function and
therefore it is not accessible from the global namespace.

The Basics:
Browsers
report JavaScript errors in a hidden console. If you want to see the errors
produced by your code, you have to open this console from the options in the
browser's menu (Developer Tools in Google Chrome). We will learn more about
consoles and how to manage errors at the end of this chapter.

Because variables with different
scopes are considered to be different variables, two variables with the same
name, one defined in the global namespace and another inside a function, will
be considered two separate variables (they are assigned different spaces in
memory).

Listing 6-53: Declaring
two variables with the same name

var myvariable =
5;

function myfunction(){

var myvariable =
"This is a local variable";

alert(myvariable);

}

myfunction();

alert(myvariable);

In the code in Listing 6-53, we
declare two variables called myvariable, one in the global namespace
and another inside the function myfunction(). There are also two alert() functions in the code, one inside myfunction() and another in the global
namespace at the end of the code. Both alert() functions show the content of myvariable, but they reference different
variables. The variable inside the function is referencing a space in memory
that contains the string "This is a local variable", while the
variable in the global namespace is referencing a space in memory containing
the value 5.

The Basics:
Global
variables can also be created from functions. Omitting the var keyword when
a variable is declared inside a function will be enough to set that variable as
global.

Global variables are useful when
functions have to share common values, but because they are accessible from
every function in the code, there is always the possibility of accidentally overwriting
their values from somewhere else in the same code, or even from other codes
(every JavaScript code included in the document shares the same global
namespace). Therefore, using global variables from functions is not a good idea.
As an alternative, JavaScript allows us to send values to a function when the
function is called.

To receive a value, the function must
include a name between the parentheses. These names are called parameters.
When the function
is executed, these parameters are turned into variables that we can read inside
the function to access the values.

Listing 6-54: Sending a
value to a function

function
myfunction(value) {

alert(value);

}

myfunction(5);

In
Listing 6-54, we no longer use global variables. The value to be processed is
sent to the function when it is called and received by the function through its
parameter. When the function is called, the value between the parentheses of
the call (5) is assigned to the value variable created
by the function to receive this value, and this variable is read inside the function
to show its value on the screen.

The Basics: The names declared between parentheses
in the function to receive values are called parameters. On the other
hand, the values specified in the call are called attributes. In these
terms, we can say that a function’s call has attributes that are sent to the function
and received by its parameters.

The
advantage of using functions is that we can execute their instructions as many
times as we need, and because we can send different values in every call, the
result produced by each call will be different. The following example executes myfunction() two times,
but each time it sends a different value to be processed.

Listing 6-55: Calling the
function with different values

function
myfunction(value) {

alert(value);

}

myfunction(5);

myfunction(25);

The
interpreter executes the first call with the value 5 and, when the execution of
the function is over, it calls it again with the value 25. In consequence, two
popup windows are opened, one with the value 5 and another with the value 25.

In this and
the previous examples, we sent values to the function, but we can also send the
current value of a variable.

Listing 6-56: Sending the
value of a variable to a function

var
counter = 100;

function
myfunction(value) {

alert(value);

}

myfunction(counter);

This time we include the variable counter in the call instead of a number. The interpreter reads
this variable and sends the current value to the function. The rest of the
process is the same; the function receives the value, assigns it to the value variable, and shows it on the screen.

Functions can also receive multiple
values. All we have to do is to declare the values and the parameters separated
by commas.

Listing 6-57: Sending
multiple values to a function

var
counter = 100;

var items = 5;

function
myfunction(value1, value2) {

var total =
value1 + value2;

alert(total);
// 105

}

myfunction(counter, items);

In Listing 6-57, we add the values
received by the function and show the result on the screen, but there are times
when the result is required outside the function. To send values from the
function to the global namespace, JavaScript includes the return instruction. This instruction determines which value must
be returned to the code that called the function.

If we want to process the value
returned by a function, we have to assign that function to a variable. The
interpreter first executes the function and then assigns the value returned by
the function to the variable, as in the following example.

Listing 6-58: Returning
values from functions

var
counter = 100;

var items = 5;

function
myfunction(value1, value2) {

var total =
value1 + value2;

return total;

}

var result = myfunction(counter, items);

alert(result);

The code in Listing 6-58 defines the
same myfunction() function used before, but this time the value produced by the
function is not shown on the screen but returned with the return instruction. Back to the global namespace, the value
returned by the function (17) is assigned to the variable result, and the content of that variable is shown on the screen.

Notice that at the beginning of this
code we declare the variable myresult but do not assign any value to
it. This is perfectly acceptable and even recommended. It is good to declare
all the variables we are going to work with at the beginning, to avoid
confusion and be able to identify each one of them later from other parts of
the code.

The return instruction finishes the execution of the function. Any
instruction declared after a value is returned will not be executed. For this
reason, the return instruction is usually placed
at the end of the function, but this is not mandatory. We can return a value
anywhere in the code if we have conditions to satisfy. For example, the
following function returns the result of the addition of two values if the
total is greater than 100. Otherwise, it returns the value 0.

Listing 6-59: Returning
different values from a function

var
counter = 100;

var items = 5;

function
myfunction(value1, value2) {

var total =
value1 + value2;

if (total
> 100) {

return total;

} else {

return 0;

}

}

var result =
myfunction(counter, items);

alert(result);

Anonymous Functions

Another way
to declare a function is to use an anonymous function. Anonymous functions are functions
without a name or identifier (hence "anonymous"). They can be passed
as values to other functions or assigned to variables. When an anonymous
function is assigned to a variable, the name of the variable is the one we use
later to call the function, as in the following example.

Listing 6-60: Declaring
anonymous functions

var myfunction =
function(value) {

value = value * 2;

return value;

};

var total = 2;

for (var f = 0;
f < 10; f++) {

total =
myfunction(total);

}

alert("The
total is " + total);  // "The total is 2048"

In Listing 6-60, we declare an anonymous function
that receives a value, multiplies that value times 2, and returns the result.
Because the function is assigned to a variable, we can use the name of that
variable to call the function, so after the definition of the function, we
create a for loop that calls myfunction()
multiple times with the current value of the total variable. The instruction in the loop assigns the value returned by
the function back to the total variable, doubling its value in every
cycle.

Anonymous functions can be executed right away
adding parentheses at the end of their declaration. This is useful when we want
to assign the result of a complex operation to a variable. The function
performs the operation and returns the result. In this case, it is the value
returned by the function the one assigned to the variable and not the function
itself.

Listing 6-61: Executing
anonymous functions

var myvalue =
function(value) {

value = value * 2;

return value;

}(35);

alert("The value is
" + myvalue);  // "The value is 70"

The anonymous function in Listing 6-61 is defined
and executed as soon as the instruction is processed by the interpreter. The
function receives the value 35, multiplies that value times 2, and returns the
result, which is assigned to the myvalue variable.

The Basics:
Anonymous
functions are extremely useful in JavaScript because they allow us to construct
complicated programming patterns that are necessary to build professional
applications. We will see practical applications of these types of functions
and some of the patterns available in JavaScript in later chapters.

Standard Functions

Besides the functions we can create ourselves, we also
have access to functions predefined by JavaScript. These functions perform
processes that simplify complex tasks. The following are the most frequently
used.

isNaN(value)—This function returns true if the value
between parentheses is not a number.

parseInt(value)—This function turns a string with a number into an integer value
that we can process in arithmetic operations.

parseFloat(value)—This function turns a string with a number into a floating-point
value that we can process in arithmetic operations.

encodeURIComponent(value)—This function encodes a string. It is used to codify characters
in a string that might present a conflict when the string is included in a URL.

decodeURIComponent(value)—This function decodes a string.

Standard functions are global functions that we
can call from anywhere in the code. We have to call them as we do with any
other function, with the value we want to be processed between parentheses.

Listing 6-62: Checking
whether a value is a number or not

var
myvalue = "Hello";

if (isNaN(myvalue)) {

alert(myvalue
+ " is not a number");

} else {

alert(myvalue
+ " is a number");

}

The isNaN() function returns a Boolean value, so we
can use it to set a condition. The interpreter first calls the function and
then executes the blocks of code defined by the if else instructions depending on the result. In this case, the value of the
variable is a string of letters, so the isNaN() function returns true and the message "Hello is not a number" is shown on the
screen.

The isNaN() function returns false not only when the variable contains a number but also when it contains
a string with a number. This means that we cannot use the value in an
arithmetic operation because it could be a string and the process will not
produce the result we are expecting. To make sure that the value can be
included in an operation, we have to convert it to a numeric value. For this
purpose, JavaScript offers two functions: parseInt() for integers and parseFloat() for floating-point numbers.

Listing 6-63: Converting a
string into a number

var
myvalue = "32";

if
(isNaN(myvalue)) {

alert(myvalue
+ " is not a number");

} else {

var number = parseInt(myvalue);

number =
number * 10;

alert("The number is: " + number);  // "The
number is 320"

}

The code in Listing 6-63 checks whether the value
in the myvalue variable is a number or not, as we did
before, but this time the value is converted into a numeric value if a number
is found. After the value is extracted from the string, we use it to perform a
multiplication and then show the result on the screen.

Another useful standard function is encodeURIComponent(). With this function we can prepare a string to be
included in a URL. The problem with URLs is that they provide special meaning
to some characters, like ? or &, as we have seen in previous chapters (see Figure 2-43). Because users are not aware of these
restrictions, we have to codify the strings before to include them in a URL
every time they are provided by the user or come from an unreliable source.

Listing 6-64: Encoding a
string to include it in a URL

var
name = "John Doe";

var encodedName = encodeURIComponent(name);

var myURL =
"http://www.example.com/contact.html?name=" + encodedName;

alert(myURL);

The code in Listing 6-64 adds the value of the name variable to a URL. In this example we assume that the value of the
variable was defined by the user, so we encode it with the encodeURIComponent() function to make sure the final URL is valid. The
function analyzes the string and replaces every conflictive character with
hexadecimal numbers that represent the character preceded by the % character.
In this case, the only character that requires codification is the space
between the first and last names. The final URL is
http://www.example.com/contact.html?name=John%20Doe.

6.3 Objects

Objects are units of information capable of containing
other variables (called properties) as well as functions (called methods).
Because objects store values along with functions, they are like independent
programs that communicate with each other to perform common tasks.

The idea behind objects in computer programming
is to simulate the role of objects in real life. A real object has properties
and performs actions. For instance, a person has a name and an address, but it
can also walk and talk. The characteristics and functionality are part of the person
and is the person who defines how is going to walk and what is going to say. By
organizing our code in this manner, we can create independent processing units
that can perform tasks and have all the information they need to do it. For
instance, we can create an object that controls a button, shows its title and
performs a task when the button is pressed. Because all the information to
present and manage the button is stored inside the object, the rest of the code
does not need to know how to do it. As long as we are aware of the methods
provided by the object and the values returned, the code inside the object may
be updated or entirely replaced without affecting the rest of the program.

Being able to create independent processing
units, duplicate those units as many times as necessary, and modify their
values to adapt them to the circumstances, are the main advantages introduced
by objects and the reason why Object-Oriented Programming has become the most
popular programming paradigm available. JavaScript was created around the
concept of objects, and therefore understanding objects is necessary to
understand the language and its capabilities.

Declaring Objects

There are different ways of declaring objects in JavaScript;
the simplest is to use literal notation. The object is declared as any other
variable using the var keyword and the properties and methods
that define the object are declared between braces using a colon after the name
and commas to separate each declaration.

Listing 6-65: Creating
objects

var myobject
= {

name:
"John",

age: 30

};

In Listing 6-65, we declare the object myobject with two properties: name and age. The value of the property name is "John" and the value of the property age is 30.

Unlike variables, we cannot access the
properties' values using only their names; we also have to specify the object
they belong to using dot notation or square brackets.

Listing 6-66: Accessing
properties

var myobject
= {

name:
"John",

age: 30

};

var message = "My name is " + myobject.name +
"\r\n";

message += "I'm " + myobject["age"] +
" years old";

alert(message);

In
Listing 6-66, we use both notations to access the values of the object’s
properties and create the message we are going to show on the screen. The use
of any notation is irrelevant, except in some special circumstances. For
instance, when we need to access the property through the value of a variable,
we have to use square brackets.

Listing 6-67: Accessing
properties using variables

var
propertyName = "name";

var myobject =
{

name:
"John",

age: 30

};

alert(myobject[propertyName]);
// "John"

In
Listing 6-67, we couldn’t have accessed the property with dot notation (myobject.propertyName) because the
interpreter would have tried to find a property called propertyName that does
not exist. Using square brackets, the variable is resolved first, and then the
object is accessed with its value ("name") instead of its name.

It is
also necessary to access a property using square brackets when its name is
considered invalid for a variable (it includes invalid characters, such as a
space, or begins with a number). In the following example, the object includes
a property declared with a string as a name. We are allowed to declare names of
properties as strings, but because this particular string contains a space, the
code myobject.my age will return
an error, so we have to use square brackets to access the property.

Listing 6-68: Accessing
properties with invalid names

var
myvariable = "name";

var myobject =
{

name:
"John",

'my age': 30

};

alert(myobject['my age']);
// 30

Besides reading
the values of properties, we can also set new properties or modify them using
both notations. In the next example, we change the value of the name property and
add a new property to our object called job.

Listing 6-69: Updating
values and adding new properties to the object

var myobject
= {

name:
"John",

age: 30

};

myobject.name = "Martin";

myobject.job = "Programmer";

alert(myobject.name
+ " " + myobject.age + " " + myobject.job);

Objects can also contain other objects. In the
following example, we assign an object to the property of another object.

Listing 6-70: Creating
objects inside objects

var myobject
= {

name:
"John",

age: 30,

bike: {

model: "Susuki",

year: 1981

}

};

alert(myobject.name + " owns a " +
myobject.bike.model);

The myobject object in the code in Listing 6-70
includes a property called bike which value is another object with the
properties model and year. If we want to access these properties, we
have to indicate the name of the object they belong to (bike) and the name of the object that object belongs to (myobject). The names are concatenated with dot notation in the
order they were included in the hierarchy. For example, the model property is inside the bike property that in turn is inside the myobject property, so if we want to read the value of the model property, we have to write myobject.bike.model.

Methods

As we mentioned before, objects can also include
functions. Functions inside objects are called methods. Methods have the
same syntax as properties; they require a colon after the name and commas to
separate each declaration, but instead of values they are assigned anonymous
functions.

Listing 6-71: Declaring
and executing methods

var myobject
= {

name:
"John",

age: 30,

showinfo: function() {

var message = "Name: " + myobject.name +
"\r\n";

message += "Age: " + myobject.age;

alert(message);

},

changename: function(newname) {

myobject.name = newname;

}

};

myobject.showinfo();  //
"Name: John Age: 30"

myobject.changename("Jonathan");

myobject.showinfo();  //
"Name: Jonathan Age: 30"

In this example, we add two methods to the
object: showinfo()
and changename().
The showinfo()
method shows a popup window with the values of the name and age properties, and the changename() method assigns the value received by its
parameter to the name property. These are two independent
methods that work on the same properties, one reads the current values, and the
other assigns new values. To execute the methods, we use the dot notation and
parentheses after the name, as we do with functions.

Like functions, methods can also return values.
In the following example, we modify the changename()
method to return the old name after replacing it by the new one.

Listing 6-72: Returning
values from methods

var myobject
= {

name:
"John",

age: 30,

showinfo:
function() {

var message
= "Name: " + myobject.name + "\r\n";

message +=
"Age: " + myobject.age;

alert(message);

},

changename: function(newname) {

var oldName = myobject.name;

myobject.name = newname;

return oldName;

}

};

var old = myobject.changename("Jonathan");

alert("Old
name was: " + old);  // "Old name was: John"

The new changename()
method stores the current value of the name property in a temporary variable called oldName to be able to return the previous name after the new one is assigned
to the property.

The this Keyword

In the last examples, we mentioned the name of the
object every time we wanted to modify its properties from the methods. Although
this works, it is not a good practice. Because the name of the object is
determined by the name of the variable to which the object is assigned, it may
change. Also, as we will see later, JavaScript allows us to create multiple
objects from the same definition or create new objects from other objects,
which produce different objects that share the same definition. To make sure
that we are referencing the object we are working with from the methods, JavaScript
includes the this keyword. The this keyword is used instead of the object's name to reference the object
the instruction belongs to. The following code reproduces the previous example,
but this time we use the this keyword instead of the object's name to
reference the properties. The results are the same as before.

Listing 6-73: Referencing
the object's properties with the this keyword

var myobject
= {

name:
"John",

age: 30,

showinfo:
function() {

var message = "Name: " + this.name +
"\r\n";

message += "Age: " + this.age;

alert(message);

},

changename:
function(newname) {

var oldName = this.name;

this.name = newname;

return
oldName;

}

};

var old =
myobject.changename("Jonathan");

alert("Old
name was: " + old);  // "Old name was: John"

IMPORTANT:
Every time you want to
access properties and methods from inside the object, you should use the this keyword to reference the object,
but if you try to do the same from outside the object, you will be referencing the
JavaScript's global object. The this keyword references the object in
which the instruction is being executed. This is why, in the code in Listing
6-73 we only use the this keyword inside the methods of the myobject object, but the instructions in the
global namespace use the name of the object instead.

Constructors

With literal notation, we can create single objects,
but if we want to create copies of that object with the same properties and
methods, we have to use constructors. A constructor is an anonymous function
that defines a new object and returns it, creating copies of the object (also
called instances), each with its own properties, methods, and values.

Listing 6-74: Using a
constructor to create an object

var
constructor = function() {

var obj = {

name:
"John",

age: 30,

showname:
function() {

alert(this.name);

},

changename:
function(newname) {

this.name
= newname;

}

};

return obj;

};

var employee = constructor();

employee.showname();  //
"John"

In Listing 6-74, an anonymous function is
assigned to the constructor variable. Inside the function, an object
is created and returned by the return instruction. Finally, the object returned
by the function is stored in the employee variable and its showname() method is executed.

Using constructors, we can create new objects
dynamically. For instance, we can set initial values for the properties by
sending the values to the function when the object is constructed.

Listing 6-75: Sending
initial values to a constructor

var
constructor = function(initialname) {

var obj = {

name:
initialname,

age: 30,

showname:
function() {

alert(this.name);

},

changename:
function(newname) {

this.name
= newname;

}

};

return obj;

};

var employee = constructor("John");

employee.showname();  //
"John"

The purpose of a constructor is to work as an
objects factory. The following example illustrates how to create multiple
objects from the same constructor.

Listing 6-76: Using
constructors to create multiple objects

var
constructor = function(initialname) {

var obj = {

name:
initialname,

age: 30,

showname:
function() {

alert(this.name);

},

changename:
function(newname) {

this.name
= newname;

}

};

return obj;

};

var employee1 = constructor("John");

var employee2 = constructor("Robert");

var employee3 = constructor("Arthur");

alert(employee1.name + ", " + employee2.name +
", " + employee3.name);

Although the objects created from a constructor
share the same properties and methods, they are stored in different places in memory,
and therefore they store different values. Every time the constructor function is called, a new object is created, and
therefore we can assign different values to each one of them. In Listing 6-76,
we have created three objects: employee1, employee2, and employee3, and the values "John", "Robert",
and "Arthur" were assigned to their name properties. When we read the name property of any of these objects, we get
different values depending on the object the property belongs to (The alert() function at the end of the code shows the message "John, Robert,
Arthur").

The Basics:
Every
new object created from a constructor is stored in a different space in memory
and therefore has its own properties and values, but you can also assign the
same object to multiple variables. If you want to make sure that a variable is
not referencing the same object, you can compare the variables with the special
operators === and !==. JavaScript
also includes the is() method inside a global
object called Object that can be
used to know if two variables are referencing the same object (e.g., Object.is(object1,
object2)).

An advantage of using constructors to create
objects is the possibility to define private properties and methods. All the
objects we have created have public properties and methods, which means that they
can be accessed and modified from outside the objects. To avoid this situation
and make the properties and methods available only for the object that created
them, we have to define them as private using a technique called closure.

As we explained before, variables
created in the global namespace are accessible from anywhere in the code, while
variables created inside functions are only accessible from the functions in
which they were created. What we did not mention is that functions, and
therefore methods, always keep a link to the space where they were created, and
therefore they are always bound to the variables declared in that space. When
we return an object from a constructor, its methods can still access the
function’s variables, even when they are no longer in the same scope, and
therefore these variables become accessible only to the object.

Listing 6-77: Defining
private properties

var
constructor = function() {

var name = "John";

var age = 30;

var obj = {

showname:
function() {

alert(name);

},

changename:
function(newname) {

name =
newname;

}

};

return obj;

};

var employee =
constructor();

employee.showname();  //
"John"

The code in Listing 6-77 is exactly the same from
the previous example except that instead of declaring name and age as properties of the object we declare
them as variables of the function that is returning the object. The object
returned remembers these variables and is the only one that has access to them.
There is no way to modify the value of those variables from other instructions
in the code other than to use the methods of the object returned by the
function (in this case, showname()
and changename()).
This is the reason why the procedure is called closure. The function is closed, and its space is
no longer accessible, but we keep an element bonded to it (an object in our
example).

Notice that the methods in this example access
the variables without the this keyword. This is because the values are
now stored in variables defined by the function and not in properties defined
by the object.

The new Operator

With literal notation and constructors, we have all we
need to create single objects or multiple objects from the same definition. But
to preserve consistency with
other Object-Oriented Programming languages, JavaScript offers a third option.
This is a special kind of constructor that works with an operator called new. The object is defined by a function, and then the function is called
with the new operator to create an object from that definition.

Listing 6-78: Creating
objects with the new operator

function
Myobject(){

this.name =
"John";

this.age =
30;

this.showname
= function(){

alert(this.name);

};

this.changename = function(newname){

this.name =
newname;

};

}

var employee = new Myobject();

employee.showname();  //
"John"

These types of constructors require the object's
properties and methods to be identified with the this keyword, but except for the use of this keyword, the definition of these
constructors and those studied before is the same. As with other constructors,
we can also provide initialization values.

Listing 6-79: Defining
initial values for the object

function
Myobject(initialname, initialage){

this.name =
initialname;

this.age =
initialage;

this.showname
= function(){

alert(this.name);

};

this.changename
= function(newname){

this.name =
newname;

};

}

var employee = new Myobject("Robert", 55);

employee.showname();  //
"Robert"

Inheritance

An important characteristic of objects is that we can
create them from other objects. When objects are created from other objects,
they inherit their properties and methods. Inheritance in JavaScript (how
objects get the same properties and methods from other objects) is done through
prototypes. An object does not inherit properties and methods directly from
another object; it does it from the object's prototype. Working with prototypes
can be very confusing, but JavaScript includes the create() method to simplify our job. This method is part of a
global object predefined by JavaScript called Object. It uses an existing object as prototype of a new one, so we can
create objects from other objects without worrying about how their properties
and methods are shared among them.

Listing 6-80: Creating
objects from other objects

var myobject
= {

name:
"John",

age: 30,

showname:
function(){

alert(this.name);

},

changename:
function(newname){

this.name =
newname;

}

};

var employee = Object.create(myobject);

employee.changename('Robert');

employee.showname();  //
"Robert"

myobject.showname();  //
"John"

The code in Listing
6-80 creates the myobject object using literal notation
and then calls the create() method to create a new
object based on the myobject object. The create() method only requires the name of
the object that is going to be the prototype of the new one, and it returns a
new object that we can assign to a variable for further use. In this example,
the new object is first created with Object.create() and then assigned to the
variable employee. Once we have the new object, we
can update its values. Using the changename() method, we change the
name of employee to "Robert" and then show
the value of the name property of each object on the
screen.

This code creates two independent objects, myobject and employee, with their own properties, methods and
values, but linked through a prototype chain. The new employee object is not just a copy; it is an object that keeps
a link to the myobject's prototype. When changes are introduced to this prototype, the objects
down the chain inherit those changes.

Listing 6-81: Adding a new
method to the prototype

var myobject
= {

name:
"John",

age: 30,

showname:
function(){

alert(this.name);

},

changename:
function(newname){

this.name =
newname;

}

};

var employee =
Object.create(myobject);

employee.age =
24;

myobject.showage = function(){

alert(this.age);

};

employee.showage();  // 24

In Listing 6-81, a method called showage() is added to the prototype object (myobject). Because of the prototype chain, this new method is
now accessible from the other instances as well. When we call the showage() method of employee at the end of the code, the interpreter first looks
for the method in employee and continues searching up the prototype
chain until it finds it in myobject. When the method is finally found, it
shows the value 24 on the screen. This is because, even when the showage() method belongs to myobject, the this keyword in this method points to the
object we are working with (employee). Because of the prototype chain, the showage() method can be invoked from employee, and because of the this keyword, the value of the age property shown on the screen is the one we
assigned before to employee.

The create() method is as simple as it is powerful. It
takes an object and turns it into the prototype of a new one. This allows us to
build a chain of objects where each one of them inherits properties and methods
from its predecessor.

Listing 6-82: Testing the
prototype chain

var myobject
= {

name:
"John",

age: 30,

showname:
function(){

alert(this.name);

},

changename:
function(newname){

this.name =
newname;

}

};

var employee1 = Object.create(myobject);

var employee2 = Object.create(employee1);

var employee3 = Object.create(employee2);

employee2.showage
= function(){

alert(this.age);

};

employee3.age =
24;

employee3.showage();  //
24

In Listing 6-82, the existence of the prototype
chain is demonstrated adding the method showage()
to employee2. Now, employee2 and employee3 (as well as any object created later based
on these two objects) have access to this method, but because inheritance works
down the chain, not up, the method is not available for the employee1 object.

IMPORTANT: If
you get used to working with the create() method, you will not
need to access and modify the object's prototypes. In fact, the create() method should be the way
we work with JavaScript, creating objects without getting into the complexity
of their prototypal nature. However, prototypes are the essence of the language,
and in some circumstances, we cannot avoid them. To
learn more about prototypes and how to work with objects, visit our website and
follow the links for this chapter.

6.4 Standard Objects

Objects are like wrappers of code, and JavaScript
takes advantage of this feature extensively. In fact, almost everything in JavaScript
is an object. For instance, the numbers and strings we assign to variables are
turned into objects by the JavaScript interpreter in the background. Every time we assign a new
value to a variable, we are assigning an object that contains that value.

Because the values we store in
variables are of different types, there are different kinds of objects
available to represent them. For example, if the value is a string, the object
created to store it is of type String. When we assign a string to
a variable, JavaScript creates a String object, stores the string
inside the object, and assigns that object to the variable.

If we want, we can create JavaScript
objects directly using their constructors. There are different constructors available depending
on the type of value we want to store. The
following are the most frequently used.

Number(value)—This constructor creates objects to store numerical values. It
can take a number and also a string with a number. If the value specified by
the attribute cannot be converted into a number, the constructor returns the special
value NaN (Not a Number).

String(value)—This constructor creates objects to store strings of characters.
It can take a string or any value that can be converted into a string, such as
a number.

Boolean(value)—This constructor creates objects to store Boolean values. It can
take the values true and false. If other
values are provided, they are converted into a Boolean value. If the value is
omitted or is equal to 0, NaN, null, undefined, or an empty
string, the value stored in the object is false. Otherwise, it is true.

Array(values)—This constructor creates objects to store arrays. If multiple
values are provided, the constructor creates an array with those values, but if
only one value is provided, and that value is an integer, the constructor
creates an array with the number of elements indicated by the integer, and the
values stored at each index are undefined.

These constructors work with the new operator to create new objects. The following example stores a number.

Listing 6-83: Creating
numbers with a constructor

var value = new Number(5);

alert(value);  //
5

The advantage of using constructors is that they
can create the value from values of different types. For example, we can get a
number from a string.

Listing 6-84: Creating
numbers from strings

var value = new
Number("5");

alert(value);
// 5

The string provided to the Number() constructor in Listing 6-84 is converted into a
numeric value by the constructor and stored in a Number object. Because JavaScript takes care of converting these objects into
primitive values and vice versa, we can perform arithmetic operations on the
value stored in the object, as we do with any other numeric value.

Listing 6-85: Performing
arithmetic operations with objects

var value =
new Number("5");

var total = value * 35;

alert(total);
// 175

The Array() constructor operates in a different way.
If we provide multiple values, the array is created as if we had declared it
with square brackets.

Listing 6-86: Creating an
array with a constructor

var list = new
Array(12, 35, 57, 8);

alert(list);
// 12,35,57,8

On the other hand, if we only provide one value
and that value is an integer, the constructor uses that number to determine the
length of the array and creates an array of that size with undefined values.

Listing 6-87: Creating an
empty array with a constructor

var list = new
Array(2);

alert(list[0] + " -
" + list[1]);  // undefined - undefined

The Basics:
Whether
to assign values directly or use these constructors is up to you. JavaScript
can tell the difference between a value and an object, but it is usually
irrelevant, and most of the time both types of values operate the same way.

String Objects

Converting values into objects allows the language to
provide all the functionality necessary to manipulate the values within the
objects. The following are the most frequently used properties and methods
included in String objects.

length—This
property returns an integer representing the number of characters in the string.

toLowerCase()—This method
converts the characters in the string to lowercase letters.

toUpperCase()—This method
converts the characters in the string to uppercase letters.

trim()—This method
removes whitespace characters, such as spaces or tabs, on both sides of the string.
JavaScript also includes the methods trimLeft() and trimRight() to clean the
string on only one side.

substr(start, length)—This method returns a new string with
characters from the original. The start attribute indicates the position
of the first character to read, and the length attribute
determines how many characters we want to include. If the length is not
specified, the method returns all the characters to the end of the string.

substring(start, end)—This method returns a new string with characters from the
original. The start and end attributes are integer values that
determine the position of the first and last characters to include.

split(separator, limit)—This method splits the string into parts
and returns an array with the substrings. The separator attribute
indicates the character at which the string is going to be cut, and the limit
attribute is an integer that determines the maximum number of cuts. If no limit
is specified, the string is cut every time the separator is found.

startsWith(value)—This method returns true if the string
specified by the value attribute is found at the beginning of the string.

endsWith(value)—This method returns true if the string
specified by the value attribute is found at the end of the string.

includes(search, position)—This method searches for the value of the search
attribute in the string and returns true or false according to
the result. The search attribute is the string we want to search, and
the position attribute determines the index at which we want to begin
the search. If the position attribute is not specified, the search
starts from the beginning of the string.

indexOf(value, position)—This method returns the index at which the
string specified by the value attribute is found for the first time. The
position attribute determines the index at which we want to begin the
search. If the position attribute is not specified, the search starts
from the beginning of the string. It returns the value -1 if no match is found.

lastIndexOf(value, position)—This method returns the index at which the
string specified by the value attribute is found for the first time.
Unlike indexOf(), this method performs
the search backward, from the end of the string. The position attribute
determines the index at which we want to begin the search. If the position
attribute is not specified, the search starts from the end of the string. It
returns the value -1 if no match is found.

replace(expression, replacement)—This method replaces
the part of the string that matches the value of the expression
attribute with the string specified by the replacement attribute. The expression
attribute can be specified as the string we want to replace or as a Regular
Expression to search for a string with a particular format.

Strings are stored as arrays of characters, so we
can access each character using square brackets, as we do with any other array.
JavaScript includes the length property to count the number of characters
in a string.

Listing 6-88: Counting the
number of characters in a string

var text =
"Hello World";

var message = "The text is " + text.length + "
characters long";

alert(message);
// "The text is 11 characters long"

Because strings are arrays of characters, we can
iterate through the characters with a loop. In the following example, we add a
space character between letters.

Listing 6-89: Iterating through the characters of a
string

var
text = "Hello World";

var message =
"";

for (var f = 0;
f < text.length; f++) {

message += text[f] + " ";

}

alert(message);
// "H e l l o   W o r l d "

The length property returns the number of characters
in the string, but the indexes start counting from 0, so we have to create a
loop that goes from 0 to the previous value of the length property to get all the indexes of the characters in the string. Using
these indexes, the for loop in Listing 6-89 reads every character
using square brackets and adds them to the current value of the message variable along with a space character. In consequence, we get a string
with all the characters in the original string separated by a space.

This example adds a space character after every
character in the string, which means that the final string ends with a space. String objects include the trim() method to remove these unwanted space
characters.

Listing 6-90: Removing
space characters

var
text = "Hello World";

var message =
"";

for (var f = 0;
f < text.length; f++) {

message +=
text[f] + " ";

}

message = message.trim();

alert(message);
// "H e l l o   W o r l d"

The ability to access each character of a string
allows us to achieve more interesting effects. We just have to detect the
position of the character we want to manipulate and then perform the changes we
want. The following example adds dots between the letters of each word, but not
between the words.

Listing 6-91: Processing a
string

var
text = "Hello World";

var message =
"";

var previous =
"";

for (var f = 0;
f < text.length; f++) {

if (message
!= "") {

previous =
text[f - 1];

if
(previous != " " && text[f] != " ") {

message
+= ".";

}

}

message +=
text[f];

}

alert(message);
// "H.e.l.l.o W.o.r.l.d"

The code in Listing 6-91 checks whether the
character we are currently reading and the one we have read in the previous
cycle are not space characters before adding a dot "." to the current
value of the message variable. This way, the dots are inserted
only between letters and not at the end or beginning of the words.

Because of the complexity of some of the
processes performed on strings, JavaScript includes several methods to simplify
our work. For instance, we can replace all the characters in a string with
uppercase letters by calling the toUpperCase() method.

Listing 6-92: Converting a
string to uppercase

var
text = "Hello World";

var message = text.toUpperCase();

alert(message);
// "HELLO WORLD"

Sometimes we do not need to work with the whole
text but rather with a few words or characters. String objects offer the substr()
and substring() methods to copy a piece of text from a string. The substr() method copies the set of characters that
begins at the index specified by the first attribute. A second attribute can
also be provided to determine how many characters we want to include from the
initial position.

Listing 6-93: Copying a
set of characters

var
text = "Hello World";

var word = text.substr(0, 5);

alert(word);
// "Hello"

The substr()
method in Listing 6-93 copies a total of 5 characters starting with the
character at index 0. In return, we get the string "Hello". If we do
not specify the number of characters to include, the method returns characters
until it reaches the end of the string.

Listing 6-94: Copying all
the characters from a specific index to the end of the string

var
text = "Hello World";

var word = text.substr(6);

alert(word);
// "World"

The substr()
method can also take negative values. When a negative index is specified, the
method counts backward from the end of the string. The following code copies
the same characters as in the previous example.

Listing 6-95: Referencing
characters with negative indexes

var
text = "Hello World";

var word = text.substr(-5);

alert(word);
// "World"

The substring() method works slightly different than the substr() method. This method takes two values to
determine the first and last characters we want to copy, but it does not
include the last character.

Listing 6-96: Copying
characters between two indexes

var
text = "Hello World";

var word = text.substring(6, 8);

alert(word);
// "Wo"

The substring() method in Listing 6-96 copies the
characters from the index 6 to 7 of the string (the character at the last index
is not included). These are the positions where the characters "Wo"
are located and that is the string we get in return.

If we need to extract the words in a string, we
can use the split() method. This method cuts the string into
pieces and returns an array with the values. The method requires a value with
the character we want to use as a separator, so if we use a space character, we
can split the string into words.

Listing 6-97: Getting
words from a string

var
text = "Hello World";

var words = text.split(" ");

alert(words[0]
+ " / " + words[1]);  // "Hello / World"

The split() method in Listing 6-97 creates two strings
with the words "Hello" and "World" and returns an array
with these values, so we can read them as we do with the values of any other array.

Sometimes we do not know where the characters we
want to modify are located or if the string even contains those characters.
There are several methods available in String objects to search for a string. What we
use depends on what we want to achieve. For example, the startsWith() and endsWith() methods search for a string at the
beginning and end of the string and return true if the string is found.

Listing 6-98: Searching
for a string at the beginning of another string

var
text = "Hello World";

if (text.startsWith("He")) {

alert("The text begins with 'He'");

}

Because these methods return Boolean values, we
can use them as the condition of an if instruction. In the code in Listing 6-98,
we search for the string "He" at the beginning of the text variable and show a message in case of success. In this example, the
string is found, the method returns true, and therefore the message is shown on the
screen.

If the text we are looking for can be found in
any part of the string, not only at the beginning or the end, we can use the includes() method. Like the previous methods, the includes() method searches for a string and returns true in case of success, but the search is performed in the whole string.

Listing 6-99: Searching
for a string inside another string

var
text = "Hello World";

if (text.includes("ll")) {

alert("The text includes double L");

}

So far, we have checked whether one or more
characters are found in a string, but sometimes we need to know where those
characters were found. There are two methods we can use for this purpose: indexOf() and lastIndexOf(). Both methods return the index where the
first match is found, but the indexOf() method searches from the beginning of the string
and the lastIndexOf() method does it from the end.

Listing
6-100: Finding the location of a string inside another string

var
text = "Hello World";

var index = text.indexOf("World");

alert("The
word is at index " + index);  // "The word is at index
6"

Knowing the positions of the characters we are
looking for, we could program a small piece of code that replaces those
characters with a different set, but this is not necessary. There is a method
included in String objects called replace() specifically designed for this purpose. This is a
complex method that may process multiple values and work with Regular
Expressions to perform the search, but we can also use it with simple strings.
The following example replaces the word "World" with the word "Planet".

Listing
6-101: Replacing a string with another string

var
text = "Hello World";

var newText = text.replace("World",
"Planet");

alert(newText);
// "Hello Planet"

Array Objects

As we already mentioned, arrays are also objects in JavaScript,
and they include their own properties and methods to manipulate their values.
The following are the most frequently used.

length—This
property returns an integer representing the number of values in the array.

push(values)—This method adds one or more values at the end of the array and
returns the array's new length. There is a counterpart called unshift(), which adds
the values at the beginning of the array.

pop()—This method
removes the last value in the array and returns it. There is a counterpart called
shift(), which
removes the first value in the array.

concat(array)—This method concatenates the array with the array specified by
the attribute and returns a new array with the result. The original arrays are
not modified.

splice(index, remove, values)—This method adds and
removes values from an array and returns a new array with the elements removed
if any. The index attribute is the index at which we are going to
introduce the modifications, the remove attribute is the number of
values we want to remove from the index, and the values attribute is the
list of values separated by commas that we want to add to the array from the
index. If we only want to add values, the remove attribute may be
specified as 0, and if we only want to remove values, we can ignore the third
attribute.

slice(start, end)—This method copies the values at the positions indicated by the
attributes into a new array. The start and end attributes
indicate the indexes of the first and last value to copy. The last value is not
included in the new array.

indexOf(value, position)—This method returns the index at which the
value specified by the value attribute is found for the first time. The position
attribute determines the index at which we want to begin the search. If the position
attribute is not specified, the search starts from the beginning of the array.
It returns the value -1 if no match is found.

lastIndexOf(value, position)—This method returns the index at which the
value specified by the value attribute is found for the first time.
Unlike indexOf(), this method performs
the search backward, from the end of the array. The position attribute
determines the index at which we want to begin the search. If the position
attribute is not specified, the search starts from the end of the array. It
returns the value -1 if no match is found.

filter(function)—This method sends the values of the array to a function one by
one and returns a new array with all the values approved by the function. The function
attribute is a reference to a function or an anonymous function in charge of
validating the values. The function receives three values: the value to be
checked, its index, and a reference to the array. After processing the value,
the function must return a Boolean value indicating if it is valid or not.

every(function)—This method sends the values of the array to a function one by
one and returns true when all the values are
approved by the function. The function attribute is a reference to a
function or an anonymous function in charge of checking the values. The
function receives three values: the value to be checked, its index, and a
reference to the array. After processing the value, the function must return a
Boolean value indicating if it is valid or not. There is a similar method
available called some() that returns
true if at least
one value is approved by the function.

join(separator)—This method creates a string with all the values in the array.
The separator attribute specifies the character or string of characters
we want to include between values.

reverse()—This method
reverses the order of the values in the array.

sort(function)—This method sorts the values in the array. The function
attribute is a reference to a function or an anonymous function in charge of
comparing the values. The function receives two values from the array and must
return a Boolean value indicating their order. If the attribute is not
specified, the method sorts the elements alphabetically in ascending order.

map(function)—This method sends the values of the array to a function one by
one and creates a new array with the values returned by the function. The function attribute is
a reference to a function or an anonymous function in charge of processing the
values. The function receives three values: the value to be processed, its
index, and a reference to the array.

As well as String objects, Array objects offer the length property to get the number of values in the
array. The implementation is the same; we have to read the property on the
array to get the value in return.

Listing
6-102: Getting the number of values in the array

var
list = [12, 5, 80, 34];

alert(list.length);  // 4

Using this property, we can iterate through the
array with a for loop, as we did with strings before (see Listing 6-89). The value returned by the property is
used to define the condition of the loop.

Listing
6-103: Iterating through the array

var
list = [12, 5, 80, 34];

var total = 0;

for (var f = 0; f < list.length; f++) {

total += list[f];

}

alert("The
total is: " + total);  // "The total is: 131"

In the code in Listing 6-103, the value returned
by the property is 4, so the loop goes from 0 to 3. Using these values inside
the loop, we can read the array's values and process them.

Although we can iterate through the array to read
and process the values one by one, Array objects offer other methods to access the
values. For example, if we want to process only some of the values in the
array, we can get a copy with the slice() method.

Listing
6-104: Creating an array from the values of another array

var
list = [12, 5, 80, 34];

var newList = list.slice(1, 3);

alert(newList);
// 5,80

The slice() method returns a new array with the values
between the index specified by the first attribute and the one before the index
specified by the second attribute. In Listing 6-104, the method accesses the
values in the indexes 1 and 2, returning the numbers 5 and 80.

If we want to examine the values before they are
included in the new array, we have to use a filter. For this purpose, Array objects offer the filter() method. This method sends each value to a
function and includes the value in the new array only when the function returns
true. In the following example, we return true when the value is less than or equal to 50. As a result, the new array
contains all the values in the original array except the value 80.

Listing
6-105: Filtering the values of an array

var
list = [12, 5, 80, 34];

var newList = list.filter(function(value) {

if (value <= 50) {

return true;

} else {

return false;

}

});

alert(newList);
// 12, 5, 34

In the function provided to the filter() method in Listing 6-105, we compare the value from
the array against the number 50 and return true or false depending on the condition, but conditions
already return a Boolean value when they are evaluated, so we can return the
condition itself and simplify the code.

Listing
6-106: Returning a condition to filter the elements

var list =
[12, 5, 80, 34];

var newList =
list.filter(function(value) {

return value <= 50;

});

alert(newList);
// 12, 5, 34

Similar to filter() are the methods every() and some(). These methods check the values with a
function, but instead of returning an array with the values validated by the
function, they return the values true or false. The every() method returns true if all the values are validated, and the some() method returns true if at least one of the values is
validated. The following example uses the every() function to check that all the values in
the array are less than or equal to 100.

Listing
6-107: Checking the values of an array

var
list = [12, 5, 80, 34];

var valid = list.every(function(value) {

return value <= 100;

});

if (valid) {

alert("The values are not greater than 100");

}

If all we want is to put all the values in a
string to show them to the user, we can call the join() method. This method creates a string with the array's values separated
by a character or a string of characters. The following example creates a
string with a hyphen between the values.

Listing
6-108: Creating a string with the values of an array

var
list = [12, 5, 80, 34];

var message = list.join("-");

alert(message);
// "12-5-80-34"

Another way to access the values of an array is
with the indexOf() and lastIndexOf() methods. These methods search for a value
and return the index of the first match found. The indexOf() method starts searching from the beginning of the
array and the lastIndexOf() method starts searching from the end.

Listing
6-109: Getting the index of a value in an array

var
list = [12, 5, 80, 34, 5];

var index = list.indexOf(5);

alert("The
value " + list[index] + " is at index " + index);

The code in Listing 6-109 looks for the value 5
in the array and returns the index 1. This is because the method only searches
for the first match. If we want to find all the values that match the value we
are looking for, we have to create a loop to perform multiple searches, as in
the following example.

Listing
6-110: Searching for multiple values in an array

var
list = [12, 5, 80, 34, 5];

var search = 5;

var last = 0;

var counter =
0;

while (last >= 0) {

var last =
list.indexOf(5, last);

if (last != -1) {

last++;

counter++;

}

}

alert("There
is a total of " + counter + " values " + search);

The indexOf() method can take a second attribute that
specifies the location at which we want to begin the search. Using this
attribute, we can tell the method not to look at locations where a value was
already found. In Listing 6-110, we use the last variable for this purpose. This variable
stores the index of the last value found by the indexOf() method. At first, the variable is initialized with
the value 0, which means that the method will begin the search from the index 0
of the array, but as soon as a search is performed, the variable is updated
with the index returned by the indexOf() method, moving the starting point for the
next search to a new location. The loop keeps going until the indexOf() method returns -1 (no matches are found).

Besides establishing the new location with the last variable, the loop also increments the value of the counter variable to keep track of the number of matches found. After the
process is over, the code shows a message on the screen to communicate this
information ("There is a total of 2 values 5").

Up to this point, we have been working with the
same original array, but arrays can be extended or reduced. Adding values to an
array is as simple as calling the push() or unshift() methods. The push() method adds the value at the end of the array, and the unshift() method adds it at the beginning.

Listing
6-111: Adding values to an array

var
list = [12, 5, 80, 34];

list.push(100);

alert(list);
// 12,5,80,34,100

To add multiple values, we have to specify them
separated by commas.

Listing
6-112: Adding multiple values to an array

var
list = [12, 5, 80, 34];

list.push(100, 200, 300);

alert(list);
// 12,5,80,34,100,200,300

Another way to add multiple values to an array is
with the concat()
method. This method concatenates two arrays and returns a new array with the
result (the original arrays are not modified).

Listing
6-113: Concatenating two arrays

var
list = [12, 5, 80, 34];

var newList = list.concat([100, 200, 300]);

alert(newList);  //
12,5,80,34,100,200,300

Removing values is also easy with the pop() and shift() methods. The pop() method removes the value at the end of the array and the shift() method removes it at the beginning.

Listing
6-114: Removing values from an array

var
list = [12, 5, 80, 34];

list.pop();

alert(list);
// 12,5,80

The previous methods add or remove values from
the beginning or the end of the array. If we want to have more flexibility, we
can use the splice() method. This method adds or removes values
from any location in the array. The first attribute of the splice() method specifies the index where we want to start
removing and the second attribute determines how many elements will be removed.
For example, the following code removes 2 values from index 1.

Listing
6-115: Removing values from an array

var
list = [12, 5, 80, 34];

var removed = list.splice(1, 2);

alert("Values
remaining: " + list);  // 12,34

alert("Values
removed: " + removed);  // 5,80

With this method, we can also add new values to
an array in specific positions. The values must be specified after the first
two attributes separated by commas. The following example adds the values 24,
25, and 26 at index 2. Because no values are going to be removed, we declare
the value 0 for the second attribute.

Listing
6-116: Adding values to an array in a specific position

var
list = [12, 5, 80, 34];

list.splice(2, 0, 24, 25, 26);

alert(list);
// 12,5,24,25,26,80,34

Array objects also include methods to order the
values in the array. For example, the reverse() method sorts the array in reverse order.

Listing
6-117: Sorting the values of an array in reverse order

var
list = [12, 5, 80, 34];

list.reverse();

alert(list);
// 34,80,5,12

A better method to sort an array is called sort(). This method can take a function to decide in which order the values
are going to be placed. If no function is provided, the method sorts the array
alphabetically in ascending order.

Listing 6-118: Sorting the
values in alphabetical order

var
list = [12, 5, 80, 34];

list.sort();

alert(list);
// 12,34,5,80

Notice that the values in the last example are
ordered alphabetically, not numerically. If we want the method to consider the
numerical order or achieve a different type of arrangement, we have to provide
a function. The function receives two values from the array and must return a
Boolean value to indicate their order. For instance, to sort the values in
ascending order, we have to return true if the first value is greater than the
second value or false otherwise.

Listing
6-119: Sorting the values in numerical order

var
list = [12, 5, 80, 34];

list.sort(function(value1,
value2) {

return value1 > value2;

});

alert(list);
// 5,12,34,80

A useful method included in Array objects is map().
With this method, we can process the values of the array one by one and create
a new array with the results. Like previous methods, this method sends the
values to a function, but instead of a Boolean value, the function must return
the value we want to store in the new array. For example, the following code
multiplies each value times 2 and returns the results.

Listing
6-120: Processing the values and storing the results

var
list = [12, 5, 80, 34];

var newList = list.map(function(value) {

return value * 2;

});

alert(newList);
// 24,10,160,68

Date Objects

Managing dates is a complicated task, not only because
they are composed of several values that represent different things but also
because the values are dependent on one another. If one value is increased over
its limit, it affects the rest of the values on the date. And there are
different limits for each component. The limit for minutes and seconds is 60,
but the limit for hours is 24, and every month has a different number of days.
Dates also have to comply with different time zones, daylight saving times,
etc. To simplify the work for developers, JavaScript defines an object called Date. The Date object stores a date and takes care of
keeping the values within their range. The date in these objects is stored in
milliseconds, which allows us to perform operations between dates, calculate
intervals, etc. JavaScript offers the following constructor to create Date objects.

Date(value)—This constructor creates a value in milliseconds to represent a
date based on the value provided by the attribute. The value attribute
may be specified as a string or as the components of a date separated by commas
in the order year, month, day, hours, minutes, seconds, milliseconds. If no
value is provided, the constructor creates a date from the current date in the
system.

The date stored in these objects is represented
by a value in milliseconds calculated since January 1st, 1970. Because the
value is not familiar to users, Date objects offer the following methods to get
the components of the date, such as the year or the month.

getFullYear()—This method
returns an integer representing the year (a 4 digits value).

getMonth()—This method
returns an integer representing the month (a value from 0 to 11).

getDate()—This method
returns an integer representing the day of the month (a value from 1 to 31).

getDay()—This method
returns an integer representing the day of the week (a value from 0 to 6).

getHours()—This method
returns an integer representing the hours (a value from 0 to 23).

getMinutes()—This method
returns an integer representing the minutes (a value from 0 to 59).

getSeconds()—This method
returns an integer representing the seconds (a value from 0 to 59).

getMilliseconds()—This method
returns an integer representing the milliseconds (a value from 0 to 999).

getTime()—This method
returns an integer representing the milliseconds from January 1st, 1970 to the
date.

The Date objects also include the following methods
to modify the components of the date.

setFullYear(year)—This method sets the year of the date (a 4 digits value). It can
also take values to set the month and day.

setMonth(month)—This method sets the month of the date (a value from 0 to 11). It
can also take a value to set the day.

setDate(day)—This method sets the day of the date (a value from 1 to 31).

setHours(hours)—This method sets the hours of the date (a value from 0 to 23). It
can also take values to set the minutes and seconds.

setMinutes(minutes)—This method sets the minutes of the date (a value from 0 to 59).
It can also take a value to set the seconds.

setSeconds(seconds)—This method sets the seconds of the date (a value from 0 to 59).
It can also take a value to set the milliseconds.

setMilliseconds(milliseconds)—This method sets the milliseconds of the
date (a value from 0 to 999).

The Date object also offers methods to turn the
date into a string.

toString()—This method
turns a date into a string. The value in the string is expressed in American
English and the format "Wed Jan 04 2017 22:32:48 GMT-0500 (EST)".

toDateString()—This method
turns a date into a string, but it only returns the date part of the value. The
value in the string is expressed in American English and the format "Wed
Jan 04 2017".

toTimeString()—This method
turns a date into a string, but it only returns the time part of the value. The
value in the string is expressed in American English and the format
"23:21:55 GMT-0500 (EST)".

Every time we need a date, we have to create a
new object with the Date() constructor. If we do not provide a date,
the constructor creates the Date object with the current date in the
system.

Listing
6-121: Creating a Date object

var mydate = new Date();

alert(mydate);
// "Wed Jan 04 2017 20:51:17 GMT-0500 (EST)"

The date may be declared with a string containing
a date expressed in human-readable format. The constructor takes care of
converting the string into milliseconds.

Listing
6-122: Creating a Date object from
a string

var mydate = new
Date("January 20 2017");

alert(mydate);
// "Fri Jan 20 2017 00:00:00 GMT-0500 (EST)"

Because browsers interpret the string in
different ways, instead of a string, it is recommended to create the date
declaring the values of the components separated by commas. The following
example creates a Date object with the date 2017/02/15 12:35.

Listing
6-123: Creating a Date object from
date components

var mydate = new
Date(2017, 1, 15, 12, 35, 0);

alert(mydate);
// "Wed Feb 15 2017 12:35:00 GMT-0500 (EST)"

The values must be declared in the order Year,
Month, Day, Hours, Minutes, and Seconds. Notice that the month is represented
by a number from 0 to 11, so we had to declare the value 1 in the constructor
to represent the month of February.

The Basics:
Dates
are represented with a value in milliseconds, but every time you show them on
the screen with the alert() function the
browser automatically turns them into strings with a format the user can
understand. This is only done by the browser when the date is going to be
presented to the user with predefined functions like alert(). If we want
to format the date in our code, we can extract the components and include them
in a string, as we will see later, or call the methods provided by Date objects for
this purpose. The toString() method
creates a string with the whole date, the toDateString() method
includes only the date components, and the toTimeString() method includes
only the time.

Because dates are stored in milliseconds, every
time we want to process the values of their components, we have to retrieve
them with the methods provided by the object. For example, to get the year of a
date, we have to use the getFullYear() method.

Listing
6-124: Reading the components of a date

var today =
new Date();

var year =
today.getFullYear();

alert("The year is
" + year);  // "The year is 2017"

The other components are retrieved the same way
with their corresponding methods. The only thing we have to consider is that
months are represented with values from 0 to 11, so we have to add 1 to the
value returned by the getMonth() method to get a value our users can
understand.

Listing
6-125: Reading the month

var
today = new Date();

var year = today.getFullYear();

var month = today.getMonth() + 1;

var day = today.getDate();

alert(year +
"-" + month + "-" + day);  //
"2017-1-5"

These methods are also useful for incrementing or
decrementing a date. For example, if we want to determine the date 15 days from
now, we have to get the current day, add the value 15 to it, and assign the
result back to the date.

Listing
6-126: Incrementing a date

var
today = new Date();

alert(today);
// "Thu Jan 05 2017 14:37:28 GMT-0500 (EST)"

today.setDate(today.getDate() + 15);

alert(today);
// "Fri Jan 20 2017 14:37:28 GMT-0500 (EST)"

If instead of incrementing or decrementing a date
by a period of time we want to calculate the difference between two dates, we
have to subtract one date from another.

Listing
6-127: Calculating an interval

var
today = new Date(2017, 0, 5);

var future =
new Date(2017, 0, 20);

var interval = future - today;

alert(interval);
// 1296000000

The value returned by the subtraction is
expressed in milliseconds. From this value, we can extract any component we
want. All we have to do is to divide the number by the maximum values of each
component. For example, if we want to express the number in seconds, we have to
divide it by 1000 (1000 milliseconds = 1 second).

Listing
6-128: Calculating an interval in seconds

var
today = new Date(2017, 0, 5);

var future =
new Date(2017, 0, 20);

var interval =
future - today;

var seconds = interval / 1000;

alert(seconds +
" seconds");  // "1296000 seconds"

Of course, seconds are not easy to put into
context, but days are. To express the interval in days, we have to keep
dividing the number. If we divide milliseconds by 1000 we get seconds, the
result divided by 60 gives us minutes, that result divided by 60 again gives us
hours, and dividing the result by 24 we get days (24 hours = 1 day).

Listing
6-129: Calculating an interval in days

var
today = new Date(2017, 0, 5);

var future =
new Date(2017, 0, 20);

var interval =
future - today;

var days = interval / (24 * 60 * 60 * 1000);

alert(days +
" days");  // "15 days"

Another useful thing we can do with dates is to
compare them. Although we can compare Date objects directly to one another, some
comparator operators do not compare the dates but the objects themselves, so
the best way to do it is to first get the values of the dates in milliseconds
with the getTime() method and then compare those values.

Listing
6-130: Comparing two dates

var
today = new Date(2017, 0, 20, 10, 35);

var future =
new Date(2017, 0, 20, 12, 35);

if (future.getTime() == today.getTime()) {

alert("Equal dates");

} else {

alert("Different dates");

}

The code in Listing 6-130 checks whether two
dates are equal or not and displays a message to communicate the result. These
dates are the same, except for the time. One date was set with the time 10:35
and the second date was set with the time 12:35, and therefore they are considered
different. If we want to compare only the date part of the date without
considering the time, we have to reset the time components. For example, we can
set the hours, minutes and seconds on both dates to 0 and then the only values
left to compare are the year, month and day.

Listing
6-131: Comparing only the date without the time

var
today = new Date(2017, 0, 20, 10, 35);

var future =
new Date(2017, 0, 20, 12, 35);

today.setHours(0, 0, 0);

future.setHours(0, 0, 0);

if
(future.getTime() == today.getTime()) {

alert("Equal dates");

} else {

alert("Different dates");

}

In Listing 6-131, before comparing the dates, we
reset the hours, minutes and seconds with the setHours() method. Now, the dates have their time set to 0, and
the comparison operator only has to check whether the date is equal or not. In
this case, the dates are the same and the message "Equal dates" is
shown on the screen.

IMPORTANT:
Beside the methods
studied in this chapter, Date objects also include methods that
take into consideration the locale (the user location and language). You should
consider implementing these methods when developing a website or application
that could be used by people that speak a language other than English, or when
you have to take into account local variations, such as daylight saving times.
A JavaScript library that developers often use to simplify working with dates
is called MomentJS, available at www.momentjs.com. We will learn more about libraries
and how to implement them later in this chapter.

Math Object

There are objects in JavaScript which function is not
to store values but to provide properties and methods to process values from
other objects. This is the case of the Math object. This object does not include a
constructor to create more objects, but it defines several properties and
methods that we can access from its definition to get the values of
mathematical constants and perform arithmetic operations. The following are the
most frequently used.

PI—This
property returns the value of PI.

E—This
property returns the Euler's constant.

LN10—This
property returns the natural logarithm of 10. The object also includes the
properties LN2 (natural logarithm of
2), LOG2E (base 2 logarithm of E), and LOG10E (base 10 logarithm
of E).

SQRT2—This
property returns the square root of 2. The object also includes the property SQRT1_2 (the square
root of 1/2).

ceil(value)—This method rounds a value up to the next integer and returns the
result.

floor(value)—This method rounds a value down to the next integer and returns
the result.

round(value)—This method rounds a value to the nearest integer and returns the
result.

trunc(value)—This method removes the fractional digits of a value and returns
the integer.

abs(value)—This method returns the absolute value of a number (inverts
negative values to get a positive number).

min(values)—This method returns the smallest number of a list of values
separated by commas.

max(values)—This method returns the largest number of a list of values
separated by commas.

random()—This method
returns a random number in a range between 0 and 1.

pow(base, exponent)—This method returns the result of raising
the base to the power of the exponent.

exp(exponent)—This method returns the
result of raising E to the power of the exponent. The object also includes the expm1() method,
which returns the same result minus 1.

sqrt(value)—This method returns the square root of a value.

log10(value)—This method returns the base 10 logarithm of a value. The object
also includes the methods log() (returns the base E
logarithm), log2() (returns the
base 2 logarithm), and log1p() (returns the
base E logarithm of 1 plus a number).

sin(value)—This method returns the sine of a number. The object also
includes the methods asin() (returns the
arcsine of a number), sinh() (returns the
hyperbolic sine of a number), and asinh() (returns the
hyperbolic arcsine of a number).

cos(value)—This method returns the cosine of a number. The object also
includes the methods acos() (returns the
arccosine of a number), cosh() (returns the
hyperbolic cosine of a number), and acosh() (returns the
hyperbolic arccosine of a number).

tan(value)—This method returns the tangent of a number. The object also
includes the methods atan() (returns the
arctangent of a number), atan2() (returns the
arctangent of a number), tanh() (returns the
hyperbolic tangent of a number), and atanh() (returns the
hyperbolic arctangent of a number).

Because no copies are created from this object,
we have to read the properties and call the methods from the object itself
(e.g., Math.PI). Other than that, the application of these methods
and values is straightforward, as shown in the following example.

Listing
6-132: Performing arithmetic operations with the Math object

var
square = Math.sqrt(4);  // 2

var power =
Math.pow(2, 2);  // 4

var maximum =
Math.max(square, power);

alert("The
maximum value is " + maximum);  // "The maximum value is
4"

The code in Listing 6-132 gets the square root of 4,
calculates 2 to the power of 2, and then compares the results to get the
maximum value using the max() method. The following example demonstrates how to
calculate a random number.

Listing
6-133: Getting a random number

var randomnumber =
Math.random() * (11 - 1) + 1;

var value =
Math.floor(randomnumber);

alert("The number is:
" + value);

The random() method
returns a number between 0 and 1. If we want to get a different range of numbers,
we have to multiply this value by the formula (max
- min) + min, where min and max are the minimum
and maximum values we want to include. In our example, we want to get a random
number from 1 to 10, but we had to define the values as 1 and 11 because the
maximum value is not included in the range. Also, the number returned by the random() method is
a floating-point value. If we want to get integers, we have to round the numbers
with the floor() method.

The Basics:
The
floor() and ceil() methods
are probably two of the most frequently used methods included in the Math object.
The floor() method rounds a number down to the
nearest integer, and the ceil() method
rounds the number up to the nearest integer. For instance, if the number to be
processed is 5.86, the floor() method
will return the value 5 and the ceil() method
will return the value 6. We will implement these and other Math methods in
practical situations in later chapters.

Window Object

Every time the browser is opened or a new tab is
initialized, a global object called Window is created to reference the browser window
and provide some methods and properties to control it. The object is stored in
a property of the JavaScript's global object called window. Through this property, we can connect with the browser and the
document from our code.

The Window object includes, in turn, other objects to
provide additional information related to the window and the document. The
following are some of the properties available to access these objects.

location—This property contains a Location object with
information about the origin of the current document. It can also be used as a
property to set or return the URL of the document (e.g., window.location
= "http://www.formasterminds.com").

history—This property contains a History object with
properties and methods to manipulate the navigation history. We will study this
object in Chapter 19.

navigator—This property contains a Navigator object with
information about the application and the device. We will study some of the
properties of this object, such as geolocation (used to
detect the user's location) in later chapters.

document—This property contains a Document object,
which provides access to the objects that represent the HTML elements in the
document.

Besides these valuable objects, the Window object also offers its own properties and methods. The following are
the most frequently used.

innerWidth—This property returns the width of the
window in pixels.

innerHeight—This property returns the height of the
window in pixels.

scrollX—This property returns the number of pixels the document has been
scrolled horizontally.

scrollY—This property returns the number of pixels the document has been
scrolled vertically.

alert(value)—This method
shows a popup window on the screen displaying the value between parentheses.

confirm(message)—This method
is similar to alert(), but it
offers two buttons, OK and Cancel, for the user to choose from. It returns true or false, according
to the user’s response.

prompt(message)—This method
shows a popup window with an input field to let the user insert a value. The
method returns the value inserted by the user.

setTimeout(function,
milliseconds)—This method executes the function specified in the first
attribute after the time in milliseconds specified by the second attribute. The
Window object also
offers the clearTimeout() method to
cancel the process.

setInterval(function,
milliseconds)—This method is similar to setTimeout() but it calls
the function repeatedly. The Window object also
offers the clearInterval() method to
cancel the process.

open(URL, window,
parameters)—This method opens a document in a new window. The URL
attribute is the URL of the document we want to open, the window
attribute is the name of the window where we want to show the document (if no
name is provided or the window does not exist, the document is opened in a new
window), and the parameters attribute is a list of configuration
parameters separated by comma that set the characteristics of the window (e.g.,
" resizable=no,scrollbars=no").
The Window object also offers the close() method to
close a window opened with this method.

The Window object
controls aspects of the window, its content, and the metadata associated with
it, such as the location of the current document, size, offset, etc. For
instance, we can load a new document by changing the value of the location.

Listing
6-134: Defining a new location

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function doit() {

window.location =
"http://www.formasterminds.com";

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button" onclick="doit()">Press
Button</button>

</section>

</body>

</html>

In the document
of Listing 6-134, we declare a function call doit() that assigns
a new URL to the location property of
the Window object. A call to the function is assigned
later to the onclick attribute of
a <button> element to
execute the function when the button is pressed.

The location property
contains a Location object with
its own properties and methods, but we can also assign a string with a URL
directly to the property to define a new location for the content of the
window. Once the value is assigned to the property, the browser loads the
document in that URL and shows it on the screen.

Do It Yourself: Create a new HTML file
with the document of Listing 6-134 and open the document in your browser. You
should see a title and a button. Press the button. The browser should load the
www.formasterminds.com website.

Besides
assigning a new URL to the location property, we
can also work with the location from the methods provided by the Location object.

assign(URL)—This method
asks the browser to load the document in the location indicated by the URL
attribute.

replace(URL)—This method
asks the browser to replace the current document with the document in the location
indicated by the URL attribute. It differs with the assign() method in
that it does not add the URL to the browser's history.

reload(value)—This method
asks the browser to reload the current document. It can take a Boolean
attribute that determines whether the resource has to be downloaded from the
server or it can be loaded from the browser's cache (true or false).

The
following example refreshes the page when the user presses the button. Notice that
this time we do not mention the window property.
The Window object is a global object, and therefore
the interpreter infers that the properties and methods belong to this object.
This is the reason why in previous examples we never called the alert() method with
the instruction window.alert().

Listing
6-135: Refreshing the page

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function doit() {

location.reload();

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button" onclick="doit()">Press
Button</button>

</section>

</body>

</html>

The Window object also
offers the open() method to
load new content. In the following example, the www.formasterminds.com website
is opened in a new window or tab.

Listing
6-136: Opening a new window

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function doit() {

open("http://www.formasterminds.com");

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button" onclick="doit()">Press
Button</button>

</section>

</body>

</html>

Other
important methods of the Window object are setTimeout() and setInterval(). These
methods execute an instruction after a certain period of time. The setTimeout() method
executes the instruction once, and the setInterval() method
executes the instruction repeatedly until the process is canceled. If instead
of one instruction we want to execute several, we can specify a reference to a function.
Every time the time runs out, the function is executed.

Listing
6-137: Using timers to execute functions

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function
doit() {

var today
= new Date();

var time
= today.toTimeString();

alert(time);

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button" onclick="setTimeout(doit, 5000)">Press
Button</button>

</section>

</body>

</html>

These
methods take values in milliseconds. A millisecond is 1000 parts of a second,
so if we want to specify the time in seconds, we have to multiply the value
times 1000. In our example, we want the doit() function to
be executed every 5 seconds, and therefore we declare the value 5000 as the
time to wait before the function is called.

IMPORTANT:
Notice
that the function is declared without the parentheses. When we want to call a
function, we have to declare the parentheses after the name, but when we want
to reference the function, we have to omit the parentheses. Because this time
we wanted to assign a reference to the function and not the result of its
execution, we declared the name without parentheses.

If we
need to execute a function over and over again after a period of time, we have
to use the setInterval() method. This
method works exactly like setTimeout() but it keeps
running until we tell it to stop with the clearInterval() method. To
identify the method we want to be canceled, we have to store the reference
returned by the setInterval() method in a
variable and use that variable to reference the method later, as in the
following example.

Listing
6-138: Cancelling a timer

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

var seconds
= 0;

var timer =
setInterval(doit, 1000);

function
doit() {

seconds++;

}

function
cancel() {

clearInterval(timer);

alert("Total " + seconds + " seconds");

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button" onclick="cancel()">Press
Button</button>

</section>

</body>

</html>

The code
in Listing 6-138 includes two functions. The doit() function is
executed every 1 second by the setInterval() method and
the cancel() function is executed
when the user presses the button. The purpose of this code is to increase the
value of a variable called seconds every one
second until the user decides to cancel the process and see the total amount
accumulated up to that moment.

Do It Yourself: Replace the document in
your HTML file with the code in Listing 6-138 and open the new document in your
browser. Wait a moment and press the button. You should see a popup window with
the number of seconds elapsed so far.

IMPORTANT:
The
setTimeout() and setInterval() methods are
required in the construction of small applications and animations. We will
review these methods in more practical situations in later chapters.

Document Object

As mentioned before, almost everything in JavaScript
is an object, and this includes the elements in the document. When an HTML
document is loaded, the browser creates an internal structure to process it.
The structure is called DOM (Document Object Model), and it is composed
of multiple objects of type Element (or more specific types that inherit from Element) that represent each element in the document.

The Element objects keep a permanent connection with
the elements they represent. When an object is modified, its element is
modified as well, and the result is shown on the screen. To provide access to
these objects and let us work with their properties from our JavaScript code,
they are stored in an object called Document that is later assigned to the document property of the Window object.

Among other alternatives, the Document object includes the following properties to provide quick
access to the Element objects that represent the most common
elements in the document.

forms—This property returns an array with references to all the Element objects that
represent the <form> elements in
the document.

images—This property returns an array with references to all the Element objects that
represent the <img> elements in
the document.

links—This property returns an array with references to all the Element objects that
represent the <a> elements in
the document.

These properties return an array of objects
referencing all the elements of a particular type, but the Document object also includes the following methods to access
individual objects or to obtain lists of objects based on other parameters.

getElementById(id)—This method
returns a reference to the Element object that
represents the element identified with the value specified by the attribute
(the value assigned to the id attribute).

getElementsByClassName(class)—This method returns an array with references to the Element objects that
represent the elements identified by the class specified by the attribute (the
value assigned to the class attribute).

getElementsByName(name)—This method returns an array with references to the Element objects that
represent the elements identified by the name specified by the attribute (the
value assigned to the name attribute).

getElementsByTagName(type)—This method returns an array with references to the Element objects that
represent the type of elements specified by the attribute. The attribute is the
keyword that identifies the elements, such as h1, p, img, div, etc.

querySelector(selector)—This method
returns a reference to the Element object that
represents the element that matches the selector specified by the selector
attribute. The method returns the first element in the document that matches
the CSS selector (see Chapter 3 to learn how to build these selectors).

querySelectorAll(selectors)—This method returns an array with
references to the Element objects that
represent the elements that match the selectors specified by the attribute. One
or more selectors may be declared separated by commas.

By accessing the Element objects in the DOM, we can read and modify the elements in the
document, but
before doing it, we must consider that the document is read sequentially by the
browser and we cannot reference an element that has not yet been created. The
best solution is to execute the JavaScript code only when the load event occurs. We have studied this event at the beginning
of this chapter. Browsers fire the event after the document is loaded and all
the objects in the DOM were created and are accessible. The following example
includes the onload attribute in the <body> element to be able to access a <p> element from JavaScript.

Listing
6-139: Getting a reference to the Element object that
represents an element

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function
initiate() {

var element =
document.getElementById("subtitle");

}

</script>

</head>

<body
onload="initiate()">

<section>

<h1>Website</h1>

<p
id="subtitle">The best website ever!</p>

</section>

</body>

</html>

The code in Listing 6-139 is not performing any action;
it is just getting a reference to the Element object that represents the <p> element and storing it in the element variable as soon as the document is loaded. To do something with the
element, we have to work with the object's properties.

Every Element object automatically gets properties that
reference each attribute in the element they represent. Accessing these properties,
we can get or modify the values of the corresponding attributes. For example,
if we read the id property in the object stored in the element variable, we get the string "subtitle".

Listing
6-140: Reading the element's attributes from JavaScript

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function
initiate() {

var
element = document.getElementById("subtitle");

alert("The id is: " + element.id);  // "The id is: subtitle"

}

</script>

</head>

<body
onload="initiate()">

<section>

<h1>Website</h1>

<p
id="subtitle">The best website ever!</p>

</section>

</body>

</html>

In these examples, we have accessed the element
with the getElementById() method because the <p> element in our document has an id
attribute, but this may not be the case all the time. If the id
attribute is not present or we want to get a list of elements that share
similar characteristics, we can use the rest of the methods provided by the Document object. For instance, we can get a list of all the <p> elements in the document with the getElementsByTagName() method.

Listing
6-141: Accessing elements by keyword

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function
initiate() {

var list = document.getElementsByTagName("p");

for (var f = 0; f < list.length; f++) {

var element = list[f];

alert("The id is: " + element.id);

}

}

</script>

</head>

<body
onload="initiate()">

<section>

<h1>Website</h1>

<p
id="subtitle">The best website ever!</p>

</section>

</body>

</html>

The getElementsByTagName() method returns an array with references to
all the elements which keyword is equal to the value provided between
parentheses. In Listing 6-141, the attribute was defined with the string "p"
to obtain a list of all the <p> elements in the document. After the
references are fetched, we access each element with a for loop and show the value of their id attributes on the screen.

If we know the position of the element we want to
access, we can provide the index ourselves. In our example, there is only one <p> element, so the reference to this element will be at
the position 0 in the array.

Listing
6-142: Accessing an element by keyword

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function
initiate() {

var list
= document.getElementsByTagName("p");

var element = list[0];

alert("The id is: " + element.id);

}

</script>

</head>

<body
onload="initiate()">

<section>

<h1>Website</h1>

<p
id="subtitle">The best website ever!</p>

</section>

</body>

</html>

A better method to find elements is querySelector(). This method searches for an element using a CSS
selector. The advantage is that we can exploit the power of CSS selectors to
find the right element. In the following example, we use the querySelector() method to look for <p> elements that are direct children of a <section> element.

Listing
6-143: Finding an element with the querySelector() method

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function
initiate() {

var element = document.querySelector("section >
p");

alert("The id is: " + element.id);

}

</script>

</head>

<body
onload="initiate()">

<section>

<h1>Website</h1>

<p
id="subtitle">The best website ever!</p>

</section>

</body>

</html>

The Basics:
The
querySelector() method
returns only a reference to the first element it finds. If we want to get a
list of all the elements that match the selector, we have to use the querySelectorAll() method.

The methods we have just studied look for
elements in the entire document, but we can narrow the search by looking inside
an element. For this purpose, Element objects also include their own versions of
methods like getElementsByTagName() and querySelector(). For example, we can search for <p> elements inside <section> elements with a particular ID.

Listing
6-144: Searching for an element inside another element

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function
initiate() {

var
mainelement = document.getElementById("mainsection");

var list =
mainelement.getElementsByTagName("p");

var
element = list[0];

alert("The id is: " + element.id);

}

</script>

</head>

<body onload="initiate()">

<section
id="mainsection">

<h1>Website</h1>

<p
id="subtitle">The best website ever!</p>

</section>

</body>

</html>

The code in Listing 6-144 gets a reference to the
element identified with the string "mainsection" and then calls the getElementsByTagName() method to find <p> elements inside that element. Because we
only have one <p> element inside the <section> element, we read the reference at index 0 and show
the value of its id attribute on the screen.

Element Objects

Getting a reference to access an element and read its
attributes may sometimes be useful, but what makes JavaScript a dynamic
language is the possibility to modify those elements and the entire document.
For this purpose, Element objects also contain properties to define
and modify the styles of an element and its content. One of these properties is style, which contains an object called Styles that in turn provides properties for modifying the element's
styles.

The names of the styles in JavaScript are not the same
as in CSS. There was no consensus on this regard, so although we are able to
provide the same values for the properties, we have to learn their names in JavaScript.
The following are the most frequently used.

color—This
property sets the color of the element's content.

background—This property sets the element's
background styles. We can also work with each style individually using the
associated properties backgroundColor, backgroundImage, backgroundRepeat, backgroundPosition and backgroundAttachment.

border—This
property sets the styles for the element's border. We can modify each style individually
with the properties borderColor, borderStyle, and borderWidth, or affect
each border using the associated properties borderTop (borderTopColor, borderTopStyle, and borderTopWidth), borderBottom (borderBottomColor, borderBottomStyle, and borderBottomWidth), borderLeft (borderLeftColor, borderLeftStyle, and borderLeftWidth), and borderRight (borderRightColor, borderRightStyle, and borderRightWidth).

margin—This
property sets the element's margin. We can also use the associated properties marginBottom, marginLeft, marginRight, and marginTop.

padding—This
property sets the element's padding. We can also use the associated properties paddingBottom, paddingLeft, paddingRight, and paddingTop.

width—This
property sets the element's width. There are two associated properties to set
the element's maximum and minimum width: maxWidth and minWidth.

height—This
property sets the element's height. There are two associated properties to set
the element's maximum and minimum height: maxHeight and minHeight.

visibility—This
property determines whether the element is visible or not.

display—This
property defines the type of box used to render the element.

position—This
property defines the type of positioning used to position the element.

top—This property specifies the distance between the element's top
margin and the top margin of its container.

bottom—This property specifies the distance between the element's bottom
margin and the bottom margin of its container.

left—This property specifies the distance between the element's left
margin and the left margin of its container.

right—This property specifies the distance between the element's right
margin and the right margin of its container.

cssFloat—This property allows an element to float to one side or another.

clear—This property recovers the document's normal flow, stopping
elements from floating to the left, right, or both sides.

overflow—This property specifies how the content that overflows its
container's box is going to be shown.

zIndex—This property defines an index that determines the position of
the element in the z axis.

font—This
property sets the element's font styles. We can also set the styles
individually using the associated properties fontFamily, fontSize, fontStyle, fontVariant and fontWeight.

textAlign—This property aligns the text inside the element.

verticalAlign—This property aligns inline elements
vertically.

textDecoration—This property highlights the text with a
line. We can also set the styles individually assigning the values true or false to the
properties textDecorationBlink, textDecorationLineThrough, textDecorationNone, textDecorationOverline, and textDecorationUnderline.

Modifying the values of these properties is
straightforward. We have to get a reference to the Element object that represents the element we want to modify, as we did in
previous examples, and then assign a new value to the property of the Styles object we want to change. The only difference with CSS, besides some
of the names of the properties, is that the values have to be assigned between
quotes.

Listing
6-145: Assigning new styles to an element

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function
initiate() {

var
element = document.getElementById("subtitle");

element.style.width = "300px";

element.style.border = "1px solid #FF0000";

element.style.padding = "20px";

}

</script>

</head>

<body
onload="initiate()">

<section>

<h1>Website</h1>

<p
id="subtitle">The best website ever!</p>

</section>

</body>

</html>

The code
in Listing 6-145 assigns a width of 300 pixels, a red border of 1 pixel, and a
padding of 20 pixels to the <p> element in
the document. The result is shown in Figure 6-4.

Figure 6-4: Styles
assigned from JavaScript

The properties of the Styles object are independent of the CSS styles assigned to the document. If
we have not previously assigned a value to one of these properties from JavaScript,
the value returned is an empty string. To provide some information about the
element, Element objects include additional properties. The
following are the most frequently used.

clientWidth—This
property returns the width of the element, including the padding.

clientHeight—This property returns the height of the
element, including the padding.

offsetTop—This property returns the number of pixels the element is
displaced from the top of its container.

offsetLeft—This property returns the number of pixels
the element is displaced from the left side of its container.

offsetWidth—This property returns the width of the
element, including the padding and the border.

offsetHeight—This property returns the height of the
element, including the padding and the border.

scrollTop—This property returns the number of pixels the content of the
element is scrolled to the top.

scrollLeft—This property returns the number of pixels
the content of the element is scrolled to the left.

scrollWidth—This property returns the width of the
element's content.

scrollHeight—This property returns the height of the
element's content.

These are read-only properties, but we can get
the value we need by reading these properties and then use the properties of
the Styles object to assign a new one.

Listing 6-146: Reading CSS styles from JavaScript

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<style>

#subtitle {

width:
300px;

padding:
20px;

border:
1px solid #FF0000;

}

</style>

<script>

function
initiate() {

var
element = document.getElementById("subtitle");

var width = element.clientWidth;

width = width + 100;

element.style.width = width + "px";

}

</script>

</head>

<body
onload="initiate()">

<section>

<h1>Website</h1>

<p
id="subtitle">The best website ever!</p>

</section>

</body>

</html>

In this example, we assign the styles of the <p> element from CSS and then modify its width from JavaScript.
The current width is obtained from the clientWidth property, but because this property is
read-only, the new value has to be assigned to the width property of the Styles object (Notice that the value assigned to the
property must be a string with the units "px" at the end). After the code is executed, the <p> element has a width of 400 pixels.

Figure 6-5: Styles
modified from JavaScript

It is not common to modify the styles of an
element individually, as we did in these examples. Usually, elements are styled by a group of
CSS properties assigned through the class attribute. As we explained in
Chapter 3, these rules are called classes. Classes are permanently
defined in CSS Style Sheets, but Element objects include the following
properties to assign a different class to an element and therefore change its
styles all at once.

className—This
property sets or returns
the
value of the class attribute.

classList—This property returns an array with the list of the classes
assigned to the element.

The array returned by the classList property is of type DOMTokenList, which includes the following methods to
modify the classes on the list.

add(class)—This method adds another class to the element.

remove(class)—This method removes a class from the element.

toggle(class)—This method adds or removes a class depending on the current
status. If the class was already assigned to the element, then it is removed.
Otherwise, the class is added to the element.

contains(class)—This method detects whether the class was assigned to the element
or not and returns true or false accordingly.

The easiest way to replace the class
of an element is assigning a new value to the className property.

Listing 6-147: Replacing the element's
class

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<style>

.supercolor {

background: #0099EE;

}

.blackcolor {

background: #000000;

}

</style>

<script>

function changecolor() {

var element =
document.getElementById("subtitle");

element.className =
"blackcolor";

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<p
id="subtitle" class="supercolor"
onclick="changecolor()">The best website ever!</p>

</section>

</body>

</html>

In the
code in Listing 6-147, we have declared two classes: supercolor and blackcolor. Both
classes define the background color of the element. By default, the supercolor class is
assigned to the <p> element,
which gives the element a blue background, but when the changecolor() function is
executed, that class is replaced with the blackcolor class and a
black color is assigned to the background (notice that this time we execute the
function when the user clicks on the element, not when the document finishes
loading).

As
mentioned in Chapter 3, multiple classes may be assigned to an element. When
this is the case, instead of the className property it
is better to apply the methods of the classList property.
The following example implements the contains() method to detect
whether a class was already assigned to an element and adds it or removes it,
depending on the current state.

Listing 6-148: Turning classes on and off

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<style>

.supercolor {

background: #000000;

}

</style>

<script>

function changecolor() {

var element =
document.getElementById("subtitle");

if
(element.classList.contains("supercolor")) {

element.classList.remove("supercolor");

} else {

element.classList.add("supercolor");

}

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<p
id="subtitle" class="supercolor"
onclick="changecolor()">The best website ever!</p>

</section>

</body>

</html>

With the code in Listing 6-148, every time the user
clicks on the <p> element, the styles change, going from
having a background color to no background. The same may be achieved with the toggle() method. This method checks the status of the element and adds the
class if it was not assigned before or removes it otherwise.

Listing 6-149: Turning classes on and off
with the toggle() method

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<style>

.supercolor {

background: #000000;

}

</style>

<script>

function changecolor() {

var element =
document.getElementById("subtitle");

element.classList.toggle("supercolor");

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<p
id="subtitle" class="supercolor"
onclick="changecolor()">The best website ever!</p>

</section>

</body>

</html>

The toogle() method simplifies our work. We do not have to check whether the
class exists or not, the method does the work for us and adds the class or
removes it depending on the current state.

Do It Yourself: Create a new HTML file with the
document you want to try. Open the document in your browser and click on the
area occupied by the <p> element. You should see the
background of that element changing colors.

Besides the styles of an element, we
can also modify its content. The following are the properties and methods
provided by Element objects for this purpose.

innerHTML—This
property sets or returns the content of an element.

outerHTML—This
property sets or returns an element and its content. Unlike innerHTML, this
property replaces not only the content but also the element.

insertAdjacentHTML(location, content)—This method inserts content in a location
according to the value of the location attribute. The values available
are beforebegin (before the element), afterbegin (inside the
element, before its first child), beforeend (inside the
element, after its last child), and afterend (after the
element).

The easiest way to replace the content
of an element is with the innerHTML property. By assigning a new
value to this property, the current content is replaced with the new one. The
following example replaces the content of the <p> element with the string "This is my
website" when the element is clicked.

Listing 6-150: Assigning content to an
element

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function changecontent() {

var element =
document.getElementById("subtitle");

element.innerHTML = "This is my
website";

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<p
id="subtitle" onclick="changecontent()">The best website
ever!</p>

</section>

</body>

</html>

The innerHTML property is used not only to assign new
content but also to read and process the current content. The following example
reads the content of an element, adds a string at the end, and assigns the
result back to the element.

Listing 6-151: Modifying the content of an
element

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function changecontent() {

var element =
document.getElementById("subtitle");

var text = element.innerHTML + " We
are the best!";

element.innerHTML = text;

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<p
id="subtitle" onclick="changecontent()">The best website
ever!</p>

</section>

</body>

</html>

Besides
text, the innerHTML property can also
process HTML code. When HTML code is assigned to these properties, the code is interpreted,
and the result is shown on the screen.

Listing 6-152: Inserting HTML code into
the document

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function addElement() {

var element =
document.querySelector("section");

element.innerHTML = "<p>This is a new text</p>";

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button" onclick="addElement()">Add
Content</button>

</section>

</body>

</html>

The code
in Listing 6-152 gets a reference to the first <section> element in
the document and replaces its content with a <p> element.
From then on, the user will only see the text of the <p> element on
the screen.

If we do
not want to replace the entire content of an element but just add some new
content, we can use the insertAdjacentHTML() method. This
method can add content before or after the current content and also outside the
element, depending on the value assigned to the first attribute.

Listing 6-153: Adding HTML code inside an
element

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function addElement() {

var element =
document.querySelector("section");

element.insertAdjacentHTML("beforeend", "<p>This is a new
text</p>");

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button" onclick="addElement()">Add
Content</button>

</section>

</body>

</html>

The insertAdjacentHTML() method adds content to the document, but
without affecting the previous content. When we press the button in the
document of Listing 6-153, the JavaScript code adds a <p> element below the <button> element (before the end of the <section> element). The result is shown in Figure 6-6.

Figure 6-6: Content
added to an element

Creating Element Objects

When HTML code is added to the document
through properties and methods like innerHTML or insertAdjacentHTML(), the browser parses the document and
generates the Element objects necessary to represent
the new elements. Although this is an accepted procedure to modify the
structure of a document, the Document object includes methods to
work directly with Element objects.

createElement(keyword)—This method creates a new Element object of
the type specified by the keyword attribute.

appendChild(element)—This method inserts the element represented by an Element object as
the child of an existent element in the document.

removeChild(element)—This method removes a child from an element. The attribute must
be a reference to the child to be removed.

If our intention is to create a new Element object to add an element to the document, we first have to create the
object with the createElement() method, and then use this object to add
the element to the document with the appendChild() method.

Listing 6-154: Creating Element objects

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function addElement() {

var element =
document.querySelector("section");

var
newelement = document.createElement("p");

element.appendChild(newelement);

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button" onclick="addElement()">Add
Element</button>

</section>

</body>

</html>

The code in Listing 6-154 adds a <p> element at the end of the <section> element, but the element has no content,
so we cannot see it on the screen. If we want to define the element's content,
we can assign a new value to its innerHTML property. The Element objects returned by the createElement() method are the same as the objects the
browser creates automatically to represent the document and therefore we can
modify their properties to assign new styles or define their content. The
following code assigns content to an Element object before adding the
element to the document.

Listing 6-155: Adding content to an Element object

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function addElement() {

var element =
document.querySelector("section");

var newelement =
document.createElement("p");

newelement.innerHTML = "This is a new element";

element.appendChild(newelement);

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button" onclick="addElement()">Add
Element</button>

</section>

</body>

</html>

Figure 6-7: Element
added to the document

The Basics:
There
is no much difference between adding elements with the innerHTML property or
these methods, but the createElement() method
becomes useful when working with APIs that require Element objects to
process information, such as when we have to process an image or a video that
is not going to be shown on the screen but sent to a server or stored on the
hard drive. We will learn more about APIs in this chapter and study practical
applications of the createElement() method
later.

6.5 Events

As we have already seen, HTML provides
attributes to execute JavaScript code when an event occurs. In recent examples,
we have implemented the onload attribute to execute a
function when the browser finishes loading the document and the onclick attribute that executes JavaScript code when the user
clicks on the element. What we did not mention is that these attributes, like
any other attribute, can be set from JavaScript. This is because, as we have
also seen, element's attributes are turned into properties inside Element objects, and therefore we can define their values from JavaScript
code. For instance, if we want to respond to the click event, we have to define the onclick property on the element.

Listing 6-156: Defining event attributes
from JavaScript code

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function addClick() {

var element =
document.querySelector("section > button");

element.onclick = showmessage;

}

function showmessage() {

alert("You pressed
the button");

}

window.onload = addClick;

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button">Show</button>

</section>

</body>

</html>

The
document of Listing 6-156 does not include any event attribute inside the elements;
they are all declared in the JavaScript code. Two attributes are defined in
this case, the onload attribute of
the Window object and the onclick attribute of
the <button> element. When the
document is loaded, the load event is fired, and the
addClick() function is
executed. In this function, we get a reference to the <button> element and
define its onclick attribute to
execute the showmessage() function
when the button is pressed. With this information, the document is ready to
work. If the user presses the button, a message is shown on the screen.

The Basics:
Declaring
the onload attribute in the <body> element or
the Window object presents no difference whatsoever,
but because we should always separate the JavaScript code from the HTML
document and from the code it is easier to set up the attribute for the Window object, this
is the recommended practice.

The addEventListener() Method

The use of event attributes in HTML
elements is not recommended because they defeat the main purpose of HTML5 that
is to provide a specific task to each language. HTML should define the
structure of the document, CSS its presentation, and JavaScript the
functionality. But the definition of these attributes from JavaScript code, as
we have done in the previous example, is not completely compatible with all
browsers and present some limitations, so it is not recommended either. For
these reasons, new methods were defined for the Window object to manage and respond to events.

addEventListener(event, listener, capture)—This method prepares an
element to respond to an event.
The
first attribute is the event’s name (without the prefix on), the second
attribute is a reference to the function that will respond to the event (called
listener), and the third attribute is a Boolean value that determines
whether the event is captured by the element or it propagates to other elements
(usually ignored or declared as false).

removeEventListener(event, listener)—This method removes a listener from an element.

The
names of the events required by these methods are not the same as the names of
the attributes. Actually, the names
of the attributes are made by adding the prefix on to the real name of the
event. For example, the onclick attribute represents the
click event. In the same way,
we have the load event (onload), the mouseover event (onmouseover), and so on. When we use
the addEventListener() method to make an element respond
to an event, we have to provide the real name of the event between quotes, as
in the following example.

Listing 6-157: Listening to events with
the addEventListener() method

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function addClick() {

var element =
document.querySelector("section > button");

element.addEventListener("click", showmessage);

}

function showmessage() {

alert("You pressed
the button");

}

window.addEventListener("load", addClick);

</script>

</head>

<body>

<section>

<h1>Website</h1>

<button
type="button">Show</button>

</section>

</body>

</html>

The code in Listing 6-157 is the same
as the previous example, but now the addEventListener() method is used to add event
listeners to the Window object and the <button> element.

Event Objects

Every function that responds to an event
receives an object that contains information about the event. Although some
events generate their own specific objects, there is an object called Event that is common to every event. The following are some of
its properties and methods.

target—This
property returns a reference to the object that received the event (usually an Element object).

type—This
property returns a string with the name of the event.

preventDefault()—This method
cancels the event to prevent the system from performing tasks by default (see
Chapter 17, Listing 17-3).

stopPropagation()—This method stops
the propagation of the event to other elements, so only the first element that
receives the event can process it (usually applied to elements that overlap and
can respond to the same event).

The Event object is automatically sent to the function as an attribute,
and therefore we have to declare a parameter that will receive this value. The
name of the parameter is irrelevant, but by convention it is defined as e or event. In the following example,
we use the Event object to identify the
element that was clicked by the user.

Listing 6-158: Using Event objects

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function addClicks() {

var list =
document.querySelectorAll("section > p");

for (var f = 0; f <
list.length; f++) {

var element = list[f];

element.addEventListener("click", changecolor);

}

}

function changecolor(event) {

var element = event.target;

element.style.backgroundColor =
"#999999";

}

window.addEventListener("load", addClicks);

</script>

</head>

<body>

<section>

<h1>Website</h1>

<p>Message number
1</p>

<p>Message number
2</p>

<p>Message number
3</p>

</section>

</body>

</html>

The code in Listing 6-158 adds a
listener for the click event to every <p> element inside the <section> element of our document, but they are
all handled by the same function. To identify which element was clicked from
the function, we read the target property of the Event object. This property returns a reference to the Element object that represents the element that was clicked.
Using this reference, we modify the element's background. In consequence, every
time the user clicks on the area occupied by a <p> element, the element's background turns
gray.

Figure 6-8: Only the
element that received the event is affected

The Event object is automatically passed to the function when the
function is called. If we want to send our own values along with this object,
we can handle the event with an anonymous function. The anonymous function only
receives the Event object, but from inside this
function we can call the function that responds to the event with any
attributes we want.

Listing 6-159: Responding to an event with
an anonymous function

<script>

function addClicks() {

var list =
document.querySelectorAll("section > p");

for (var f = 0; f <
list.length; f++) {

var element = list[f];

element.addEventListener("click", function(event) {

var ourvalue = 125;

changecolor(event, ourvalue);

});

}

}

function changecolor(event,
ourvalue) {

var element = event.target;

element.innerHTML =
"Value " + ourvalue;

}

window.addEventListener("load", addClicks);

</script>

The code in Listing 6-159 replaces
the code from the previous example. In this code, instead of calling the changecolor() function directly, we first execute
an anonymous function. This function receives the Event object, sets another variable called ourvalue with the value 125, and then calls the changecolor() function with both values. With
these values, the changecolor() function modifies the element's content.

Figure 6-9: The element
is modified with the values received by the function

Do It Yourself: Create a new HTML file with the
document of Listing 6-158. Open the document in your browser and click on the
area occupied by the <p>
elements. The background color of the element you clicked should change to
gray. Update the JavaScript code with the code in Listing 6-159 and open the
document again or refresh the page. Click on an element. The content of that
element should be replaced with the string "Value 125", as
illustrated in Figure 6-9.

In Listing 6-159, the value passed
to the changecolor() function along with the Event object was an absolute value
(125), but a problem arises when we try to pass the value of a variable. In
that case, because the instructions inside the anonymous function are not
processed until the event occurs, the function will contain a reference to the
variable instead of its current value. The issue becomes evident when we work
with values generated by a loop.

Listing 6-160: Passing values to a
listener

<script>

function addClicks() {

var list =
document.querySelectorAll("section > p");

for (var f = 0; f <
list.length; f++) {

var element = list[f];

element.addEventListener("click", function(event) {

var ourvalue = f;

changecolor(event, ourvalue);

});

}

}

function changecolor(event,
ourvalue) {

var element = event.target;

element.innerHTML =
"Value " + ourvalue;

}

window.addEventListener("load", addClicks);

</script>

In this example, instead of the
value 125, we assign the f variable to the ourvalue variable, but because the instruction
is not processed until the user clicks on the element, the system assigns a
reference of the f variable to ourvalue, not its value. This means that the
system is going to read the f variable and assign its
value to the ourvalue variable only when the click event is fired, and by that time the for loop will already be over and the current value of f will be 3 (after the loop is over, the final value of f is 3 because there are three <p> elements inside the <section> element). This means that the value
this code passes to the changecolor() function is always 3, no matter what element is clicked.

Figure 6-10: The value
passed to the function is always 3

This issue may be solved by
combining two anonymous functions. One function is executed right away, and the
other is returned by the first one. The main function receives the current
value of f, stores it in another variable, and
then returns a second anonymous function with these values. The anonymous
function returned is the one that is going to be executed when the event
occurs.

Listing 6-161: Passing values to a
listener with anonymous functions

<script>

function addClicks() {

var list =
document.querySelectorAll("section > p");

for (var f = 0; f <
list.length; f++) {

var element = list[f];

element.addEventListener("click", function(x) {

return function(event) {

var ourvalue = x;

changecolor(event, ourvalue);

};

}(f));

}

}

function changecolor(event,
ourvalue) {

var element = event.target;

element.innerHTML =
"Value " + ourvalue;

}

window.addEventListener("load", addClicks);

</script>

The main anonymous function is executed when the addEventListener() method is processed. It receives the current value of the f variable
(see the parentheses at the end) and puts that value in the x
variable. Then, the function returns a second anonymous function that assigns
the value of the variable x to ourvalue and calls the changecolor()
function to respond to the event. Because the interpreter reads the value of f in every
cycle of the loop to send it to the main anonymous function, the anonymous
function returned always works with a different value. For the first <p>
element, the value will be 0 (it is the first element on the list and f starts
counting from 0), the second element gets 1, and the third element gets 2. Now
the code produces a different content for each element.

Figure 6-11: The value is
different for each element

Some events generate unique values
that are passed to the listener for processing. These events work with their
own type of objects that inherit from the Event object. For instance, mouse
events send a MouseEvent object to the function. The
following are some of its properties.

button—This
property returns an integer that represents the button that was pressed (0 =
left button).

ctrlKey—This
property returns a Boolean value that determines if the Control key was pressed
when the event occurred.

altKey—This
property returns a Boolean value that determines if the Alt (Option) key was
pressed when the event occurred.

shiftKey—This
property returns a Boolean value that determines if the Shift key was pressed
when the event occurred.

metaKey—This
property returns a Boolean value that determines if the Meta key was pressed
when the event occurred (the Meta key is the Windows key on Windows keyboards
or the Command key on Macintosh keyboards).

clientX—This
property returns the horizontal coordinate where the mouse was located when the
event occurred. The coordinate is returned in pixels, and it is relative to the
area occupied by the window.

clientY—This
property returns the vertical coordinate where the mouse was located when the
event occurred. The coordinate is returned in pixels, and it is relative to the
area occupied by the window.

offsetX—This
property returns the horizontal coordinate where the mouse was located when the
event occurred. The coordinate is returned in pixels, and it is relative to the
area occupied by the element that received the event.

offsetY—This
property returns the vertical coordinate where the mouse was located when the
event occurred. The coordinate is returned in pixels, and it is relative to the
area occupied by the element that received the event.

pageX—This
property returns the horizontal coordinate where the mouse was located when the
event occurred. The coordinate is returned in pixels, and it is relative to the
document. It takes scrolling into account to calculate the value.

pageY—This
property returns the vertical coordinate where the mouse was located when the
event occurred. The coordinate is returned in pixels, and it is relative to the
document. It takes scrolling into account to calculate the value.

screenX—This
property returns the horizontal coordinate where the mouse was located when the
event occurred. The coordinate is returned in pixels, and it is relative to the
screen.

screenY—This
property returns the vertical coordinate where the mouse was located when the
event occurred. The coordinate is returned in pixels, and it is relative to the
screen.

movementX—This
property returns the
difference between the previous and the current positions of the mouse in the
horizontal axis. The value is returned in pixels, and
it is relative to the screen.

movementY—This
property returns the
difference between the previous and the current positions of the mouse in the
vertical axis. The value is returned in pixels, and
it is relative to the screen.

In Chapter 3, we explained that the
screen is divided into rows and columns of pixels and that computers use a
coordinate system to identify the position of each pixel (see Figure 3-50). What we did not mention is
that the same coordinate system is applied to every area, including the screen,
the browser window, and the HTML elements, so every one of them has its own
origin (their coordinates always start at 0, 0 at the top-left corner). The MouseEvent object can give us the coordinates of
the mouse when the event occurred, but because each area has its own coordinate
system, different values are reported. For example, the clientX and clientY properties contain the
coordinates of the mouse in the coordinate system of the window, but the offsetX and offsetY properties report the
position in the coordinate system of the element that received the event. The
following example detects a click and shows the position of the mouse inside
the window using the clientX and clientY properties.

Listing 6-162: Reporting the position of
the mouse

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function showposition(event)
{

alert("Position: " +
event.clientX + " / " + event.clientY);

}

window.addEventListener("click", showposition);

</script>

</head>

<body>

<section>

<h1>Website</h1>

<p>This is my
website</p>

</section>

</body>

</html>

The code in Listing 6-162 is
listening to the click event on the Window object, so a click anywhere on the window will execute
the showposition() function and show the mouse's position on the screen. This function
reads the clientX and clientY properties to get the position of the mouse relative to
the screen. If we want to get the position relative to an element, we have to
listen to the event on the element and read the properties offsetX and offsetY. The following example uses
these properties to create a progress bar which size is determined by the
current position of the mouse when it is over the element.

Listing 6-163: Calculating the position of
the mouse on an element

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<style>

#container {

width: 500px;

height: 40px;

padding: 10px;

border: 1px solid #999999;

}

#progressbar {

width: 0px;

height: 40px;

background-color: #000099;

}

</style>

<script>

function initiate() {

var element =
document.getElementById("container");

element.addEventListener("mousemove", movebar);

}

function movebar(event) {

var widthbar = event.offsetX - 10;

if (widthbar < 0) {

widthbar = 0;

} else if (widthbar > 500) {

widthbar = 500;

}

var element =
document.getElementById("progressbar");

element.style.width = widthbar +
"px";

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section>

<h1>Level</h1>

<div
id="container">

<div
id="progressbar"></div>

</div>

</section>

</body>

</html>

The document of Listing 6-163
includes two <div> elements, one inside another,
to recreate a progress bar. The <div> element identified with the
name container works as a container to
establish the boundaries of the bar, and the one identified with the name progressbar represents the bar itself. The purpose
of the application is to let the user set the size of the bar with the mouse,
so we have to listen to the mousemove event to follow every move
of the mouse and read the offsetX property to calculate the
size of the bar based on the current position.

Because the area occupied by the progressbar element will always be different (it is
defined with a width of 0 pixels by default), we have to listen to the mousemove event from the container element. This requires the code to
adjust the values returned by the offsetX property to the position of
the progressbar element. The container element includes
a padding of 10 pixels, so the bar will be 10 pixels from the left side of its
container, and that's the number we must subtract from the current value of offsetX to determine the width of the bar (event.offsetX - 10). If the mouse is 20 pixels from the
left side of the container, it means that it is 10 pixels from the left side of
the bar, so the bar should have a width of 10 pixels. This works until the
mouse is over the paddings of the container element. When the mouse is
located over the left padding, lets say at the position 5, the operation
returns the value -5, but we cannot set a negative size for the bar. Something
similar happens when the mouse is located over the right padding. In this case,
the bar will exceed the maximum size of the container. The issues are solved by
the if instructions. If the new width is less
than 0, we set it as 0, and if it is greater than 500, we set it as 500. With
the limits established, we get a reference to the progressbar element and modify its width property to set the new width.

Figure 6-12: Progress bar

Do It Yourself: Create a new HTML file with the
document of Listing 6-163 and open the document in your browser. Move the mouse
over the container
element. You should see the progressbar element expand or shrink to follow
the mouse, as shown in Figure 6-12.

Other events that produce their own
Event object are those related to the
keyboard (keypress, keydown, and keyup). The object is of type KeyboardEvent, and it includes the following
properties.

key—This
property returns a string that identifies the key or keys that triggered the
event.

ctrlKey—This
property returns a Boolean value that determines if the Control key was pressed
when the event occurred.

altKey—This
property returns a Boolean value that determines if the Alt (Option) key was
pressed when the event occurred.

shiftKey—This
property returns a Boolean value that determines if the Shift key was pressed
when the event occurred.

metaKey—This
property returns a Boolean value that determines if the Meta key was pressed
when the event occurred (the Meta key is the Windows key on Windows keyboards
or the Command key on Macintosh keyboards).

repeat—This
property returns a Boolean value that determines if the user is holding down
the key.

The most important property of the KeyboardEvent object is key. This property returns a string that represents the key
that triggered the event. Common keys like numbers and letters produce a string
with the same characters in lowercase letters. For example, if we want to check
if the key pressed was the letter A, we have to compare the value with the
string "a". The following example compares the value returned by the key property against a series of numbers to check whether
the key pressed was 0, 1, 2, 3, 4, or 5.

Listing 6-164: Detecting the key pressed

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<style>

section {

text-align: center;

}

#block {

display: inline-block;

width: 150px;

height: 150px;

margin-top: 100px;

background-color: #990000;

}

</style>

<script>

function detectkey(event) {

var element =
document.getElementById("block");

var code = event.key;

switch (code) {

case "0":

element.style.backgroundColor = "#990000";

break;

case "1":

element.style.backgroundColor = "#009900";

break;

case "2":

element.style.backgroundColor = "#000099";

break;

case "3":

element.style.backgroundColor = "#999900";

break;

case "4":

element.style.backgroundColor = "#009999";

break;

case "5":

element.style.backgroundColor
= "#990099";

break;

}

}

window.addEventListener("keydown", detectkey);

</script>

</head>

<body>

<section>

<div
id="block"></div>

</section>

</body>

</html>

The document of Listing 6-164 draws
a red block at the center of the window. To respond to the keyboard, we add a
listener for the keydown event to the window (the keydown event is fired for every key, while the keypress event is only fired for common keys,
such as letters and numbers). Every time a key is pressed, we read the value of
the key property and compare it to a series of
numbers. If a match is found, we assign a different color to the element's background.
Otherwise, the code does nothing.

Besides the common keys, the key property also reports special keys like Alt or Control.
The strings for the most common keys are "Alt", "Control",
"Shift", "Meta", "Enter", "Tab",
"Backspace", "Delete", "Escape", " "
(space bar), "ArrowUp", "ArrowDown", "ArrowLeft",
"ArrowRight", "Home", "End", "PageUp",
and "PageDown". The following code detects the arrow keys to change
the size of the block created by the previous example.

Listing 6-165: Detecting special keys

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<style>

section {

text-align: center;

}

#block {

display: inline-block;

width: 150px;

height: 150px;

margin-top: 100px;

background-color: #990000;

}

</style>

<script>

function detectkey(event) {

var element =
document.getElementById("block");

var size =
element.clientWidth;

var code = event.key;

switch (code) {

case "ArrowUp":

size += 10;

break;

case "ArrowDown":

size -= 10;

break;

}

if (size < 50) {

size = 50;

}

element.style.width = size
+ "px";

element.style.height =
size + "px";

}

window.addEventListener("keydown", detectkey);

</script>

</head>

<body>

<section>

<div
id="block"></div>

</section>

</body>

</html>

As we did in the example of Listing 6-146, we get the current width of
the element from the clientWidth property and then assign the
new value to the width property of the Styles object. The new value depends on the key that was
pressed. If the key was the up arrow, we increase the size in 10 pixels, but if
the key was the down arrow, the size is reduced by 10 pixels. In the end, we
check this value to make sure the block is never smaller than 50 pixels.

Do It Yourself: Create a new HTML file with the
document of Listing 6-165. Open the document in your browser and press the up
or down arrow. The block should expand or shrink according to the key pressed.

6.6 Debugging

Debugging is the process of finding and
correcting errors in our code. There are different types of errors, from
programming errors to logical errors, or even custom errors we generate to
indicate a problem found by the code itself. Some require the use of tools to
find them and figure out a solution and others just a little bit of patience
and perseverance. Most of the time, determining what is wrong with our code
requires reading the instructions one by one and following the logic until
something does not make sense. Fortunately, browsers provide a set of tools to
deal with these issues, and JavaScript includes some techniques we can
implement to make our lives easier.

Console

The most useful tool to check for errors
and debug our code is the Console. Consoles are available in almost every
browser but in different forms. Usually, they are opened at the bottom of the
browser window and are composed of several panels detailing information of
every aspect of the document, including the HTML code, CSS styles and, of
course, JavaScript. The panel called Console is the one that displays
errors and custom messages.

Figure 6-13: Google
Chrome console

The Basics: How to access this console varies
from browser to browser, and even among different versions of the same browser,
but the options are usually in the browser's main menu under the name Development
Tools or More Tools.

The types of errors we will often see
displayed on this console are programming errors. For instance, if we call a
non-existent function or try to read a property that is not part of an object,
it is considered a programming error and it is reported on the console.

Listing 6-166: Generating an error

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

wrongfunction();

</script>

</head>

<body>

<section>

<h1>Website</h1>

</section>

</body>

</html>

In Listing 6-166 we try to execute a
function called wrongfunction() that was not previously defined. The browser finds the error and
shows the message "wrongfunction is not defined" on the console to
report it.

Figure 6-14: Error
reported on the console

Do It Yourself: Create a new HTML file with the
document of Listing 6-166 and open the document in your browser. Access the
browser's main menu and find the option to open the console. In Google Chrome,
the menu is at the upper-right corner, and the option is called More Tools /
Developer Tools. You should see the error produced by the function printed on
the console, as illustrated in Figure 6-14.

Console Object

Sometimes errors are not programming
errors but logical errors. The JavaScript interpreter cannot find any errors in
the code, but the application is not doing what we expected. This may be caused
by a number of reasons, from an operation we forgot to make to a variable set
with an incorrect value. These are the most difficult errors to detect, but
there is a traditional programming technique that can help us find a solution
called breakpoints. Breakpoints are stopping points in our code that we
set to check the current state of the application. In a breakpoint, we show the
current values of variables or a message letting us know that the interpreter
reached that point.

Traditionally, JavaScript programmers
inserted an alert() method in parts of the code to
expose some values that could provide information to help them find the bug,
but this method was not suitable for most common situations because it stops
the execution of the code until the popup window is closed. Browsers simplify
this process by creating an object of type Console. This object is assigned to
the console property of the Window object and it becomes the connection between our code and the browser's
console. The following are some of the methods provided by the Console object to manage the console.

log(value)—This method displays the value of the attribute on the console.

assert(condition, values)—This method shows on the console the
values specified by the attributes if the condition specified by the first
attribute is false.

clear()—This method
clears the console. Browsers also offer a button at the top of the console with
the same functionality.

The most important method in the Console object is log(). With this method, we can
print a message on the console any time we want, without interrupting the
execution of the code, which means that we can check the values of the
variables and properties at any time and see if they match our expectations.
For example, we can print on the console the values generated by a loop in
every cycle to make sure that we are creating the right sequence of numbers.

Listing 6-167: Displaying messages on the
console with the log()
method

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

var list = [0, 5, 103, 24,
81];

for(var f = 0; f <
list.length; f++) {

console.log("The current number is " + f);

}

console.log("The final value of f is: " + f);

</script>

</head>

<body>

<section>

<h1>Website</h1>

</section>

</body>

</html>

The code in Listing 6-167 calls the log() method to show a message in every cycle of the loop and
also at the end to show the last value of the f variable. A total of 5 messages are printed on the console.

Figure 6-15: Messages on
the console

This little example illustrates the
power of the log() method and how it can help us
understand the way our code works. In this case, it demonstrates the mechanics
of a for loop. The value of the f variable in the loop goes from 0 to one number less than the
total number of values in the array (5), so the instruction inside the loop
prints a total of 5 messages with the values 0, 1, 2, 3, and 4. This may be
expected, but the log() method at the end of the code
prints the final value of f, which is not 4 but 5. In the
first cycle of the loop, the interpreter checks the condition with the initial
value of f. If the condition is true, it executes
the code. But the next cycle, the interpreter executes the operation assigned
to the loop (f++) before checking the
condition. If this time the condition is false, the loop is interrupted. This
is the reason why the final value of f is 5. At the end of the loop,
the value of f was increased once again
before the condition was checked and the loop interrupted.

The Basics: The log() method can also print objects on the
console, allowing us to read the content of an unknown object and identify its
properties and methods.

Error Event

At some point, we will find that sometimes
errors are not our fault. When our applications get more complex and
incorporate sophisticated libraries and APIs, errors are caused by factors
beyond our control, such as momentarily inaccessible resources or an unexpected
change in the device running the application. In order to help the code to
detect these errors and correct itself, JavaScript provides the error event. This event is available in several APIs, as we will
see later, but also as a global event that we can listen from the Window object.

Like other events, the error event creates its own Event object, called ErrorEvent, to transmit information to the listener.
This object includes the following properties.

error—This
property returns an object with information about the error.

message—This
property returns a string describing the error.

lineno—This
property returns the line in the document where the error occurred.

colno—This
property returns the column where the instruction that produced the error
begins.

filename—This
property returns the URL of the file where the error occurred.

Taking advantage of this event, we
can program our code to respond to expected and unexpected errors.

Listing 6-168: Responding to errors

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

function showerror(event){

console.log('Error: ' + event.error);

console.log('Message: ' +
event.message);

console.log('Line: ' + event.lineno);

console.log('Column: ' + event.colno);

console.log('URL: ' + event.filename);

}

window.addEventListener('error',
showerror);

wrongfunction();

</script>

</head>

<body>

<section>

<h1>Website</h1>

</section>

</body>

</html>

In the code in Listing 6-168, the
error is produced by the execution of a nonexistent function called wrongfunction(). When the browser tries to
execute this function, it finds the error and triggers the error event to report it. To identify the error, we print
messages on the console with the values of the ErrorEvent object's properties.

Figure 6-16: Information
about an error

Exceptions

There are times when we know that our code
could produce an error. For example, we may have a function that calculates a
number from a value inserted by the user. If the value received is out of a
particular range, the operation may be invalid. In computer programming, errors
that can be handled by the code are usually called exceptions, and the
process to generate an exception is called throw. In this terms, when we
report our own errors we say that we are throwing an exception. JavaScript
includes the following instructions to throw exceptions and catch errors.

throw—This
instruction generates an exception.

try—This
instruction indicates the set of instructions that should be tested for errors.

catch—This
instruction indicates the set of instructions that should be executed if an
exception occurs.

If we know that a function may
produce an error, we can detect it, throw an exception with the throw instruction, and then respond to the exception with the
combination of the try and catch instructions. The following example illustrates how this process works.

Listing 6-169: Throwing exceptions

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>JavaScript</title>

<script>

var stock = 5;

function sold(amount) {

if (amount > stock) {

var error = {

name: "ErrorStock",

message: "Out of stock"

};

throw error;

} else {

stock = stock - amount;

}

}

try {

sold(8);

} catch(error) {

console.log(error.message);

}

</script>

</head>

<body>

<section>

<h1>Website</h1>

</section>

</body>

</html>

The throw instruction works in a similar way than the return instruction. It breaks the execution of the function and
returns a value that is captured by the catch instruction. The value must be
specified as an object with the properties name and message. The name property should have a value
that identifies the exception and the message property should contain a
string describing the error. Once we have a function that throws an exception,
we have to call it from the try
catch
instructions. The syntax of these instructions is similar to the if else instructions. They define two blocks of code. If the instructions inside
the try block throw an exception, the instructions in the catch block are executed.

In our example, we have created a
function called sold() that keeps track of the items
sold in a store. When a customer makes a purchase, we call this function with the
number of items sold. The function receives this value and subtracts it from
the stock variable. Here is when we check if this
is a valid transaction. If there is not enough stock to fulfill the order, we
throw an exception. In this case, the stock variable is initialized with
the value 5, and the sold() function is called with the
value 8, so the function throws and exception. Because the call is made inside
a try block, the exception is captured, the
instructions inside the catch block are executed, and the
message "Out of stock" is printed on the console.

6.7 APIs

No matter the
experience and the knowledge we have about computer programming and the
programming language we use to create our applications, we cannot program the
whole applications ourselves. Creating a database system from scratch or
generating complex graphics on the screen could take a lifetime if we do not
have the help of other programmers and developers. In computer programming,
that help comes in the form of libraries and APIs. A library is a collection of
variables, functions and objects that perform common tasks, such as calculating
the pixels the system has to activate on the screen to show a tridimensional
object or filtering the values returned by a database. They reduce the amount
of code a developer has to write and provide a standard solution that works in
every browser. Because of their complexity, libraries always offer an
interface, a set of variables, functions, and objects, that we can use to
communicate with the code and describe what we want the library to do for us.
This visible part of the library is called API (Application Programming Interface), and it is
what we have to learn to be able to include a library in our projects.

Native Libraries

What turned
HTML5 into the leading development platform that it is today was not the
improvements on HTML, or the integration between this language with CSS and JavaScript,
but the definition of a path to standardize the tools and features that vendors
provide in their browsers by default. This includes a set of libraries that
address common issues like the generation of 2D and 3D graphics, data storage,
communication, and more. Because of HTML5, browsers now come packed with
powerful libraries, and their APIs are integrated with common JavaScript
objects and therefore readily available from our documents. Implementing these
APIs in our code, we can perform complex tasks by calling a method or setting a
property.

IMPORTANT:
Native
APIs have become a fundamental part of the development of professional
applications and video games, which is why they will be the subject of study in
the rest of this book.

External Libraries

Before the
appearance of HTML5, several JavaScript libraries were developed to overcome
the limitations of the technologies available at the time. Some of those
libraries were created with specific purposes, from processing and validating forms
to the generation and manipulation of graphics. Through the years, they have
become extremely popular, and some of them, like Google Maps, are almost
impossible to imitate by independent developers.

These
libraries are not part of HTML5 but are an important aspect of web development,
and some of them have been implemented in today’s most successful websites and
applications. They enhance JavaScript and contribute to the development of new
technologies for the Web. The following is a list of the most popular.

·
jQuery (www.jquery.com) is a multipurpose library that
simplifies JavaScript code and the interaction with the document. It makes HTML
elements easier to select and animations easy to generate as well as handling
events and helping implement Ajax in our applications.

·
React (facebook.github.io/react) is a graphic library that helps
with the creation of interactive user interfaces.

·
AngularJS (www.angularjs.org) is a library that expands HTML
elements to make them more dynamic and interactive.

·
Node.js (www.nodejs.org) is a server-side library which
purpose is the construction of network applications.

·
Modernizr (www.modernizr.com) is a library that can detect the
features available in the browser, including CSS properties, HTML elements, and
JavaScript APIs.

·
Moment.js (www.momentjs.com) is a library which sole purpose is
to process dates.

·
Three.js (www.threejs.org) is a 3D graphic library based on
an API included in browsers called WebGL (Web Graphics Library). We will study
this library in Chapter 12.

·
Google Maps (developers.google.com/maps/) is a set of libraries designed to
include maps in our websites and applications.

These
libraries are usually small files that we can download from a website and
include in our documents with the <script> element, as we do with
our own JavaScript files. Once the library is included in our document, we can
access its API from our code. For instance, the following document includes the
Modernizr library to detect whether the CSS property box-shadow is available
or not.

Listing 6-170: Detecting features with
Modernizr

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Modernizr</title>

<script
src="modernizr-custom.js"></script>

<script>

function initiate(){

var element = document.getElementById("subtitle");

if (Modernizr.boxshadow) {

element.innerHTML = 'Box Shadow is
available';

} else {

element.innerHTML = 'Box Shadow is NOT
available';

}

}

window.addEventListener('load', initiate);

</script>

</head>

<body>

<section>

<h1>Website</h1>

<p id="subtitle"></p>

</section>

</body>

</html>

Modernizr creates an object
called Modernizr
that offers properties for every HTML5 feature we want to detect. These
properties return a Boolean value that will be true or false depending on whether or not the
features are available. To include the library, we have to download the file
from their website (www.modernizr.com) and then add it to our
document with a <script> element, as we did in
Listing 6-170.

The file generated
by the website is called modernizr-custom.js and contains a detection
system for all the features we have selected. In our example, we have selected
the feature Box Shadow because that is the only thing we want to check. Once
the library is loaded, we have to read the value of the property that represents
the feature and respond accordingly. In this case, we insert a string in a <p> element. If the box-shadow property is available,
the element will show the message "Box Shadow is available", if not,
the message shown by the element will be "Box Shadow is NOT available".

Do It Yourself: Create a new HTML file with the
document of Listing 6-170. Go to www.modernizr.com, select the feature Box
Shadow (or the features you want to check), and click on Build to create your
file. A file called modernizr-custom.js
will be downloaded to your computer. Move the file to your document's folder
and open the document in your browser. If your browser supports the CSS
property box-shadow, you should see the message "Box Shadow is
available" on the screen.

IMPORTANT: There are dozens of external JavaScript
libraries available. This book does not cover the subject, but you can visit our
website and follow the links for this chapter to find more information.

Chapter 7 - Forms API

7.1 Processing Forms

The Forms API is a set of
properties, methods, and events that we can use to process forms and create our
own validation system. The API is embedded in forms and form elements, so we
can listen to the events or call the methods from the elements themselves. The
following are some of the methods available for <form> elements.

submit()—This method submits the form.

reset()—This method resets the form (it sets
the elements to their default value).

checkValidity()—This method returns a Boolean value
to indicate if the form is valid or not.

The API also offers the
following event to report every time a character is inserted or a value is
selected.

input—This event is fired on the form or
the elements when the user inserts or erase a character in an input field, and
also when a new value is selected.

change—This event is fired on the form or
the elements when a new value is inserted or selected.

Using these methods and events,
we can control the submission process from JavaScript. The following example
submits the form when a button is pressed.

Listing 7-1: Submitting a
form from JavaScript

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

<script>

function initiate() {

var button =
document.getElementById("send");

button.addEventListener("click",
sendit);

}

function sendit() {

var form =
document.querySelector("form[name='information']");

form.submit();

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<form name="information"
method="get" action="process.php">

<p><label>Email: <input
type="email" name="myemail" id="myemail"
required></label></p>

<p><button type="button"
id="send">Sign Up</button></p>

</form>

</section>

</body>

</html>

The code in Listing 7-1 adds a
listener for the click event to the <button> element to execute the sendit() function every time the button is
pressed. In this function, we get a reference to the <form> element and then submit the form
with the submit()
method.

In this example, we have
decided to get a reference to the <form> element with the querySelector() method and a selector that matches
its name
attribute, but we could have also added an id attribute to the element to get the
reference with the getElementById() method, as we did with the button.
Another alternative is to get the reference from the forms property of the Document object. This property returns an
array with references to all the <form> elements in the document, so we
have to read the value at the index corresponding to the form we want to
process (in our case, there is only one <form> element and therefore its reference is at
index 0).

Listing 7-2: Getting a
reference to the <form> element from
the forms property

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

<script>

function initiate() {

var button =
document.getElementById("send");

button.addEventListener("click",
sendit);

}

function sendit() {

var list =
document.forms;

var form = list[0];

form.submit();

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<form name="information"
method="get" action="process.php">

<p><label>Email: <input
type="email" name="myemail" id="myemail"
required></label></p>

<p><button type="button"
id="send">Sign Up</button></p>

</form>

</section>

</body>

</html>

Submitting the form with the submit() method is the same as doing it with
an <input> element of type submit (see Chapter 2), but the procedure
skips the browser's validation process. If we want the form to be validated, we
have to do it ourselves with the checkValidity() method. The method checks the form
and returns true or false to indicate whether it is valid or
not.

Listing 7-3: Checking the
validity of the form

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

<script>

function initiate() {

var button =
document.getElementById("send");

button.addEventListener("click",
sendit);

}

function sendit() {

var form =
document.querySelector("form[name='information']");

var valid =
form.checkValidity();

if (valid) {

form.submit();

} else {

alert("The
form can't be submitted");

}

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<form name="information"
method="get" action="process.php">

<p><label>Email: <input
type="email" name="myemail" id="myemail"
required></label></p>

<p><button type="button"
id="send">Sign Up</button></p>

</form>

</section>

</body>

</html>

The code in Listing 7-3 checks
the values in the form to determine its validity. If the form is valid, it is
submitted with the submit() method. Otherwise, a message is shown on the screen to
warn the user.

Do It Yourself: Create a new HTML file with the
document of Listing 7-3. Open the document in your browser and try to submit
the form. You should see a popup window telling you that the form cannot be
submitted. Insert a valid email address into the field. Now the form should be submitted.

7.2 Validation

As we have seen in Chapter 2, there are
many ways to validate forms in HTML. We can use input types that require
validation, such as email, turn a regular text type into a required field with the
required attribute, or even use special
types like pattern
to customize the validation requirements. However, when it comes to more
complex validation mechanisms, like comparing the values of two or more fields
or checking the results of an operation, our only option is to customize the
validation process using the Forms API.

Custom Errors

Browsers show an error message when
the user tries to submit a form that has an invalid field. These are predefined
messages that describe known errors, but we can define custom messages to
generate our own errors. To that end, Element objects representing form elements
include the following method.

setCustomValidity(message)—This
method sets a custom error and the message to be shown to the user if the form
is submitted. If no message is provided, the error is cleared.

The following example presents
a situation of complex validation. Two input fields are created to receive the
user’s first and last name. However, the form is only invalid when both fields
are empty. Users may enter either their first or last name to validate the form.
In a case like this, it is impossible to use the required attribute, because we do not know
which input field the user will choose to fill. Only by using customized errors,
we are able to create an effective validation mechanism for this scenario.

Listing 7-4: Setting
custom error messages

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

<script>

var name1, name2;

function initiate() {

name1 =
document.getElementById("firstname");

name2 =
document.getElementById("lastname");

name1.addEventListener("input",
validation);

name2.addEventListener("input",
validation);

validation();

}

function validation() {

if (name1.value == "" && name2.value ==
"") {

name1.setCustomValidity("Insert at least one
name");

name1.style.background = "#FFDDDD";

name2.style.background = "#FFDDDD";

} else {

name1.setCustomValidity("");

name1.style.background = "#FFFFFF";

name2.style.background = "#FFFFFF";

}

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<form name="registration"
method="get" action="process.php">

<p><label>First Name: <input
type="text" name="firstname"
id="firstname"></label></p>

<p><label>Last Name: <input
type="text" name="lastname"
id="lastname"></label></p>

<p><input type="submit"
value="Sign Up"></p>

</form>

</section>

</body>

</html>

The code in Listing 7-4 begins
by creating references to the two <input> elements and adding listeners for
the input event to both of them. This event is fired every time the
user adds or erase a character, so we can track every value inserted into the
field and validate or invalidate the form from the validation() function.

Because the <input> elements are empty when the
document is loaded, we have to stop the user from submitting the form before at
least one name is typed into the fields. For this reason, the validation() function is also called at the end
of the initiate()
function to check this condition.

The validation() function checks whether the form is
valid or not and sets or removes a custom error with the setCustomValidity() method. If both names are empty
strings, a custom error is set, and the background color for both elements is
changed to red to indicate the error to the user. However, if that condition
does not hold true later because at least one of the names was inserted, the
error is cleared assigning an empty string to the method, and the backgrounds
are set back to white.

It is important to mention that
the only change produced during this process is the modification of the
background color. The message declared for the error with the setCustomValidity() method will only be displayed when
the user tries to submit the form.

Do It Yourself: Create a new HTML file with the
document of Listing 7-4 and open the document in your browser. Try to submit
the form. You should see an error with the message "Insert at least one name".
Insert your name. The error should be removed.

The Basics:
Several
variables may be declared in the same line separated by commas. In Listing 7-4,
we declared two global variables called name1 and name2. The
instruction could have been avoided because variables declared inside functions
without the var operator have global
scope, but declaring the variables at the beginning of the code makes it easier
to maintain because we can identify with no much effort the variables our code
requires to work.

The invalid Event

Every time the user submits a form,
an event is fired if an invalid entry is detected. The event is called invalid and it is fired on the element that
produced the error. To customize the response, we can listen to this event from
the <form> element, as in the following example.

Listing 7-5: Creating a
custom validation system

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

<script>

var form;

function initiate() {

var button =
document.getElementById("send");

button.addEventListener("click",
sendit);

form =
document.querySelector("form[name='information']");

form.addEventListener("invalid", validation, true);

}

function
validation(event) {

var element =
event.target;

element.style.background = "#FFDDDD";

}

function sendit() {

var valid = form.checkValidity();

if (valid) {

form.submit();

}

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<form name="information"
method="get" action="process.php">

<p><label>Nickname: <input
pattern="[A-Za-z]{3,}" name="nickname"
id="nickname" maxlength="10"
required></label></p>

<p><label>Email: <input
type="email" name="myemail" id="myemail"
required></label></p>

<p><button type="button"
id="send">Sign Up</button></p>

</form>

</section>

</body>

</html>

In Listing 7-5, we create a new
form with two input fields to ask for a nickname and an email. The email input has its natural limitations
due to its type and a required attribute, but the nickname input has three validation
attributes: the pattern attribute that only admits a
minimum of 3 characters from A to Z (uppercase and lowercase), the maxlength attribute that limits the input to
a maximum of 10 characters, and the required attribute that invalidates the
field if it is empty.

The code is very similar to
previous examples. We listen to the load event to execute the initiate() function when the document finishes
loading, add the listener for the click event to the <button> element, as always, but then a
listener for the invalid event is added to the <form> element instead of the <input> elements. This is because we want to
achieve a validation control for the entire form, not just for a single element.
To do this, we have to include the value true as the third attribute of the addEventListener() method. This attribute tells the
browser that it has to propagate the event to the rest of the elements in the
hierarchy. As a result, even when the listener is added to the <form> element, it responds to events
fired by the elements within the form. To determine which is the invalid
element that calls the validation() function, we read the value of the target property. As we have seen in the
previous chapter, this property returns a reference to the element that fired
the event. Using that reference, the last instruction in this function changes
the background color of the element to red.

The ValidityState Object

The document of Listing 7-5 does not
perform any real-time validation. The fields are validated only when the form
is submitted. Considering the need for a more dynamic validation system, the
Forms API includes the ValidityState object. This object offers a series
of properties to indicate the validity status of a form element.

valid—This
property returns true if the value of the
element is valid.

The valid property returns the validity state
of an element considering every other possible validity status. If every
condition is valid, then the valid property returns true. If we want to check a particular
condition, we can read the rest of the properties offered by the ValidityState object.

valueMissing—This
property returns true when the required attribute
was declared, and the input field is empty.

typeMismatch—This
property returns true when the syntax of the
value inserted by the user does not comply with the type of the field. For
example, if the text inserted for an input of type email is not an
email address.

patternMismatch—This
property returns true when the value inserted
by the user does not match the provided pattern.

tooLong—This
property returns true when the maxlength attribute was declared, and the value
inserted by the user is longer than the value specified by the attribute.

rangeUnderflow—This
property returns true when the min attribute
was declared, and the value inserted by the user is less than the value specified by the attribute.

rangeOverflow—This
property returns true when the max attribute was declared, and the value
inserted by the user is greater than the value specified by the attribute.

stepMismatch—This
property returns
true when the step attribute was declared, and its
value does not correspond to the value of the attributes min, max and value.

customError—This
property returns true when we set a custom error. For
example, when we use the setCustomValidity() method studied earlier.

The ValidityState object is assigned to a property
called validity,
available in every form element. The following example reads the value of this
property to dynamically determine the validity of the elements in the form.

Listing 7-6: Validation
in real time

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

<script>

var form;

function initiate() {

var button =
document.getElementById("send");

button.addEventListener("click",
sendit);

form =
document.querySelector("form[name='information']");

form.addEventListener("invalid",
validation, true);

form.addEventListener("input", checkval);

}

function validation(event) {

var element = event.target;

element.style.background =
"#FFDDDD";

}

function sendit() {

var valid = form.checkValidity();

if (valid) {

form.submit();

}

}

function
checkval(event) {

var element =
event.target;

if
(element.validity.valid) {

element.style.background = "#FFFFFF";

}else{

element.style.background = "#FFDDDD";

}

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<form name="information"
method="get" action="process.php">

<p><label>Nickname: <input
pattern="[A-Za-z]{3,}" name="nickname"
id="nickname" maxlength="10"
required></label></p>

<p><label>Email: <input
type="email" name="myemail" id="myemail"
required></label></p>

<p><button type="button"
id="send">Sign Up</button></p>

</form>

</section>

</body>

</html>

In the code in Listing 7-6, a
listener for the input event is added to the form. Every
time the user modifies a field, inserting or erasing a character, the checkval() function is executed to respond to
the event.

The checkval() function also takes advantage of the target property to get a reference to the
element that fired the event and controls its validity by checking the value of
the valid property inside the validity property of the Element object (element.validity.valid). Using this information, we change
the color of the element's background that fired the input event in real time. The color will
be red until the user types a valid entry.

Do It Yourself: Create a new HTML file with the
document of Listing 7-6. Open the document in your browser and insert values in
the input fields. You should see the background of the fields changing colors
according to their validity (white valid, red invalid).

We can use the rest of the
properties offered by the ValidityState object to know exactly what
triggered the error, as in the following example.

Listing 7-7: Reading
validity states to display a customized error message

function
sendit() {

var element =
document.getElementById("nickname");

var valid = form.checkValidity();

if (valid) {

form.submit();

} else if (element.validity.patternMismatch ||
element.validity.valueMissing) {

alert('The nickname must have a minimum of 3 characters');

}

}

In Listing 7-7, the sendit() function is modified to detect specific errors. The
form is validated by the checkValidity() method, and if valid, it is
submitted with submit(). Otherwise, the values of the patternMismatch and valueMissing properties of the nickname input field are checked, and an
error message is shown when one or both of them return true.

Do It Yourself: Replace the sendit() function in the document of Listing 7-6 with the new function
in Listing 7-7 and open the document in your browser. Type a single character in
the nickname
field and submit the form. You should see a popup window asking you to insert a
minimum of 3 characters.

7.3 Pseudo-Classes

In addition to all the properties and methods provided by the Forms
API, CSS includes a few pseudo-classes to modify the styles of an element
depending on its validity status.

Valid and Invalid

These
pseudo-classes affect <input> elements
with a valid or an invalid value.

Listing 7-8: Using the :valid and :invalid pseudo-classes

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

<style>

input:valid{

background: #EEEEFF;

}

input:invalid{

background: #FFEEEE;

}

</style>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<input type="email"
name="myemail" required>

<input type="submit"
value="Send">

</form>

</section>

</body>

</html>

The form
in Listing 7-8 includes an <input> element for
emails. When the content of the element is valid, the :valid pseudo-class
assigns a blue background to the field. As soon as the content becomes invalid,
the :invalid pseudo-class changes
the background color to red.

Optional and Required

These
pseudo-classes affect form elements declared as required or optional.

Listing 7-9: Using the :required and :optional pseudo-classes

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

<style>

input:optional{

border: 2px solid #009999;

}

input:required{

border: 2px solid #000099;

}

</style>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<p><input type="text"
name="myname"></p>

<p><input type="text"
name="mylastname" required></p>

<p><input type="submit"
value="Send"></p>

</form>

</section>

</body>

</html>

The
example of Listing 7-9 includes two input fields: myname and mylastname. The first
one is optional, but mylastname is required.
The pseudo-classes assign a different color to the border of these fields
according to their condition (required is shown in blue and optional in green).

In-range and Out-of-range

These pseudo-classes
affect elements with a value inside or outside the specified range.

Listing 7-10: Using the :in-range and :out-of-range pseudo-classes

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Forms</title>

<style>

input:in-range{

background: #EEEEFF;

}

input:out-of-range{

background: #FFEEEE;

}

</style>

</head>

<body>

<section>

<form name="myform"
method="get" action="process.php">

<input type="number"
name="mynumber" min="0" max="10">

<input type="submit"
value="Send">

</form>

</section>

</body>

</html>

An input field of type number is included in the example above to test these
pseudo-classes. When the value entered in the element is less than 0 or greater than 10, the background turns red, but
as soon as we enter a value inside the specified range, the background turns
blue.

Chapter 8 - Media

8.1 Video

A video is a powerful form of
communication. Nobody can deny the importance of video in websites and web
applications today, let alone those developing the technologies for the Web.
This is the reason why HTML5 includes an element to load and play videos.

<video>—This element
embeds a video in the document.

The <video> element
includes the following attributes to set the area occupied by the video and
configure the player.

src—This
attribute specifies the URL of the video to be played.

width—This
attribute determines the width of the display area.

height—This
attribute determines the height of the display area.

controls—This is a
Boolean attribute. If present, the browser displays an interface to let the
user control the video.

autoplay—This is a
Boolean attribute. If present, the browser automatically plays the video as
soon as it can.

loop—This is a
Boolean attribute. If present, the browser plays the video in a loop.

muted—This is a
Boolean attribute. If present, the audio is silenced.

poster—This
attribute specifies the URL of the image that will be shown while waiting for
the video to be played.

preload—This attribute
determines if the browser should preload the video. It can take three values: none, metadata or auto. The first
value indicates that the video shouldn’t be cached, usually with the purpose of
minimizing unnecessary traffic. The second value, metadata, recommends
the browser to fetch some information about the resource, such as the
dimensions, duration, first frame, etc. The third value, auto, prompts the
browser to download the file as soon as possible (this is the value by
default).

The <video> element requires opening and
closing tags and only a few parameters to accomplish its function. The syntax
is simple, and only the src attribute is necessary to load a
video.

Listing 8-1: Playing
video with the <video> element

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Video Player</title>

</head>

<body>

<section>

<video
src="trailer.mp4">

</video>

</section>

</body>

</html>

The <video> element loads the video specified
by the src
attribute and reserves an area in the document of the size of the video, but
the video is not played. We have to tell the browser when we want to play the
video or provide the tools for the user to decide. There are two attributes we
can add to the element for this purpose: controls and autoplay. The controls attribute indicates to the browser
that it should provide its own elements (buttons and sliders) to let the user control
the video, and the autoplay attribute asks the browser to start
playing the video as soon as it can.

Listing 8-2: Activating
the browser's controls

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Video Player</title>

</head>

<body>

<section>

<video
src="trailer.mp4" controls autoplay>

</video>

</section>

</body>

</html>

Do It Yourself: Create a new HTML file with the
document of Listing 8-2. Download the trailer.mp4 video from our website. Open
the document in your browser. The browser should begin playing the video right
away and provide buttons to control it.

The browsers determine the size
of the display area from the size of the video, but we can define a custom size
with the width
and height attributes. These attributes
declare the dimensions of the element in pixels. When they are present, the
size of the video is automatically adjusted to fit into those dimensions, but
they are not intended to stretch the video. We may use them to limit the area
occupied by the media and preserve consistency in our design.

Listing 8-3: Defining the
display area

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Video Player</title>

</head>

<body>

<section>

<video
src="trailer.mp4" width="720" height="400"
controls>

</video>

</section>

</body>

</html>

The Basics: If you have to make the video
responsive, you can ignore the width and height attributes and declare the size of
the video from CSS and Media Queries. The <video> element may be adapted to the size
of its container as we did with images in Chapter 5.

The <video> element includes additional
attributes that may be useful sometimes. For instance, the preload attribute asks the browser to start
downloading the video as soon as it can, so when the user decides to play it,
the video is ready. We also have the loop attribute to play the video on a
loop, and the poster attribute to specify an image that
will be shown instead of the video while it is not being played.

Listing 8-4: Including a
placeholder for the video

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Video Player</title>

</head>

<body>

<section>

<video
src="trailer.mp4" width="720" height="400"
preload controls loop poster="poster.jpg">

</video>

</section>

</body>

</html>

The document of Listing 8-4
preloads the video, provides default controls, plays the video repeatedly, and
shows an image instead of the video until the video starts playing. Figure 8-1
shows what we see before we press the play button.

Figure 8-1: Video poster

© Copyright
2008, Blender Foundation / www.bigbuckbunny.org

Do It Yourself: Update your document with the code in Listing 8-4.
Download the poster.jpg file from our website. Open the document in your
browser. You should see something similar to Figure 8-1.

Video Formats

In theory, the <video> element alone should be more than
enough to load and play a video, but things get a little more complicated in
real life. This is because even when the <video> element and its attributes are
standard, there is no standard video format for the Web. The problem is that
some browsers support a group of codecs that others do not, and the codec used
in the MP4 format (the only one supported by major browsers like Safari and
Internet Explorer) has a commercial license.

The most common options are
OGG, MP4, and WebM. These formats are containers for video and audio. OGG
contains Theora video and Vorbis audio codecs, MP4 contains H.264 for video and
AAC for audio, and WebM uses VP8 video codec and Vorbis audio codec. Presently, OGG and WebM are
supported by Mozilla Firefox, Google Chrome, and Opera, while MP4 works on
Safari, Internet Explorer and Google Chrome.

HTML considers this scenario
and includes an additional element that works along with the <video> element to define possible sources
for a video.

<source>—This element
defines a source for a video. It must include the src attribute to
define the file's URL.

When we need to specify
multiple sources for the same video, we have to replace the src attribute in the <video> element with <source> elements between the tags, as in
the following example.

Listing 8-5: Creating a
cross-browser video player with default controls

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Video Player</title>

</head>

<body>

<section>

<video
width="720" height="400" controls>

<source
src="trailer.mp4">

<source
src="trailer.ogg">

</video>

</section>

</body>

</html>

In Listing 8-5, the <video> element is expanded. Now, within
the element’s tags, there are two <source> elements. These elements provide
different video sources for the browser to choose from. The browser reads the <source> elements and decides which file
should be played according to the supported formats (in this case, MP4 or OGG).

Do It Yourself: Create a new HTML file with the document
of Listing 8-5. Download the files trailer.mp4 and trailer.ogg from our
website. Open the document in your browser. The video should play as usual, but
now the browser selects which source to use.

IMPORTANT: Browsers require videos to be sent
by the server with the corresponding MIME type. Every file has a MIME type associated to indicate the
format of its content. For instance, the MIME type for an HTML file is text/html. Servers are already configured for
the most common video formats but usually are not for new formats such as OGG
or WEBM. How to include
this new MIME type depends on the characteristics
of your server. A simple way to do it is to append a new
line to the .htaccess file. Most servers
provide this configuration file in the root folder of every website. The
corresponding syntax is Addtype MIME/type
extension
(e.g., AddType video/ogg ogg).

8.2 Audio

Audio is not as popular as video. We
can shoot a video with a personal camera that will generate millions of views
but accomplishing the same result with an audio file would be almost
impossible. However, audio is still present on the Web through radio shows and
podcasts. For this reason, HTML5 also provides an element to play audio files.

<audio>—This element
embeds audio in the document.

This element works in the same
way and shares several attributes with the <video> element.

src—This
attribute specifies the URL of the file to be played.

controls—This is a Boolean
attribute. If present, it activates the interface provided by browsers.

autoplay—This is a
Boolean attribute. If present, the browser automatically plays the audio as
soon as it can.

loop—This is a
Boolean attribute. If present, the browser plays the audio in a loop.

preload—This
attribute determines if the browser should preload the audio file. It can take
three values: none, metadata or auto. The first
value indicates that the audio shouldn’t be cached, usually with the purpose of
minimizing unnecessary traffic. The second value, metadata, recommends
the browser to fetch some information about the resource, such as the duration.
The third value, auto, prompts the browser to
download the file as soon as possible (this is the value by default).

The implementation of the <audio> element in our document is very
similar to the <video> element. We have to specify the
source and provide a way for the user to play the audio.

Listing 8-6: Playing
audio with the <audio> element

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Audio Player</title>

</head>

<body>

<section>

<audio
src="beach.mp3" controls>

</audio>

</section>

</body>

</html>

Again, we have to talk about
codecs, and again, the HTML code in Listing 8-6 should be more than enough, but
it is not. MP3 is under commercial license; therefore, it is not supported by
browsers like Mozilla Firefox or Opera. Vorbis (the audio codec in the OGG
container) is supported by these browsers but not by Safari and Internet
Explorer. So, once again, we have to use the <source> element to provide at least two
formats for the browser to choose from.

Listing 8-7: Creating a
cross-browser audio player with default controls

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Audio Player</title>

</head>

<body>

<section>

<audio
id="media" controls>

<source
src="beach.mp3">

<source
src="beach.ogg">

</audio>

</section>

</body>

</html>

The document of Listing 8-7
plays music in every browser with controls by default. Those that cannot play
MP3 will play OGG and vice versa. What we have to remember is that MP3, as well
as the MP4 format for video, are restricted by commercial licenses, so we can
only use them in some circumstances according to what is determined those licenses.

8.3 Media API

Adding the controls attribute to the <video> or <audio> elements, we activate the interface
provided by each browser. This interface may work for simple websites or small
applications, but in a professional application, where every detail counts, it
is necessary to have absolute control over the whole process and provide a
consistent design across all devices and applications. Browsers include the
Media API to provide this alternative. This is a group of properties, methods,
and events designed to manipulate and integrate video and audio. Combining this
API with HTML and CSS, we can create our own video or audio player with the
features we want.

The API is embedded in the Element objects that represent the <video> and <audio> elements. The following are some of
the properties included in these objects to provide information about the
media.

paused—This
property returns true if the media is
currently paused or hasn’t started playing.

ended—This
property returns true if the media has
finished playing.

duration—This
property returns the length of the media in seconds.

currentTime—This
property sets or returns a value that determines the position in which the
media is being played or should start playing.

volume—This
property sets or returns the volume of the media. The possible values range
from 0.0 to 1.0.

muted—This
property sets or returns the condition of the audio. The values are true (muted) or false (not muted).

error—This
property returns the error value if an error has occurred.

buffered—This
property offers information about how much of the file has been loaded into the
buffer. Because users may force the browser to download the media from
different positions in the timeline, the information returned by buffered is an array
containing every part of the media that was downloaded, not just the one that
starts from the beginning. The elements of the array are accessible by the
attributes end() and start(). For
example, the code buffered.start(0) returns the time when the first portion of
the media begins, and buffered.end(0) returns the time when it ends.

The elements also include the
following methods to manipulate the media.

play()—This method
plays the media.

pause()—This method
pauses the media.

load()—This method loads the media file.
It is useful for loading the media in advance from dynamic applications.

canPlayType(type)—This
method returns a value that determines whether a file format is supported by
the browser or not. The type attribute is a MIME type that
represents the media format, such as
video/mp4 or video/ogg. The method can return three values
depending on how confident it is that it can play the media: an empty string
(not supported), the string "maybe" and the string "probably".

Events are also available for
this API to inform about the current situation of the media, like the progress
downloading the file, whether the video has reached the end, or whether the
video is paused or playing, among others. The following are the most frequently
used.

progress—This event
is fired periodically to provide updates on the progress downloading the media.
The information is accessible through the buffered attribute.

canplaythrough—This event
is fired when the entire media can be played without interruption. The status
is established considering the current download rate and assuming that it will
be the same for the rest of the process. There is another event for this
purpose called canplay, but it does not consider the whole situation, and it is fired
when there are only a couple of frames available.

ended—This event
is fired when the media reaches the end.

pause—This event
is fired when the media is paused.

play—This event
is fired when the media starts playing.

error—This event
is fired when an error occurs. It is delivered to the <source> element
corresponding to the media source that produces the error.

Video Player

Every video player needs a control
panel with some basic features. The API does not provide a way to create these
buttons or sliders; we have to define them using HTML and CSS. The following
example creates an interface with two buttons to play and mute the video, a <div> element to represent a progress
bar, and a slider to control the volume.

Listing 8-8: Creating a video player with HTML

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Video Player</title>

<link
rel="stylesheet" href="player.css">

<script
src="player.js"></script>

</head>

<body>

<section id="player">

<video id="media"
width="720" height="400">

<source src="trailer.mp4">

<source src="trailer.ogg">

</video>

<nav>

<div
id="buttons">

<input
type="button" id="play" value="Play">

<input
type="button" id="mute" value="Mute">

</div>

<div
id="bar">

<div
id="progress"></div>

</div>

<div
id="control">

<input
type="range" id="volume" min="0"
max="1" step="0.1" value="0.6">

</div>

<div
class="clear"></div>

</nav>

</section>

</body>

</html>

This document also includes
resources from two external files. One of those files is player.css with the styles required by the player.

Listing 8-9: Designing
the video player

#player {

width: 720px;

margin: 20px auto;

padding: 10px 5px 5px 5px;

background: #999999;

border: 1px solid #666666;

border-radius: 10px;

}

#play, #mute {

padding: 5px 10px;

width: 65px;

border: 1px solid #000000;

background: #DDDDDD;

font-weight: bold;

border-radius: 10px;

}

nav {

margin: 5px 0px;

}

#buttons {

float: left;

width: 135px;

height: 20px;

padding-left: 5px;

}

#bar {

float: left;

width: 400px;

height: 16px;

padding: 2px;

margin: 2px 5px;

border: 1px solid #CCCCCC;

background: #EEEEEE;

}

#progress {

width: 0px;

height: 16px;

background: rgba(0,0,150,.2);

}

.clear {

clear: both;

}

The CSS code in Listing 8-9
uses techniques from the Traditional Box Model to draw a box at the center of
the screen that contains every component of the video player. There is nothing
unusual in this code except for the size assigned to the progress element. As we did in the example
of Listing 6-163 (Chapter 6), we define the initial
width of this element as 0 pixels to use it as a progress bar. The result is
illustrated in Figure 8-2.

Figure 8-2: Custom video
player

© Copyright
2008, Blender Foundation / www.bigbuckbunny.org

Do It Yourself: Create a new HTML file with the
document of Listing 8-8. Create two more files for the CSS styles and JavaScript
codes called player.css and player.js, respectively. Copy the code in
Listing 8-9 into the CSS file and then copy every JavaScript code listed below
into the JavaScript file.

As always, we should begin our JavaScript
code by declaring the variables required by our application and the function
that is going to be executed as soon as the document is loaded.

Listing 8-10: Initializing the application

var maxim,
mmedia, play, bar, progress, mute, volume, loop;

function initiate() {

maxim = 400;

mmedia =
document.getElementById("media");

play = document.getElementById("play");

bar = document.getElementById("bar");

progress =
document.getElementById("progress");

mute = document.getElementById("mute");

volume = document.getElementById("volume");

play.addEventListener("click", push);

mute.addEventListener("click", sound);

bar.addEventListener("click", move);

volume.addEventListener("change",
level);

}

In the initiate() function in Listing 8-10, we create
a reference to each element and also set the variable maxim to always know the maximum size of
the progress bar (400 pixels). In addition, we set up listeners for multiple
events to respond to the user. There are several actions we have to pay
attention to: when the user clicks on the Play and Mute buttons, when changes
the volume from the range input, and when clicks on the
progress bar to move the video forward or backward. For this purpose, listeners
for the click
event are added to the elements play, mute,
and bar, and one for the change event is added to the range input to control the volume. Every
time the user clicks on one of these elements or moves the slider, the
correspondent functions are executed: push() for the Play button, sound() for the Mute button, move() for the progress bar, and level() for the slider.

The first function we have to
implement is push().
This function is responsible for playing or pausing the video when the Play or
Pause buttons are pressed.

Listing 8-11: Playing and
pausing the video

function push()
{

if (!mmedia.paused && !mmedia.ended) {

mmedia.pause();

play.value = "Play";

clearInterval(loop);

} else {

mmedia.play();

play.value = "Pause";

loop = setInterval(status, 1000);

}

}

The push() function is executed when the user
clicks the Play button. This button has two purposes: it shows the text
"Play" to play the video or "Pause" to pause it, according
to the current state. So when the video is paused, or it has not started,
pressing the button will play the video, but the opposite occurs if the video
is already being played. To determine this condition, the code detects the
state of the media checking the properties paused and ended. This is done by the if instruction in the first line of
the function. If the values of the paused and ended properties are false, it means that the video is
playing, and therefore the pause() method is executed to pause the video,
and the text of the button is changed to "Play". Notice that we apply
the !
operator to each property (logical NOT) to achieve this purpose. If the
properties return false, the operator changes this value to
true. The if instruction should be read "if
the media is not paused and the media has not ended, then do this".

If the video is paused or has
finished playing, the condition is false,
and the play() method is executed to start or
resume the video. In this case, we are also performing an important action
which is to initiate the execution of the status() function each second with the setInterval() method of the Window object (see Chapter 6) to update
the progress bar. The following is the implementation of this function.

Listing 8-12: Updating the
progress bar

function
status() {

if (!mmedia.ended) {

var size =
parseInt(mmedia.currentTime * maxim / mmedia.duration);

progress.style.width = size + "px";

} else {

progress.style.width = "0px";

play.value = "Play";

clearInterval(loop);

}

}

The status() function is executed each second
while the video is being played. In this function, we also have an if instruction to test the status of the video. If
the ended property returns false, we calculate how long the progress
bar should be in pixels and set a new size for the <div> element that is representing it.
But if the value of the ended property is true (which means the video has
finished), we set the size of the progress bar back to 0 pixels, change the text of the
button to "Play", and cancel the loop with the clearInterval() method. After this, the status() function will no longer be
executed.

This code updates the progress
bar, but let's see how we calculate the current value. We need the value of the
currentTime property to know what part of the
video is being played, the value of the duration property to know how long is the
video, and the value of the maxim variable to know the maximum size
allowed for the progress bar. The formula is a simple rule of three. We have to
multiply the current time by the maximum size and divide the result by the
total duration (current-time ×
maximum / total-duration).
The result is the new size in pixels of the <div> element that represents the progress
bar.

The function to handle the click event for the play element (the Play button) has
already been created; now it is time to do the same for the progress bar. We
called this method move().

Listing 8-13: Playing from
the position selected by the user

function
move(event) {

if (!mmedia.paused && !mmedia.ended) {

var mouseX =
event.offsetX - 2;

if (mouseX < 0) {

mouseX = 0;

} else if (mouseX >
maxim) {

mouseX = maxim;

}

var newtime = mouseX *
mmedia.duration / maxim;

mmedia.currentTime = newtime;

progress.style.width = mouseX + "px";

}

}

In the initiate() function, a listener for the click event was added to the bar element to check every time the
user wants to start playing the video from a new position. When the event is
fired, the move()
function is executed to handle it. This function begins with an if instruction, like the previous functions,
but this time the goal is to perform the action only when the video is being
played. If the properties paused and ended are false it means that the video is playing
and the code has to be executed.

To calculate the time when the
video should start playing, we get the position of the mouse from the offsetX property (see Listing
6-163,
Chapter 6), calculate the size of the progress bar considering the paddings of
the bar
element, and then convert this size into seconds to start playing the video
from the new location. The value in seconds that the position of the mouse
represents in the timeline is calculated by the formula mouseX × video.duration / maxim. Once we get this value, we have to
assign it to the currentTime property to start playing the video
from that time. In the end, the position of the mouse is assigned to the width property of the progress element to reflect the new state of
the video on the screen.

There are two small functions
left we need to introduce to control the volume of the media. The first one is
the sound()
function, assigned as the listener for the click event of the Mute button.

Listing 8-14: Switching to
sound or silence with the muted property

function
sound() {

if (mute.value == "Mute") {

mmedia.muted = true;

mute.value = "Sound";

} else {

mmedia.muted = false;

mute.value = "Mute";

}

}

The function in Listing 8-14
activates or deactivates the audio of the media depending on the value of the value attribute of the Mute button. The
button displays different texts according to the situation. If the current
value is "Mute", the sound is muted, and the value of the button is
changed to "Sound". Otherwise, the audio is activated, and the value
of the button is changed to "Mute".

When the sound is active, the
volume can be controlled from the slider created by the range input, located at the end of the
progress bar. The element fires the change event when its value changes. The
event is handled by the level() function.

Listing 8-15: Controlling
the volume

function
level() {

mmedia.volume = volume.value;

}

The function in
Listing 8-15 assigns the value of the value attribute of the <input> element that represents
the slider to the volume property of the media.
The only thing we have to keep in mind is that this property takes values from 0.0 to 1.0. Numbers out of that
range will return an error.

With this small function, the
code for the video player is almost ready; all it is left to do is to listen to
the load
event to start the application when the document is loaded.

Listing 8-16: Listening to
the load event to start the application

window.addEventListener("load",
initiate);

Do It Yourself: Copy all the JavaScript codes since
Listing 8-10 into the player.js
file. Open the document of Listing 8-8 in your browser and click the Play
button. Try the application in different browsers.

8.4 Closed Captioning

Closed captioning is a feature added
to video players to show text on the screen while the video is being played. It
has been used in television and different forms of video distribution for
decades, but it has always been difficult to replicate on the Web. HTML
introduces the following element to simplify its implementation.

<track>—This element
adds closed captioning to a video.

The element has to be included as a child of the <video>
or <audio> elements. To specify the source, type, and how the
closed captioning is going to be shown on the screen, the <track>
element provides the following attributes.

src—This
attribute declares the URL of the file containing the text for the closed
captioning. The format of this file may be any of those supported by browsers,
but the specification declares the WebVTT format as the official for this
element.

srclang—This
attribute declares the human language of the text. It works with the same
values as the lang attribute of the <html> element
studied in Chapter 2.

default—This
attribute declares the track we want to include by default. If only one <track> element is
provided, this attribute may be used to activate closed captioning.

label—This
attribute provides a title for the track. If multiple <track> elements are
included, this attribute helps users find the right one.

kind—This
attribute declares the type of content assigned to the track. The possible
values are subtitles (for
subtitles), captions (usually for
captions representing sound), descriptions (for audio
synthesis), chapters (for
navigation between chapters) and metadata (for
additional information, not shown on the screen). The default value is subtitles.

The <track> element works with both the <video>
and the <audio> elements to add subtitles or show additional
information for our videos and audio tracks.

Listing 8-17: Adding
subtitles with the <track> element

<!DOCTYPE html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Video
Subtitles</title>

</head>

<body>

<section>

<video
width="720" height="400" controls>

<source
src="trailer.mp4">

<source
src="trailer.ogg">

<track
src="subtitles.vtt" srclang="en" default>

</video>

</section>

</body>

</html>

IMPORTANT: The <track> element does not allow the
construction of cross-origin applications, and therefore you have to upload all
the files to your own server and run everything under the same domain
(including the HTML document, the videos, and the subtitles). Another alternative to test this example
is to use a local server, like the one installed by the MAMP application (see
Chapter 1 for more information). This situation can be avoided using the CORS technology,
and the crossorigin
attribute explained in Chapter 11.

In
Listing 8-17, we declared only one <track> element. The
language for the source was set as English, and the default attribute
was included to set up this track as default and activate closed captioning.
The source of this element was declared as a file in the WebVTT format (subtitles.vtt). WebVTT
stands for Web Video Text Tracks, the standard format for closed captioning.
The files in this format are text files with a specific structure, as
illustrated by the following example.

Listing 8-18: Defining a WebVTT file

WEBVTT

00:02.000 --> 00:07.000

Welcome

to the
&lt;track&gt; element!

00:10.000 -->
00:15.000

This is a simple
example.

00:17.000 -->
00:22.000

Several tracks can be
used simultaneously

00:22.000 -->
00:25.000

to provide text in
different languages.

00:27.000 -->
00:30.000

Goodbye!

Listing
8-18 shows the basic structure of a WebVTT file. The first line with the text
"WEBVTT" is mandatory
as it is the empty line in between declarations. The declarations are called cues,
and they require the syntax minutes:seconds.milliseconds to indicate
the start and the end times. This is a rigid format; we always have to respect
the structure shown in the example of Listing 8-18 and declare each parameter
with the same number of digits (two for minutes, two for seconds and three for
milliseconds).

Do It Yourself: Create a new HTML file with the document of Listing 8-17.
Create a text file with the declarations in Listing 8-18 and name it subtitles.vtt. Upload these files and the video
files to your server or copy the files to your local server (see MAMP in
Chapter 1), and open the HTML document in your browser. You should see the
video playing with subtitles and a button on the side to activate or deactivate
the subtitles.

The cues (the lines of text in a closed captioning file) may
include the special tags <b>, <i>, <u>, <v> and <c>. The first three are for emphasis, as in HTML, while
the <v> tag declares whose voice the text is representing and
the <c> tag allows us to provide custom styles using CSS.

Listing 8-19: Including
tags in a WebVTT file

WEBVTT

00:02.000 -->
00:07.000

<i>Welcome</i>

to the
&lt;track&gt; element!

00:10.000 -->
00:15.000

<v Robert>This
is a simple <c.captions>example</c>.

00:17.000 -->
00:22.000

<v
Martin>Several tracks can be used simultaneously

00:22.000 -->
00:25.000

<v Martin>to
provide text in different languages.

00:27.000 -->
00:30.000

<b>Good bye!</b>

WebVTT uses a pseudo-element for referencing the cues. This
pseudo-element, called ::cue, can take a selector for a class between parentheses.
In Listing 8-19, the class is called captions (<c.captions>), so the CSS selector should be written as ::cue(.captions), as in the following example.

Listing 8-20: Declaring
styles for WebVTT

::cue(.captions){

color: #990000;

}

IMPORTANT: Only a reduced group of CSS
properties, such as color, background,
and font, are available for this
pseudo-element; the rest are ignored.

WebVTT also provides the possibility of aligning and positioning
each cue using the following parameters and values.

align—This
parameter aligns the cue in relation to the center of the space covered by the
media. The possible values are start, middle, and end.

vertical—This
parameter changes the orientation to vertical and orders the cue according to
two values: rl (right to left) or lr (left to
right).

position—This
parameter sets the position of the cue in columns. The value can be expressed
as a percentage or a number from 0 to 9. The
position declared is established according to the orientation.

line—This
parameter sets the position of the cue in rows. The value can be expressed as a
percentage or a number from 0 to 9. In a
horizontal orientation, the position declared is vertical, and vice versa.
Positive numbers set the position from one side and negative numbers from the
other, depending on the orientation.

size—This
parameter sets the size of the cue. The value may be declared as a percentage,
and it is determined from the width of the media.

These parameters and their corresponding values are declared at
the end of the cue separated by a colon. Multiple declarations can be made for
the same cue, as shown in the following example.

Listing 8-21: Configuring
cues

WEBVTT

00:02.000 -->
00:07.000 align:start position:5%

<i>Welcome</i>

to the &lt;track&gt; element!

Do It Yourself: Using the WebVTT file created in Listing
8-18, try to
combine different parameters in the same cues to see the effects available for
this format.

8.5 TextTrack API

The TextTrack API was defined to offer access from JavaScript to the
content of the tracks used in closed captioning. The API includes an object
called TextTrack to return this information. There are two ways to get
this object: from the media element or from the <track>
element.

textTracks—This
property contains an array with the TextTrack objects
corresponding to each track of the media. The TextTrack objects are
stored in the array in sequential order.

track—This
property returns the TextTrack object of the specified track.

If the media (video or audio) has several <track>
elements, it may be easier to find the track we want accessing the textTracks
array.

Listing 8-22: Getting the TextTrack object

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Working with Tracks</title>

<script>

function initiate() {

var video =
document.getElementById("media");

var track1 =
video.textTracks[0];

var mytrack =
document.getElementById("mytrack");

var track2 =
mytrack.track;

console.log(track1);

console.log(track2);

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<video id="media"
width="720" height="400" controls>

<source src="trailer.mp4">

<source src="trailer.ogg">

<track id="mytrack"
label="English Subtitles" src="subtitles.vtt"
srclang="en" default>

</video>

</section>

</body>

</html>

The document of Listing 8-22
illustrates how to access the TextTrack object using both properties. The initiate() function creates a reference to the
<video> element to get the TextTrack object accessing the textTracks array with the index 0 and then obtains the same object
from the <track> element using the track property. Finally, the content of
both variables is printed on the console.

Do It Yourself: Create a new HTML file with the
document of Listing 8-22. Like previous examples, this application does not
work on a local computer; you have to upload all the files to your server or
local server for testing.

Reading Tracks

Once we get the TextTrack object of the track we want to work
with, we can access its properties.

kind—This
property returns the type of the track, as specified by the kind attribute of
the <track> element (subtitles, captions, descriptions, chapters and metadata).

label—This
property returns the label of the track, as specified by the label attribute of
the <track> element.

language—This
property returns the language of the track, as specified by the srclang attribute of
the <track> element.

mode—This
property returns or sets the mode of the track. The three possible values are disabled, hidden, and showing. It may be
used to switch tracks.

cues—This
property is an array containing all the cues from the track file.

activeCues—This property returns the
cues currently shown on the screen (the previous, current, and next cue).

From the properties of the TextTrack object, we have access to all the information stored
in the track.

Listing 8-23: Displaying the track’s information on the
screen

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Working with Tracks</title>

<style>

#player, #info{

float: left;

}

</style>

<script>

function initiate() {

var info =
document.getElementById("info");

var mytrack =
document.getElementById("mytrack");

var obj = mytrack.track;

var list = "";

list +=
"<br>Kind: " + obj.kind;

list +=
"<br>Label: " + obj.label;

list +=
"<br>Language: " + obj.language;

info.innerHTML = list;

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section id="player">

<video id="media"
width="720" height="400" controls>

<source src="trailer.mp4">

<source src="trailer.ogg">

<track id="mytrack"
label="English Subtitles" src="subtitles.vtt"
srclang="en" default>

</video>

</section>

<aside id="info"></aside>

</body>

</html>

The document of Listing 8-23 includes styles for the two columns
created by the <section> and <aside> elements and JavaScript code to get and show the data
from the <track> element. This time, the TextTrack
object is retrieved from the track property and stored in the obj
variable. From this variable, we get the values of the object’s properties and
generate the text to be shown on the screen.

Reading Cues

In addition to the properties
applied in the last example, there is an important property called cues. This property contains an array with
TextTrackCue objects representing each cue of
the track.

Listing 8-24: Displaying
cues on the screen

<script>

function
initiate() {

var info =
document.getElementById("info");

var mytrack
= document.getElementById("mytrack");

var obj =
mytrack.track;

var mycues = obj.cues;

var list =
"";

for (var f
= 0; f < mycues.length; f++) {

list +=
mycues[f].text + "<br>";

}

info.innerHTML = list;

}

window.addEventListener("load", initiate);

</script>

The new initiate() function in Listing 8-24 accesses every cue using a for loop. Inside the loop, the values of the array are added
to the list variable along with a <br>
element to show the cues one per line, and then the whole text is inserted into
the <aside> element and displayed on the screen.

The TextTrackCue objects provide properties to access the information
of the cue. In our example, we showed the content of the text
property. The following is a list of all the properties available.

text—This property returns the text of
the cue.

startTime—This
property returns the start time of the cue in seconds.

endTime—This
property returns the end time of the cue in seconds.

vertical—This
property returns the value of the vertical parameter.
If the parameter was not defined, the value returned is an empty string.

line—This
property returns the value of the line parameter.
If the parameter was not defined, the default value is returned.

position—This
property returns the value of the position parameter.
If the parameter was not defined, the default value is returned.

size—This
property returns the value of the size parameter.
If the parameter was not defined, the default value is returned.

align—This
property returns the value of the align parameter.
If the parameter was not defined, the default value is returned.

The following code updates the previous example
to include the starting time of each cue.

Listing 8-25: Displaying
information about cues

<script>

function
initiate() {

var info =
document.getElementById("info");

var mytrack
= document.getElementById("mytrack");

var obj =
mytrack.track;

var mycues
= obj.cues;

var list =
"";

for (var f
= 0; f < mycues.length; f++) {

var cue =
mycues[f];

list += cue.startTime
+ " - ";

list += cue.text +
"<br>";

}

info.innerHTML = list;

}

window.addEventListener("load", initiate);

</script>

Do It Yourself: Create a new HTML file with the
document of Listing 8-23. Upload the files to your server or
copy the files to your local server and open the document in your browser. To
work with cues, replace the initiate() function in the document with the
one you want to try. The cues and their values should be printed on the right
side of the video.

Adding Tracks

The TextTrack object that represents a track has
not only properties but also three important methods to create new tracks and
cues from JavaScript.

addTextTrack(kind, label, language)—This method creates a new
track for the media and returns the corresponding TextTrack object. The attributes are the values of
the attributes for the new track (only kind is mandatory).

addCue(object)—This method adds a new cue to the specified track. The object attribute is a TextTrackCue object
returned by the VTTCue() constructor.

removeCue(object)—This method removes a cue from the specified track. The object attribute is a TextTrackCue object
returned by the TextTrack object.

To add
cues to the track, we have to provide a TextTrackCue object. The
API includes a constructor to create this object.

VTTCue(startTime, endTime, text)—This constructor returns
a TextTrackCue object to use with the addCue() method. The attributes
represent the data for the cue: start time, end time,
and the text for the caption.

The following
example demonstrates how to add a track to a media.

Listing 8-26: Adding
tracks and cues from JavaScript

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Working with Tracks</title>

<script>

function
initiate(){

var cue;

var cues
= [

{
start: 2.000, end: 7.000, text: "Welcome"},

{
start: 10.000, end: 15.000, text: "This is an example"},

{
start: 15.001, end: 20.000, text: "of how to add"},

{
start: 20.001, end: 25.000, text: "a new track."},

{
start: 27.000, end: 30.000, text: "Good bye!"},

];

var video
= document.getElementById("media");

var newtrack =
video.addTextTrack("subtitles");

newtrack.mode = "showing";

for (var f = 0; f
< cues.length; f++) {

cue = new
VTTCue(cues[f].start, cues[f].end, cues[f].text);

newtrack.addCue(cue);

}

video.play();

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section>

<video
id="media" width="720" height="400" controls>

<source src="trailer.mp4">

<source src="trailer.ogg">

</video>

</section>

</body>

</html>

in
Listing 8-26, we start the initiate() function by
defining the cues array with five cues
for our new track. The cues are declared as elements of the array. Each cue is
an object with the properties start, end and text. The values of
the start and end times do not use the syntax of a WebVTT file; they have to be
declared in seconds as decimal numbers.

The cues
may be added to an existing track or a new one. In our example, we create a new
track of type subtitles using the addTextTrack() method. We
also have to declare the mode of this track as showing, to ask the
browser to show the track on the screen. When the track is ready, all the cues
from the cues array are converted into TextTrackCue objects and
added to the track using the addCue() method.

Do It Yourself: Update your document with the code in
Listing 8-26 and open the document in your browser. You should see the new
track on top of the video. Remember to upload the files to your server or copy
the files to your local server.

Chapter 9 - Stream API

9.1 Capturing Media

The Stream API allows us to access media streams
produced by the device. The most common stream sources are the webcam and the
microphone, but the API was defined to access any other source that produces a
video or audio stream.

The API defines the MediaStream object to reference media streams and the LocalMediaStream object for media generated by local devices. These
objects have an input represented by the device and an output represented by
the <video> element, the <audio> element, or other APIs. To get the LocalMediaStream object representing the stream, the API includes an
object called MediaDevices that, among others, it provides the
following method.

getUserMedia(constraints)—This method asks the user
permission to access the video or audio stream and in response generates a LocalMediaStream object to represent the stream. The
constraints attribute is an object with two properties video and audio to indicate the type of media to be
captured.

The getUserMedia() method performs an asynchronous operation, which
means that the method will try to access the stream while the rest of the code
is being processed. For this purpose, it returns an object of type Promise to
report the result. This is an object
specifically designed to manage asynchronous operations. It provides two
methods to process the response.

then(function)—This method is executed by an
asynchronous operation in case of success. If the operation is successful, this
method is called, and the function specified by the attribute is executed.

catch(function)—This method is executed by an
asynchronous operation in case of failure. If the operation fails, this method
is called, and the function specified by the attribute is executed.

When a stream is accessed, the then() method sends the reference of the LocalMediaStream object that is representing the stream to
the function. To show it to the user, we have to assign this object to a <video> or <audio> element. For this purpose, the Element objects representing these elements provide the following property.

srcObject—This property sets or returns a MediaStream object representing a stream.

The most common device used to stream video is
the webcam. The following example illustrates how to access the webcam and show
the stream on the screen.

Listing 9-1: Accessing
the webcam

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Stream API</title>

<script>

function initiate() {

var promise =
navigator.mediaDevices.getUserMedia({video: true});

promise.then(success);

promise.catch(showerror);

function success(stream) {

var video =
document.getElementById("media");

video.srcObject =
stream;

video.play();

}

function showerror(error) {

console.log("Error: " +
error.name);

}

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<video
id="media"></video>

</section>

</body>

</html>

The initiate() function in
Listing 9-1 gets the video from the webcam using the getUserMedia() method. This
prompts a request to the user. If the user allows our application to access the
webcam, the then() method of
the Promise object is called and the success() function is
executed. This function receives the LocalMediaStream object
produced by the getUserMedia() method and
assigns it to the <video> element in
the document through the srcObject property.

In case
of failure, the
catch() method of the Promise object is called, and
the showerror() function is executed.
The function receives an object with information about the error. The most
common error is PermissionDeniedError, produced when the user denies access to
the media or the media is not available for other reasons.

Notice
that we did not have to declare the src attribute in
the <video> tag because the source
will be the media stream captured by the JavaScript code, but we could have set
the width and height attributes
if we wanted to specify the size of the video.

IMPORTANT: The getUserMedia() method can only be
executed from a server, and the server must be secure, which means that the
application will not work unless you upload the files to a server and the
server uses the https protocol. If you do not have a secure server, you can
test the examples in this chapter using a local server set up by an application
like MAMP. See Chapter 1 for more information.

The Basics: The MediaDevices object that provides the getUserMedia() method belongs to the Navigator object (see Chapter 6). When the
browser creates the Window
object, it stores a reference to the Navigator object in a property called navigator and the MediaDevices object in a property called mediaDevices. This is why, in the example of
Listing 9-1, we use these properties to call the getUserMedia() method (navigator.mediaDevices.getUserMedia()).

The MediaStreamTrack object

MediaStream objects (and
therefore LocalMediaStream objects) contain MediaStreamTrack objects that
represent each media track (usually one for the video and another for the
audio). MediaStream objects include the
following methods to retrieve these MediaStreamTrack objects.

getVideoTracks()—This method returns an array with MediaStreamTrack objects representing the video
tracks included in the stream.

getAudioTracks()—This method returns an array with MediaStreamTrack objects representing the audio
tracks included in the stream.

The MediaStreamTrack objects
returned by these methods include properties and methods to get information and
control the video or audio track. The following are the most frequently used.

enabled—This property returns true or false according to the track’s status. If
the track is still associated with the source, the value is true.

kind—This property returns the type of
source the track represents. It has two possible values: audio or video.

label—This property returns the name of
the source for the track.

stop()—This method stops the track.

If we want to collect
information about the stream, we have to create the stream with the getUserMedia() method as we did in the previous
example, get references to the tracks in the stream with the getVideoTracks() method, and then read the values of
the tracks returned.

Listing 9-2: Displaying
the information of the stream

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Stream API</title>

<style>

section {

float: left;

}

</style>

<script>

function initiate() {

var promise =
navigator.mediaDevices.getUserMedia({video: true});

promise.then(success);

promise.catch(showerror);

function success(stream) {

var video =
document.getElementById("video");

video.srcObject = stream;

video.play();

var databox =
document.getElementById("databox");

var videoTracks = stream.getVideoTracks();

var track = videoTracks[0];

var data = "";

data +=
"<br> Enabled: " + track.enabled;

data +=
"<br> Type: " + track.kind;

data +=
"<br> Device: " + track.label;

databox.innerHTML = data;

}

function showerror(error) {

console.log("Error: " +
error.name);

}

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<video
id="video"></video>

</section>

<section
id="databox"></section>

</body>

</html>

The getVideoTracks() method of the MediaStream object returns an array.
Because the webcam contains only one video track, to get a reference to this
track we have to read the element at index 0 (the first track on the
list).

In the document of
Listing 9-2, we use the same code from the previous example to access the
stream, but this time the track for the webcam is stored in the track variable, and then every
one of its properties is shown on the screen.

IMPORTANT: The tracks mentioned here are video
and audio tracks. These types of tracks are not related to the closed
captioning tracks studied in Chapter 8.

Once we get a
reference to the track, we can stop it with the stop() method. In the following
example, we add a button to the interface to stop the stream when it is pressed
by the user. Notice that the listener for the click event of the button is
only added if the code is able to access the camera. Otherwise, the button does
nothing.

Listing 9-3: Stopping the
stream

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Stream API</title>

<script>

function initiate() {

var promise =
navigator.mediaDevices.getUserMedia({video: true});

promise.then(success);

promise.catch(showerror);

}

function success(stream) {

var button =
document.getElementById("button");

button.addEventListener("click", function() { stopstream(stream) });

var video =
document.getElementById("video");

video.srcObject = stream;

video.play();

}

function showerror(event) {

console.log("Error: " + error.name);

}

function
stopstream(stream) {

var videoTracks =
stream.getVideoTracks();

var track =
videoTracks[0];

track.stop();

alert("Stream
Canceled");

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<video
id="video"></video>

</section>

<nav>

<button type="button"
id="button">Turn Off</button>

</nav>

</body>

</html>

In addition to the
properties and methods mentioned above, MediaStreamTrack objects also offer
events to report the state of the track.

muted—This event is fired when the track
is unable to provide data.

unmuted—This event is fired as soon as the
track starts providing data again.

ended—This event is fired when the track
can no longer provide data. There may be several reasons why this occurs, from
the user denying access to the stream to the use of the stop() method.

IMPORTANT: These events are meant to be used to
control remote streaming, a process studied in Chapter 24.

Chapter 10 - Fullscreen API

10.1 Modern Applications

The Web is a multipurpose and multimedia platform where everything is
possible. With so many interesting new applications, the word navigation
has almost lost significance. The browser's interface is not only unnecessary
but sometimes may get in the way. For this reason, browsers include the
Fullscreen API.

Full Screen

The Fullscreen API lets us expand any element in the document to occupy
the entire screen. As a result, the browser’s interface is hidden in the
background, allowing the user to focus his or her attention on our video,
picture, application or video game.

The API provides a few properties, methods and events to take an
element to full screen mode, exit the full screen mode, and retrieve some
information from the element and the document that are participating in the
process.

fullscreenElement—This
property returns a reference to the element that is in full screen mode. If no
element is full screen, the property returns null.

fullscreenEnabled—This Boolean
property returns true when the document is
able to go full screen or false otherwise.

requestFullscreen()—This method is
available for every element in the document. It activates full screen mode for
the element.

exitFullscreen()—This method is
available for the document. If an element is in full screen mode, the method
cancels the mode and returns the focus back to the browser window.

The API also offers the following events.

fullscreenchange—This event
is fired by the document when an element enters or exits the full screen mode.

fullscreenerror—This event
is fired by the element in case of failure (the full screen mode is not
available for that element or for the document).

The requestFullscreen() method and the fullscreenerror event are associated with the elements in the
document, but the rest of the properties, methods, and events are part of the Document
object, and therefore they are accessible from the document
property.

Listing 10-1: Taking a <video> element full
screen

<!DOCTYPE
html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Full Screen</title>

<script>

var video;

function initiate() {

video =
document.getElementById("media");

video.addEventListener("click",
gofullscreen);

}

function gofullscreen()
{

if (!document.webkitFullscreenElement)
{

video.webkitRequestFullscreen();

video.play();

}

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section>

<video id="media"
width="720" height="400" poster="poster.jpg">

<source src="trailer.mp4">

<source src="trailer.ogg">

</video>

</section>

</body>

</html>

The initiate() function in Listing 10-1 adds a listener for the click
event to the <video> element. The gofullscreen()
function, set as the listener of this event, is executed every time the user
clicks on the video. In this function, we use the fullscreenElement property to detect whether an element is already in full screen mode
or not, and if not, the video is made full screen by the requestFullscreen() method. At the same time, the video is played with
the play() method.

IMPORTANT: This is an experimental API. The
properties, methods, and events have been prefixed by browsers until the final
specification is implemented. In the case of Google Chrome, instead of the
original names we have to use webkitRequestFullscreen(), webkitExitFullscreen(), webkitfullscreenchange, webkitfullscreenerror and webkitFullscreenElement. For Mozilla Firefox, the names are
mozRequestFullScreen(), mozCancelFullScreen(),  mozfullscreenchange,  mozfullscreenerror and mozFullScreenElement.

Full Screen Styles

Browsers adjust the dimensions of the <video>
element to the size of the screen automatically, but for any other element, the
original dimensions are preserved, and the free space on the screen is filled
with a black background. For this reason, CSS includes a pseudo-class called :full-screen
to modify the styles of the element when it expands to full screen.

IMPORTANT: The last specification declares the
name of this pseudo-class without the hyphen (:fullscreen), but, at the time of writing,
browsers like Mozilla Firefox and Google Chrome have only implemented the previous
specification and work with the name mentioned in this chapter (:full-screen). It also has to be prefixed to work
properly (:-webkit-full-screen, :-moz-full-screen, etc.).

Listing 10-2: Taking any
element full screen

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Full Screen</title>

<style>

#player:-webkit-full-screen, #player:-webkit-full-screen #media{

width: 100%;

height: 100%;

}

</style>

<script>

var video, player;

function initiate() {

video =
document.getElementById("media");

player =
document.getElementById("player");

player.addEventListener("click",
gofullscreen);

}

function gofullscreen() {

if (!document.webkitFullscreenElement) {

player.webkitRequestFullscreen();

video.play();

} else {

document.webkitExitFullscreen();

video.pause();

}

}

window.addEventListener("load",
initiate);

</script>

</head>

<body>

<section id="player">

<video id="media"
width="720" height="400" poster="poster.jpg">

<source src="trailer.mp4">

<source src="trailer.ogg">

</video>

</section>

</body>

</html>

In Listing 10-2, we take the <section> element and its content full screen. The gofullscreen() function is modified to activate and deactivate the full
screen mode for this element. As in the previous example, the video is played
when it is in full screen mode, but now it will be paused when the mode is
canceled.

These improvements are insufficient to make our player full screen.
In full screen mode, the new container for the element is the screen, but the
original dimensions and styles for the <section> and <video> elements are preserved intact. Using the :full-screen
pseudo-class, we change the values of the width and height properties of these elements to 100%, matching the
dimensions of the container. Now the elements occupy the whole screen and truly
reflect the full screen mode.

Do It Yourself: Create a new HTML file to test the
examples in this chapter. Once the document is opened in the browser, click on
the video to activate the full screen mode and click again to deactivate it.

Chapter 11 - Canvas API

11.1 Graphics

In the introduction of this book, we discussed how
HTML5 is replacing previous plugins, like Flash. There were at least two
important things to consider in making the Web independent of third-party
technologies: video processing and graphic applications. The <video> element and the APIs studied in previous chapters
cover the first aspect very well, but they do not deal with graphics. For this
purpose, browsers introduce the Canvas API. This API lets us draw, render
graphics, animate, and process images and text, and it works with the rest of
the APIs to create full applications and even 2D and 3D video games for the
Web.

The Canvas

The Canvas API can only draw in an
area in the document that was designated for that purpose. To define that area,
HTML includes the following element.

<canvas>—This element
creates an area for drawing. It must include the width and height attributes
to determine the dimensions of the area.

The <canvas> element generates an empty
rectangular space on the page where the results of the methods provided by the
API are shown. It produces a white space, like an empty <div> element, but for an entirely
different purpose. The following example demonstrates how to include this
element in our document.

Listing 11-1: Including the <canvas> element

<!DOCTYPE html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Canvas
API</title>

<script
src="canvas.js"></script>

</head>

<body>

<section
id="canvasbox">

<canvas
id="canvas" width="500"
height="300"></canvas>

</section>

</body>

</html>

The Context

The purpose of the <canvas> element is to create an empty box on the
screen. To draw something on the canvas, we have to use a context. The API
offers the following method for this purpose.

getContext(type)—This method obtains a drawing context to draw on the canvas. It accepts two values: 2d
(two-dimensional space) and webgl
(three-dimensional space).

The getContext() method is the first method we have to call for the <canvas> element to be ready to work. Through the object returned by this
method, we are able to apply the rest of the API.

Listing 11-2: Getting the
drawing context for the canvas

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

}

window.addEventListener("load",
initiate);

In Listing 11-2, a reference to the <canvas> element is stored into the element variable, and a reference to the 2D context is obtained
with the getContext() method. The 2D canvas drawing context
returned by this method is a grid of pixels organized in rows and columns from
top to bottom and left to right, with its origin (the pixel 0, 0) located at
the top-left corner of the rectangle.

Do It Yourself: Create a new HTML file with the document of Listing
11-1. Create a file named canvas.js and copy every JavaScript code presented
from Listing 11-2 into this file. Each example in this chapter is independent
and replaces the previous one. All the images used in this chapter are
available on our website.

The Basics: Currently, the 2d context is available in every HTML5
compatible browser, while webgl is only applicable in browsers that
have implemented and enabled the WebGL library for the generation of 3D
graphics. We will study WebGL and 3D graphics in the next chapter.

11.2 Drawing

After the <canvas> element and its context are ready, we can finally create and manipulate
graphics. The list of tools
provided by this API for this purpose is extensive, allowing the generation of
multiple objects and effects, from simple shapes to text, shadows or complex
transformations. In this section of the chapter, we will study these methods
one by one.

Rectangles

Usually,
developers must prepare the figure to be drawn before sending it to the context
(as we will see soon), but there are a few methods that let us draw directly on
the canvas.

fillRect(x, y, width, height)—This method draws a solid rectangle. The top-left
corner is located at the position specified by the x
and y attributes. The width and height attributes declare the size of the
rectangle.

strokeRect(x, y, width, height)—Similar to the previous method, but this one draws an
empty rectangle (just the outline).

clearRect(x, y, width, height)—This method is used to subtract pixels from the area
specified by its attributes. It is like a rectangular eraser.

Drawing rectangles is simple; we have to call the
method on the context, and the shapes are displayed on the canvas right away.

Listing 11-3: Drawing rectangles

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.strokeRect(100,
100, 120, 120);

canvas.fillRect(110, 110,
100, 100);

canvas.clearRect(120,
120, 80, 80);

}

window.addEventListener("load",
initiate);

In Listing 11-3, the context is assigned to the
variable canvas, and from this reference, we call the
drawing methods. The first method, strokeRect(100, 100, 120, 120), draws an empty rectangle with the top-left corner at
the position 100,100 and a size of 120 pixels. The second
method, fillRect(110,
110, 100, 100), draws a
solid rectangle, this time starting from the position 110,110 of the canvas. And finally, with the last method, clearRect(120, 120, 80, 80), a square of 80 pixels is removed from the
middle of the previous rectangle.

Figure 11-1: Rectangles on the canvas

Figure 11-1 is a representation of what we see
after the execution of the code in Listing 11-3. The <canvas> element is rendered as a grid with its origin at the
top-left corner and the size specified by its attributes. The rectangles are
drawn on the canvas at the position declared by the attributes x
and y and one on top of another according to the order in
the code. The first one to appear in the code is drawn first, the second is
drawn on top of it, and so on (There is a method for customizing how the shapes
are drawn, but we will see that later).

Colors

Up to this point, we have been using the default color:
black. We can specify the color we want to use with CSS syntax and the
following properties.

strokeStyle—This property declares the color of the lines of the
shape.

fillStyle—This property declares the color of the interior of
the shape.

globalAlpha—This property sets the transparency of the shape.

The colors are defined with the same values we
use in CSS between quotes.

Listing 11-4: Changing colors

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.fillStyle =
"#000099";

canvas.strokeStyle =
"#990000";

canvas.strokeRect(100, 100, 120, 120);

canvas.fillRect(110, 110, 100, 100);

canvas.clearRect(120, 120, 80, 80);

}

window.addEventListener("load",
initiate);

The colors in Listing 11-4 are declared using
hexadecimal numbers, but we can also use functions such as rgb() or even specify transparency with the rgba() function. These functions also have to be
declared between quotes, as in strokeStyle
= "rgba(255, 165, 0, 1)". When
a new color is specified, it becomes the default color for later drawings.

The Basics: Besides the rgba() function, the context provides a
property to set the level of transparency called globalAlpha. The possible values for this
property are between 0.0 (fully opaque) and 1.0 (fully transparent).

Gradients

Gradients are part of the toolkit of every drawing application,
and the Canvas API is no exception. As in CSS, the gradients on the canvas may
be linear or radial, and we can provide stop points to combine colors.

createLinearGradient(x1, y1, x2, y2)—This method creates a linear gradient
object to apply to the canvas.

createRadialGradient(x1, y1, r1, x2, y2, r2)—This method creates a radial gradient
object to apply to the canvas using two circles. The values represent the
position of the center of each circle and their radius.

addColorStop(position, color)—This method specifies the colors to create the
gradient. The position attribute is a value between 0.0 and 1.0 to
determine where the degradation starts for that particular color.

The following example illustrates how to apply a
linear gradient to our canvas.

Listing 11-5: Applying a linear gradient to
the canvas

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

var gradient =
canvas.createLinearGradient(0, 0, 10, 100);

gradient.addColorStop(0.5, "#00AAFF");

gradient.addColorStop(1,
"#000000");

canvas.fillStyle =
gradient;

canvas.fillRect(10, 10, 100, 100);

canvas.fillRect(150, 10, 200, 100);

}

window.addEventListener("load",
initiate);

In Listing 11-5, we create the gradient object
from the position 0,0 to 10,100, producing a slight inclination to the
left. The colors are set by the addColorStop() methods, and the final gradient is applied
with the fillStyle property, like a regular color.

Figure 11-2: Linear gradient for the canvas

Notice that the positions of the gradient are
relative to the canvas, not to the shapes we want to affect. The result is
that, if we move the rectangles to a new position on the screen, the gradient
for those rectangles changes.

Do It Yourself: The radial gradient is similar to CSS. Try to replace
the linear gradient in the code in Listing 11-5 with a radial gradient using an
instruction such as createRadialGradient(0,
0, 30, 0, 0, 300). You
can also experiment with the location of the rectangles to see how the gradient
is applied.

Paths

The methods studied above draw directly on the canvas,
but that is not always the case. When we have to draw complex figures, we have
to process shapes and images in the background, and then send the result to the
context to be drawn. For this purpose, the Canvas API introduces several
methods to generate paths.

A path is like a blueprint for a pen to follow.
Once we set a path, it has to be sent to the context to be drawn permanently on
the canvas. The path may include different kinds of strokes to create complex
shapes, such as straight lines, arcs, rectangles and others. The following are
the methods to begin and close a path.

beginPath()—This method begins a new path.

closePath()—This method closes the path, generating a straight
line from the last point to the point of origin. It can be omitted when we want
an open path or when we use the fill() method to draw the path.

We also have three methods to draw the path on
the canvas.

stroke()—This method draws the path as an outlined shape.

fill()—This method draws the path as a solid shape. When we
use this method, we do not need to close the path with closePath(); it is automatically closed with a straight line from
the last point to the first one.

clip()—This method sets a new clipping area for the context.
When the context is initialized, the clipping area is the entire area occupied
by the canvas. The clip() method changes the clipping area to a new
shape, creating a mask. Everything that falls outside that mask is not drawn.

Every time we want to create a path, we have to
call the beginPath() method to initialize it, as in the
following example.

Listing 11-6: Starting and ending a path

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.beginPath();

// here goes
the path

canvas.stroke();

}

window.addEventListener("load",
initiate);

The code in Listing 11-6 does not create
anything, it simply begins the path and draws it with the stroke() method. Nothing is drawn because we have not yet define
the path. The following are the methods available to set the path and create
the shape.

moveTo(x, y)—This method moves the pen to a new position. It lets us start or
continue the path from different points on the grid, avoiding continuous lines.

lineTo(x, y)—This method generates a straight line from the pen’s current position
to the one declared by the x and y attributes.

rect(x, y, width, height)—This method generates a rectangle. Unlike the methods
studied before, this method generates a rectangle that is part of the path (not
directly drawn on the canvas). The attributes have the same function as
previous methods.

arc(x, y, radius, startAngle, endAngle, direction)—This method generates an arc or a circle
with the center at the coordinates indicated by x
and y and with the radius and angles declared by the rest
of the attributes. The last value has to be declared as true or false for the direction (counterclockwise or
clockwise, respectively).

quadraticCurveTo(cpx, cpy, x, y)—This method generates a quadratic Bézier curve
starting from the current position of the pen and ending at the position
declared by the x and y attributes. The cpx and cpy attributes define the control point that
shapes the curve.

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)—This method is similar to the previous
method but adds two more attributes to generate a cubic Bézier curve. It
generates two control points in the grid declared by the values cp1x, cp1y, cp2x and cp2y to shape the curve.

The following code generates a simple path to
illustrate how these methods work.

Listing 11-7: Creating a path

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.beginPath();

canvas.moveTo(100, 100);

canvas.lineTo(200, 200);

canvas.lineTo(100, 200);

canvas.stroke();

}

window.addEventListener("load",
initiate);

It is always recommended to set the initial
position of the pen immediately after starting the path. In the code in Listing
11-7, we first move the pen to the position 100,100 and then generate a line from this point
to 200,200. The position of the pen is now 200,200, and the next line is drawn from there to the point 100,200. Finally, the path is drawn as an outline shape by the stroke() method.

Figure 11-3: Open path

Figure 11-3 shows a representation of the open
triangle produced by the code in Listing 11-7. This triangle may be closed or
even filled using different methods, as shown in the following examples.

Listing 11-8: Completing the triangle

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.beginPath();

canvas.moveTo(100, 100);

canvas.lineTo(200, 200);

canvas.lineTo(100, 200);

canvas.closePath();

canvas.stroke();

}

window.addEventListener("load",
initiate);

The closePath() method adds a straight line to the path,
from the end point to the start point, closing the shape.

Figure 11-4: Close path

Using the stroke() method at the end of our path, we drew an
empty triangle on the canvas. If we want a solid triangle, we have to use the fill() method.

Listing 11-9: Drawing a solid triangle

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.beginPath();

canvas.moveTo(100, 100);

canvas.lineTo(200, 200);

canvas.lineTo(100, 200);

canvas.fill();

}

window.addEventListener("load",
initiate);

Now the figure on the screen is a solid triangle.
The fill() method closes the path automatically, so we no longer
have to use the closePath() method.

Figure 11-5: Solid triangle

One of the methods previously introduced to draw
a path on the canvas was clip().
This method does not draw anything, but it creates a mask with the shape of the
path to select what will be drawn and what will not be drawn. Everything that
falls outside the mask is not drawn.

Listing 11-10: Using a triangle as a mask

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.beginPath();

canvas.moveTo(100, 100);

canvas.lineTo(200, 200);

canvas.lineTo(100, 200);

canvas.clip();

canvas.beginPath();

for (var f =
0; f < 300; f = f + 10) {

canvas.moveTo(0, f);

canvas.lineTo(500, f);

}

canvas.stroke();

}

window.addEventListener("load",
initiate);

To show how the clip() method works, in Listing 11-10 we create a for loop to generate horizontal lines every 10 pixels. The lines go from
the left to the right side of the canvas, but only the parts of the lines that
fall inside the triangle mask are drawn.

Figure 11-6: Clipping area

Now that we know how to draw paths, it is time to
look at other ways to create shapes. Thus far, we have generated straight lines
and square shapes. For circular shapes, the API provides three methods: arc(), quadraticCurveTo() and bezierCurveTo(). The first one is relatively simple to use
and can generate partial or full circles, as shown in the following example.

Listing 11-11: Drawing circles with the arc() method

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.beginPath();

canvas.arc(100, 100, 50,
0, Math.PI * 2, false);

canvas.stroke();

}

window.addEventListener("load",
initiate);

In the arc() method in Listing 11-11 we use the value PI from the Math object to specify the angle. This is necessary because this method uses radians
instead of degrees. In radians, the value of PI represents 180 degrees, so the formula PI × 2 multiplies PI by 2
getting an angle of 360 degrees.

This example generates an arc with a center at
the point 100,100 and a radius of 50 pixels, starting at 0
degrees and ending at Math.PI * 2 degrees, which represents a full circle.

If we need to calculate the value in radians from
degrees, we have to use the formula: Math.PI
/ 180 × degrees, as in
the following example.

Listing 11-12: Drawing an arc of 45 degrees

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

var radians = Math.PI /
180 * 45;

canvas.beginPath();

canvas.arc(100, 100, 50, 0, radians, false);

canvas.stroke();

}

window.addEventListener("load",
initiate);

With the code in Listing 11-12 we get an arc that
covers 45 degrees of a circle, but if we change the value of the direction to true in the arc() method, the arc is generated from 0
degrees to 315, creating an open circle, as illustrated in Figure 11-7.

Figure 11-7: Semicircle with the arc() method

The Basics: If you continue working with this
path, the current starting point will be the end of the arc. If you do not want
to start at the end of the arc, you will have to use the moveTo() method to change the position of
the pen. However, if the next shape is another arc, you have to remember that
the moveTo() method moves the virtual pen to the
point at which the circle begins to be drawn, not to the center of the circle.

Besides arc(), we have two more methods for drawing more
complex curves. The quadraticCurveTo() method generates a quadratic Bézier curve,
and the bezierCurveTo() method generates a cubic Bézier curve. The
difference between the methods is that the first one has only one point of
control and the second method has two, thus creating different types of curves.

Listing 11-13: Creating complex curves

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.beginPath();

canvas.moveTo(50, 50);

canvas.quadraticCurveTo(100, 125, 50, 200);

canvas.moveTo(250, 50);

canvas.bezierCurveTo(200,
125, 300, 125, 250, 200);

canvas.stroke();

}

window.addEventListener("load",
initiate);

To create the quadratic curve in this example, we
move the virtual pen to the position 50,50 and finish the curve at the position 50,200. The control point for this curve is at the position 100,125. The cubic curve generated by the bezierCurveTo() method is more complicated. There are two
control points for this curve, the first one at the position 200,125 and the second one at the position 300,125. The result is illustrated in Figure 11-8.

Figure 11-8: Representation of Bézier curves
and their control points on the canvas

The values in Figure 11-8 indicate the position
of the control points for the curves. Moving those points changes the shape of
the curve.

Do It Yourself: You can add as many curves as you need to build your shape.
Try to change the values of the control points in Listing 11-13 to see how they
affect the curves. Build more complex shapes by combining curves and lines to
understand how a path is made.

Lines

Up to this point, we have been using the same style
for every line drawn on the canvas. The width, ending, and other aspects of the
line can be manipulated to get exactly the type of line we need for our
drawings. For this purpose, the API includes four properties.

lineWidth—This property determines the line thickness. By
default, the value is 1.0.

lineCap—This property determines the shape of the end of the
line. There are three possible values: butt, round, and
square.

lineJoin—This property determines the shape of the connection
between two lines. The possible values are round, bevel and miter.

miterLimit—This property determines how far the connections
between lines will be extended when the lineJoin property is set to miter.

These properties affect the entire path. Every
time we have to change the characteristics of the lines, we have to create a
new path with new property values.

Listing 11-14: Testing properties for lines

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.beginPath();

canvas.arc(200, 150, 50, 0, Math.PI * 2, false);

canvas.stroke();

canvas.lineWidth = 10;

canvas.lineCap =
"round";

canvas.beginPath();

canvas.moveTo(230, 150);

canvas.arc(200, 150, 30, 0, Math.PI, false);

canvas.stroke();

canvas.lineWidth = 5;

canvas.lineJoin =
"miter";

canvas.beginPath();

canvas.moveTo(195, 135);

canvas.lineTo(215, 155);

canvas.lineTo(195, 155);

canvas.stroke();

}

window.addEventListener("load",
initiate);

We start the drawing in the example of Listing
11-14 by defining a path for a full circle with the properties by default.
Then, using lineWith, we change the width of the line to 10
and set the lineCap to round. This makes the path thick with rounded
endings, which will create a smiling mouth. To create the path, we move the pen
to the position 230,150 and then generate a semicircle. Finally,
we add another path created by two lines to form a shape like a nose. Notice
that the lines for this path have a width of 5
and are joined with the property lineJoin and the value
miter. This property makes the nose pointy, expanding the
outside edges of the corner until they reach a single point.

Figure 11-9: Different types of lines

Do It Yourself: Experiment with changing the lines for the nose
modifying the property miterLimit (e.g., miterLimit
= 2). Change the value
of the lineJoin property to round or bevel. You can also modify the shape of the
mouth by trying different values for the property lineCap.

Text

Writing text on the canvas is as simple as defining a
few properties and calling the appropriate method. There are three properties
to configure text.

font—This property sets the type of font to be used for
the text. It takes the same values as the font property from CSS.

textAlign—This property aligns the text horizontally. The
alignment can be made to the start, end, left, right or center.

textBaseline—This property is for vertical alignment. It sets
different positions for the text (including Unicode text). The possible values
are top, hanging, middle, alphabetic, ideographic, and bottom.

The following are the methods available to draw the
text.

strokeText(text, x, y)—This method draws the specified text as an outline
shape at the position x and y.
It can also include a fourth value to declare the maximum size. If the text is
longer than this value, it will be shrunk to fit into that space.

fillText(text, x, y)—This method is similar to the previous method except
it draws a solid text.

The following example illustrates how to draw a
single text with a custom font and in a specific position on the canvas.

Listing 11-15: Drawing text

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.font = "bold
24px verdana, sans-serif";

canvas.textAlign =
"start";

canvas.fillText("My
message", 100, 100);

}

window.addEventListener("load",
initiate);

The font property can take several values at once
using exactly the same syntax as CSS. On the other hand, the textAlign property indicates that the text has to begin at the position
100,100 (if the value of this property was end, for example, the text would have ended at the position 100,100). Finally, the fillText() method draws a solid text on the canvas.

Besides the methods already mentioned, the API
provides another important method to work with text.

measureText(text)—This method returns information about the
size of the text between parentheses.

The measureText() method can be useful to calculate
positions and even collisions in animations, as well as combining text with
other shapes on the canvas, as in the following example.

Listing 11-16: Measuring text

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.font =
"bold 24px verdana, sans-serif";

canvas.textAlign = "start";

canvas.textBaseline = "bottom";

canvas.fillText("My message", 100, 124);

var size =
canvas.measureText("My message");

canvas.strokeRect(100, 100, size.width, 24);

}

window.addEventListener("load",
initiate);

In this example, we begin with the same code used
in the example of Listing 11-15, but this time the textBaseline is set to bottom, which means that the lower part of the
text will be at the position 124. This way, we know the exact vertical
position of the text on the canvas. Next, we read the width property in the object returned by the measureText() method to get the text's width. With all the measures
taken, we can now draw a rectangle surrounding the text.

Figure 11-10: Working with text

Do It Yourself: Using the code in Listing 11-16, test different
values for the textAlign and textBaseline properties. Use the rectangle as a reference to see
how they work. Write different texts to see how the rectangle automatically
adapts to each size.

IMPORTANT: The measureText()
method returns an object with several properties. The width property used in our example is only one of them. For a complete list,
read the specification of the Canvas API. The link is available on our website.

Shadows

Shadows are, of course, also an important part of the
Canvas API. We can generate shadows for every path, including texts. The API
provides four properties to define a shadow.

shadowColor—This property declares the color of the shadow using
CSS syntax.

shadowOffsetX—This property determines how far the shadow will be
from the object in the horizontal direction.

shadowOffsetY—This property determines how far the shadow will be
from the object in the vertical direction.

shadowBlur—This property produces a blurring effect for the
shadow.

The properties must be defined before the path is
drawn on the canvas. The following example generates a shadow for a text.

Listing 11-17: Applying shadows to text

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.shadowColor =
"rgba(0, 0, 0, 0.5)";

canvas.shadowOffsetX = 4;

canvas.shadowOffsetY = 4;

canvas.shadowBlur = 5;

canvas.font =
"bold 50px verdana, sans-serif";

canvas.fillText("My message", 100, 100);

}

window.addEventListener("load",
initiate);

The shadow created in Listing 11-17 is using the rgba() function to get a semi-transparent black
color. It is displaced 4 pixels from the text and has a blur value
of 5.

Figure 11-11: Text with a shadow

Transformations

The canvas may be transformed, which affects the
figures that are drawn later. The following are the methods available to
perform these operations.

translate(x, y)—This method is used to move the origin of the canvas.

rotate(angle)—This method rotates the canvas around the origin as many radians as
specified by the attribute.

scale(x, y)—This method increases or decreases the units of the canvas to reduce
or enlarge everything drawn on it. The scale can be changed independently for horizontal
or vertical values using the x and y attributes. The values may be negative,
producing a mirror effect. By default, the values are set to 1.0.

transform(m1, m2, m3, m4, dx, dy)—This method applies a new matrix over the current one
to modify the properties of the canvas.

setTransform(m1, m2, m3, m4, dx, dy)—This method resets the current transformation matrix
and sets a new one from the values provided by the attributes.

Every canvas has the point 0, 0 (the origin)
located at the top-left corner, with the values increasing in any direction
inside the canvas (negative values are outside the canvas). The translate() method lets us move the origin to a new position to
use it as a reference for our drawings. In the following example, we move the
origin two times to change the position of a text.

Listing 11-18: Translating, rotating and
scaling

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.font =
"bold 20px verdana, sans-serif";

canvas.fillText("TEST", 50, 20);

canvas.translate(50, 70);

canvas.rotate(Math.PI /
180 * 45);

canvas.fillText("TEST", 0, 0);

canvas.rotate(-Math.PI /
180 * 45);

canvas.translate(0, 100);

canvas.scale(2, 2);

canvas.fillText("TEST", 0, 0);

}

window.addEventListener("load",
initiate);

In Listing 11-18, we apply the translate(), rotate() and scale() methods to the same text. First, we draw a
text on the canvas with the state of the canvas by default. The text appears at
the position 50,20 with a size of 20 pixels. By using the translate() method, the canvas origin
is moved to the position 50,70, and
the entire canvas is rotated 45 degrees with the rotate() method. In consequence, the next text is drawn at the
new origin with an inclination of 45 degrees. The transformations applied to
the canvas become the default values, so to test the scale() method, we rotate the canvas 45 degrees back to its default position
and again translate the origin down 100 pixels. Finally, the scale is
duplicated and another text is drawn, this time twice the size of the original.

Figure 11-12: Applying transformations

IMPORTANT: Every transformation is cumulative. If we perform two
transformations using scale(), for example, the second method performs
the scaling using the current status. A scale(2,2) after another scale(2,2) will quadruple the scale of the canvas. This applies
to every transformation, including the methods that transform the matrix, as we
will see next.

The canvas has a matrix of values that define its
properties. Modifying this matrix, we can change the characteristics of the canvas.
The API offers two methods for this purpose: transform() and setTransform().

Listing 11-19: Cumulative transformation of
the matrix

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.transform(3, 0, 0,
1, 0, 0);

canvas.font =
"bold 20px verdana, sans-serif";

canvas.fillText("TEST", 20, 20);

canvas.transform(1, 0, 0,
10, 0, 0);

canvas.font =
"bold 20px verdana, sans-serif";

canvas.fillText("TEST", 20, 20);

}

window.addEventListener("load",
initiate);

As in the previous example, in Listing 11-19 we
apply transformation methods to the same text to compare effects. The default
values of the canvas matrix are 1, 0, 0,
1, 0, 0. By changing
the first value to 3 in the first transformation, we stretch
the canvas horizontally. The text drawn after this transformation is wider than
the text drawn in normal conditions.

With the next transformation in the code, the
canvas is stretched vertically by changing the fourth value to 10
and preserving the rest as is. The result is illustrated below.

Figure 11-13: Modifying the transformation
matrix

One important thing to remember is that
transformations are applied to the matrix set by a previous transformation, so
the second text shown by the code in Listing 11-19 is stretched horizontally
and vertically, as shown in Figure 11-13. To reset the matrix and set new
transformation values, we can use the setTransform() method.

Status

The accumulation of transformations makes it dificult
to return to previous states. In the code in Listing 11-18, for example, we had
to remember the value of the rotation to perform a new rotation and undo the
previous transformations. Considering this situation, the Canvas API provides
two methods to save and retrieve the canvas state.

save()—This method saves the canvas state including
transformations already applied, values of styling properties, and the current
clipping path (the area created by the clip() method, if any).

restore()—This restores the last state saved.

Every time we want to go back to a previous
state, we have to store the state we want to preserve with the save() method and then get it back with the restore() method. Any change performed on the canvas in the
middle will not affect the drawings after the state is restored.

Listing 11-20: Saving and restoring the canvas
state

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.save();

canvas.translate(50, 70);

canvas.font =
"bold 20px verdana, sans-serif";

canvas.fillText("TEST1", 0, 30);

canvas.restore();

canvas.fillText("TEST2", 0, 30);

}

window.addEventListener("load",
initiate);

After executing the JavaScript code in Listing
11-20, the text "TEST1" is drawn in big letters at the center of the
canvas and "TEST2" in a smaller size, close to the origin. What we do
in this example is to save the canvas state and then set a new position for the
origin and styles for the text. Before drawing the second text on the canvas,
the original state is restored. As a result, the second text is shown with the
styles by default, not with those declared for the first text.

It does not matter how many transformations are
performed on the canvas; the state will return exactly to the previous
condition when the restore() method is called.

The GlobalCompositeOperation
Property

When we were talking about paths, we mentioned that
there is a property to determine how a shape is positioned and combined with
previous shapes on the canvas. That property is globalCompositeOperation and its default value is source-over, which means that the new shape will be drawn over those
already on the canvas. There are 11 more values available.

source-in—Only the part of the new shape that overlaps the existing
shape is drawn. The rest of the shape and the rest of the existing shape are
made transparent.

source-out—Only the part of the new shape that does not overlap
the existing shape is drawn. The rest of the shape and the rest of the existing
shape are made transparent.

source-atop—Only the part of the new shape that overlaps the existing
shape is drawn. The existing shape is preserved, but the rest of the new shape
is made transparent.

lighter—Both shapes are drawn, but the color of the parts
that overlap is determined by adding the values of the colors.

xor—Both shapes are drawn, but the parts that overlap are
made transparent.

destination-over—This is the opposite of the default value
(source-over). The new shape is drawn behind the existing shapes
on the canvas.

destination-in—The parts of the existing shape on the canvas that
overlap with the new shape are preserved. The rest, including the new shape,
are made transparent.

destination-out—The parts of the existing shape on the canvas that do
not overlap with the new shape are preserved. The rest, including the new shape,
are made transparent.

destination-atop—The existing shape and the new shape are
only preserved where they overlap.

darker—Both shapes are drawn, but the color of the parts
that overlap is determined by subtracting the values of the colors.

copy—Only the new shape is drawn, and the existing shape
is made transparent.

As with most properties of this API, we first have
to define the property and then draw the path on the canvas.

Listing 11-21: Testing the globalCompositeOperation
property

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

canvas.fillStyle = "#666666";

canvas.fillRect(100, 100, 200, 80);

canvas.globalCompositeOperation = "source-atop";

canvas.fillStyle = "#DDDDDD";

canvas.font =
"bold 60px verdana, sans-serif";

canvas.textAlign = "center";

canvas.textBaseline = "middle";

canvas.fillText("TEST", 200, 100);

}

window.addEventListener("load",
initiate);

Only visual representations of each possible
value for the globalCompositeOperation property can help us understand how they
work, so we prepared the example of Listing 11-21 with this purpose in mind.
When this code is executed, a red rectangle is drawn in the middle of the
canvas, but as a result of the source-atop value, only the part of the text that
overlaps the rectangle is drawn on the screen.

Figure 11-14: Applying globalCompositeOperation

Do It Yourself: Replace the value source-atop with any of the other values available for
this property and check the result in your browser. Test your code in different
browsers.

11.3 Images

The Canvas API would be useless without the capacity
to process images, but despite the importance of images, only one method is
required to draw them on the canvas. However, there are three versions of this
method available that produce different results. The following are the possible
combinations.

drawImage(image, x, y)—This syntax is used to draw an image on the canvas at
the position declared by the x and y attributes. The first attribute is a
reference to the image, which can be a reference to an <img> element, a <video> element, or another <canvas> element.

drawImage(image, x, y, width, height)—This syntax lets us scale the image before
drawing it on the canvas, changing its size to the values of the width and height attributes.

drawImage(image, x1, y1, width1, height1, x2, y2, width2,
height2)—This is the
most complex syntax. There are two values for every parameter. The purpose is
to slice part of an image and then draw it on the canvas with a customized size
and position. The attributes x1 and y1 set the top-left corner of the portion of
the image to be sliced. The attributes width1 and height1 indicate the size of the slice. The rest
of the attributes (x2, y2, width2 and height2) declare where the slice will be drawn on
the canvas and its size (which can be different from the original).

In every case, the first attribute is always a
reference to an image or a media element, including another canvas. It is not
possible to use a URL or load files from external sources directly with this method;
we first have to create an <img> element to load the image and then call
the method with a reference to this element, as in the following example.

Listing 11-22: Drawing an image

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

var image =
document.createElement("img");

image.src =
"snow.jpg";

image.addEventListener("load", function() {

canvas.drawImage(image,
20, 20);

});

}

window.addEventListener("load",
initiate);

The code in Listing
11-22 loads an image and draws it on the canvas. Because the canvas can only
receive images that are already loaded, we need to monitor this situation
checking the load event. After the image is created
by the createElement() method and loaded, it is drawn at
the position 20, 20 with the drawImage() method.

Figure 11-15: Image on the canvas

The following example illustrates how
to resize an image adding more attributes to the drawImage() method.

Listing 11-23: Adjusting the image to the size
of the canvas

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

var image =
document.createElement("img");

image.src =
"snow.jpg";

image.addEventListener("load", function() {

canvas.drawImage(image,
0, 0, element.width, element.height);

});

}

window.addEventListener("load",
initiate);

In Listing 11-23, the size of the image is
determined from the width and height properties of the <canvas> element, so the image is stretched by the method to
cover the entire canvas.

Figure 11-16: Image stretched to cover the
canvas

The next example implements the most complex
syntax available for the drawImage() method to extract a portion of the
original image and draw it on the canvas.

Listing 11-24: Extracting, resizing and
drawing

function
initiate() {

var element =
document.getElementById("canvas");

var canvas =
element.getContext("2d");

var image =
document.createElement("img");

image.src =
"snow.jpg";

image.addEventListener("load", function() {

canvas.drawImage(image,
135, 30, 50, 50, 0, 0, 300, 300);

});

}

window.addEventListener("load",
initiate);

In this example, we take a square of the original
image starting from the position 135,50, and with a size of 50,50 pixels. The block is resized to 300,300 pixels and, finally, drawn on the canvas
at the position 0,0.

Figure 11-17: A portion of the image on the canvas

Patterns

Patterns let us add texture to the shapes using an
image. The procedure is similar to the one we used with gradients; the pattern
is created and then applied to the path as a color. The following is the method
included in the API to create a pattern.

createPattern(image, type)—This method creates and returns an object
representing a pattern. The image attribute is a reference to the image we
want to use for the pattern, and the type attribute determines its type. The
possible values are repeat, repeat-x, repeat-y, and no-repeat.

As well as with gradients, the object
representing the pattern must be created and then assigned to the fillStyle property of the canvas.

Listing 11-25: Adding a pattern to our paths

var
canvas, image;

function
initiate() {

var element =
document.getElementById("canvas");

canvas =
element.getContext("2d");

image =
document.createElement("img");

image.src =
"bricks.jpg";

image.addEventListener("load", modimage);

}

function
modimage() {

var pattern =
canvas.createPattern(image, "repeat");

canvas.fillStyle =
pattern;

canvas.fillRect(0, 0, 500, 300);

}

window.addEventListener("load",
initiate);

The code in Listing 11-25 creates a rectangle of
the size of the canvas and fills it with a pattern using the image bricks.jpg.

Figure 11-18: Pattern

Do It Yourself: Update your canvas.js file with the code in Listing
11-25. Download the file bricks.jpg from our website and open the document of Listing 11-1 in your browser. You should see something
similar to Figure 11-18. Experiment with the different values available for the
createPattern() method and assign patterns to other
shapes.

Image Data

Earlier, we stated that drawImage() was the only method available to draw images on the
canvas, but that is not entirely correct. There are a few methods to process
images that can also draw them on the canvas; however, these methods work with
data not images.

getImageData(x, y, width, height)—This method takes a rectangle of the canvas of the
size declared by its attributes and converts it to data. It returns an object
with the properties width, height and data.

putImageData(imagedata, x, y)—This
method turns the data declared by the imagedata attribute into an image and draws the image on the canvas at the
position specified by the x and y attributes. It is the opposite of getImageData().

Every image can be described as a sequence of
integers representing RGBA values. These are four values that define the levels
of color red, green, blue and alpha (transparency) for each pixel. This
information results in a one-dimensional array that can be used to generate an
image. The position of every value in the array is calculated by the formula (width × 4 × y) + (x × 4) + n, where n is an index number for the values of the
pixel, starting at 0. The formula for red is (width × 4 × y) + (x × 4) + 0; for green is (width × 4 × y) + (x × 4) + 1; for blue is (width × 4 × y) + (x × 4) + 2; and for the alpha value is (width × 4 × y) + (x × 4) + 3. The following example implements these
formulas to create the negative of an image.

Listing 11-26: Generating the negative of an
image

var
canvas, image;

function
initiate() {

var element =
document.getElementById("canvas");

canvas =
element.getContext("2d");

image =
document.createElement("img");

image.src =
"snow.jpg";

image.addEventListener("load", modimage);

}

function
modimage() {

canvas.drawImage(image, 0, 0);

var info =
canvas.getImageData(0, 0, 175, 262);

var pos;

for (var x =
0; x < 175; x++) {

for (var y
= 0; y < 262; y++) {

pos =
(info.width * 4 * y) + (x * 4);

info.data[pos] = 255 - info.data[pos];

info.data[pos+1] = 255 - info.data[pos+1];

info.data[pos+2] = 255 - info.data[pos+2];

}

}

canvas.putImageData(info,
0, 0);

}

window.addEventListener("load",
initiate);

IMPORTANT:
These methods present
limitations for cross-origin access. The files for this example have to be
uploaded to a server or a local server to work properly (including the snow.jpg image that you can download from our
website). We will study
cross-origin access next.

In Listing 11-26, we create a new function to
process the image called modimage(). This function draws the image on the canvas at the
position 0,0 using the drawImage() method and then processes the image data
pixel by pixel.

The image from our example is 350 pixels wide by
262 pixels tall. Using the getImageData() method with the values 0,0 for the top-left corner and 175,262 for the horizontal and vertical
dimensions, we extract the left half of the original image. The data obtained
is stored in the info variable and then processed to get the
desired effect.

Because every color is declared by a value
between 0 and 255 (one byte), the negative value is obtained by subtracting the
original value from 255 using the formula color =
255 - color. To do this
for every pixel in our image, we create two for loops, one for columns and another for
rows. Notice that the for loop for the x
values ranges from 0 to 174 (the width of the part of the image we cut from the
canvas), and the for loop for the y
values ranges from 0 to 261 (the total number of vertical pixels in the image
we are processing).

After every pixel is processed, the data
contained by the info variable is transferred to the canvas
using the putImageData() method. This new image is located at the
same position than the original, replacing the left half of our original image
with the negative we have just created.

Figure 11-19: Negative image

Cross-Origin

A cross-origin application is an application that is
located in one domain and is accessing resources from another. Because of
security concerns, some APIs restrict cross-origin access. In the case of the
Canvas API, no information can be retrieved from the <canvas> element after an image from another domain has been
drawn.

These restrictions may be avoided using a
technology called CORS (Cross-Origin Resource Sharing). CORS defines a
protocol for servers to share their resources and allow requests from other
origins. The access from one origin to
another must be authorized by the server. The authorization is made by
declaring in the server the origins (domains) allowed to access the resources.
This is done in the header sent by the server that allocates the file
processing the request. For example, if our application is located at
www.domain1.com and is accessing resources from www.domain2.com, this second
server must be configured to declare the origin www.domain1.com as a valid
origin for the request.

CORS provides several headers
to be included as part of the HTTP header sent by the server, but the only one
required is Access-Control-Allow-Origin. This header indicates what origins
(domains) are able to access the server’s resources. The * character may be declared to allow
requests from any origin.

IMPORTANT: Your server must be configured to send CORS
HTTP headers with every request to allow applications from other domains to
access its files. An easy way to do this is to append a new line to the .htaccess file. Most servers
provide this configuration file in the root folder of every website. The syntax
is Header
set CORS-Header value (e.g.,
Header
set Access-Control-Allow-Origin *). For more information on how to
add HTTP headers to your server, visit our website and follow the links for
this chapter.

Adding the HTTP
headers to the server’s configuration is only one of the steps we have to take
to turn the codes into cross-origin applications. The codes also have to
declare the files as a cross-origin resource using the crossOrigin attribute.

Listing 11-27: Enabling cross-origin access

var
canvas, image;

function
initiate() {

var element =
document.getElementById("canvas");

canvas =
element.getContext("2d");

image =
document.createElement("img");

image.crossOrigin =
"anonymous";

image.src =
"http://www.formasterminds.com/content/snow.jpg";

image.addEventListener("load", modimage);

}

function
modimage() {

canvas.drawImage(image, 0, 0);

var info =
canvas.getImageData(0, 0, 175, 262);

var pos;

for (var x =
0; x < 175; x++) {

for (var y
= 0; y < 262; y++) {

pos =
(info.width * 4 * y) + (x * 4);

info.data[pos] = 255 - info.data[pos];

info.data[pos+1] = 255 - info.data[pos+1];

info.data[pos+2] = 255 - info.data[pos+2];

}

}

canvas.putImageData(info, 0, 0);

}

window.addEventListener("load",
initiate);

The crossOrigin attribute can take two values: anonymous or use-credentials. The first value ignores
credentials while the second value requires credentials to be sent in the
request. Credentials are shared automatically by the client and the server
using cookies. To use credentials, we have to include a second header in the
server called Access-Control-Allow-Credentials with the value true.

In Listing 11-27, only one
modification was made from the previous example: we added the crossOrigin attribute to the img element to declare the image as a
cross-origin resource. Now, we can run this code on our computer and use the
image from a server without violating the same-origin policy (CORS headers were
already added to the www.formasterminds.com server).

IMPORTANT: The crossOrigin attribute has to be
declared before the src attribute to set the source as
cross-origin. Of course, the attribute may also be declared in the opening tag
of the <img>, <video> and <audio> elements, as any other
HTML attribute.

Extracting Data

The getImageData() method previously studied returns an
object that can be processed through its properties (width, height, and
data) or used as is by the putImageData() method. The purpose of these methods is to provide
access to the canvas' content and return that data to the same or another
canvas after being processed. But sometimes this information may be required
for other purposes, like assigning it as the source of an <img> element, sending it to a server, or storing it in a
database. The Canvas API includes the following methods to get the canvas'
content in a data format we can use to accomplish these tasks.

toDataURL(type)—This method returns data in data:url format containing a
representation of the canvas’ content in the format specified by the type attribute and a resolution of 96dpi. If no type was declared, the data
is returned in the PNG format. The possible values for the attribute are image/jpeg and image/png.

toBlob(function, type)—This method returns an object with a blob containing
a representation of the canvas’ content in the format specified by the type attribute and a resolution of 96dpi. The first attribute is the
function in charge of processing the object. If no type was declared, the blob
is returned in the PNG format. The possible values for the attribute are image/jpeg and image/png.

The following document adds a box next to the <canvas> element to show the image produced from its content.

Listing 11-28: Creating a document to show an
image with the canvas' content

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Canvas API</title>

<style>

#canvasbox,
#databox {

float:
left;

width:
400px;

height:
300px;

padding:
10px;

margin:
10px;

border:
1px solid;

}

.clearfloat
{

clear:
both;

}

</style>

<script
src="canvas.js"></script>

</head>

<body>

<section
id="canvasbox">

<canvas
id="canvas" width="400"
height="300"></canvas>

</section>

<section
id="databox"></section>

<div
class="clearfloat"></div>

</body>

</html>

The code for this example draws the image on the canvas, extracts
the content, and generates an <img> element to show it on the screen.

Listing 11-29: Creating an image with the
canvas' content

function
initiate() {

var element =
document.getElementById("canvas");

var width =
element.width;

var height =
element.height;

element.addEventListener("click", copyImage);

var canvas =
element.getContext("2d");

canvas.beginPath();

canvas.arc(width / 2, height / 2, 150, 0, Math.PI * 2, false);

canvas.clip();

var image =
document.createElement("img");

image.src =
"snow.jpg";

image.addEventListener("load", function() {

canvas.drawImage(image, 0, 0, width, height);

});

}

function
copyImage() {

var element =
document.getElementById("canvas");

var data =
element.toDataURL();

var databox =
document.getElementById("databox");

databox.innerHTML = '<img src="' + data + '">';

}

window.addEventListener("load",
initiate);

The code in Listing 11-29 creates a circular
clipping area and then draws an image on the canvas. The effect makes it seem
like the image was cropped in a circular shape. When the user clicks on the canvas,
we extract this image from the canvas with the toDataURL() method and use the data returned by the method as the
source of a new <img> element. The element processes the data
and shows the image on the screen.

Figure 11-20: Image created from the canvas'
content

Do It Yourself: Create a new HTML file with the document of Listing
11-28 and a JavaScript file called canvas.js with the code in Listing 11-29.
Upload the files, including the snow.jpg file, to your server or local server
and open the document in your browser. Click on the canvas to extract the
content and show the image inside the box on the right.

The Basics: Data:url
and blobs are two different formats for data distribution. The data:url format
is officially called Data URI Scheme. This is a string of text
representing the data necessary to recreate the resource and therefore it may
be used as the source of HTML elements, as demonstrated by the example of
Listing 11-29. Blobs, on the other hand, are blocks of raw data. We will study
blobs in Chapter 16.

11.4 Animations

There is no method to help us animate things on the
canvas, and there is no predetermined procedure to do it. We have to erase the
area of the canvas we want to animate, draw the shapes on it, and repeat the
process again and again. Once the shapes are drawn, they cannot be moved, and
we are only able to construct an animation by erasing the area and drawing the
shapes again in a different position. For this reason, in games or applications
that require a large number of objects to be animated, it is better to use
images instead of shapes built by complex paths (for example, games usually use
PNG images).

Simple Animations

There are many techniques to create animations; some
are simple and others more complex, depending on the application. In the
following example, we are going to implement a basic animation technique using
the clearRect() method introduced before to clear the canvas and draw
the shapes again (the code assumes that we are using the document of Listing 11-1 with a single <canvas> element of 500 pixels by 300 pixels).

Listing 11-30: Creating an animation

var
canvas;

function
initiate() {

var element =
document.getElementById("canvas");

canvas =
element.getContext("2d");

window.addEventListener("mousemove", animation);

}

function
animation(event) {

canvas.clearRect(0, 0,
500, 300);

var xmouse =
event.clientX;

var ymouse =
event.clientY;

var xcenter =
220;

var ycenter =
150;

var ang =
Math.atan2(xmouse - xcenter, ymouse - ycenter);

var x =
xcenter + Math.round(Math.sin(ang) * 10);

var y =
ycenter + Math.round(Math.cos(ang) * 10);

canvas.beginPath();

canvas.arc(xcenter, ycenter, 20, 0, Math.PI * 2, false);

canvas.moveTo(xcenter + 70, 150);

canvas.arc(xcenter + 50, 150, 20, 0, Math.PI * 2, false);

canvas.stroke();

canvas.beginPath();

canvas.moveTo(x + 10, y);

canvas.arc(x,
y, 10, 0, Math.PI * 2, false);

canvas.moveTo(x + 60, y);

canvas.arc(x
+ 50, y, 10, 0, Math.PI * 2, false);

canvas.fill();

}

window.addEventListener("load",
initiate);

This animation is about a pair of eyes that look
at the pointer of the mouse all the time. To move the eyes, we have to update
their position every time the mouse is moved. This is accomplished by detecting
the position of the mouse on the window with the mousemove event. Every time the event is fired, the animation() function is called. This function clears the canvas
with the instruction clearRect(0, 0,
500, 300) and then
initializes the variables. The position of the mouse is captured by the
properties clientX and clientY and the coordinates for the first eye are
stored in the variables xcenter and ycenter.

After the initial values are ready, it is time to
do the math. Using the values of the position of the mouse and the center of
the left eye, we calculate the angle of the invisible line from one point to
another using the atan2() method of the Math object (see Chapter 6). This angle is used to calculate the point at
the center of the iris with the formula xcenter + Math.round(Math.sin(ang) × 10). The number 10
in the formula represents the distance from the center of the eye to the center
of the iris (because the iris is not at the center of the eye, it is close to
the edge).

With these values, we can start drawing the eyes
on the canvas. The first path is two circles representing the eyes. The arc() method for the left eye is positioned at the values of xcenter and ycenter, and the circle for the right eye is
generated 50 pixels to the right using the instruction arc(xcenter + 50, 150, 20, 0, Math.PI * 2, false).

The animation is created by the second path. This
path uses the x and y variables with the position previously
calculated from the angle. The irises are drawn as solid black circles using
the fill() method.

The process is repeated and the values
recalculated every time the mousemove event is fired (every time the user moves
the mouse).

Figure 11-21: Simple Animation

Do It Yourself: Copy the code in Listing 11-30 into the JavaScript
file called canvas.js and open the document of Listing 11-1 in your browser. Move the mouse around the
circles. You should see the eyes following the pointer.

IMPORTANT: In this example, the distance was calculated without taking
into account the position of the canvas on the screen. This is because the <canvas> element in the document of Listing 11-1 was created at the top-left corner of the
page and therefore the canvas' origin is the same as the document's origin. For
more information on the clientX and clientY properties, see Chapter 6.

Professional Animations

The loop for the animation in the previous example was
generated by the mousemove event. In a professional animation, loops
are controlled by the code and running all the time, independently of the user’s
activity. In Chapter 6, we introduced two methods provided by JavaScript to run
a loop: setInterval() and setTimeout(). These methods execute a function after a certain
period of time. Working with these methods, we can produce decent animations,
but they are not synchronized with the browser, causing glitches that
are not tolerated in a professional application. In order to solve this problem,
browsers include a little API with only two methods, one to generate a new
cycle of the loop and another to cancel it.

requestAnimationFrame(function)—This method tells the browser that the function
between parentheses should be executed. The browser calls the function when it
is ready to refresh the window, synchronizing the animation with the browser
window and the computer’s display. We can assign this method to a variable and
then cancel the process using the cancelAnimationFrame() method with the name of the variable
between parentheses.

The requestAnimationFrame() method works like the setTimeout() method, we have to call it again in every cycle of
the loop. The implementation is simple, but the code for a professional
animation requires a certain organization that can only be achieved with
advanced programming patterns. For our example, we are going to use a global
object and distribute the tasks among several methods. The following is the
document with the <canvas> element that we need to present the
animation on the screen.

Listing 11-31: Creating the document to show a
professional animation

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Animations</title>

<style>

#canvasbox
{

width:
600px;

margin:
100px auto;

}

#canvas {

border:
1px solid #999999;

}

</style>

<script
src="canvas.js"></script>

</head>

<body>

<div
id="canvasbox">

<canvas
id="canvas" width="600"
height="400"></canvas>

</div>

</body>

</html>

The CSS styles in the document of Listing 11-31 center
the canvas on the screen and provide a border to identify its area. The
document also loads a JavaScript file for the following code.

Listing 11-32: Creating a 2D video game

var mygame
= {

canvas: {

ctx:
"",

offsetx: 0,

offsety: 0

},

ship: {

x: 300,

y: 200,

movex: 0,

movey: 0,

speed: 1

},

initiate:
function() {

var element
= document.getElementById("canvas");

mygame.canvas.ctx = element.getContext("2d");

mygame.canvas.offsetx = element.offsetLeft;

mygame.canvas.offsety = element.offsetTop;

document.addEventListener("click", function(event) {

mygame.control(event);

});

mygame.loop();

},

loop:
function() {

if
(mygame.ship.speed) {

mygame.process();

mygame.detect();

mygame.draw();

requestAnimationFrame(function() {

mygame.loop();

});

} else {

mygame.canvas.ctx.font = "bold 36px verdana, sans-serif";

mygame.canvas.ctx.fillText("GAME OVER", 182, 210);

}

},

control:
function(event) {

var
distancex = event.clientX - (mygame.canvas.offsetx + mygame.ship.x);

var
distancey = event.clientY - (mygame.canvas.offsety + mygame.ship.y);

var ang =
Math.atan2(distancex, distancey);

mygame.ship.movex = Math.sin(ang);

mygame.ship.movey = Math.cos(ang);

mygame.ship.speed += 1;

},

draw:
function() {

mygame.canvas.ctx.clearRect(0, 0, 600, 400);

mygame.canvas.ctx.beginPath();

mygame.canvas.ctx.arc(mygame.ship.x, mygame.ship.y, 20, 0, Math.PI / 180 * 360,
false);

mygame.canvas.ctx.fill();

},

process:
function() {

mygame.ship.x += mygame.ship.movex * mygame.ship.speed;

mygame.ship.y += mygame.ship.movey * mygame.ship.speed;

},

detect:
function() {

if
(mygame.ship.x < 0 || mygame.ship.x > 600 || mygame.ship.y < 0 ||
mygame.ship.y > 400) {

mygame.ship.speed = 0;

}

}

};

window.addEventListener("load",
function() {

mygame.initiate();

});

A professional application should avoid global
variables and functions and concentrate the code inside a unique global object.
In Listing 11-32, we call this object mygame. All the properties and methods necessary
to create a small video game are declared inside this object.

Our game is about a black spaceship that moves in
the direction indicated by the click of the mouse. The goal is to change the
direction of the ship to avoid the walls. Every time the direction is changed,
the speed of the ship increases, making it more and more difficult to keep the
ship inside the canvas.

The organization required to create this kind of
application always includes some standard elements. We have to declare initial
values, control the animation loop, and distribute the rest of the tasks in
multiple methods. In the example of Listing 11-32, this organization is
achieved with the inclusion of a total of six methods: initiate(), loop(), control(), draw(), process(), and detect().

We start by declaring the canvas and ship properties. These properties contain
objects with basic information for the game. The canvas object has three properties: ctx to store the context of the canvas, and offsetx and offsety to store the position of the <canvas> element related to the page. The ship object, on the other hand, has five properties: x
and y to store the coordinates of the ship, movex and movey to determine the direction, and speed to know the ship's current speed. These are important properties
required in almost every section of the code, but some of their values have to
be initialized. As we did in previous examples, this work is performed by the initiate() method. This method is called by the load event when the document has finished loading, and it is responsible
for assigning to the properties all the values required to initiate the
application.

The first task of the initiate() method is to get a reference to the context of the
canvas and the position of the element in the window using the offsetLeft and offsetTop properties. Then, a listener is added to
the click event to respond when the user clicks anywhere on the
document.

The loop() method is the second most important method
in the underlying organization of a professional application. This method calls
itself again and again while the application is running, creating a loop that
goes through every part of the process. In our example, this process is divided
into three methods: process(),  detect() and draw(). The process() method calculates the new position of the
ship according to the current direction and speed, the detect() method compares the coordinates of the ship with the boundaries
of the canvas to determine if the ship has crashed into the walls, and the draw() method draws the ship on the canvas. The loop executes these methods
one by one and then calls itself with the requestAnimationFrame() method,
starting a new cycle.

The last step to finish the basic structure of our application is
to check the user’s feedback. In our game, this is done by the control()
method. When the user clicks on any part of the document, this method is called
to calculate the direction of the ship. The formula is similar to previous
examples; we calculate the distance from the ship to the place where the event
occurred, then we get the angle of the invisible line between those two points,
and finally, the direction in coordinates obtained by the sin() and cos()
methods is stored in the movex and movey properties. The last instruction in the control()
method increases the speed of the ship to make our game more interesting.

The game starts as soon as the document is loaded and finishes
when the ship crashes into a wall. To make this small application look more
like a video game, we added an if instruction in the loop to check the condition of the
ship. This condition is determined by the value of the speed. When the detect()
method determines that the coordinates of the ship are out of the boundaries of
the canvas, the speed is reduced to 0. This value turns the condition of the loop false and
the message "GAME OVER" is shown on the screen.

Figure 11-22: Simple Video Game

11.5 Video

As with animations, there is no particular method to
show video on a <canvas> element. The only way to do it is by
taking every frame from the <video> element and drawing it as an image on the
canvas using the drawImage() method. The following document includes a
short JavaScript code to turn the canvas into a mirror.

Listing 11-33: Displaying video on canvas

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Video on Canvas</title>

<style>

section {

float:
left;

}

</style>

<script>

var canvas,
video;

function
initiate() {

var
element = document.getElementById("canvas");

canvas =
element.getContext("2d");

video =
document.getElementById("media");

canvas.translate(483, 0);

canvas.scale(-1, 1);

setInterval(processFrames, 33);

}

function
processFrames() {

canvas.drawImage(video, 0, 0);

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section>

<video
id="media" width="483" height="272" autoplay>

<source src="trailer.mp4">

<source src="trailer.ogg">

</video>

</section>

<section>

<canvas
id="canvas" width="483"
height="272"></canvas>

</section>

</body>

</html>

As explained before, the drawImage() method can take three types of sources: an image, a
video, or another canvas. Because of this, to show video on the canvas, we have
to provide a reference to the <video> element as the first attribute of the
method. Although this is enough to draw the video on the canvas, we have to consider
that videos are composed of multiple frames, and the drawImage() method is only able to draw one frame at a time. For
this reason, to show the entire video, we have to repeat the process for every
frame.  In the code in Listing 11-33, a setInterval() method is used to call the processFrames() function every 33 milliseconds. This function
executes the drawImage() method with the video as its source (The
time set by setInterval() is approximately the time it takes every
frame to be shown on the screen).

When we draw an image on the canvas, it is bound
to the canvas’ properties and can be processed like any other content. In our
example, the image is inverted to create a mirror effect. The effect is
produced by the application of the scale() method to the context (everything drawn on
the canvas is inverted).

Figure 11-23: Mirror image of a video

© Copyright
2008, Blender Foundation / www.bigbuckbunny.org

Real-Life Application

There are a million things we can do with the canvas,
but it is always fascinating to see how far we can go combining simple methods
from different APIs. The following example describes how to take a snapshot
with the webcam and show it on the screen using the <canvas> element.

Listing 11-34: Programming a snapshot
application

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Snapshot Application</title>

<style>

section {

float:
left;

width:
320px;

height:
240px;

border:
1px solid #000000;

}

</style>

<script>

var video,
canvas;

function
initiate() {

var
promise = navigator.mediaDevices.getUserMedia({video: true});

promise.then(success);

promise.catch(showerror);

}

function
success(stream) {

var
element = document.getElementById("canvas");

canvas =
element.getContext("2d");

var
player = document.getElementById("player");

player.addEventListener("click", snapshot);

video =
document.getElementById("media");

video.srcObject = stream;

video.play();

}

function
showerror(event) {

console.log("Error: " + event.name);

}

function snapshot() {

canvas.drawImage(video, 0, 0, 320, 240);

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section
id="player">

<video
id="media" width="320"
height="240"></video>

</section>

<section>

<canvas
id="canvas" width="320" height="240"></canvas>

</section>

</body>

</html>

This document provides some CSS styles, the JavaScript
code, and a few HTML elements, including the <video> and <canvas> elements. The <canvas> is declared as usual, but we did not specify the
source for the <video> element because we are going to use it to
show the video from the webcam.

The code is simple. Our standard initiate() function calls the getUserMedia() method as soon as the document is loaded to access to
the webcam. In case of success, the success() function is called. Most of the work is
done by this function. It gets the context of the canvas, adds a listener for
the click event to the box corresponding to the video, and
assigns the video from the webcam to the <video> element. In the end, the video is played
using the play() method.

At this point, we have the video from the webcam
displayed in the box on the left. To take a snapshot of that video and put it
in the right box, we created the snapshot() function. This function responds to the click event. When the user clicks on the video, the function executes the drawImage() method with a reference to the video and the values
corresponding to the size of the <canvas> element. The method takes the current
frame and draws it on the canvas to show the snapshot on the screen.

Do It Yourself: Create a new HTML file with the document of Listing
11-34. Open the document in your browser and authorize the browser to access
the camera. Click on the video. The image should be drawn on the canvas.
Remember to upload the file to your server before testing.

Chapter 12 - WebGL

12.1 3D Canvas

WebGL is a
low-level API that works with the <canvas> element to
create 3D graphics for the Web. It
utilizes the GPU (Graphic Processing Unit) of the video card to perform some
operations and relieves the CPU (Central Processing Unit) of the hard work. It is
based on OpenGL, a well-known library developed by Silicon Graphics Inc. that
has been applied in the creation of 3D video games and applications since 1992.
These characteristics make WebGL very reliable and efficient, and that is
probably the reason why it became the standard 3D API for the Web.

WebGL
has been implemented in almost every HTML5 compatible browser, but its
complexity forces developers to work with JavaScript libraries built on top of
it, instead of the API itself. This complexity goes beyond the fact that WebGL
does not incorporate native methods to perform basic 3D operations, it is also
related to its low level nature that requires the inclusion of external codes
programmed in a language called GLSL (OpenGL Shading Language) to
provide essential features for the production of the image on the screen. For
these reasons, developing an application with WebGL always requires the use of
external libraries, from glMatrix to calculate matrix and vectors (github.com/toji/gl-matrix), to more
general-purpose libraries such as Three.js (www.threejs.org), GLGE (www.glge.org), SceneJS (www.scenejs.org), among others.

IMPORTANT: Because of the characteristics of
WebGL and the extension of the subject, we will not study and apply WebGL
itself. In this chapter, you will learn how to generate and work with 3D
graphics using a simple external library called Three.js.

12.2 Three.js

Three.js is
probably the most popular and powerful library available for the generation of
3D graphics using WebGL. This library works on top of WebGL, simplifying most
tasks and providing all the tools we need to control cameras, lights, objects,
textures, and create a 3D World. The library is easy to install; we have to
download the package from www.threejs.org and include
the three.min.js file in our documents.

IMPORTANT: This chapter is not intended to be
a manual of Three.js. For a complete reference, go to the library's website and
read the documentation.

Do It Yourself: Go to www.threejs.org and click on Download to download the
package with the library. The package includes multiple files and examples, but
you only need the three.min.js file inside the build folder. Copy this file
into your projects folder to be able to incorporate it into your documents.

Renderer

The renderer is the surface where the graphics are drawn.
Three.js uses a <canvas> element with a WebGL context to render 3D
scenes in the browser (it takes care of creating the context with the webgl parameter for us). The library provides the following constructor
to obtain and configure this renderer.

WebGLRenderer(parameters)—This constructor returns a WebGLRenderer object with the properties and methods to configure
the drawing surface and render the graphics on the screen. The parameters attribute has to be specified as an object. The
properties currently available for this object are canvas (<canvas> element), precision (values highp, mediump, lowp), alpha (Boolean value), premultipliedAlpha (Boolean value), antialias (Boolean value), stencil (Boolean value), preserveDrawingBuffer (Boolean value), clearColor (integer value), and clearAlpha (decimal value).

A WebGLRenderer object provides multiple methods and
properties to set and get information from the renderer. The most frequently
used are the following.

setSize(width, height)—This method resizes the canvas to the values of the width and height attributes.

setViewport(x, y, width, height)—This method determines the area of the canvas that
will be used to render the scene. The size of the area used by WebGL does not
necessarily have to be the same as the drawing surface. The x
and y attributes indicate the starting coordinates for the
viewport, and width and height indicate its size.

setClearColor(color, alpha)—This method sets the color of the surface in
hexadecimal values. The alpha attribute declares the opacity (from 0.0 to 1.0).

render(scene, camera, target, clear)—This method renders the scene using a
camera. The attributes scene and camera are objects representing the scene and the
camera, the target attribute declares where the rendering
will take place (it is not necessary if we want to use the canvas set by the
constructor), and the Boolean attribute clear determines whether the canvas has to be
cleared before rendering or not.

IMPORTANT: The library also provides the CanvasRenderer() constructor. This constructor
returns an object to work with a 2D canvas context when the browser does not include
support for WebGL. The canvas renderer does not work with the GPU and is not
recommended for demanding applications.

Scene

A scene is a global object that contains the rest
of the objects representing each element of the 3D world, such as the camera,
lights, meshes, etc. Three.js provides a simple constructor to generate a scene.

scene()—This constructor returns an object that represents
the scene. A Scene object provides the add() and remove()
methods to add and remove elements from the scene.

The scene establishes a three-dimensional space
that helps allocate each element in the virtual world. The coordinates of this
space are identified with the letters x, y and z. Each element will have its own coordinate
and position in the 3D world, as represented below.

Figure 12-1: Cube in a 3D coordinate system

If we increase the value of the x
coordinate of an element, that element will be displaced to a new position on
the x axis,
maintaining the same position on the rest of the axes, as shown in Figure
12-2.

Figure 12-2: Object displaced on the x axis

For every new element of the world,
such as the camera, lights and physical objects, we have to set the values of
the three coordinates to establish its position in the scene. When these
coordinates are not declared, the element is positioned at the origin (the
coordinates 0, 0, 0).

Because there are no parameters to
determine the size or the scale of a 3D world, the values have no unit of
measurement; they are declared as decimal numbers. The scale is established from
the relation between the elements already defined in the world and the camera.

Camera

The camera is an essential part of a 3D scene. It
determines the point of view of the viewer (the user) and provides the
perspective necessary to make our world look realistic. Three.js includes
constructors to create different types of cameras. The following are the most
frequently used.

PerspectiveCamera(fov, aspect, near, far)—This constructor returns an object representing a
camera with perspective projection. The fov attribute determines the vertical field of
view, aspect declares the aspect ratio, and the near and far attributes limit what the camera sees (the
nearest and farthest points).

OrthographicCamera(left, right, top, bottom, near, far)—This constructor returns an object
representing a camera with orthographic projection. The attributes left, right, top, and bottom declare the corresponding frustum plane.
The near and far attributes limit what the camera sees (the
nearest and farthest points).

Perspective and orthographic projections are
different ways of projecting the three-dimensional world onto a two-dimensional
surface like the canvas. The perspective projection is more realistic in terms
of the imitation of the real world and is the one recommended for animations,
but the orthographic projection is useful for the visualization of structures
and drawings that require access to details because it ignores some effects
produced by the human eye.

The Camera object returned by these constructors
includes a method to set the vector to which the camera has to look at.

lookAt(vector)—This method orients the camera towards a point in the
scene. The vector attribute is a Vector object containing the properties with the values of the three
coordinates x, y, and z (e.g., {x: 10,
y: 10, z: 20}). By
default, the camera looks at the origin (the coordinates 0,
0, 0).

Meshes

In a 3D world, physical objects are represented through
meshes. A mesh is a collection of vertices that define a shape. Each vertex of
the mesh is a node in the three-dimensional space. Nodes are joined by
invisible lines that generate small faces all around the shape. The group of
faces obtained by the intersection of all the vertices of a mesh constitutes
the surface of the shape.

Figure 12-3: Mesh to build a sphere

Figure 12-3 shows a mesh representing a sphere. To
create this particular mesh, we have to define 256 vertices that generated 225
faces. That means that we have to declare the x, y, and z coordinates 256 times to create all the
nodes required for the definition of a single sphere. This example shows how difficult
it is to define three-dimensional objects. Because of this complexity,
designers work with modeling applications to generate 3D graphics. Software
like Blender (www.blender.org), for example, exports the entire mesh in
special formats that other applications can read and implement in a 3D world
without having to create the vertices one by one (we will come back to this
later).

Meshes, like any other elements, have to be enclosed
in an object before they can be introduced into the scene. The library provides
a general method for this purpose.

Mesh(geometry, material)—This constructor returns an object representing a
mesh. The geometry and material attributes are objects returned by
geometry and material constructors, as we will see next.

Geometric Primitives

A sphere, like the one shown in Figure 12-3, is called primitive or geometric primitive. Primitives
are basic geometric figures that have a defined structure. For some
applications, including small video games, primitives simplify the work of
designers and developers. Three.js offers several constructors to create the
most common geometric primitives.

SphereGeometry(radius, horizontal_segments, vertical_segments,
phiStart, phiLength, thetaStart, thetaLength)—This constructor returns an object containing the
mesh to build a sphere. The radius attribute defines the radius of the
sphere, the horizontal_segments and vertical_segments attributes declare the number of segments
included horizontally and vertically to create the shape, and the combination
of phiStart, phiLength, thetaStart and thetaLength allows us to generate incomplete spheres.
Most attributes are optional and have values by default.

BoxGeometry(width, height, depth, horizontal_segments,
vertical_segments, depth_segments, materials, sides)—This constructor returns an object
containing the mesh to build a cube. The width, height, and
depth attributes determine the size of each side of the
cube, the horizontal_segments, vertical_segments and depth_segments attributes establish the number of
segments used to create the cube’s faces, the materials attribute is an array containing different materials
for each face, and the sides attribute contains six Boolean values to
specify whether or not each face will be generated. Most attributes are
optional and have values by default.

CylinderGeometry(top_radius, bottom_radius, height, horizontal_segments,
vertical_segments)—This
constructor returns an object containing the mesh to build a cylinder. The top-radius and bottom_radius attributes specify the radius for the top
and bottom of the cylinder (different values build a cone), the height attribute declares the height of the cylinder, and the horizontal_segments and vertical_segments attributes declare the number of
horizontal and vertical segments to be used to create the mesh.

IcosahedronGeometry(radius, detail)—This constructor returns an object
containing the mesh to build an icosahedron. The radius attribute declares the radius, and detail specifies the level of detail. Higher
levels of detail require more segments to be used. Usually, the value of this
attribute ranges from 0 to no more than 5,
depending on the size of the shape and the level of details we need.

OctahedronGeometry(radius, detail)—This constructor returns an object
containing the mesh to build an octahedron. The radius attribute declares the radius, and detail specifies the level of detail. Higher
levels of detail require more segments to be used. Usually, the value of this
attribute ranges from 0 to no more than 5,
depending on the size of the shape and the level of details we need.

TetrahedronGeometry(radius, detail)—This constructor returns an object
containing the mesh to build a tetrahedron. The radius attribute declares the radius, and detail specifies the level of detail. Higher
levels of detail require more segments to be used. Usually, the value of this attribute
ranges from 0 to no more than 5,
depending on the size of the shape and the level of details we need.

PlaneGeometry(width, height, horizontal_segments, vertical_segments)—This constructor returns an object
containing the mesh to build a flat plane. The width and height attributes declare the width and height of
the plane, while horizontal_segments and vertical_segments specify how many segments will be used to
build it.

CircleGeometry(radius, segments, starting_angle, ending_angle)—This constructor returns an object
containing the mesh to build a flat circle. The radius attribute declares the radius of the circle, the segments attribute specifies the number of segments to use to build
the shape, and the starting_angle and ending_angle attributes declare the angles in radians
where the circle starts and ends (for a full circle, the values should be 0
and Math.PI * 2).

Materials

WebGL uses shaders to represent 3D
graphics on the screen. Shaders
are codes programmed in the GLSL language (OpenGL Shading Language) that work
directly with the GPU to produce the image. They provide the right level of brightness
and darkness for each pixel of the image to generate the perception of a
three-dimensional world. This complex concept is hidden by Three.js behind
materials and lights. By defining materials and lights, Three.js determines how
the 3D world will be shown on the screen using preprogrammed shaders that are
of common implementation in 3D animations.

Three.js defines a few types of materials that
have to be applied according to the requirements of the application and the
resources available. More realistic materials require more processing power.
The material chosen for our projects will have to balance the level of realism
our application needs with the amount of processing required. Different
materials may be applied to various objects in the same world. The following
constructors are provided to define them.

LineBasicMaterial(parameters)—This material is used to render meshes composed of
single lines (for example, a grid). The parameters attribute is an object containing
properties for the configuration of the material. The properties available are color (hexadecimal value), opacity (decimal value), blending (constant), depthTest (Boolean value), linewidth (decimal value), linecap (values butt, round or square), linejoin (values round, bevel or miter), vertexColors (Boolean value), and fog (Boolean value).

MeshBasicMaterial(parameters)—This material renders the mesh with a single color,
without emulating the reflection of lights. It is unrealistic but useful in
some circumstances. The parameters attribute is an object containing
properties for the configuration of the material. The properties available are color (hexadecimal value), opacity (decimal value), map (Texture object), lightMap (Texture object), specularMap (Texture object), envMap (TextureCube object), combine (constant), reflectivity (decimal value), refractionRatio (decimal value), shading (constant), blending (constant), depthTest (Boolean value), wireframe (Boolean value), wireframelinewidth (decimal value), vertexColors (constant), skinning (Boolean value), morphTargets (Boolean value), and fog (Boolean value).

MeshNormalMaterial(parameters)—This material defines one shade of color for each face
of the mesh. The effect is unrealistic because the faces are distinguishable
from one another, but it is useful when the hardware required for the
calculation of a more realistic material is not available. The parameters attribute is an object containing
properties for the configuration of the material. The properties available are opacity (decimal value), shading (constant), blending (constant), depthTest (Boolean value), wireframe (Boolean value), and wireframelinewidth (decimal value).

MeshLambertMaterial(parameters)—This material generates a smooth shading on the
surface of the mesh, producing the effect of the light reflecting on the
object. The effect is independent of the observer’s point of view. The parameters attribute is an object containing properties for the
configuration of the material. The properties available are color (hexadecimal value), ambient (hexadecimal value), emissive (hexadecimal value), opacity (decimal value), map (Texture object), lightMap (Texture object), specularMap (Texture object), envMap (TextureCube object), combine (constant), reflectivity (decimal value), refractionRatio (decimal value), shading (constant), blending (constant), depthTest (Boolean value), wireframe (Boolean value), wireframelinewidth (decimal value), vertexColors (constant), skinning (Boolean value), morphTargets (Boolean value), morphNormals (Boolean value), and fog (Boolean value).

MeshPhongMaterial(parameters)—This material produces a realistic effect with a
smooth shading over the whole surface of the mesh. The parameters attribute is an object containing properties for the
configuration of the material. The properties available are color (hexadecimal value), ambient (hexadecimal value), emissive (hexadecimal value), specular (hexadecimal value), shininess (decimal value), opacity (decimal value), map (Texture object), lightMap (Texture object), bumpMap (Texture object), bumpScale (decimal value), normalMap (Texture object), normalScale (Vector object), specularMap (Texture object), envMap (TextureCube object), combine (constant), reflectivity (decimal value), refractionRatio (decimal value), shading (constant), blending (constant), depthTest (Boolean value), wireframe (Boolean value), wireframelinewidth (decimal value), vertexColors (constant), skinning (Boolean value), morphTargets (Boolean value), morphNormals (Boolean value), and fog (Boolean value).

MeshFaceMaterial()—This constructor is applied when different
materials and textures were declared for each face of the geometry.

ParticleBasicMaterial(parameters)—This material is designed for rendering particles
(e.g., smoke, explosion, etc.). The parameters attribute is an object containing
properties for the configuration of the material. The properties available are color (hexadecimal value), opacity (decimal value), map (Texture object), size (decimal value), sizeAttenuation (Boolean value) blending (constant), depthTest (Boolean value), vertexColors (Boolean value), and fog (Boolean value)

ShaderMaterial(parameters)—This constructor allows us to provide our own
shaders. The parameters attribute is an object containing
properties for the configuration of the material. The properties available are:
fragmentShader (string), vertexShader (string), uniforms (object), defines (object), shading (constant), blending (constant), depthTest (Boolean value), wireframe (Boolean value), wireframelinewidth (decimal value), lights (Boolean value), vertexColors (constant), skinning (Boolean value), morphTargets (Boolean value), morphNormals (Boolean value), and fog (Boolean value).

IMPORTANT: Most of the parameters for material
constructors have values by default that are what developers and designers
usually require. We are going to explain how to set some of them, but for a
complete reference, see the official documentation and examples at www.threejs.org.

These constructors share common
properties that may be modified to configure the material.

name—This property defines the name of the material. An
empty string is set by default.

opacity—This property defines the material’s opacity. It
takes values from 0.0 to 1. The value 1
is set by default (fully opaque).

transparent—This property defines whether the material is
transparent or not. The default value is false.

visible—This property defines whether the material is visible
or not. The default value is true.

side—This property defines which side of the mesh will be
rendered. It can take three constants: THREE.FrontSide, THREE.BackSide or THREE.DoubleSide.

Implementation

All this theory may be daunting, but the
implementation is very simple. The following example creates a sphere like the
one in Figure 12-3. We are going to use a <canvas> element of 500 pixels by 400 pixels, a
perspective camera, and a basic material to render the wireframe on the screen.

Listing 12-1: Including the Three.js library
in the document

<!DOCTYPE
html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Three.js</title>

<script
src="three.min.js"></script>

<script
src="webgl.js"></script>

</head>

<body>

<section>

<canvas
id="canvas" width="500"
height="400"></canvas>

</section>

</body>

</html>

The most important step in any Three.js
application is loading the library. In Listing 12-1, the three.min.js file is included by one of the <script> elements in the document. The second <script> is for our own JavaScript code.

Listing 12-2: Creating a wireframe sphere

function
initiate() {

var canvas =
document.getElementById("canvas");

var width = canvas.width;

var height = canvas.height;

var renderer = new THREE.WebGLRenderer({canvas:
canvas});

renderer.setClearColor(0xFFFFFF);

var scene = new THREE.Scene();

var camera = new THREE.PerspectiveCamera(75,
width / height, 0.1, 1000);

camera.position.set(0, 0, 150);

var geometry = new THREE.SphereGeometry(80, 15,
15);

var material = new THREE.MeshBasicMaterial({color:
0x000000, wireframe: true});

var mesh = new THREE.Mesh(geometry, material);

scene.add(mesh);

renderer.render(scene, camera);

}

window.addEventListener("load",
initiate);

As before, our code includes the initiate() function to start running the application. In this function,
we go through the process of creating a 3D world; we set the renderer, the
scene, the camera, and a mesh. First, the renderer is set up by the WebGLRenderer() constructor. The constructor receives the canvas property to set the <canvas> element in the document as the rendering
surface and returns an object to represent the renderer. Next, we set the
background color of this renderer to white with the setClearColor() method and create the scene using the Scene() constructor. No parameters are required for this constructor. The Scene object returned is stored in the scene variable for later use.

Once we have the scene, the next step is to
create the camera. Using the PerspectiveCamera() constructor, we get a camera with perspective
projection (the one recommended for animations). The first attribute declares a
width of 75 for the point of view. This value is appropriate for
our scene but may be changed according to the scale of the world. The second
parameter sets the ratio of the camera as the same as the rendering surface. This
value is obtained by dividing the width by the height of the canvas, using the width and height variables defined at the beginning of the
code. Finally, the nearest limit is set at 0.1, and the farthest limit is set at 1000. These values also depend on the scale we are using for our world. Our
objects are not going to be bigger than 100 or 150 units, so the limit of 1000 is enough for this small scene. The objects that go far from that
limit are not going to be drawn on the screen.

The camera, like any other element of the world,
is initially located at the origin (the coordinates 0,
0, 0). To see the mesh created next, the camera
has to be moved to a new position. This is done by the position property and the set() method. In our example, this method
establishes the camera’s coordinates as 0 for x, 0 for y and 150 for z, effectively displacing the camera 150
units on the z axis (We will learn more about this method
later).

The last thing we need to add to our scene is the
mesh. The mesh and the corresponding material are first defined by the
constructors SphereGeometry() and MeshBasicMaterial() and then used as the attributes for the Mesh() constructor to get the final object. The material is defined with the wireframe property set to true to get a wireframe sphere on the screen
instead of a solid object. The mesh is finally added to the scene by the add() method and the scene is rendered on the canvas at the end using the render() method from the renderer object.

Do It Yourself: Create a new HTML file with the document of Listing
12-1, and a JavaScript file called webgl.js with the code in Listing 12-2. Remember to copy the three.min.js file
into your project's folder. Open the document in your browser. The result
should be similar to Figure 12-3.

IMPORTANT: Colors in this library are
represented by hexadecimal numbers with a syntax that requires prefixing the
value with the characters 0x (e.g., 0xFF00FF).

The Basics: All the properties, methods, and
constructors offered by Three.js are part of the THREE object created by the library, and therefore they must be accessed from this object, as shown in Listing 12-2 (e.g., THREE.WebGLRenderer()).

Transformations

Every element of the 3D world, including camera and
lights, has attributes assigned by default. They are positioned at the origin
of the scene (the coordinates 0, 0, 0), and their sizes and orientations are
determined by the coordinates of each of their vertices. Modifications for
cameras and lights are easy to perform, but meshes require the recalculation of
each vertex. To perform this task, Three.js offers a group of properties and
methods that can achieve the most common transformations. Using a few
properties, we can translate, rotate or scale an element of the world without
having to recalculate thousands of vertices.

position—This property returns an object containing a vertex
that we can use to get or set the element’s position in the world. The object
provides the x, y and z properties to read or change each
coordinate independently.

rotation—This property returns an object containing a vertex
that we can use to get or set the angle of the element in radians. The object
provides the x, y and z properties to read or change the angle of
each coordinate independently.

scale—This property returns an object containing a vertex
that we can use to get or set the scale of an element. The object provides the x,
y and z properties to read or change the scale of
each coordinate independently. By default, the values are set to 1.

Usually, the values of the three coordinates have
to be modified at the same time. Three.js provides the set() method to simplify this process. The method is applicable to each
transformation property, and the values are declared separated by commas, as shown
in the code in Listing 12-2 (camera.position.set(0,
0, 150)). In the
following example, we show how to perform a rotation.

Listing 12-3: Rotating a cube with the mouse

var renderer,
scene, camera, mesh;

function initiate() {

var canvas =
document.getElementById("canvas");

var width = canvas.width;

var height = canvas.height;

renderer = new THREE.WebGLRenderer({canvas:
canvas});

renderer.setClearColor(0xFFFFFF);

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(45, width /
height, 0.1, 1000);

camera.position.set(0, 0, 150);

var geometry = new THREE.BoxGeometry(50, 50, 50);

var material = new
THREE.MeshBasicMaterial({color: 0x000000, wireframe: true});

mesh = new THREE.Mesh(geometry, material);

scene.add(mesh);

renderer.render(scene, camera);

canvas.addEventListener("mousemove",
move);

}

function move(event) {

mesh.rotation.x =
event.pageY * 0.01;

mesh.rotation.z =
-event.pageX * 0.01;

renderer.render(scene,
camera);

}

window.addEventListener("load",
initiate);

The example of Listing 12-3 demonstrates how to
transform a mesh dynamically. For better visualization, we generate a cube with
the BoxGeometry() constructor. The rest of the code is
similar to the previous example, but a listener for the mousemove event is added at the end of the initiate() function to create a simple animation. Now, every
time the mouse moves over the <canvas> element, the move() function is called, and the mesh is rotated in the x
and z axes
according to the position of the mouse.

Figure 12-4: Animated wireframe cube

Do It Yourself: Copy the JavaScript code in Listing 12-3 into the webgl.js file created for the previous example and
open the document of Listing 12-1 in your browser. Move the mouse to rotate the cube.

Lights

To create meshes with a material that can emulate the
reflection of light, we need some light. Three.js offers the following
constructors to generate light.

AmbientLight(color)—This is a global light that is not attributed to any light source.
This light is formed by a general light reflected by every object in the scene.
It affects all objects alike. The color attribute is the color of the light in
hexadecimal value.

DirectionalLight(color, intensity, distance)—This type of light is located far away
from the 3D world and affects the objects from only one direction. The possible
attributes are color (hexadecimal value), intensity (decimal value), and distance (decimal value).

PointLight(color, intensity, distance)—This constructor creates a light source in
a specific location in the world. It affects objects in every direction. The
possible attributes are color (hexadecimal value), intensity (decimal value), and distance (decimal value).

SpotLight(color, intensity, distance, castShadow)—This constructor creates a directional
light source in a specific location. The possible attributes are color (hexadecimal value), intensity (decimal value), distance (decimal value), and castShadow (Boolean value).

The following example adds light to illuminate
the cube.

Listing 12-4: Adding light to the scene

function
initiate() {

var canvas =
document.getElementById("canvas");

var width =
canvas.width;

var height =
canvas.height;

var renderer
= new THREE.WebGLRenderer({canvas: canvas, antialias: true});

renderer.setClearColor(0xFFFFFF);

var scene =
new THREE.Scene();

var camera =
new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

camera.position.set(0, 0, 150);

var geometry
= new THREE.BoxGeometry(50, 50, 50);

var material
= new THREE.MeshPhongMaterial({color: 0x0000FF});

var mesh =
new THREE.Mesh(geometry, material);

scene.add(mesh);

mesh.rotation.set(10, 10, 0);

var light = new
THREE.SpotLight(0xFFFFFF);

light.position.set(50,
50, 150);

scene.add(light);

renderer.render(scene, camera);

}

window.addEventListener("load",
initiate);

In addition to the spotlight added at the end,
there are a few more changes in the code in Listing 12-4 from the previous
example. The configuration of the renderer now includes a second property
called antialias with the value true. This softens the image on the screen, creating a more realistic
effect. The material is defined as MeshPhongMaterial() in color blue. The Phong material is
probably the most realistic material but also the most computationally
demanding. It is perfect for our example, but we should always consider the possibility
of alternate materials to achieve a balance between performance and quality.

The cube is created at the origin of the world
(the coordinates 0, 0, 0) and faces the camera from one side. If we
render the mesh in this position, we will only see a square on the screen. To
initialize the cube in a better position, we apply a transformation using the rotation property and the set() method.

For the light, we used the SpotLight() constructor with the color white. Because this light
has a specific location, we have to move it to the right position. This is done
by the position property and the set() method.

Figure 12-5: Light reflecting on a cube

Do It Yourself: Copy the JavaScript code in Listing 12-4 into the webgl.js file created for the previous example, and
open the document of Listing 12-1 in your browser. You should see something similar to
Figure 12-5.

Textures

Colors and lights produce a very realistic
effect, but the level of detail, in reality, is much higher than that.
Protuberances, particles, fabrics and even small molecules on a surface can
produce millions of visual effects. The details in the real world are usually
so complex that reproducing a realistic object would require resources not
available in today's computers. To solve this problem, 3D engines offer the
possibility of adding textures to meshes. Textures are images that are drawn on
the surface of a shape to simulate complex details.

Textures in Three.js are created by
an object of type Texture and then declared as part of
the material of the mesh. The library provides the following constructor to
create these objects.

Texture(image, mapping, wrapS, wrapT, magFilter, minFilter)—This constructor returns an object
representing the texture to be applied to the material of the mesh. The image attribute is the image for the texture. It may be declared as an image,
a <canvas> element or a video. The mapping attribute defines how the texture will be mapped on the surface of the
mesh, the wrapS and wrapT attributes determine how the image will be
distributed on the surface, and the magFilter and minFilter attributes set the filters to be applied
to the texture to smooth the image and produce a more realistic effect.

In the following example, we add texture to the
cube to simulate a wooden box.

Listing 12-5: Adding texture to an object

var canvas,
image, renderer, scene, camera, mesh;

function initiate() {

canvas =
document.getElementById("canvas");

image =
document.createElement("img");

image.src =
"crate.jpg";

image.addEventListener("load", createworld);

}

function createworld() {

var width = canvas.width;

var height = canvas.height;

renderer = new THREE.WebGLRenderer({canvas:
canvas, antialias: true});

renderer.setClearColor(0xFFFFFF);

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(45, width /
height, 0.1, 1000);

camera.position.set(0, 0, 150);

var geometry = new THREE.BoxGeometry(50, 50, 50);

var texture = new
THREE.Texture(image);

texture.needsUpdate =
true;

var material = new
THREE.MeshPhongMaterial({map: texture});

mesh = new THREE.Mesh(geometry, material);

scene.add(mesh);

var light = new THREE.SpotLight(0xFFFFFF, 1);

light.position.set(0, 100, 250);

scene.add(light);

renderer.render(scene, camera);

canvas.addEventListener("mousemove",
move);

}

function move(event){

mesh.rotation.z = -event.pageX * 0.01;

mesh.rotation.x = event.pageY * 0.01;

renderer.render(scene, camera);

}

window.addEventListener("load",
initiate);

The file containing the image for the
texture has to be completely downloaded from the server before it can be
applied to the material. In most applications, a small code is designed to
download the resources and indicate the progress to the user. In Listing 12-5,
we separate this process from the creation of the world to offer a perspective
of how the code should be organized. Later in this chapter, we will study
better alternatives.

The initiate() function creates an <img> object, declares the source of that image
as the  crate.jpg file and adds the createworld() function as the listener for
the load event to continue the process when the
file has been loaded.

The createworld() function follows the same
procedure we have used before to create the 3D world and all of its elements,
but in this example, the texture is defined before the material is applied to
the mesh. The Texture object is generated by the Texture() constructor using the reference to the
image previously downloaded. Once the texture is ready, it is stored in the texture variable and assigned to the material as one of its
attributes. This whole process is usually called texture mapping, and
for that reason, the property in charge of assigning the texture to the
material is called map.

Do It Yourself: Copy the JavaScript code in Listing 12-5 into the webgl.js file. Download the crate.jpg file from our
website. Upload these files and the document of Listing 12-1 to your server or local server, and open
the document in your browser. Move the mouse to rotate the box.

IMPORTANT: Because of the cross-origin restrictions studied in
the previous chapter, the files for the textures have to be located on the same
server as the application. To test this example, you have to upload the files
to a server or a local server (see MAMP in Chapter 1).

Figure 12-6: Cube with texture

The Texture object provides some properties to define and configure
the texture.

needsUpdate—This property informs the renderer that the texture
has to be updated. It is required every time a new texture is defined or
changes to an existing texture are introduced (see the example of Listing
12-5).

repeat—This property defines how many times the image of the
texture has to be repeated on the same side of the mesh. It returns or sets a
vertex with two coordinates, x and y (for each axis of the face). This property
requires the wrapS and wrapT attributes of the Texture constructor to be set as THREE.RepeatWrapping, and the image used for the texture has to be of a size power of 2
(e.g., 128 x 128, 256 x 256, 512 x 512, 1024 x 1024, etc.).

offset—This property displaces the image on the surface, allowing,
among other effects, the creation of a simple but effective animation. It
returns and sets a vertex with two coordinates, x
and y (for each axis of the face). This property requires
the wrapS and wrapT attributes of the Texture constructor to be set as THREE.RepeatWrapping, and the image used for the texture has to be of a size power of 2
(e.g., 128 x 128, 256 x 256, 512 x 512, 1024 x 1024, etc.).

UV Mapping

UV Mapping is a process in which points of the image
of the texture are associated with vertices of the mesh to draw the image in
the exact position. The name derives from the names assigned to the axes of the
image, U corresponding to x and V corresponding to y
(x and y are already used to describe the axes for
the vertices of the mesh).

A useful effect achieved by UV Mapping is the
application of different materials, and therefore textures, to the same
geometry. The procedure is usually reserved for complex meshes created by 3D
software, but in the case of the cube, Three.js provides an easy way to do it.
The following example takes advantage of this feature to turn our cube into a
dice.

Listing 12-6: Applying a different texture
for each face of the cube

var renderer,
scene, camera, mesh;

function initiate() {

canvas =
document.getElementById("canvas");

var width = canvas.width;

var height = canvas.height;

renderer = new THREE.WebGLRenderer({canvas:
canvas, antialias: true});

renderer.setClearColor(0xFFFFFF);

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(45, width /
height, 0.1, 1000);

camera.position.set(0, 0, 150);

var materials = [

new
THREE.MeshPhongMaterial({map:
THREE.ImageUtils.loadTexture("dice3.jpg")}),

new
THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture("dice4.jpg")}),

new
THREE.MeshPhongMaterial({map:
THREE.ImageUtils.loadTexture("dice5.jpg")}),

new
THREE.MeshPhongMaterial({map:
THREE.ImageUtils.loadTexture("dice2.jpg")}),

new
THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture("dice1.jpg")}),

new
THREE.MeshPhongMaterial({map:
THREE.ImageUtils.loadTexture("dice6.jpg")})

];

var geometry = new
THREE.BoxGeometry(50, 50, 50, 1, 1, 1);

mesh = new
THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));

scene.add(mesh);

var light = new THREE.SpotLight(0xFFFFFF, 2);

light.position.set(0, 100, 250);

scene.add(light);

renderer.render(scene, camera);

canvas.addEventListener("mousemove",
move);

}

function move(event){

mesh.rotation.x = event.pageY * 0.01;

mesh.rotation.z = -event.pageX * 0.01;

renderer.render(scene, camera);

}

window.addEventListener("load",
initiate);

In this example, we use the loadTexture() method of the ImageUtils object provided by the library to download the files
and assign the textures to the material without disrupting the rest of the
process. The meshes and the entire 3D world are generated, and the textures are
applied later when the images have been downloaded. This is an easy way to work
with textures in simple applications. The loadTexture() method downloads the image and returns the
corresponding Texture object, all in one step. It saves us from
creating a function to download the resources, but because it does not provide
good control of the process, it is not recommended for professional
applications (we included the method here to simplify the example).

Using loadTexture() and the MeshPhongMaterial() constructor, we create an array with six different Material objects, each one with its corresponding texture.
This array is later assigned as the second parameter of the Mesh() constructor using the MeshFaceMaterial() constructor. This is a constructor that
takes an array and returns a composite material for the application of
different materials to the same mesh.

The order of the materials and textures declared
in the array is important. It determines the exact location where the images
will be drawn on the surface of the mesh. In our example, this means that the
faces of the dice will be in the right place.

Figure 12-7: Cube turned into a dice

Do It Yourself: Copy the JavaScript code in Listing 12-6 into the webgl.js file. Download the files dice1.jpg,
dice2.jpg, dice3.jpg, dice4.jpg, dice5.jpg, and dice6.jpg from our website.
Upload the files and the document of Listing 12-1 to your server or local server, and open
the document in your browser. Move the mouse to rotate the dice.

IMPORTANT: Applying textures to complex shapes
requires a deep knowledge of UV Mapping and texture mapping in general. For
more information, visit our website and follow the links for this chapter.

Canvas Textures

There are different techniques we can apply to create
impressive effects using textures, but probably one of the most interesting is
the use of an additional <canvas> element. This alternative gives us access
to a variety of effects provided by the Canvas API to dynamically generate the
texture, including the possibility to add 3D text to our scene.

Listing 12-7: Including 3D text in our scene

var renderer,
scene, camera, mesh;

function initiate() {

canvas =
document.getElementById("canvas");

var width = canvas.width;

var height = canvas.height;

renderer = new THREE.WebGLRenderer({canvas:
canvas, antialias:true});

renderer.setClearColor(0xCCFFFF);

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(45, width /
height, 0.1, 1000);

camera.position.set(0, 0, 150);

var textcanvas =
document.createElement("canvas");

textcanvas.width = 400;

textcanvas.height = 200;

var context =
textcanvas.getContext("2d");

context.fillStyle =
"rgba(255,0,0,0.95)";

context.font = "bold
70px verdana, sans-serif";

context.fillText("Text in 3D", 0, 60);

var geometry = new THREE.PlaneGeometry(100, 40);

var texture = new THREE.Texture(textcanvas);

texture.needsUpdate = true;

var material = new THREE.MeshPhongMaterial({map:
texture, transparent: true, side: THREE.DoubleSide});

mesh = new THREE.Mesh(geometry, material);

scene.add(mesh);

var light = new THREE.PointLight(0xffffff);

light.position.set(0, 100, 250);

scene.add(light);

renderer.render(scene, camera);

canvas.addEventListener("mousemove",
move);

}

function move(event){

mesh.rotation.y = event.pageX * 0.02;

renderer.render(scene, camera);

}

window.addEventListener("load",
initiate);

The canvas for the texture is a second <canvas> element that will not be shown on the screen. Its
only purpose is to generate the image for the texture. We could have declared
this element in the HTML document and used CSS to change its visibility property, but the best way to include the element is
to create the Element object dynamically with the createElement() method, as we did before for images. In Listing 12-7,
the second <canvas> element is created with this method and
its width and height attributes are defined. The process to
draw a text on the canvas is the same explained in the previous chapter: the 2D
context is created, the style for the text defined, and the string is drawn by
the fillText() method.

The <canvas> element turns transparent the parts of the
drawing surface that are not in use. To show the effect produced by this
feature, we declare a background color for the renderer with the setClearColor() method and apply the texture to a plane geometry.
This is a plane mesh with only two sides, like a three-dimensional square. The
geometry is created by the PlaneGeometry() constructor.

The rest of the process is the same as before,
but we added a new parameter to the MeshPhongMaterial() constructor to declare the material as
double sided. This means that the texture will be shown on the front and the
back of the mesh.

Figure 12-8: 3D text on the canvas

Do It Yourself: Copy the JavaScript code in Listing 12-7 into the webgl.js file, and open the document of Listing 12-1 in your browser. Move the mouse over the
canvas to rotate the mesh and see the texture from both sides.

Video Textures

The greatest effect we can achieve with
textures is probably the inclusion of video in our scene. This is as simple as
applying the reference to a <video> element as the attribute of
the Texture() constructor.

Listing 12-8: Introducing video in a 3D
world

var canvas,
video, renderer, scene, camera, mesh;

function initiate() {

canvas =
document.getElementById("canvas");

video =
document.createElement("video");

video.src =
"trailer.ogg";

video.addEventListener("canplaythrough", createworld);

}

function createworld() {

var width = canvas.width;

var height = canvas.height;

renderer = new THREE.WebGLRenderer({canvas:
canvas, antialias:true});

renderer.setClearColor(0xFFFFFF);

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera( 45, width /
height, 0.1, 1000);

camera.position.set(0, 0, 250);

texture = new
THREE.Texture(video);

texture.minFilter =
THREE.LinearFilter;

texture.magFilter =
THREE.LinearFilter;

texture.generateMipmaps =
false;

var material = new THREE.MeshPhongMaterial({map:
texture, side: THREE.DoubleSide});

var geometry = new THREE.PlaneGeometry(240, 135);

mesh = new THREE.Mesh(geometry, material);

scene.add(mesh);

var light = new THREE.PointLight(0xffffff);

light.position.set(0, 100, 250);

scene.add(light);

canvas.addEventListener("mousemove",
move);

video.play();

render();

}

function move(event) {

mesh.rotation.y = event.pageX * 0.02;

}

function render() {

texture.needsUpdate = true;

renderer.render(scene, camera);

requestAnimationFrame(render);

}

window.addEventListener("load",
initiate);

The code begins with the creation of
the <video> element. The source of the
element is declared as the trailer.ogg
file (the
same file used in the examples of Chapter 8). A listener for the canplaythrough event was also added to know when the
video is ready to be played. Once the browser considers that there is enough
data to start playing the video, the event is fired, and the createworld() function is executed.

The procedure for the creation of the
world follows previous examples, but this time we had to configure the Texture object to use the video as a texture. By default, Three.js
applies a mipmap filter to the textures. This filter produces an antialias
effect to smooth the texture and make it look more realistic, but video
textures do not support it. After the creation of the Texture object, we set the minFilter and magFilter properties to THREE.LinearFilter (a simple filter) and deactivate the
generation of mipmaps.

Our work does not stop there. We
still have to create a loop to update the texture for every frame of the video
and render the scene in each cycle. This is done by the render() function. In this function, the needsUpdate property of the texture is set to true, and the scene is rendered again.

At the end of the createworld() function, a listener for the mousemove event is added to provide some
interaction with the mesh. We also start playing the video with the play() method and call the render() function for the first time to
initiate the rendering loop.

Figure 12-9: Video texture

© Copyright 2008, Blender Foundation / www.bigbuckbunny.org

Do It Yourself: Copy the JavaScript code in Listing 12-8 into the webgl.js file. Download the trailer.ogg video from
our website. Upload the files and the document of Listing 12-1 to your server and open the document in
your browser. Move the mouse to rotate the video.

IMPORTANT: An OGG video only plays in
browsers that support the OGG format, like Google Chrome, Mozilla Firefox or
Opera. See Chapter 8 for more information.

3D Models

Complex meshes are almost impossible to
develop declaring the vertices individually or even using the constructors for
basic geometric primitives studied before. The work requires professional 3D
software capable of constructing elaborated models that can later be loaded and
implemented in 3D animations. One of the most popular programs on the market is
Blender (www.blender.org), created by the Blender Foundation and
distributed for free. This software offers all the tools necessary to create
professional 3D models and also several formats in which the models can be
exported. Developers and collaborators of the Three.js library have built
external libraries to help programmers to load files in these formats and adapt
them to what Three.js can understand and process.

Loaders are already available for the
COLLADA format, OBJ format, and JSON format, among others. These are small
libraries that have to be included in the document along with the Three.js
file. The following example includes the ColladaLoader.js file with the COLLADA
library to process objects in the COLLADA format and introduce a police car
into our 3D world.

Listing 12-9: Loading 3D models

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Three.js</title>

<script src="three.min.js"></script>

<script
src="ColladaLoader.js"></script>

<script>

var canvas, video, renderer, scene, camera, mesh;

function initiate() {

canvas = document.getElementById("canvas");

var loader = new
THREE.ColladaLoader();

loader.load("police.dae",
createworld);

}

function createworld(collada) {

var width = canvas.width;

var height = canvas.height;

renderer = new THREE.WebGLRenderer({canvas: canvas,
antialias:true});

renderer.setClearColor(0xFFFFFF);

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(45, width / height,
0.1, 1000);

camera.position.set(0, 0, 150);

mesh = collada.scene;

mesh.scale.set(20, 20, 20);

mesh.rotation.set(-Math.PI / 2, 0, 0);

scene.add(mesh);

var light = new THREE.PointLight(0xffffff, 2, 0);

light.position.set(0, 100, 250);

scene.add(light);

renderer.render(scene, camera);

canvas.addEventListener("mousemove", move);

}

function move(event) {

mesh.rotation.z = -event.pageX * 0.01;

renderer.render(scene, camera);

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section>

<canvas id="canvas" width="500"
height="400"></canvas>

</section>

</body>

</html>

The models exported as COLLADA are
stored in a group of files. After exporting the model, we have one text file
with the .dae extension containing all the
specifications of the model and one or more images for the textures. The model
of our example is stored in the police.dae file and two files are included for the textures (SFERIFF.JPG and SFERIFFI.JPG).

Model files have to be downloaded
like any other resource, but the COLLADA loader provides its own method to do
so. In the initiate() function in Listing 12-9, the ColladaLoader() constructor is used to create the Loader object, and the load() method provided by this object
is called to download the .dae file (only the .dae file has to be declared in the method,
the files for the textures are downloaded automatically). The load() method has two attributes, the file attribute to indicate the path of the file to be
downloaded and the callback attribute to declare a
function to be called when the file has been downloaded. The load() method sends a Scene object to this function that
we can process to insert the model into our own scene.

In our example, the function that
processes the Scene object is createworld(). After following the standard
procedure of creating the renderer, the scene and the camera, the Scene object representing the model is stored in the mesh variable, scaled to the dimensions of our world, rotated
to the right angle, and finally added to our scene.

Figure 12-10: COLLADA model

Model provided by TurboSquid Inc. (www.turbosquid.com)

Do It Yourself: The file for the COLLADA
loader is available in the Three.js package in the examples folder. Follow the path examples/js/loaders/ and copy the ColladaLoader.js file to your project’s folder.
This file has to be included in the document as we did in Listing 12-9 to have
access to the methods that allow us to read and process files in this format. Create a new HTML file with the document
of Listing 12-9, upload this file, the HTML document, and the three files for
the model to your server or local server, and open the document in your
browser.

IMPORTANT: The model used in this
example is accompanied by two files containing the images for the textures (SFERIFF.JPG  and SFERIFFI.JPG). The three files are
available on our website.

3D Animations

Animations in 3D only differ from 2D
animations in how the frames are constructed. In Three.js, the renderer does
most of the hard work and the whole process looks almost automatic, while in a
2D canvas context, we have to take care of clearing the surface ourselves.
Despite minor differences, the requirements of the codes for a professional
application are always the same. The greater the complexity, the greater the
need for a proper organization and the best way to establish this organization
is by working with properties and methods declared inside a global object, as
we did in Chapter 11.

To provide an example of 3D
animation, we are going to create a small video game. In the game, we will
drive a car inside a room. The purpose is to capture green spheres that are
floating around. The following is the document required for this application.

Listing 12-10: Creating the document for a 3D
video game

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Three.js</title>

<style>

body {

margin:
0px;

overflow:
hidden;

}

</style>

<script
src="three.min.js"></script>

<script
src="ColladaLoader.js"></script>

<script
src="webgl.js"></script>

</head>

<body></body>

</html>

This is a basic document. Only the
CSS styles for the <body> element may seem strange. We
are going to use the browser’s entire window for the renderer, so these
properties are required to make sure no margins or scroll bars take space from
our application.

The JavaScript files included in this
document are the three.min.js file for the Three.js library,
the ColladaLoader.js file to bring back to the
scene our police car, and the webgl.js file with the code for the game.

Do It Yourself: Create a new HTML file with
the document of Listing 12-10 and copy the JavaScript codes introduced below
into an empty file called webgl.js. All the codes studied from here on are necessary to run
the application.

The code for a 3D application is
usually more extensive than the JavaScript code required for other purposes. To
simplify this example, we are going to present the global object with the
elemental properties first and add the rest of the properties and methods
required by the game later.

Listing 12-11: Defining basic properties

var mygame = {

renderer: "",

scene: "",

camera: "",

light: "",

car: {

mesh: "",

speed: 0,

speedUP: false,

left: false,

right: false,

wheelangle: 0

},

walls: [{x: 0, y: 100, z: -1000},

{x: -1000, y: 100, z: 0},

{x: 0, y: 100, z: 1000},

{x: 1000, y: 100, z:0}],

textures: {

car: "",

floor: "",

walls: ""

},

targets: {

mesh: "",

edges: [],

},

input: []

};

The global object for this application is called mygame. We start the definition of mygame by declaring the properties necessary to
control and modify the status of the game. The renderer, scene, camera and light properties are used to store the
references to the elements of our 3D world. The car property contains an object with the car’s basic data, including a
reference to the mesh and the value of the current speed. The walls property is an array containing four vertices for the definition of
the position of the walls. The textures property is an object with additional properties
to reference the textures of the car, the floor, and the walls. The targets property is also an
object to store the mesh and the edges of the green spheres (the targets in our
game). Finally, the input property is an empty array we will use to
store the keys pressed by the user.

Some of these properties are initialized with
empty values. The next step is to declare the right values and define the core
elements of our scene when the code is loaded. As usual, this is done in the initiate() method.

Listing 12-12: Defining the initiate() method

mygame.initiate
= function() {

var width = window.innerWidth;

var height =
window.innerHeight;

var canvas =
document.createElement("canvas");

canvas.width = width;

canvas.height = height;

document.body.appendChild(canvas);

mygame.renderer = new
THREE.WebGLRenderer({canvas: canvas, antialias:true});

mygame.scene = new
THREE.Scene();

mygame.camera = new
THREE.PerspectiveCamera(45, width / height, 0.1, 10000);

mygame.camera.position.set(0,
50, 150);

mygame.light = new
THREE.PointLight(0x999999, 1);

mygame.light.position.set(0,
50, 150);

mygame.scene.add(mygame.light);

window.addEventListener("keydown", function(event) {

mygame.input.push({type:
"keydown", key: event.key});

});

window.addEventListener("keyup",
function(event) {

mygame.input.push({type:
"keyup", key: event.key});

});

mygame.loading();

mygame.create();

};

We want to use the browser’s entire window for
our game, but the dimensions of this space always vary according to the device
or the settings established by the user. To create a <canvas> element as big as the window, we first have to get
the current dimensions of the window by consulting the innerWidth and innerHeight properties of the Window object (see Chapter 6). Using these values, the canvas is created
dynamically, sized, and added to the body by the appendChild() method.

After the usual definitions of the renderer, the
scene, the camera and the light, two listeners are added for the keydown and keyup events. These listeners are necessary to
control the user’s input. The keydown event is fired when a key is pressed, and
the keyup event is fired when a key is released. Both send a KeyboardEvent object
to the listener with the key property containing a value identifying
the key that fired the event (see Chapter 6, Listing 6-165). Anonymous functions are set to listen to
the events and store the key values in the input array when they are fired. We will process this input later.

At the end of the initiate() method, the loading() method is called to download the files with the
description of the model and the textures.

Listing 12-13: Defining the loading() method

mygame.loading = function() {

var loader = new THREE.ColladaLoader();

loader.load("police.dae", function(collada) {

mygame.textures.car = collada;

});

var image1 = document.createElement("img");

image1.src = "wet_asphalt.jpg";

image1.addEventListener("load", function(event) {

mygame.textures.floor = event.target;

});

var image2 = document.createElement("img");

image2.src = "wall.jpg";

image2.addEventListener("load", function(event) {

mygame.textures.walls = event.target;

});

var controlloop = function() {

if (mygame.textures.car &&
mygame.textures.floor && mygame.textures.walls) {

mygame.create();

} else {

setTimeout(controlloop, 200);

}

};

controlloop();

};

This method is a small but practical loader. It begins
the downloading process for each of the files (the COLLADA model, the texture
for the floor and the texture for the walls) and defines a small internal
function to check the progress. When the files are completely downloaded, the
resulting objects are stored in the corresponding properties (textures.car, textures.floor and textures.walls). The controlloop()
function calls itself generating a loop to constantly check for the value of
these properties and execute the create() method only when the model and both
textures have already been loaded.

Listing 12-14: Defining the create() method

mygame.create = function() {

var geometry, material, texture, mesh;

mesh = mygame.textures.car.scene;

mesh.scale.set(20, 20, 20);

mesh.rotation.set(-Math.PI / 2, 0, Math.PI);

mesh.position.y += 14;

mygame.scene.add(mesh);

mygame.car.mesh = mesh;

geometry = new THREE.PlaneGeometry(2000,
2000, 10, 10);

texture = new
THREE.Texture(mygame.textures.floor, THREE.UVMapping, THREE.RepeatWrapping,
THREE.RepeatWrapping);

texture.repeat.set(20, 20);

texture.needsUpdate = true;

material = new THREE.MeshPhongMaterial({map: texture});

mesh = new THREE.Mesh(geometry, material);

mesh.rotation.x = Math.PI * 1.5;

mygame.scene.add(mesh);

for (var f = 0; f < 4; f++) {

geometry = new THREE.PlaneGeometry(2000,
200, 10, 10);

texture = new
THREE.Texture(mygame.textures.walls, THREE.UVMapping, THREE.RepeatWrapping,
THREE.RepeatWrapping);

texture.repeat.set(10, 1);

texture.needsUpdate = true;

material = new THREE.MeshPhongMaterial({map: texture});

mesh = new THREE.Mesh(geometry, material);

mesh.position.set(mygame.walls[f].x, mygame.walls[f].y,
mygame.walls[f].z);

mesh.rotation.y = Math.PI / 2 * f;

mygame.scene.add(mesh);

}

mygame.loop();

};

In the create() method, the meshes for the car, the floor,
and the four walls are created. There is nothing new in this method except for
the use of the repeat property. This property declares how many
times the image has to be repeated on the side of the mesh to create the
texture. This is necessary because the image of the texture for the walls is a
square, but the walls are 10 times longer than tall. Setting the values for the
repeat property as 10, 1 (texture.repeat.set(10,
1);), the image is
drawn on the wall in the right proportions.

As we mentioned before, for the repeat property to work properly, a few conditions have to be established.
The size of the image for the texture has to be a power of 2 (e.g., 128 x 128,
256 x 256, 512 x 512, 1024 x 1024, etc.), more than one segment has to be
declared for the side of the plane geometry, and the wrapS and wrapT attributes of the Texture() constructor have to be set to THREE.RepeatWrapping. By default, these attributes are set with
the value THREE.ClampToEdgeWrapping, which means that the image is
scaled to occupy the whole size of the side of the mesh (as it happened in
previous examples). The THREE.RepeatWrapping constant cancels this effect
and allows us to distribute the image the way we want.

The definition of the 3D world is
complete. Now, it is time to program the methods that will handle the main
process. We need a method to control the user’s input, another method to
calculate the new position of the car, a third method to detect collisions, and
one more to update the renderer.

Listing 12-15: Defining the control() method

mygame.control = function(event) {

var action;

while (mygame.input.length) {

action = mygame.input.shift();

switch (action.type) {

case "keydown":

switch(action.key){

case "ArrowUp":

mygame.car.speedUP = true;

break;

case "ArrowLeft":

mygame.car.left = true;

break;

case "ArrowRight":

mygame.car.right = true;

break;

}

break;

case "keyup":

switch(action.key){

case "ArrowUp":

mygame.car.speedUP = false;

break;

case "ArrowLeft":

mygame.car.left = false;

break;

case "ArrowRight":

mygame.car.right = false;

break;

}

break;

}

}

};

The control() method in Listing 12-15 processes the
values stored in the input array by the functions that respond to the
keydown and keyup events. This is part of a technique used
to avoid the delay produced by the system every time a key is pressed. When the
user presses or releases a key, the action is detected by the keydown and keyup events, and the new condition is stored in
the speedUP, left or right properties accordingly. These properties
return true from the moment the key is pressed until it is released.
By using this procedure, there is no delay and the car responds instantly.

The value of the key property taken from the input array is compared with the strings "ArrowUp",
"ArrowLeft" and "ArrowRight" to identify which key was
pressed or released by the user. These strings correspond to the up arrow, the
left arrow, and the right arrow, respectively (see the KeyboardEvent object in Chapter 6). According to these
values and the event we are responding to, the speedUP, left and right properties are set to true or false to indicate the current condition to the rest of the
methods in the application.

IMPORTANT: An input system similar to the one programmed for this application
may be used to store any kind of input, not only keys. We can store custom
values to represent mouse events and then respond to those values from the control() method the same way we did in this
example for the arrow keys.

The speedUP, left and right properties are used to define the speed and the rotation
of the car. Everything else is calculated using this information, from the
position of the camera to the detection of collisions. A big part of this work
is done by the process() method.

Listing 12-16: Defining the process() method

mygame.process
= function() {

if (mygame.car.speedUP) {

if (mygame.car.speed < 8)
{

mygame.car.speed += 0.1;

}

} else {

if (mygame.car.speed > 0)
{

mygame.car.speed -= 0.1;

} else {

mygame.car.speed += 0.1;

}

}

if (mygame.car.left &&
mygame.car.wheelangle > - 0.5) {

mygame.car.wheelangle -=
0.01;

}

if (!mygame.car.left
&& mygame.car.wheelangle < 0) {

mygame.car.wheelangle +=
0.02;

}

if (mygame.car.right
&& mygame.car.wheelangle < 0.5) {

mygame.car.wheelangle +=
0.01;

}

if (!mygame.car.right
&& mygame.car.wheelangle > 0) {

mygame.car.wheelangle -=
0.02;

}

var angle =
mygame.car.mesh.rotation.z;

angle -= mygame.car.wheelangle
* mygame.car.speed / 100;

mygame.car.mesh.rotation.z =
angle;

mygame.car.mesh.position.x +=
Math.sin(angle) * mygame.car.speed;

mygame.car.mesh.position.z +=
Math.cos(angle) * mygame.car.speed;

var deviation =
mygame.car.wheelangle / 3;

posx =
mygame.car.mesh.position.x - (Math.sin(mygame.car.mesh.rotation.z + deviation)
* 150);

posz =
mygame.car.mesh.position.z - (Math.cos(mygame.car.mesh.rotation.z + deviation)
* 150);

mygame.camera.position.set(posx, 50, posz);

mygame.camera.lookAt(mygame.car.mesh.position);

mygame.light.position.set(posx,
50, posz);

};

There are two important values we
have to recalculate in each cycle of the loop to process the user’s input: the
speed and the angle of the car. The speed increases while the user keeps the up
arrow pressed and decreases when the key is released. The first if else instruction in the process() method in Listing 12-16
controls this situation. If the value of speedUP is true, the car’s speed is increased by 0.1, to a maximum of 8. Otherwise, the speed is
gradually reduced to 0.

The next four if instructions control the angle of the wheels. This angle is added
or subtracted from the angle of the car to steer to the left or the right
according to the values of the left and right properties. As well as the instructions for the speed, the
if instructions for the angle of the wheels
establish limits for the possible value (from -0.5 to 0.5).

Once the values of the speed and the
angle of the wheels are set, we can determine the new position of each element
of the scene. To begin with, the current angle of the car is stored in the angle variable (this is the angle of the z axis).
Next, the angle of the wheels is subtracted from the value of the angle of the
car, and this new value is assigned back to the rotation property of the car to rotate the mesh (mygame.car.mesh.rotation.z
= angle). With
the angle set, it is time to determine the new position of the car. The values of
the x and z coordinates are calculated
from the angle and the speed using the formula Math.sin(angle) × speed and Math.cos(angle)
× speed.
The results are immediately assigned to the position.x and position.z properties of the car, effectively moving
the mesh to the new position, but we still have to move the camera and the
light or our vehicle will soon disappear in the shadows. Using the values of
the new position of the car, its angle and the permanent distance between the
camera and the car (150), the values for the
coordinates of the camera are calculated. These values are stored in the posx and posy variables and assigned to the position property of the camera and the light (in
our game, the camera and the light always have the same coordinates). With this
formula, the camera looks as if it is attached to the back of the car. To
achieve a more realistic effect, a small deviation value is calculated from the
angle of the wheels and added to the angle of the car in the last formula to
move the camera slightly to one side or another (deviation = mygame.car.wheelangle / 3).

At the end of the process() method, we point the camera to the new
position of the car using the lookAt() method and the vertex returned
by the position property (mygame.car.mesh.position). No matter how much the value
of this property changes, the camera always aims at the car.

The Basics: The mathematic formulas implemented in this example are simple
trigonometric functions used to obtain the values of coordinates from the
angles and points in a two-dimensional coordinate system. For more examples,
see Chapter 11, Listing 11-30.

With the positions of the car, camera and light
defined, we are ready to render the scene, but there is still one object left.
The green spheres that our car has to chase are positioned randomly all over
the scene, one at a time. When we pass through the current sphere, the mesh is removed,
and a new one is created in a different position. Because this operation may
occur at any time during the animation, we decided to include it along with the
rendering process in the draw() method.

Listing 12-17: Defining the draw() method

mygame.draw = function()
{

if (!mygame.targets.mesh) {

var geometry = new
THREE.SphereGeometry(20, 10, 10);

var material = new
THREE.MeshBasicMaterial({color: 0x00FF00, wireframe: true});

var mesh = new THREE.Mesh(geometry,
material);

var posx = (Math.random() * 1800) - 900;

var posz = (Math.random() * 1800) - 900;

mesh.position.set(posx, 30, posz);

mygame.scene.add(mesh);

mygame.targets.mesh = mesh;

mygame.targets.edges[0] =
posx - 30;

mygame.targets.edges[1] =
posx + 30;

mygame.targets.edges[2] =
posz - 30;

mygame.targets.edges[3] =
posz + 30;

} else {

mygame.targets.mesh.rotation.y += 0.02;

}

mygame.renderer.render(mygame.scene, mygame.camera);

};

If no sphere has yet been created, the draw() method generates a new one. The material is set as a green wireframe,
and the position is determined randomly by the random() method. After the mesh is added to the scene, an area
around the sphere is calculated to later be able to detect a collision.

Because the position of every element in the
scene is determined by only one vertex, it is almost impossible to detect a
collision between elements comparing these single coordinates. By adding and
subtracting 30 units to each coordinate of the sphere and storing those values
in the targets.edges array, we generate a virtual area of 60
units wide containing the sphere (see Figure 12-11 below). When the vertex of
the position of the car is inside this area, the collision is confirmed.

Figure 12-11: Expanding the area occupied by
the sphere

The last operation of the draw() method in Listing 12-17 renders the scene on the canvas. Although this
is important, it is not the last thing we have to do. We still have to improve
the collision detection system. The car in our game
can crash into any of the four walls and the green spheres. The detect() method is responsible for detecting and
responding to these situations.

Listing 12-18: Defining the detect() method

mygame.detect =
function() {

var posx =
mygame.car.mesh.position.x;

var posz =
mygame.car.mesh.position.z;

if (posx < -980 || posx
> 980 || posz < -980 || posz > 980) {

mygame.car.speed = -7;

}

if (posx >
mygame.targets.edges[0] && posx < mygame.targets.edges[1] &&
posz > mygame.targets.edges[2] && posz < mygame.targets.edges[3])
{

mygame.scene.remove(mygame.targets.mesh);

mygame.targets.mesh =
"";

}

};

Comparing the current coordinates of
the car with the position of the walls and the edges of the area occupied by
the sphere, we determine the possible collisions. If the vertex of the position
of the car falls outside the area of the game (beyond the walls), the value of
the speed property is set to -7, making the car bounce off the wall. In
the case where this vertex falls inside the area of the sphere, the mesh is
removed from the 3D world by the remove() method and the targets.mesh property is set to null. When this
happens, the draw() function in Listing 12-17
detects the absence of a sphere and draws a new one.

The detection is the last feature
necessary for our game, but we still have to build a loop that calls the
methods over and over again.

Listing 12-19: Defining the loop() method

mygame.loop =
function() {

mygame.control();

mygame.process();

mygame.detect();

mygame.draw();

requestAnimationFrame(function() {

mygame.loop();

});

};

window.addEventListener("load",
function() {

mygame.initiate();

});

The JavaScript code in this example
was simplified for educational purposes. There is no game over, no points to
celebrate, no lives to lose. The good news is that we have already studied all
we need to know to add those features and customize the game.

Figure 12-12: 3D video game for the Web

Model and textures provided by TurboSquid Inc. (www.turbosquid.com)

Do It Yourself: All the codes presented in
this section of the chapter work together to create the video game, including
the HTML document of Listing 12-10. Try to apply the Fullscreen API to this
example to take your video game full screen.

IMPORTANT: The meshes, textures, and models in the examples of this chapter
are not royalty-free and do not include the rights for distribution. Please
consult TurboSquid Inc. (www.turbosquid.com) or the Blender Foundation (www.blender.org) before using this material
for your own projects.

Chapter 13 - Pointer Lock API

13.1 Custom Pointer

Visual applications, like those created
with the <canvas> element or WebGL, sometimes
demand the use of other techniques to express the movement of the mouse. There
are countless reasons for this. We may need to change the graphic that
represents the pointer to indicate a different function for the mouse or hide
it to avoid distracting the user from an image or a video. Considering these
requirements, browsers include the Pointer Lock API.

Mouse Capture

The Pointer Lock API is a small group of
properties, methods, and events that let the application assume control over
the mouse. Two methods are provided to lock and unlock the mouse.

requestPointerLock()—This method locks the mouse and binds it
to an element. It is available on Element objects.

exitPointerLock()—This method unlocks the mouse and makes
the mouse pointer visible again. It is available in the Document object.

When the requestPointerLock() method is called, the graphic
representing the pointer (usually a small arrow) is hidden, and the code
becomes responsible for providing the visual clues the user needs to interact
with the application. The following example illustrates how to take control of
the mouse.

Listing 13-1: Taking control of the mouse

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Pointer Lock
API</title>

<script>

function initiate() {

var element =
document.getElementById("control");

element.addEventListener("click", lockmouse);

}

function lockmouse(event) {

var element =
event.target;

element.requestPointerLock();

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section>

<p
id="control">Click here to lock the mouse</p>

</section>

</body>

</html>

Unless the element requesting control
of the mouse is in full screen mode, the requestPointerLock() method will return an error
when it is called without user intervention. A user gesture, such as the click of the mouse, must precede the action. Considering this,
the code in Listing 13-1 adds a listener for the click event to the <p> element. When the user clicks
on this element, the lockmouse() function is called, and the requestPointerLock() method is used to lock the
mouse. At that moment, the pointer disappears from the screen, and it is not
shown again unless the user switches to another window or cancels the mode
pressing the escape key on the keyboard.

Do It Yourself: Create a new HTML file with the document of Listing 13-1. Open the
document in your browser and click on the area occupied by the <p> element. You should see the mouse's
pointer disappear. Press the Escape key on your keyboard to unlock the mouse.

When the mouse is locked for an
element, the rest of the elements do not fire any mouse event until the mouse
is unlocked by the application or the mode is canceled by the user. To report
what is going on, the API includes the following events.

pointerlockchange—This event is fired on the Document object when an element locks or unlocks the mouse.

pointerlockerror—This event is fired on the Document object when the attempt to lock the mouse fails.

The code in the following example
listens to the pointerlockchange event and prints a message on
the console every time the condition of the mouse changes.

Listing 13-2: Reporting a change in the
condition of the mouse

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Pointer Lock
API</title>

<script>

function initiate() {

var element =
document.getElementById("control");

element.addEventListener("click",
lockmouse);

document.addEventListener("pointerlockchange", checkmouse);

}

function lockmouse(event) {

var element =
event.target;

element.requestPointerLock();

}

function checkmouse() {

console.log("The condition of the
mouse changed");

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section>

<p
id="control">Click here to lock the mouse</p>

</section>

</body>

</html>

Do It Yourself: Update the document in your HTML file with the code in Listing
13-2. Open the document in your browser and activate the console to see the
messages printed by the code. Every time you click on the element or press the
Escape key, a message should be shown on the console.

The mouse is locked for a specific
element. The API includes the following property to report which element locked
the mouse.

pointerLockElement—This property returns a reference to the Element object representing the element that locked the mouse or the value null if the mouse hasn't been locked.

The pointerLockElement property may
be used along with the pointerlockchange event to
determine if the mouse was locked or unlocked.

Listing 13-3: Checking the condition of the
mouse

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Pointer Lock
API</title>

<script>

function initiate() {

var element =
document.getElementById("control");

element.addEventListener("click", lockmouse);

document.addEventListener("pointerlockchange", checkmouse);

}

function lockmouse(event) {

var element =
event.target;

element.requestPointerLock();

}

function checkmouse() {

var element =
document.getElementById("control");

if (document.pointerLockElement) {

console.log("Mouse
Locked");

} else {

console.log("Mouse
Unlocked");

}

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section>

<p
id="control">Click here to lock the mouse</p>

</section>

</body>

</html>

Every
time the mouse is locked or unlocked, either by our code or by the user, the checkmouse() function in
Listing 13-3 checks the value of the pointerLockElement property. If
the property returns a reference to an element, it means that the mouse is
currently locked, but if the property returns the value null, it means that no element is locking the mouse at the moment.

Do It Yourself: Update the document in your HTML file with the code in Listing
13-3. Open the document in your browser and activate the console to see the
messages. Click on the <p> element to deactivate the
mouse. You should see the message "Mouse Locked" on the console.
Press the Escape key. You should see the message "Mouse Unlocked" on
the console.

The Basics: In the if instruction inside the checkmouse() function we use the value of
the pointerLockElement property to set the condition.
This is possible because a condition is always considered to be true unless the
value is a null value, such as 0, "", undefined, null, etc.

As we mentioned, if no element has control over
the mouse, the pointerLockElement property returns the value null, so we can use it to decide whether to lock or unlock the mouse
depending on the current condition.

Listing 13-4: Locking and unlocking the mouse

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Pointer Lock
API</title>

<script>

var canvas;

function initiate() {

var element =
document.getElementById("canvas");

canvas =
element.getContext("2d");

element.addEventListener("click", lockmouse);

element.addEventListener("mousemove", draw);

}

function draw(event) {

canvas.clearRect(0, 0,
500, 400);

var posx = event.clientX;

var posy = event.clientY;

canvas.beginPath();

canvas.moveTo(posx, posy -
20);

canvas.lineTo(posx, posy +
20);

canvas.moveTo(posx - 20,
posy);

canvas.lineTo(posx + 20,
posy);

canvas.moveTo(posx + 20,
posy);

canvas.arc(posx, posy, 20,
0, Math.PI * 2);

canvas.stroke();

}

function lockmouse(event) {

var element =
event.target;

if (!document.pointerLockElement) {

element.requestPointerLock();

} else {

document.exitPointerLock();

}

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section>

<canvas
id="canvas" width="500"
height="400"></canvas>

</section>

</body>

</html>

In this example, we create a small application to
show a more realistic approach to the use of this API. We use the pointerLockElement property to determine whether the canvas is in
control of the mouse or not. Every time the user clicks on the <canvas> element, we check this condition and lock or unlock
the mouse with requestPointerLock() or exitPointerLock() accordingly.

When the mouse is locked, the browser assigns
control of the mouse to the element that made the request. All the mouse
events, such as mousemove, click or mousewheel, are only fired for this element, but
events that are related to the mouse's pointer, such as mouseover or mouseout, are no longer fired. As a result, to
interact with the mouse and detect its movements, we have to listen to the mousemove event. The code in Listing 13-2 adds a listener for the mousemove event to draw a gun sight on the canvas in the
current position of the mouse, determined by the clientX and clientY properties (see MouseEvent in Chapter 6).

Do It Yourself: Update the document in your HTML file with the code in Listing
13-4. Open the document in your browser and move the mouse to the area occupied
by the <canvas> element. You should see a gun
sight following the pointer. Click to lock the mouse and fix the gun sight in
place. Click again to unlock it.

In Listing 13-4, the gun sight moves with the
mouse pointer, but as soon as the mouse is locked, the gun sight is frozen on
the screen. This is because when the mouse is locked, only the values of the movementX and movementY properties are updated (the rest of the
event properties keep the last values stored prior to the activation of the
mode). These properties do not return the exact position of the mouse; they return the difference between the previous and the
current position. To work with
the mouse when it is locked, we must calculate its position from the values
returned by these properties.

Listing 13-5: Calculating the position of
the mouse with movementX and movementY

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Pointer Lock
API</title>

<script>

var canvas, posx, posy;

function initiate() {

var element =
document.getElementById("canvas");

canvas =
element.getContext("2d");

element.addEventListener("click",
lockmouse);

startmessage();

}

function draw(event) {

canvas.clearRect(0, 0,
500, 400);

var test1, test2;

test1 = posx + event.movementX;

test2 = posy + event.movementY;

if(test1 > 0 &&
test1 < 500){

posx = test1;

}

if(test2 > 0 &&
test2 < 400){

posy = test2;

}

canvas.beginPath();

canvas.moveTo(posx, posy -
20);

canvas.lineTo(posx, posy +
20);

canvas.moveTo(posx - 20,
posy);

canvas.lineTo(posx + 20,
posy);

canvas.moveTo(posx + 20,
posy);

canvas.arc(posx, posy, 20,
0, Math.PI * 2);

canvas.stroke();

}

function lockmouse(event) {

var element =
event.target;

if
(!document.pointerLockElement) {

element.requestPointerLock();

posx = event.clientX;

posy = event.clientY;

element.addEventListener("mousemove", draw);

} else {

document.exitPointerLock();

element.removeEventListener("mousemove",
draw);

startmessage();

}

}

function startmessage() {

canvas.clearRect(0, 0,
500, 400);

canvas.font = "bold
36px verdana, sans-serif";

canvas.fillText("Click to Play", 120, 180);

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section>

<canvas
id="canvas" width="500"
height="400"></canvas>

</section>

</body>

</html>

We made a few changes in this code to increase
the level of control and show how to take advantage of this API in certain
phases of the application. The initiate() function sets the canvas, adds the
listener for the click event to lock the mouse, and calls the startmessage() function to show a welcome message on the
screen, but it does not add a listener for the mousemove event. This listener is added in the lockmouse() function after the mouse is locked, and it
is also removed in the same function after the mouse is unlocked. By following
this procedure, the <canvas> element gets control of the mouse only
after the first stage of the application is executed (where we ask the user to
click on the screen). If the user returns to this first stage, the mouse is
unlocked again, and the listener for the mousemove event is removed by the removeEventListener() method.

The values returned by the movementX and movementY properties reflect the change of the
position of the mouse. Before the mouse is locked, we capture its coordinates
with the clientX and clientY properties to set the initial position of
the gun sight. Usually, this is not necessary, but in an application like this,
where the mouse is constantly locked and unlocked, it helps produce a better
transition from one state to another. This initial position is stored in the
variables posx and posy and is only modified by the amount of
displacement when it does not exceed the limits established by the size of the <canvas> element (see the if instructions in the draw() function). Unless we are just checking the direction of the mouse,
this control is necessary to avoid storing values that our application cannot
process.

Do It Yourself: Update the document in your HTML file with the code in Listing
13-5. Open the document in your browser. Click on the canvas to lock the mouse.
You should see the gun sight moving with the mouse, but not the mouse's
pointer. Click again to go back to the initial screen.

Chapter 14 - Web Storage API

14.1 Storage Systems

The Web Storage API lets us store data on
the user’s hard drive and access it later when the user comes back to our
website. The storage system provided by this API may be used in two particular
situations: when the information has to be available only during a session and
when it has to be preserved as long as it is determined by our code or the
user. With the purpose of clarifying the situation for developers, the API was
divided into two parts called Session Storage and Local Storage.

Session
Storage—This is a storage mechanism that keeps the data available only
for the duration of a session. The information stored through this mechanism is
only accessible from a single window or tab and lasts until the window is
closed.

Local
Storage—This mechanism works similarly to a storage system for a desktop
application. The data stored is permanently preserved and always available from
the application that created it.

These mechanisms work with a similar
interface and share the same properties and methods, and both are origin
dependent, which means that the information is only available for the website
that created it. Every website is assigned its own storage space, and the
information is preserved or removed depending on the mechanism applied.

14.2 Session Storage

The Session Storage system is the simplest
of all. It stores the data only for a session, which means that the data is
removed when the user closes the window or tab. Applications may use it for
support or to store information that may be required later in the process but
becomes irrelevant when the user leaves the website.

The following is the document we are
going to use to test this API. Because both systems work with the same
interface, we are only going to need one document and a simple form to try the
examples in this chapter.

Listing 14-1: Creating a document for
testing the Web Storage API

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Web Storage API</title>

<link rel="stylesheet" href="storage.css">

<script src="storage.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<p><label>Keyword: <input type="text"
name="keyword" id="keyword"></label></p>

<p><label>Value: <textarea name="text"
id="text"></textarea></label></p>

<p><button type="button"
id="save">Save</button></p>

</form>

</section>

<section id="databox">

<p>No Information available</p>

</section>

</body>

</html>

We also need a simple set of styles
to differentiate the form from the box where the data will be shown.

Listing 14-2: Designing the interface

#formbox {

float: left;

padding: 20px;

border: 1px solid #999999;

}

#databox {

float: left;

width: 400px;

margin-left: 20px;

padding: 20px;

border: 1px solid #999999;

}

#keyword, #text {

width: 200px;

}

#databox > div {

padding: 5px;

border-bottom: 1px solid #999999;

}

Do It
Yourself: Create an HTML file with the document of Listing 14-1 and a CSS
file called storage.css with the
styles in Listing 14-2. You will also need a JavaScript file called storage.js to save and test the codes presented below.

Storing Data

The data is stored as items composed of a
keyword/value pair. These items are like variables, each with a name and a
value, that can be created, modified or deleted. The following are the methods
provided by the API to create and get an item from the storage space.

setItem(key, value)—This method creates and stores an item with the keyword and the
value specified by the attributes. If there is already an item with the same
keyword, it will be updated with the new value, so this method may also be used
to modify data previously stored.

getItem(key)—This method retrieves the value of the item with the keyword
specified by the attribute.

The Window object includes two properties to provide access to the
storage systems called sessionStorage and localStorage. To store and read items, we have to call
the corresponding methods from this properties, as in the following example.

Listing 14-3: Storing and retrieving data

function initiate() {

var button = document.getElementById("save");

button.addEventListener("click", newitem);

}

function newitem() {

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

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

sessionStorage.setItem(keyword, value);

show(keyword);

}

function show(keyword) {

var databox = document.getElementById("databox");

var
value = sessionStorage.getItem(keyword);

databox.innerHTML = "<div>" + keyword + " -
" + value + "</div>";

}

window.addEventListener("load", initiate);

In the code in Listing 14-3, the newitem() function is executed every
time the user clicks the Save button on the form. This function creates an item
with the information inserted in the input fields and then calls the show() function. In this second function, the item is retrieved
using the value of the keyword attribute and the getItem() method, and then the content of the item
is inserted in the databox element to show it on the
screen.

Do It Yourself: Copy the code in Listing 14-3
in your storage.js file and open the document of Listing 14-1 in your browser. Insert values
in the fields and press the button to store them. You should see the values of
the item inside the databox element.

The Basics: Methods and properties may be concatenated with dot notation. The
interpreter processes the components of the instruction from left to right. In
Listing 14-3, we concatenated the getElementById() method with the value property. The interpreter first executes the method, gets
a reference to the Element object, and then reads the
value of the value property of that object. This
is just a shortcut, we could have stored the reference to the element in a
variable and then read the property from that variable in another instruction,
as we did in other examples, but this way we save some lines of code. The
result is the same, the values inserted by the user in the fields are assigned
to the keyword and value variables.

In addition to these methods, the API
also provides a shortcut to create and get an item from the storage space. We
can use the keyword of the item as a property of sessionStorage and get the values of the item this way.
As with any other property, there are two syntaxes available: we can enclose the
variable representing the keyword in square brackets (sessionStorage[keyword] = value), or we can use dot notation (sessionStorage.myitem = value).

Listing 14-4: Using a shortcut to work
with items

function initiate() {

var button = document.getElementById("save");

button.addEventListener("click", newitem);

}

function newitem() {

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

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

sessionStorage[keyword] = value;

show(keyword);

}

function show(keyword) {

var databox = document.getElementById("databox");

var
value = sessionStorage[keyword];

databox.innerHTML = "<div>" + keyword + " -
" + value + "</div>";

}

window.addEventListener("load", initiate);

Reading Data

The items are stored in an array, so we
can also access the values with an index or a loop. The API offers the
following property and method for this purpose.

length—This
property returns the number of items accumulated in the storage space for the
application.

key(index)—This method returns the keyword of the item at the index
specified by the attribute.

The previous examples only retrieve
the last item stored. Taking advantage of the key() method, we are going to improve the code to list all the
values available in the storage space.

Listing 14-5: Listing all the items in
the storage space

function initiate() {

var button = document.getElementById("save");

button.addEventListener("click", newitem);

show();

}

function newitem() {

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

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

sessionStorage.setItem(keyword, value);

document.getElementById("keyword").value = "";

document.getElementById("text").value = "";

show();

}

function show() {

var databox = document.getElementById("databox");

databox.innerHTML = "";

for (var f = 0; f < sessionStorage.length; f++) {

var
keyword = sessionStorage.key(f);

var value = sessionStorage.getItem(keyword);

databox.innerHTML += "<div>" + keyword + " -
" + value + "</div>";

}

}

window.addEventListener("load", initiate);

The purpose of this example is to display
the complete list of items on the screen. The show() function was improved using the length property and the key() method. Inside a for loop, the key() method is called to return the
keyword corresponding to each item. For example, if the item at the position 0
of the storage space was created with the keyword "myitem", the instruction sessionStorage.key(0) will return the value of "myitem". Calling this method from a loop, allows
us to list all the items on the screen, each with its corresponding keyword and
value.

Notice that the show() function is also called at the end of the initiate() function to show the items already stored
in the storage space as soon as the application is loaded.

Do It Yourself: Update your storage.js file
with the code in Listing 14-5 and open the document of Listing 14-1 in your browser. Insert new
values in the form and press the button. You should see a list with all the
values you have inserted inside the databox element.

The Basics: You can implement the Forms API studied in Chapter 7 to check the
validity of the input fields and not allow the insertion of invalid or empty
items.

Deleting Data

The items may be created, read and, of
course, deleted. The API includes two methods to remove items from the storage
space.

removeItem(key)—This method deletes a single item. The key attribute
specifies the keyword of the item to be removed.

clear()—This method
removes all the items from the storage space.

The following example includes
buttons on the side of each value to remove it.

Listing 14-6: Deleting items from the
storage space

function initiate() {

var button = document.getElementById("save");

button.addEventListener("click", newitem);

show();

}

function newitem() {

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

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

sessionStorage.setItem(keyword, value);

document.getElementById("keyword").value = "";

document.getElementById("text").value = "";

show();

}

function show() {

var databox = document.getElementById("databox");

databox.innerHTML = '<div><input type="button"
onclick="removeAll()" value="Erase
Everything"></div>';

for (var f = 0; f < sessionStorage.length; f++) {

var keyword = sessionStorage.key(f);

var value = sessionStorage.getItem(keyword);

databox.innerHTML += "<div>" + keyword + " -
" + value + "<br>";

databox.innerHTML += '<input type="button"
onclick="removeItem(\'' + keyword + '\')"
value="Remove"></div>';

}

}

function removeItem(keyword) {

if (confirm("Are you sure?")) {

sessionStorage.removeItem(keyword);

show();

}

}

function removeAll() {

if (confirm("Are you sure?")) {

sessionStorage.clear();

show();

}

}

window.addEventListener("load", initiate);

The initiate() and newitem() functions in Listing 14-6 are the same as the previous example.
Only the show() function changed to
incorporate buttons with the onclick event attribute to call the functions that will delete an
individual item or all of them. The code creates a Remove button for each item
on the list and also a single button at the top of the list to erase the entire
storage space.

The removeItem() and removeAll() functions are responsible for
deleting the selected item or clearing the storage space, respectively. Each
function calls the show() function at the end to update
the list of items on the screen.

Do It Yourself: With the code in Listing 14-6,
you will be able to test how the information is processed by Session Storage. Open the HTML document of Listing 14-1 in your browser, create new
items and then open the same document in a new window. The information in each
window is different. The old window keeps its data available, but the storage
space for the new window is empty. Unlike other systems, Session Storage considers every window as an
independent instance of the application and the information of the session is
not shared between them.

14.3 Local Storage

Having a reliable system to store data
during a window session may be useful in some circumstances, but when we try to
emulate desktop applications on the Web, a temporary data storage system is
usually insufficient. To keep the information permanently available, the Web
Storage API includes the Local Storage system. With Local Storage, we can store large amounts of data and
let the user decide whether the information is still useful and must be
preserved or not.

This system uses the same interface
as Session Storage; therefore, every method and
property studied in this chapter is available for Local Storage as well. Only the substitution of the sessionStorage property by the localStorage property is required to prepare the
codes.

Listing 14-7: Using Local Storage

function initiate() {

var button = document.getElementById("save");

button.addEventListener("click", newitem);

show();

}

function newitem() {

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

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

localStorage.setItem(keyword, value);

show();

document.getElementById('keyword').value = "";

document.getElementById('text').value = "";

}

function show() {

var databox = document.getElementById("databox");

databox.innerHTML = "";

for (var f = 0; f < localStorage.length; f++) {

var keyword = localStorage.key(f);

var value = localStorage.getItem(keyword);

databox.innerHTML += "<div>" + keyword + " -
" + value + "</div>";

}

}

window.addEventListener("load", initiate);

In Listing 14-7, we take one of the
previous codes and replace the sessionStorage property with the localStorage property. Now every item created is
preserved through different windows or even after the browser is completely
closed.

Do It Yourself: Using the document of Listing 14-1, test the code in Listing
14-7. This code creates a new item with the information from the form and
automatically lists every item available in the storage space reserved for the
application. Close the browser and open the document again. You should see all
the items on the list.

Storage Event

Because Local Storage makes the
information available in every window where the same application is loaded, at
least two problems arise: how these windows communicate and how we update the
information in a window that is not active or focused. The API includes the
following event to solve these problems.

storage—This event is fired by the
window every time a change occurs in the storage space. It can be used to
inform every window opened with the same application that a change was made to
the storage space and that something needs to be done about it.

Keeping the list of items up-to-date
in our example is easy, we have to listen to the storage event and call the show() function every time the event
is fired.

Listing 14-8: Listening to the storage event to keep the list of
items updated

function initiate() {

var button = document.getElementById("save");

button.addEventListener("click", newitem);

window.addEventListener("storage", show);

show();

}

function newitem() {

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

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

localStorage.setItem(keyword, value);

document.getElementById("keyword").value = "";

document.getElementById("text").value = "";

show();

}

function show() {

var databox = document.getElementById("databox");

databox.innerHTML = "";

for (var f = 0; f < localStorage.length; f++) {

var keyword = localStorage.key(f);

var value = localStorage.getItem(keyword);

databox.innerHTML += "<div>" + keyword + " -
" + value + "</div>";

}

}

window.addEventListener("load", initiate);

In this example, the show() function responds to the storage event, and therefore it is executed every time an item is
created, updated or erased. Now, if something changes in one window, it will be
automatically shown in other windows that are running the same application.

Do It Yourself: Using the document of Listing 14-1, test the code in Listing
14-8. The storage event only works when the
application is stored in a server or a local server. Upload the files to your
server and open the document in two different windows. Insert or update an item
in one window and move to the other window to see how the storage event updated the information.

The storage event sends to the function an object of type StorageEvent that contains the following properties to
provide information about the modifications produced to the storage space.

key—This property returns the key of the
affected item.

oldValue—This property returns the
previous value of the affected item.

newValue—This property returns the new
value assigned to the item.

url—This property returns the URL of the
document that produced the modification. Remember that the storage space is
reserved for the entire domain, so different documents have access to it.

storageArea—This property returns an
object containing all the keyword/value pairs in the storage space after the
modification.

The following example prints on the
console the values of these properties to show the information available.

Listing 14-9: Accessing the event's
properties

function initiate() {

var button = document.getElementById("save");

button.addEventListener("click", newitem);

addEventListener("storage", storagechange);

show();

}

function newitem() {

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

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

localStorage.setItem(keyword, value);

document.getElementById("keyword").value = "";

document.getElementById("text").value = "";

show();

}

function storagechange(event)
{

console.log(event.key);

console.log(event.oldValue);

console.log(event.newValue);

console.log(event.url);

console.log(event.storageArea);

show();

}

function show() {

var databox = document.getElementById("databox");

databox.innerHTML = "";

for (var f = 0; f < localStorage.length; f++) {

var keyword = localStorage.key(f);

var value = localStorage.getItem(keyword);

databox.innerHTML += "<div>" + keyword + " -
" + value + "</div>";

}

}

window.addEventListener("load", initiate);

In Listing 14-9, we add a new
function to handle the storage event. The storagechange() function is called by the
event when the storage space is modified by another window. In this function,
we print the values of the properties one by one on the console and call the show() function at the end to update the information on the
screen.

Chapter 15 - IndexedDB API

15.1 Data Structure

The Web Storage API studied in the
previous chapter is useful for storing small amounts of data, but when it comes
to large amounts of structured data, we must resort to a database system. For
this purpose, browsers include the IndexedDB API.

The IndexedDB API is a database
system capable of storing indexed information on users’ computers. It was
developed as a low-level API with the intention of allowing a broad range of
uses. This turned it not only into a powerful API but also into one of the most
complex. The goal was to provide the most basic infrastructure possible to
allow developers to build things on top of it and create high-level interfaces
for every need. In a low-level API like this one, we have to take care of
everything and control the condition of every process in every operation
performed. The result is an API that most developers will take time to get used
to and probably apply indirectly through third-party libraries.

The data structure proposed by
the IndexedDB API is also different from SQL or other popular database systems familiar
to developers. The information is stored in the database as objects (records)
inside what are called Object Stores (tables). The Object Stores have no
structure, just a name and indexes to find the objects inside. These objects do
not have a predefined structure either; they can be different from one another
and as complex as we want. The only condition for objects is that they have to
have at least one property declared as the index, so the Object Store is able
to find them.

Database

The database itself is simple.
Because every database is associated with one computer and one website or
application, there are no users to assign or any other access restrictions to
manage. We just have to specify the name and the version, and the database is
ready.

The Window object includes the indexedDB property to have access to the
database. This property stores an object with the following methods.

open(name, version)—This method creates or opens the database
with the name and version specified by the attributes. The version may be
ignored when the database is created, but it is required when we want to set a
new version for an existing database. The method returns an object that fires
two events (error and success) to indicate
an error or the success in creating or accessing the database.

deleteDatabase(name)—This method deletes the database with the name specified by the
attribute.

The API offers the opportunity
to assign a version to the database to be able to update its structure. When we
have to update the structure of a database on the server to add additional
tables or indexes, we usually create a second database with the new structure
and then move data from the old database to the new one. This process requires
full access to the server and even the ability to turn it off. However, we cannot
shut down the user’s computer to repeat this process in a browser. As a result,
a version number has to be declared in the open() method to be able to migrate the
information from the old version to the new one. To work with database
versions, the API provides the following event.

upgradeneeded—This event is fired when the open() method tries to open a non-existent
database or a new version for the current database was specified.

Objects and Object Stores

What we use to call records
in a standard database, are called objects in the IndexedDB API. These
objects include properties to store and identify values. The number of
properties and how the objects are structured is irrelevant; they just have to include
at least one property declared as the index for the Object Store to be able to
find them.

Likewise, what is called tables
in a standard database, is called Object Stores in the IndexedDB API, because
they store the objects with the information we want to preserve. Object Stores
do not have a particular structure either. Only the name and one or more
indexes must be declared when they are created.

Figure 15-1: Objects with different
properties stored in an Object Store

As shown in Figure 15-1, an
Object Store may contain various objects with different properties. In this
example, some objects have the DVD property, others
have the Book
property, etc. Each one has its own structure, but they must have at least one
property selected as the index (in this example, the property assigned for this
purpose could be Id, because all the objects have it).

To work with objects and Object
Stores, we need to create the Object Store, declare the properties that will be
used as indexes, and then start storing objects in it. We do not have to think
about the structure and content of the objects right now, but only consider the
indexes we are going to use later to perform a search. The following are the
methods provided by the API to manage Object Stores.

createObjectStore(name, object)—This method creates a new Object Store
with the name and configuration set by its attributes (The name
attribute is mandatory). The object attribute is an object that may
include the properties keyPath and autoIncrement. The keyPath property declares a common index for every object, and the autoIncrement property is
a Boolean property that determines whether or not the Object Store will have a
key generator to organize the objects.

deleteObjectStore(name)—This method destroys the Object Store with the name declared by
the attribute.

objectStore(name)—This method opens the Object Store with the name declared by the
attribute.

The createObjectStore() and deleteObjectStore() methods, as well as other methods
responsible for the configuration of the database, can only be applied when the
database is created or upgraded to a new version.

The IndexedDB API provides the
following methods to interact with Object Stores, read and store information.

add(object)—This method receives a keyword/value pair or an object containing
several keyword/value pairs, and it adds an object to the Object Store with
this information. If an object with the same index already exists, this method
returns an error.

put(object)—This method is similar to the add() method
except it overwrites an existing object with the same index. It is useful to
update an object already stored in the Object Store.

get(key)—This method retrieves an object from the Object Store. The key
attribute is the value of the index of the object we want to read.

delete(key)—This method deletes an object from the Object Store. The key
attribute is the value of the index of the object we want to remove.

Indexes

As we already mentioned, we have to
set some of the object's properties as indexes to find objects in an Object
Store. A simple way to do it is to declare the keyPath property in the createObjectStore() method. The property declared as keyPath will be the common index for every
object stored in that particular Object Store. When we set a keyPath, the property declared as the index
must be present in every object.

Besides the keyPath, we can also set all the indexes we
want for an Object Store using the following methods.

createIndex(name, property, object)—This method creates an
index. The name attribute is the name that identifies the index, the property
attribute is the object’s property used for the index, and the object
attribute is an object that may include the properties unique or multiEntry. The unique property
indicates whether the index value has to be unique or multiple objects can
share the same index value. The multiEntry property
determines how the keys for an object will be generated when the index is an
array.

deleteIndex(name)—This method deletes an index. It can only be called from a versionchange transaction.

index(name)—This method returns a reference to the index with the name
specified by the attribute.

Transactions

A database system working on a
browser must consider some unique circumstances that are not present on other
platforms. For example, the browser could fail, it could be closed abruptly,
the process could be stopped by the user, or the user may leave our website in
the middle of an operation. There are many situations in which working directly
with the database can cause a malfunction or even data corruption. To prevent
this from happening, every action is performed through transactions. The IndexedDB
API offers the following method to perform a transaction.

transaction(store, type)—This method performs a transaction. The store attribute is
the name of the Object Store involved in the transaction, and the type attribute is
a string describing the type of transaction we want to perform.

The following are the types of
transactions available for this method.

readonly—This is a
read-only transaction. Modifications are not allowed.

readwrite—This is a
read-write transaction. Using this type of transaction, we can read and write to
the database. Modifications are allowed, but we cannot add or remove Object
Stores and indexes.

versionchange—This type of
transaction is used for updating the version of the database as well as adding
or removing Object Stores and indexes.

The most common transactions
are readwrite.
However, to prevent misuse, the readonly type is set by default, so when we
need to get information from the database, the only thing we have to do is to specify
the scope of the transaction (usually the name of the Object Store) and the
type of transaction is set automatically.

The following events are
provided to keep track of the transaction.

complete—This event
is fired when the transaction is completed.

abort—This event
is fired when the transaction is aborted.

error—This event is fired
when an error occurs during the transaction.

15.2 Implementation

It is time to create our first database
and apply some of the methods already mentioned in this chapter. In this
section, we are going to simulate an application that stores information about
movies. The following is the information we are going to use in our examples.

id: tt0068646 name:
The Godfather date: 1972

id: tt0086567 name:
WarGames date: 1983

id: tt0111161 name:
The Shawshank Redemption date: 1994

id: tt1285016 name:
The Social Network date: 2010

IMPORTANT: The names of these
properties (id, name, and date) are going to be used
for the examples of the rest of the chapter. The information was collected from
the website www.imdb.com, but you can
make your own list or use random information to test the codes.

As
usual, we need an HTML document and some CSS styles to define the boxes for the
form and the space to show the information retrieved from the database. The
form provides fields for inserting information about the movies, including a
keyword, the title of the movie, and the year when it was made.

Listing 15-1: Creating a document for
testing the IndexedDB API

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>IndexedDB API</title>

<link rel="stylesheet" href="indexed.css">

<script src="indexed.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<label for="keyword">Keyword: </label><br>

<input type="text" name="keyword"
id="keyword"><br>

<label for="text">Title: </label><br>

<input type="text" name="text"
id="text"><br>

<label for="year">Year: </label><br>

<input type="text" name="year"
id="year"><br>

<button type="button"
id="save">Save</button>

</form>

</section>

<section id="databox">

<p>No Information available</p>

</section>

</body>

</html>

The CSS styles define the boxes
for the form and the data.

Listing 15-2: Defining the styles for the
boxes

#formbox {

float: left;

padding: 20px;

border: 1px solid #999999;

}

#databox {

float: left;

width: 400px;

margin-left: 20px;

padding: 20px;

border: 1px solid #999999;

}

#keyword, #text {

width: 200px;

}

#databox > div {

padding: 5px;

border-bottom: 1px solid #999999;

}

Do It
Yourself: Create a new HTML file with the document of Listing 15-1, a CSS
file called indexed.css with the
styles in Listing 15-2, and a JavaScript file called indexed.js for all the codes introduced below.

Opening the Database

The first
step in the JavaScript code is to open the database. The open() method of
the indexedDB object opens the
database with the given name or creates a new one if it does not exist.

Listing 15-3: Opening the database

var databox, db;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("save");

button.addEventListener("click", addobject);

var
request = indexedDB.open("mydatabase");

request.addEventListener("error", showerror);

request.addEventListener("success", start);

request.addEventListener("upgradeneeded", createdb);

}

The initiate() function in Listing 15-3 creates
the references to the elements of the document and opens the database. The open() method tries to open a database
with the name "mydatabase" and returns an object with the result.
Because the IndexedDB API is an asynchronous API, the error, success and upgradeneeded events are fired from this object to
report the result of the operation. When the error and success events are fired, the showerror() and the start() functions are executed to control
the errors or continue with the definition of the database.

Listing 15-4: Reporting errors and getting a
reference to the database

function showerror(event) {

alert("Error: " + event.code + " " +
event.message);

}

function start(event) {

db =
event.target.result;

}

Because we do not need to
process errors or do anything other than getting a reference to the database
for this small application, the showerror() and start() functions are simple. The
information about the error is shown using the code and message properties of the Event object, and a reference to the database is
captured by the result property and then stored in the global
variable db. This variable will be used to
access the database from the rest of the code.

The functions that listen to
these events receive an object of type IDBRequest. These types of objects include
properties to provide information about the operation. The following are the
most frequently used.

result—This
property returns an object with the result of the request. This could be an
object representing the database or an object representing the object retrieved
from the Object Store.

source—This
property returns an object with information about the source of the request.

transaction—This
property returns an object with information about the transaction.

readyState—This
property returns the current condition of the transaction. The possible values
are pending or done.

error—This
property returns the error produced by the request.

In our examples, we are going
to use only the result property to get a reference to the
database and the objects from the Object Store, as we did in Listing 15-4.

Defining Indexes

In case a new version of the
database is declared by the open() method or the database does not yet exist, the upgradeneeded event is fired, and the createdb() function is called to handle the
event. At this point, we have to think about what kind of objects we need to
store in the database and how we are going to get this information from the
Object Stores later.

Listing 15-5: Declaring Object Stores and
indexes

function createdb(event) {

var datababase = event.target.result;

var
mystore = datababase.createObjectStore("movies", {keyPath:
"id"});

mystore.createIndex("SearchYear", "date", {unique: false});

}

For our example, we only need
one Object Store (to store the movies) and two indexes. The first index, id, is set as the keyPath attribute for the createObjectStore() method when the Object Store is
created. The second index is assigned to the Object Store using the createIndex() method. This index is identified
with the name SearchYear and declared for the date property. We are going to use this
index to order the movies by year.

Notice that in this function we
had to get the reference to the database from the result property again because the success event has not yet been fired and
the reference to the database has not been created in our code (the value for
the db variable
has not yet been defined).

IMPORTANT: If the structure is incorrect or we want to add
something to the configuration of our database in the future, we will have to
set a new version and migrate the data from the previous one or modify the
structure through a versionchange transaction.

Adding Objects

So far, we have a database with the
name mydatabase and one Object Store named movies with two
indexes: id
and date (called SearchYear). Now, it
is time to start adding objects to this Object Store.

Listing 15-6: Adding objects to our
Object Store

function addobject() {

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

var title = document.getElementById("text").value;

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

var
mytransaction = db.transaction(["movies"], "readwrite");

var
mystore = mytransaction.objectStore("movies");

mytransaction.addEventListener("complete", function() {

show(keyword);

});

var
request = mystore.add({id: keyword, name: title, date: year});

document.getElementById("keyword").value = "";

document.getElementById("text").value = "";

document.getElementById("year").value = "";

}

At the beginning of the initiate() function, we added a listener for
the click event to the button in the form.
The addobject() function in Listing 15-6 is
executed when this event is fired. This function takes the values from the form
(keyword, text and year) and generates a transaction to
store a new object using this information.

The transaction is initiated by
calling the transaction() method and specifying the Object Stores involved in
the transaction and its type. In this case, the only Object Store available is movies,
and the type is set as readwrite.

The next step is to select the
Object Store we are going to use. Because the transaction can be initiated for
several Object Stores, we have to declare which one corresponds to the
operation we want to perform. Using the objectStore() method, we open the movies Object
Store and assign it to the transaction with the instruction mytransaction.objectStore("movies").

Most of the transactions are
going to be queries to the database, and in those cases, we can listen to the success event of the request to check the
result (as we will see soon), but this event may be fired before the
transaction fails. For this reason, when we are adding objects or modifying the
content of the database, it is better to listen to the complete event of the transaction. Because
of this, the next step in our example is to register the show() function as the handler of this
event. As usual, there is also an error event, but since the answer depends
on the requirements of the application, we are not considering this possibility
in our example.

Finally, it is time to add the
object to the Object Store. In this example, we use the add() method because we want to create
new objects, but we could have used the put() method if we wanted to modify or
replace old objects. The add() method takes the properties id, name and date and the variables keyword, title and year and creates the object using these
values as keyword/value pairs.

Retrieving Objects

If the object is stored correctly, the complete event is fired and the show() function is executed. In the code in Listing 15-6,
this function was called from inside an anonymous function to pass the variable
keyword. Now, we are going to use this value to read the
object previously stored.

Listing 15-7: Reading the new object

function show(keyword) {

var mytransaction = db.transaction(["movies"]);

var mystore = mytransaction.objectStore("movies");

var request = mystore.get(keyword);

request.addEventListener("success", showlist);

}

function showlist(event) {

var result = event.target.result;

databox.innerHTML = "<div>" + result.id + " -
" + result.name + " - " + result.date +
"</div>";

}

The functions in Listing 15-7
generate a readonly
transaction and use the get() method to retrieve the object with
the keyword received from the complete event (we do not have to declare
the transaction type because readonly is set by default).

The get() method returns the object stored
with the property id = keyword. If for example, we inserted the
movie The Godfather from our list, the variable keyword would have
the value "tt0068646". This value is received by the show() function and
used by the get() method to retrieve the movie The
Godfather. Of course, this code is for illustration purposes because it
returns the same movie we just added.

Because every operation is
asynchronous, we need two functions to show this information. The show() function generates the transaction,
and the showlist() function shows the values of the
properties on the screen in case of success. This time, we only listen to the success event of the request, but an error event will also be fired from this
operation in case something goes wrong.

The showlist() function takes the object returned
by the result
property and stores its value in the result variable. The value is the object
retrieved from the Object Store, so to access its content we write the variable
representing the object and the name of the property, as in result.id. In this case, the variable result represents the object and id is one of its properties.

As in any previous examples, to
end this application, we must listen to the load event and execute the initiate() function when the document is
loaded.

Listing 15-8: Initiating the application

window.addEventListener("load", initiate);

Do It Yourself: Copy all the JavaScript codes from
Listings 15-3 to 15-8 inside the indexed.js file, and open the HTML document of
Listing 15-1 in your browser. Using the form on
the screen, insert the movies listed at the beginning of this chapter. Every
time a new movie is inserted, the same information is shown in the box on the
right.

15.3 Listing Data

The get() method
implemented in the code in Listing 15-7 returns only one object at a time (the
last movie inserted by the user). To generate a list that includes all the
objects stored in an Object Store, we have to use a cursor.

Cursors

A cursor is the tool provided by the
API to retrieve and navigate through a group of objects returned by the
database in a transaction. The cursor gets a list of objects from the Object
Store and initializes a pointer that points to one object of the list at a
time.

The API provides the openCursor() method to generate a cursor.
This method extracts information from the selected Object Store and returns an IDBCursor object that has its own methods to
manipulate the cursor.

continue(index)—This method moves the pointer of the cursor forward one position
and fires the success event on the cursor to report the end of
the operation. The event sends the values of the object retrieved from the
database to the listener. When the pointer reaches the end of the list, the success event is
also fired, but the object sent to the function is empty. The pointer may be
moved to a specific position by providing an index value.

delete()—This method
deletes the object in the current position of the cursor.

update(value)—This method is similar to put(), but updates
the value of the object in the current position of the cursor.

The following example illustrates how
to work with a cursor.

Listing 15-9: Listing objects

var databox, db;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("save");

button.addEventListener("click", addobject);

var request = indexedDB.open("mydatabase");

request.addEventListener("error", showerror);

request.addEventListener("success", start);

request.addEventListener("upgradeneeded", createdb);

}

function showerror(event) {

alert("Error: " + event.code + " " +
event.message);

}

function start(event) {

db = event.target.result;

show();

}

function createdb(event) {

var datababase = event.target.result;

var mystore = datababase.createObjectStore("movies",
{keyPath: "id"});

mystore.createIndex("SearchYear", "date", {unique: false});

}

function addobject() {

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

var title = document.getElementById("text").value;

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

var mytransaction = db.transaction(["movies"],
"readwrite");

var mystore = mytransaction.objectStore("movies");

mytransaction.addEventListener("complete",
show);

var request = mystore.add({id: keyword, name: title, date: year});

document.getElementById("keyword").value = "";

document.getElementById("text").value = "";

document.getElementById("year").value = "";

}

function show() {

databox.innerHTML = "";

var mytransaction = db.transaction(["movies"]);

var mystore = mytransaction.objectStore("movies");

var
newcursor = mystore.openCursor();

newcursor.addEventListener("success", showlist);

}

function showlist(event) {

var cursor = event.target.result;

if (cursor) {

databox.innerHTML += "<div>" + cursor.value.id +
" - " + cursor.value.name + " - " + cursor.value.date +
"</div>";

cursor.continue();

}

}

window.addEventListener("load", initiate);

Listing 15-9 includes the full JavaScript
code necessary for this example. Of all the functions used to initialize and
configure the database, only start() presents a small change. Now, the show() function is called at the end of
this function to show the list of objects in the Object Store on the screen as
soon as the document is loaded. The significant changes in this code are in the
show() and showlist() functions, where we are working
with cursors for the first time.

Reading information from the
database with a cursor is also an operation that has to be made through a
transaction. So the first step in the show() function is to generate a readonly transaction for the movies Object
Store. This Object Store is selected for this transaction, and then the cursor
is opened for this Object Store with the openCursor() method.

If the operation is successful,
an object is returned with all the information retrieved from the Object Store,
a success event is fired from this object,
and the showlist() function is executed. The object
received by this function provides the following properties to read the
information.

key—This
property returns the value of the key for the object in the current position of
the cursor.

value—This
property returns an object with the values of the properties of the object in
the current position of the cursor.

direction—This
property returns the order in which the objects are read (ascending or
descending).

count—This
property returns the approximate number of objects in the cursor.

In the showlist() function in Listing 15-9, we used an
if instruction to check the content of the cursor.
If no objects are returned, or the pointer reaches the end of the list, then
the value of the cursor variable will be null,
and the loop will
be over. However, when the pointer is pointing to a valid object, the
information is shown on the screen, and the pointer is moved to the next
position with continue().

It is important to mention that
we do not have to use a loop here because the continue() method fires the success event, and the whole function is
executed again until the cursor returns null.

Do It Yourself: The code in Listing 15-9 replaces
all the previous JavaScript codes in this chapter. Copy this code into the indexed.js
file. Open the document of Listing 15-1 in your browser and, if you haven’t
yet, insert all the movies listed at the beginning of this chapter. You will
see the complete list of the movies in the box on the right in ascending order
according to the value of the id property.

Order

The movies in our example are listed
in ascending order, and the property used to organize the objects is id. This property is the keyPath for the movies Object Store, but it
is not likely the value that will interest our users. Considering this
situation, in the createdb() function in Listing 15-9, we added
another index to our store. The name of this additional index is SearchYear, and the property assigned to the
index is date.
This index lets us order the movies according to the value of the year they
were made. The following are the changes we need to introduce to the show() function for this purpose.

Listing 15-10: Listing objects by year in descending
order

function show() {

databox.innerHTML = "";

var
mytransaction = db.transaction(["movies"]);

var mystore
= mytransaction.objectStore("movies");

var myindex =
mystore.index("SearchYear");

var newcursor =
myindex.openCursor(null, "prev");

newcursor.addEventListener("success", showlist);

}

The function in Listing 15-10
replaces the show()
function in Listing 15-9. This new function generates a transaction, then
assigns the index SearchYear to the Object Store used in the
transaction, and finally calls the openCursor() method to get the objects that have
the property corresponding to that index (in this case, date).

There are two attributes we can
provide to the openCursor() method to select and order the
information returned by the cursor. The first attribute specifies the range to
select the objects, and the second is one of the following constants.

next—The objects will be
returned in ascending order (by default).

nextunique—The objects will be
returned in ascending order, and duplicated objects will be ignored (only the
first object is returned if a duplicated keyword is found).

prev—The objects will be
returned in descending order.

prevunique—The objects will be
returned in descending order, and duplicated objects will be ignored (only the
first object is returned if a duplicated keyword is found).

In the openCursor() method inside the show() function in Listing 15-10, we
declared the range attribute as null and the order of the objects as
descending. These values return all the objects in descending order. We are
going to see how to build a range at the end of this chapter.

Do It Yourself: Take the previous code in Listing
15-9 and replace the show() function with the new function in
Listing 15-10. This new function lists the movies on the screen by year in
descending order.

15.4 Deleting Data

We have
learned how to add, read, and list data. It is time to see how to delete
objects from the Object Stores. As we previously mentioned, the delete() method
provided by the API receives a value and deletes the object with the keyword
corresponding to that value. The
code for our example is simple; we only have to modify the showlist() function to create buttons for every
object listed on the screen and generate a readwrite transaction to delete them when the
buttons are pressed.

Listing 15-11: Deleting objects

function showlist(event) {

var cursor = event.target.result;

if (cursor) {

databox.innerHTML += "<div>" + cursor.value.id +
" - " + cursor.value.name + " - ";

databox.innerHTML += cursor.value.date + ' <input
type="button" onclick="removeobject(\'' + cursor.value.id +
'\')" value="remove"></div>';

cursor.continue();

}

}

function removeobject(keyword) {

if (confirm("Are you sure?")) {

var mytransaction = db.transaction(["movies"],
"readwrite");

var mystore = mytransaction.objectStore("movies");

mytransaction.addEventListener("complete", show);

var
request = mystore.delete(keyword);

}

}

The buttons added for every
object in the showlist() function in Listing 15-11 have an
event attribute. Every time the user clicks on one of these buttons, the removeobject() function is executed with the value
of the object's id property as its attribute. This function
generates a readwrite
transaction, and then, using the value of the keyword attribute, proceeds to delete the
corresponding object from the movies Object Store. In the end, if the
transaction is successful, the complete event is fired, and the show() function is executed to update the
list of movies on the screen.

Do It Yourself: Replace the showlist() function in the code in Listing
15-9 with the new one and add the removeobject() function in Listing 15-11. Open the
document of Listing 15-1 in your browser to test the
application. You should see the list of movies, but now, every line includes a
button to delete the movie from the Object Store.

15.5 Searching Data

The most
important operation performed in a database is probably a data search. The
purpose of this kind of system is to organize information to make it easier to
find. As we studied earlier in this chapter, the get() method is
useful to return one object at a time when we know the value of its keyword,
but a search operation is usually more complex than that.

To get a
specific list of objects from an Object Store, we have to pass a range as the
first attribute of the openCursor() method. The
API provides the IDBKeyRange object with
several methods for declaring a range and filtering the objects returned.

only(value)—This method returns a range where only the objects with the keyword
equal to the value attribute are returned.
For example, if we search movies by year using only("1972"), the movie The
Godfather is the only one returned from our list.

bound(lower, upper, lowerOpen, upperOpen)—This method returns a
closed range with starting and ending values. The lower attribute
specifies the starting point of the range, the upper attribute
specifies the ending point of the range, and the lowerOpen and upperOpen attributes
are Boolean values that declare if the objects that exactly match the values of
the lower and upper attributes
will be ignored. For example, bound("1972",
"2010", false, true) returns the list of the movies made from
the year 1972 to the year 2010 but not including those made in 2010.

lowerBound(value, open)—This method creates an open range that
starts from the value specified by the value attribute and goes to the
end of the list. For example, lowerBound("1983", true) returns all
the movies made after 1983, not including those made that year.

upperBound(value, open)—This method creates an open range, but
unlike the lowerBound() method the
objects returned will be from the beginning of the list to the value specified
by the value attribute. For example, upperBound("1983",
false) returns the movies made before 1983, including those made that
year.

For this
example, we need a new document that includes a form to search for movies.

Listing 15-12: Creating a document to search
for movies

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>IndexedDB API</title>

<link rel="stylesheet" href="indexed.css">

<script src="indexed.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<label for="year">Find Movie by Year:
</label><br>

<input type="text" name="year"
id="year"><br>

<button type="button"
id="find">Search</button>

</form>

</section>

<section id="databox">

<p>No Information available</p>

</section>

</body>

</html>

This new
document provides an input field to let us type the year of the movie we want
to find. The following code creates a range from that value.

Listing 15-13: Searching for movies

var databox, db;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("find");

button.addEventListener("click", findobjects);

var request = indexedDB.open("mydatabase");

request.addEventListener("error", showerror);

request.addEventListener("success", start);

request.addEventListener("upgradeneeded", createdb);

}

function showerror(event) {

alert("Error: " + event.code + " " +
event.message);

}

function start(event) {

db = event.target.result;

}

function createdb(event) {

var database = event.target.result;

var mystore = database.createObjectStore("movies", {keyPath:
"id"});

mystore.createIndex("SearchYear", "date", {unique:
false});

}

function findobjects() {

databox.innerHTML = "";

var find = document.getElementById("year").value;

var mytransaction = db.transaction(["movies"]);

var mystore = mytransaction.objectStore("movies");

var myindex = mystore.index("SearchYear");

var
myrange = IDBKeyRange.only(find);

var
newcursor = myindex.openCursor(myrange);

newcursor.addEventListener("success", showlist);

}

function showlist(event) {

var cursor = event.target.result;

if (cursor) {

databox.innerHTML += "<div>" + cursor.value.id +
" - " + cursor.value.name + " - " + cursor.value.date +
"</div>";

cursor.continue();

}

}

window.addEventListener("load", initiate);

The new findobjects() function in
Listing 15-13 generates a readonly transaction
for the movies Object Store, selects the index SearchYear to use the date property as index,
and creates a range that only includes the objects with a keyword equal to the
value of the variable find (the year inserted in
the form). This range is passed as an attribute of the openCursor() method.
After a successful operation, the showlist() function
prints the list of the movies on the screen that match the selected year.

Do It Yourself: The only() method
returns only movies that exactly match the value of the variable find. To test
other methods, you can provide your own values for the attributes (e.g., bound(find,
"2012", false, true).

Chapter 16 - File API

16.1 Files

The File API was created to provide
access to local files for web applications. It lets our applications work with
the user's files. With this API, we can read files, show their content to the
user, process it, and even cut them in pieces for better processing or upload
them to a server.

Loading Files

Working with local files from a web
application can be dangerous. Browsers have to consider security measures
before even contemplating the possibility of letting applications have access
to the user’s files. Considering these restrictions, the File API admits only
two ways to load a file: the <input> element and a drag and drop
operation. We are going to use an <input> element for the examples of this
chapter and study how to work with the Drag and Drop API in Chapter 17. The
following document illustrates how these types of input fields are implemented.

Listing 16-1: Creating a document to load
files

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>File API</title>

<link rel="stylesheet" href="file.css">

<script src="file.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<label for="myfiles">File: </label>

<input type="file" name="myfiles"
id="myfiles">

</form>

</section>

<section id="databox">

<p>No File Selected</p>

</section>

</body>

</html>

The CSS styles for this
document define two boxes on the screen to separate the form from the area
where we are going to show the information obtained from the files.

Listing 16-2: Defining the styles for the
form and the databox
element

#formbox {

float: left;

padding: 20px;

border: 1px solid #999999;

}

#databox {

float: left;

width: 500px;

margin-left: 20px;

padding: 20px;

border: 1px solid #999999;

}

Reading Files

To read and process the content of a
file, the API defines an object called FileReader. The following is the constructor
provided by the API to create this object.

FileReader()—This constructor returns a FileReader object. The object must be created before reading the
files. The process is asynchronous, and the result is informed through events.

The FileReader object includes the following
methods to get the file’s content.

readAsText(file, encoding)—This method processes the content of the file as
text. The content is returned as text in the UTF-8 format unless the encoding
attribute is specified. This method will try to interpret every byte or
multi-byte sequences as text characters.

readAsBinaryString(file)—This method reads the content of the file as a
succession of integers in the range of 0 to 255. It ensures each byte is read
as-is, without any attempt to interpret it. The method is useful to process
binary content like images or videos.

readAsDataURL(file)—This method generates a base64 encoded data:url representing the file’s data.

readAsArrayBuffer(file)—This method generates data as an ArrayBuffer from the
file’s data.

The following code shows how to read a
file selected by the user.

Listing 16-3: Reading a text file

var databox;

function initiate() {

databox = document.getElementById("databox");

var myfiles = document.getElementById("myfiles");

myfiles.addEventListener("change", process);

}

function process(event) {

var files = event.target.files;

var myfile = files[0];

var
reader = new FileReader();

reader.addEventListener("load", show);

reader.readAsText(myfile);

}

function show(event) {

var result = event.target.result;

databox.innerHTML = result;

}

window.addEventListener("load", initiate);

The input field in the document
of Listing 16-1 lets the user select the file to be
processed. To detect the selection, in the initiate() function in Listing 16-3 we added a
listener for the change event to the <input> element, and the process() function was declared to respond to
it.

The files property sent by the <input> element is an array containing
objects of type File representing all the files selected
by the user. Because we did not include the multiple attribute in the <input> element, the user cannot select multiple
files, so the first element of the array is the only one available in this
case. At the beginning of the process() function, we read this value from
the files
array and store it in the myfile variable for processing (var
myfile = files[0]).

The first thing we have to do
to process the file is to use the FileReader() constructor to get a FileReader object. In the process() function in Listing 16-3, we call
this object reader. Next, we must add a listener for
the load
event to detect when the file has been loaded and is ready to be processed.
Finally, the readAsText() method reads the file and gets its
content as text.

When the readAsText() method finishes reading the file,
the load
event is fired, and the show() function is called. This function gets
the content of the file from the result property sent by the event and
shows it on the screen.

This code, of course, expects
text files, but the readAsText() method takes everything and
interprets it as text, including files with binary content, like images.
Therefore, a lot of crazy characters are shown on the screen when a non-text
file is selected.

Figure 16-1: Text file loaded from the
user's computer

Do It Yourself: Create files with the codes in
Listings 16-1, 16-2 and 16-3. The names of the CSS and JavaScript files were
declared in the HTML document as file.css and file.js,
respectively. Open the document in your browser, and use the form to select a
file from your computer. Try with text files as well as images to see how the
content of these files is shown on the screen.

Properties

In a real application, information
such as the name of the file, its size or its type is necessary to inform the
user about the files being processed or even to filter the user’s input. The File object created by the <input> element to represent each file
provides a few properties for this purpose.

name—This property returns the full name of the file (name
and extension).

size—This property returns the size of the file in bytes.

type—This property returns the type of the file as a MIME
type.

Reading and showing the values of
these properties, we can help the user identify the files that were loaded by
the application.

Listing 16-4: Loading images

var databox;

function initiate() {

databox = document.getElementById("databox");

var myfiles = document.getElementById("myfiles");

myfiles.addEventListener("change", process);

}

function process(event) {

var files = event.target.files;

databox.innerHTML = "";

var myfile = files[0];

if(!myfile.type.match(/image.*/i)){

alert("Insert an image");

}else{

databox.innerHTML += "Name: " + myfile.name + "<br>";

databox.innerHTML += "Size: " + myfile.size + "
bytes<br>";

var reader = new FileReader();

reader.addEventListener("load", show);

reader.readAsDataURL(myfile);

}

}

function show(event) {

var result = event.target.result;

databox.innerHTML += '<img src="' + result + '">';

}

window.addEventListener("load", initiate);

The example of Listing 16-4 is
similar to the previous one, but this time we use the readAsDataURL() method to read the file. This
method returns the content in data:url format that can later be used as the
source of an <img> element to show the selected image
on the screen (see Chapter 11, Listing 11-29).

When we want to process a
particular type of file, the first step is to check the file’s type property. In the process() function in Listing 16-4, we check
the type by applying a method called match(), which compares a value against a
Regular Expression and returns an array with the values that match the
expression or null otherwise. If the file is not an
image, the alert() method shows an error message.
Otherwise, the name and size of the image are shown on the screen, and the file
is opened.

Despite the use of the readAsDataURL() method, the process to open the
file is exactly the same. The FileReader object is created, a listener is
added to the load event, and the file is loaded. Once
the process is over, the show() function uses the content of the result property as the source of an <img> element, and the image is shown on
the screen.

Figure 16-2: Information about an image
in the user's computer

The Basics: As illustrated in the example of
Listing 16-4, Regular Expressions and the match() method can be used to filter
information. This method compares the Regular Expression in parentheses with
the string and returns an array with all the strings that matched the
expression or null if no match is found. The MIME type
for images has a syntax like image/jpeg (for JPG images), or image/gif (for GIF images), so the expression
/image.*/i allows only images to be read, no
matter their format. For more information on Regular Expressions, the match() method, and MIME types, visit our
website and follow the links for this chapter.

Blobs

In addition to the data:url format,
the API works with another type of format called blob. A blob is an
object containing raw data. It was created with the purpose of overcoming JavaScript
limitations to work with binary data. A Blob object is usually generated from a
file, but not necessarily. It is a good alternative to working with data
without loading the entire file into memory, and it provides the possibility of
processing binary information in small pieces.

A blob has multiple purposes, but
focuses on providing better ways to process big pieces of raw data or large
files. The API offers the following method to generate Blob objects from a previous blob or
file.

slice(start, length, type)—This method returns a Blob object generated from another blob or a file. The first attribute
indicates the starting point, the second attribute specifies the length of the
new blob, and the third attribute specifies the type of data (optional).

The following example cuts a slice of
a file and shows the result on the screen.

Listing 16-5: Working with blobs and the slice() method

var databox;

function initiate() {

databox = document.getElementById("databox");

var myfiles = document.getElementById("myfiles");

myfiles.addEventListener("change", process);

}

function process(event) {

databox.innerHTML = "";

var files = event.target.files;

var myfile = files[0];

var reader = new FileReader();

reader.addEventListener("load", function(event) {

show(event, myfile)

});

var blob
= myfile.slice(0,1000);

reader.readAsBinaryString(blob);

}

function show(event, myfile){

var result = event.target.result;

databox.innerHTML = "Name: " + myfile.name +
"<br>";

databox.innerHTML += "Type: " + myfile.type +
"<br>";

databox.innerHTML += "Size: " + myfile.size + "
bytes<br>";

databox.innerHTML += "Blob size: " + result.length + "
bytes<br>";

databox.innerHTML += "Blob: " + result;

}

window.addEventListener("load", initiate);

In the code in Listing 16-5, we
do the same we have done before, but this time, instead of reading the entire
file, we create a blob with the slice() method. The blob is 1000 bytes long
and starts from the byte 0 of the file. If the file is smaller than 1000 bytes,
the blob will be as long as the file (from the starting point to the EOF, or
End of File). To show the information retrieved by this process, we use an
anonymous function to handle the load event. This function calls the show() function with a reference to the myfile object as its attribute. This
reference is received by the show() function, and the values of the myfile’s properties are shown on the screen.

Do It Yourself: Update your file.js file with the
code in Listing 16-5 and open the document of Listing
16-1 in your
browser. Select a file. You should see on the screen the information about the
file along with its first 1000 characters.

A blob is not suitable as the source of an
element. We have to convert it into a URL to turn it into an appropriate source
for elements like <img>, <video> or <audio>. Browsers
include an object called URL designed to create and process URL
objects that hold all the data related to a URL. These objects include two
important methods to work with blobs, one to create a URL object
from a blob and another to remove it.

createObjectURL(data)—This method returns a URL object that we can use to reference
data. The data attribute may be a file, a blob, or a stream.

revokeObjectURL(URL)—This method deletes a URL created
by the createObjectURL() method. It is useful to avoid the use of old URLs by
external codes or by accident from our own application.

JavaScript includes a
constructor for Blob objects called Blob() that we can use to generate a blob
from other types of data or unite pieces of data into one blob, but when we are
working with files, this is not necessary. File objects are in fact blobs, so we
can process these objects directly as we do with blobs. For example, we can get
a File object from the file selected by
the user, turn it into a URL with the createObjectURL() method, and assign that URL to a
media element to show an image or a video on the screen.

Listing 16-6: Assigning a blob to an <img> element

var databox;

function initiate() {

databox = document.getElementById("databox");

var myfiles = document.getElementById("myfiles");

myfiles.addEventListener("change", process);

}

function process(event) {

databox.innerHTML = "";

var files = event.target.files;

var myfile = files[0];

var reader = new FileReader();

reader.addEventListener("load", function(event) {

show(event, myfile)

});

reader.readAsBinaryString(myfile);

}

function show(event, myfile){

var url
= URL.createObjectURL(myfile);

var image = document.createElement("img");

image.src = url;

databox.appendChild(image);

}

window.addEventListener("load", initiate);

The example of Listing 16-6
assumes that the user selects an image file. The process to read the file is
the same as before, but now the show() function turns the File object into a URL and assigns it to
a new <img>
element that is added to the content of the databox element to show the image on the
screen.

This is a simple example that
turns a file into a blob and then gets the URL to show the file's content on
the screen, without performing any process on the file or its content, but it
demonstrates what blobs can offer. We can create image-processing applications
or a loop to generate several blobs from a file, for example, and then process
this information part by part, or upload them to a server, among others.

Figure 16-3: Image created from a Blob

Events

The time it takes for a file to be
loaded into memory depends on its size. For small files, the process looks like
an instantaneous operation, but large files may take a while to load. In
addition to the load event, the API includes a list of
events to get information at every instance of the process.

loadstart—This event is fired by the FileReader object when it begins reading the file.

progress—This event is fired periodically while the file or
blob is being read.

abort—This event is fired when the process is aborted.

error—This event is fired when the reading has failed.

loadend—This event is similar to load but it is fired either in case of success or failure.

The progress event sends an object of type ProgressEvent to the listener. The object includes three valuable
properties to return information about the process that is being monitoring by
the event.

lengthComputable—This is a Boolean property that returns true when
the length of the progress is computable or false otherwise. It is used to ensure that the values in
the rest of the properties are valid.

loaded—This property returns the total number of bytes already downloaded or
uploaded.

total—This property returns the total size in bytes of the data to be
downloaded or uploaded.

The
following example creates an application that loads a file and shows the state
of the operation with a progress bar.

Listing 16-7: Using events to control the
process

var databox;

function initiate() {

databox = document.getElementById("databox");

var myfiles = document.getElementById("myfiles");

myfiles.addEventListener("change", process);

}

function process(event) {

databox.innerHTML = "";

var files = event.target.files;

var myfile = files[0];

var reader = new FileReader();

reader.addEventListener("loadstart", start);

reader.addEventListener("progress", status);

reader.addEventListener("loadend", function() {

show(myfile);

});

reader.readAsBinaryString(myfile);

}

function start(event) {

databox.innerHTML = '<progress value="0"
max="100">0%</progress>';

}

function status(event) {

var per = parseInt(event.loaded / event.total * 100);

databox.innerHTML = '<progress value="' + per + '"
max="100">' + per + '%</progress>';

}

function show(myfile) {

databox.innerHTML = "Name: " + myfile.name +
"<br>";

databox.innerHTML += "Type: " + myfile.type +
"<br>";

databox.innerHTML += "Size: " + myfile.size + "
bytes<br>";

}

window.addEventListener("load", initiate);

In this example, we have added
three listeners to the FileReader object to control the reading
process. The functions to handle the events are: start(), status(), and show(). The start() function initiates the progress bar
at 0% and shows it on the screen when the loadstart event is fired. On the other hand,
the status()
function recreates the progress bar every time the progress event is fired. The percentage is
calculated from the properties loaded and total received from the event. In the
end, the show()
function displays the information of the file that has been processed.

Figure 16-4: Progress bar to show
progress while loading a file

Do It Yourself: Using the document of Listing
16-1 and the
JavaScript code in Listing 16-7, try to load a large file such as a video to
test the progress bar. If the browser does not recognize the <progress> element, the content of this
element will be shown instead.

Chapter 17 - Drag and Drop API

17.1 Drag and Drop

The Drag and Drop API was included to let
users drag and drop elements and content on a website. The API defines
properties and methods to control the process, but the most important aspect is a set of seven events that
are fired in every stage of the process. Some of these events are fired by the
source (the element that is being dragged), and others are fired by the target
(the element into which the source is being dropped). For example, when the
user performs a drag and drop operation, the source fires these three events.

dragstart—This event is fired the moment the drag operation
starts. The data associated with the source element is stored in the system at
this moment.

drag—This event is similar to the
mousemove
event, except that it is fired during a drag operation by the source element.

dragend—This event is fired when the drag operation is over,
whether it was successful or not.

The following are the events fired by the target
during the same operation.

dragenter—This event is fired when the mouse enters the area of
a possible target element during a drag operation.

dragover—This event is similar to the
mouseover
event, except that it is fired during a drag operation by possible target
elements.

drop—This event is fired when the element is dropped.

dragleave—This event is fired when the mouse leaves the area of
an element during a drag operation. It is used along with dragenter to provide feedback and help users identify the
target element.

In a drag and drop operation, we need to store
the information that will be shared between the elements. For this purpose, the
API offers the DataTransfer object. This object includes several
methods and properties to set and retrieve the data. The following are the most
frequently used.

types—This property returns an array containing the data
types set up for the data that is being transferred.

files—This property returns an array of File objects containing information about the files that are being dragged
(files may be dragged to the browser from other applications).

dropEffect—This property returns the type of operation currently
selected. It can also be set to change the selected operation. The possible
values are none, copy, link, and
move.

effectAllowed—This property returns or sets the types of operations
that are allowed. The possible values are none, copy, copyLink, copyMove, link, linkMove, move, all and uninitialized.

setData(type, data)—This method is used to declare the data to be sent
and its type. The method receives common MIME types (like text/plain, text/html or text/uri-list), special types (like URL or Text) or even personalized types. A call to
this method must be performed for every type of data we want to send in the
same operation.

getData(type)—This method returns the data of the specified type sent by the source
element.

clearData(type)—This method removes the data of the specified type.

setDragImage(element, x, y)—This method is used to customize the thumbnail of the
element being dragged and select its position relative to the mouse.

Before working with this API, there is an
important aspect that has to be considered. Browsers perform actions by default
during a drag and drop operation. To get the results we want, we may have to
prevent the default behavior with the preventDefault() method and customize the response. For
some events, like dragenter, dragover and drop, the prevention is necessary even when a
customized action was specified. The following example illustrates how to
implement these methods and events.

Listing 17-1: Creating a document to
experiment with the Drag and Drop API

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Drag and Drop</title>

<link rel="stylesheet" href="dragdrop.css">

<script src="dragdrop.js"></script>

</head>

<body>

<section id="dropbox">

<p>Drag and drop the image here</p>

</section>

<section id="picturesbox">

<img id="image" src="monster.gif">

</section>

</body>

</html>

The document of Listing 17-1 includes a <section> element identified as dropbox that we are going to use as the target, and an image to be used as the
source. It also includes two files for CSS styles and the JavaScript code that
will control the operation. The following are the styles for the boxes.

Listing 17-2: Defining the styles for the
boxes

#dropbox {

float: left;

width: 500px;

height: 300px;

margin: 10px;

border: 1px solid #999999;

}

#picturesbox {

float: left;

width: 320px;

margin: 10px;

border: 1px solid #999999;

}

#picturesbox > img {

float: left;

padding: 5px;

}

The rules in Listing 17-2 design the boxes that
help the user identify the source and the drop box. The following is the JavaScript
code that controls the operation.

Listing 17-3: Processing a drag and drop
operation

var source, drop;

function initiate() {

source = document.getElementById("image");

source.addEventListener("dragstart", dragged);

drop = document.getElementById("dropbox");

drop.addEventListener("dragenter", function(event) {

event.preventDefault();

});

drop.addEventListener("dragover", function(event) {

event.preventDefault();

});

drop.addEventListener("drop", dropped);

}

function dragged(event) {

var code = '<img src="' + source.src + '">';

event.dataTransfer.setData("Text", code);

}

function dropped(event) {

event.preventDefault();

drop.innerHTML =
event.dataTransfer.getData("Text");

}

window.addEventListener("load", initiate);

The application must listen to the dragstart and drop events to send and receive the data we
want to share between the elements, but it also has to listen to the dragenter and dragover events. This is because the drop action is usually not allowed in
most elements of the document by default. Therefore, to make this operation
available for our drop box, we must prevent the default behavior with the preventDefault() method when these events are fired.

The code to control the process is simple. When
the user starts dragging the element, the dragstart event is fired, and the dragged() function is called. In this function, we
get the value of the src attribute of the element being dragged and
set the data to be transferred using the setData() method of the DataTransfer object. From the other side, when an element is
dropped on our drop box, the drop event is fired, and the dropped() function is called. This function modifies the
content of the drop box with the information obtained by the getData() method. Browsers also perform actions by default when
this event takes place (for example, open a link or refresh the window to show
the image that was dropped), so we have to prevent this behavior using the preventDefault() method, as we did for the other events before.

In the dragged() function in Listing 17-3, we created HTML
code with the value of the src attribute of the element that fired the dragstart event and sent it as data with the setData() method. Because we are sending text, we declared the
data to be of type Text. The type of data must also be specified
when we retrieve it with the getData() method. This is because different types of
data can be sent at the same time by the same element. For example, an image
might send the image itself, the URL, and a description. In cases like this, we
have to send the information using several setData() methods with different types of values and
then retrieve them with getData() methods specifying those same types.

Figure 17-1: Drop Box

Do It Yourself: Create an HTML file with the document of Listing 17-1,
a CSS file called dragdrop.css with the styles of Listing 17-2, and a JavaScript
file called dragdrop.js with the code in Listing 17-3. Download
the image monster.gif from our website and move it to your project's folder.
Open the document in your browser and drag the image to the drop box.

IMPORTANT: By
default, most elements, such as images, are draggable, but in case we want to
drag and drop an element like <div>, for example, the API provides an
attribute called draggable. All we need to do is to add this
attribute to the element with the value true to allow users to drag it (e.g., <div draggable="true">).

Up to this point, we have only used the dragenter event to cancel the browser’s default behavior, and
we have not taken advantage of the dragleave and dragend events either. The following example implements these
events to provide feedback and guide the user while moving elements around.

Listing 17-4: Providing feedback to the user

var source, drop;

function initiate() {

source = document.getElementById("image");

source.addEventListener("dragstart", dragged);

source.addEventListener("dragend", ending);

drop = document.getElementById("dropbox");

drop.addEventListener("dragenter", entering);

drop.addEventListener("dragleave", leaving);

drop.addEventListener("dragover", function(event) {

event.preventDefault();

});

drop.addEventListener("drop", dropped);

}

function
entering(event) {

event.preventDefault();

drop.style.background = "rgba(0, 150, 0, .2)";

}

function
leaving(event) {

event.preventDefault();

drop.style.background = "#FFFFFF";

}

function
ending(event) {

elem =
event.target;

elem.style.visibility = "hidden";

}

function dragged(event) {

var code = '<img src="' + source.src + '">';

event.dataTransfer.setData("Text", code);

}

function dropped(event) {

event.preventDefault();

drop.style.background = "#FFFFFF";

drop.innerHTML = event.dataTransfer.getData("Text");

}

window.addEventListener("load", initiate);

The JavaScript code in Listing 17-4 replaces the
code in Listing 17-3. In this example, we added three functions: entering(), leaving(), and ending(). These functions respond to the dragenter, dragleave, and dragend events, respectively. Their purpose is to
provide visual feedback. The entering() and leaving() functions change the background color of
the drop box every time the mouse drags something and enters or leaves the area
occupied by the element, and the ending() function modifies the visibility property of the source element to hide it. In
consequence, every time the mouse drags something and enters the drop box area,
the box turns green, and when the element is dropped, the source image is
hidden. These changes do not affect the process but provide feedback to guide
users during the operation.

Again, to prevent the actions by default, we have
to use the preventDefault() method in every function, even when
customized actions are declared.

Do It Yourself: Replace the code in the JavaScript file with the code
in Listing 17-4, open the document of Listing 17-1 in your browser, and drag the image into
the drop box.

IMPORTANT: The
code in Listing 17-4 uses the dragend event to hide the original image when the
operation is over. This event is fired by the source when a drag operation is
finished, either in success or failure. In our example, the image is hidden in
both cases. You will have to create the proper controls to proceed only in case
of success. Next, we will see some examples of how to do this.

Validation

There is no method to detect whether the source is
valid or not. We cannot rely on the information returned by the getData() method because even when we only get the data of the
type specified in the attribute, other sources might use the same type and
provide data we did not expect. There is a property of the DataTransfer object called types that returns an array with a list of the
types set by the dragstart event, but it is also useless for
validation purposes. For this reason, the techniques for selecting and
validating the data transferred in a drag and drop operation varies, and the
procedure can be as simple or as complex as our application requires. The
following example validates the source by reading the element's id
attribute.

Listing 17-5: Designing a document to drag
and drop from multiple sources

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Drag and Drop</title>

<link rel="stylesheet" href="dragdrop.css">

<script src="dragdrop.js"></script>

</head>

<body>

<section id="dropbox">

<p>Drag and drop images here</p>

</section>

<section id="picturesbox">

<img id="image1" src="monster1.gif">

<img id="image2" src="monster2.gif">

<img id="image3" src="monster3.gif">

<img id="image4" src="monster4.gif">

</section>

</body>

</html>

The following code reads the element's id
attribute to select which image can be dropped and which cannot.

Listing 17-6: Filtering images by the id attribute

var drop;

function initiate() {

var images = document.querySelectorAll("#picturesbox >
img");

for (var i = 0; i < images.length; i++) {

images[i].addEventListener("dragstart", dragged);

}

drop = document.getElementById("dropbox");

drop.addEventListener("dragenter", function(event) {

event.preventDefault();

});

drop.addEventListener("dragover", function(event) {

event.preventDefault();

});

drop.addEventListener("drop", dropped);

}

function dragged(event) {

element = event.target;

event.dataTransfer.setData("Text", element.id);

}

function dropped(event) {

event.preventDefault();

var id = event.dataTransfer.getData("Text");

if (id
!= "image4") {

var
src = document.getElementById(id).src;

drop.innerHTML = '<img src="' + src + '">';

} else {

drop.innerHTML = "Not admitted";

}

}

window.addEventListener("load", initiate);

Not too many things changed in Listing 17-6 from
previous examples. We are using the querySelectorAll() method to add a listener for the dragstart event to every image in the picturesbox element, send the value of the id
attribute with setData() every time an image is dragged, and check
this value in the dropped() function to prevent the user from dropping
the image with the id attribute equal to "image4" (the "Not admitted" message is shown in the drop box when the user tries
to drop this particular image).

Figure 17-2: The image number 4 was not
admitted

Do It Yourself: Create a new HTML file with the document of Listing
17-5, a CSS file called dragdrop.css with the styles of Listing 17-2, and a JavaScript file called dragdrop.js
with the code in Listing 17-6. Download the images monster1.gif, monster2.gif,
monster3.gif, and monster4.gif from our website and move them to your project's
folder. Open the document in your browser and drag the images to the drop box.
You should see the message "Not admitted" inside the drop box when
you try to drop the image with the ID "image4".

The Basics: This
is a simple filter. You can improve it by checking that the image received is
in the picturesbox element, for example, or use properties
from the DataTransfer object (like types or files), but it is always a customized process.
In other words, you have to take care of it yourselves and adapt the process to
the characteristics of your application.

Thumbnail

Browsers generate a thumbnail with an image of the
source and drag it along with the mouse. The position of this thumbnail is set
according to the position of the mouse's pointer when the drag operation
started. Using the setDragImage() method, we can specify a new position as
well as customize the image. The following example illustrates the importance
of this method using a <canvas> element as the drop box.

Listing 17-7: Using a <canvas> element as a drop box

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Drag and Drop</title>

<link rel="stylesheet" href="dragdrop.css">

<script src="dragdrop.js"></script>

</head>

<body>

<section id="dropbox">

<canvas id="canvas" width="500"
height="300"></canvas>

</section>

<section id="picturesbox">

<img id="image1" src="monster1.gif">

<img id="image2" src="monster2.gif">

<img id="image3" src="monster3.gif">

<img id="image4" src="monster4.gif">

</section>

</body>

</html>

The JavaScript code for this application has to
use a few conventional techniques to process the image and set its position
when it is dropped.

Listing 17-8: Configuring the thumbnail

var drop, canvas;

function initiate() {

var images = document.querySelectorAll("#picturesbox >
img");

for (var i = 0; i < images.length; i++) {

images[i].addEventListener("dragstart", dragged);

images[i].addEventListener("dragend", ending);

}

drop = document.getElementById("canvas");

canvas = drop.getContext("2d");

drop.addEventListener("dragenter", function(event) {

event.preventDefault();

});

drop.addEventListener("dragover", function(event) {

event.preventDefault();

});

drop.addEventListener("drop", dropped);

}

function ending(event) {

element = event.target;

element.style.visibility = "hidden";

}

function dragged(event) {

element = event.target;

event.dataTransfer.setData("Text", element.id);

event.dataTransfer.setDragImage(element, 0, 0);

}

function dropped(event) {

event.preventDefault();

var id = event.dataTransfer.getData("Text");

var element = document.getElementById(id);

var posx = event.pageX - drop.offsetLeft;

var posy = event.pageY - drop.offsetTop;

canvas.drawImage(element, posx, posy);

}

window.addEventListener("load", initiate);

With this example, we are probably getting closer
to a real-life application. The code in Listing 17-8 performs three tasks: it
customizes the image being dragged with the setDragImage() method, it draws the image on the canvas
using the drawImage() method, and it hides the source after the
process is over.

For the custom thumbnail, we use the same element
that is being dragged. We have not change anything about this aspect, but we
set the position as 0,0. This means that now we know where the
thumbnail is positioned relative to the mouse. Using this information, we
calculate exactly where the source was dropped on the canvas and draw the image
in that location, making it easy for the users to find the right place to drop
the image.

Figure 17-3: Images dragged onto a
canvas

Files

The API is not only available within the document but
also integrated with the system, allowing users to drag elements from the
browser to other applications and vice versa. Because of this characteristic,
we can drag files from external applications into our document.

As we saw before, there is a special property for
this purpose in the DataTransfer object called files that returns an array of File objects representing the files that have
been dragged. We can use this information to build complex codes to help users
work with files or upload them to a server.

Listing 17-9: Creating a simple document to
drop files

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Drag and Drop</title>

<link rel="stylesheet" href="dragdrop.css">

<script src="dragdrop.js"></script>

</head>

<body>

<section id="dropbox">

<p>Drag and drop FILES here</p>

</section>

</body>

</html>

The document of Listing 17-9 provides a <section> element again to serve as a drop box. The files will be dragged
into this box from an external application like File Explorer, and the data
will be processed by the following code.

Listing 17-10: Processing the data contained
in the files property

var drop;

function initiate() {

drop = document.getElementById("dropbox");

drop.addEventListener("dragenter", function(event) {

event.preventDefault();

});

drop.addEventListener("dragover", function(event) {

event.preventDefault();

});

drop.addEventListener("drop", dropped);

}

function dropped(event) {

event.preventDefault();

var
files = event.dataTransfer.files;

var list = "";

for (var
f = 0; f < files.length; f++) {

list
+= "File: " + files[f].name + " " + files[f].size +
"<br>";

}

drop.innerHTML = list;

}

window.addEventListener("load", initiate);

The code in Listing 17-10 reads the files property of the DataTransfer object and creates a loop to show the name
and size of every file dropped inside the drop box. The files property returns an array of File objects, so we have to read the name and size property of each object to get this
information (see File objects in Chapter 16).

Do It Yourself: Create new files with the codes in Listings 17-9 and
17-10, and open the document in your browser. Drag files from the File Explorer
or any other similar program to the drop box. You should see a list with the
name and size of every file dropped inside the box.

Chapter 18 - Geolocation API

18.1 Locations

The Geolocation API was designed to
provide a standard detection mechanism for browsers to let developers determine
the user’s geographic location. Previously, we only had the option of building an
extensive database with IP addresses and programming resource-consuming
applications on the server that would only give us an approximate idea of the
user’s location (usually as vague as the country). This API takes advantage of
new systems, such as network triangulation and GPS, to return an accurate
location of the device running the application. The information returned may be
used to create applications that adapt to the user’s location or provide
localized information automatically. Three methods are included in the API for
this purpose.

getCurrentPosition(location, error, configuration)—This method is used for
single requests. It can take three attributes: a function to process the
location returned, a function to process the errors returned, and an object to
configure how the information will be acquired (only the first attribute is
required).

watchPosition(location, error, configuration)—This method is similar
to the previous method except it constantly watches the location to detect and
inform the application about changes. It works in a similar way than the setInterval() method,
repeating the process automatically in a period of time according to the values
set by default or the configuration specified by the attributes.

clearWatch(id)—This method stops the process started by the watchPosition() method. This
is similar to the clearInterval() method used
to stop the process started by setInterval().

The following is the document for
this chapter. It is a simple document with a button inside a <section> element that we are going to use to show
the information retrieved by the location system.

Listing 18-1: Creating a document for testing the
Geolocation API

<!DOCTYPE html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Geolocation</title>

<script
src="geolocation.js"></script>

</head>

<body>

<section
id="location">

<button
type="button" id="getlocation">Get my
location</button>

</section>

</body>

</html>

Getting the Location

As we mentioned above, we only need to assign one
attribute to the getCurrentPosition() method to get a location. This attribute
is a function that receives an object called Position that contains all the information retrieved by the
geolocation systems. The Position object has two properties to store the
values.

coords—This
property contains another object with a set of properties to provide the
position of the device. The properties available are latitude, longitude, altitude (in meters),
accuracy (in meters),
altitudeAccuracy (in meters), heading (in
degrees), and speed (in meters per second).

timestamp—This
property indicates the time when the information was acquired.

The API is defined in an object
called Geolocation. The Navigator object includes the geolocation property to offer access to this object.
To access the methods of this API, we have to call them from the geolocation property of the navigator property of the Window object, as in navigator.geolocation.getCurrentPosition(). The following example
implements this method to get the user's current location.

Listing 18-2: Getting the
location

function initiate() {

var get =
document.getElementById("getlocation");

get.addEventListener("click", getlocation);

}

function
getlocation() {

navigator.geolocation.getCurrentPosition(showinfo);

}

function
showinfo(position) {

var location =
document.getElementById("location");

var data =
"";

data +=
"Latitude: " + position.coords.latitude + "<br>";

data +=
"Longitude: " + position.coords.longitude + "<br>";

data +=
"Accuracy: " + position.coords.accuracy + "mts.<br>";

location.innerHTML
= data;

}

window.addEventListener("load",
initiate);

In the code in Listing 18-2, we
define a function called showinfo() to process the information produced by the getCurrentPosition() method. When this method is called, a new
Position object is created with the current
information and sent to the showinfo() function. We reference that object inside the function with the position variable and then use this variable to
show the data to the user.

The Position object has two important properties: coords and timestamp. In our example, we use coords to access the information we
want (latitude, longitude, and accuracy). These values are stored in the data variable and then shown on the screen as the new content
of the location element.

Do It Yourself: Create files with the codes in
Listings 18-1 and 18-2 and open the document in your browser. When you click
the button, the browser asks you whether or not to activate the location system
for this application. If you allow the application to access this information,
then your latitude, longitude and the accuracy of the data will be shown on the
screen (it may take a few seconds to be available).

By adding a second attribute (another
function) to the getCurrentPosition() method, we are able to capture
errors produced in the process, like the error that occurs when the user denies
access to the location system to our application.

Along with the Position object, the getCurrentPosition() method returns the PositionError object if an error is detected. The
object has two properties, error and message, to provide the value and a description of the error. The
three possible errors are represented by constants.

PERMISSION_DENIED—Value 1. This
error occurs when the user denies the Geolocation API access to his or her
location information.

POSITION_UNAVAILABLE—Value 2. This
error occurs when the position of the device couldn’t be determined.

TIMEOUT—Value 3. This
error occurs when the position couldn’t be determined in the period of time
declared in the configuration.

If we
want to report errors, we have to create a new function and assign it to the
second parameter of the getCurrentPosition() method.

Listing 18-3: Displaying
error messages

function initiate() {

var get =
document.getElementById("getlocation");

get.addEventListener("click", getlocation);

}

function
getlocation() {

navigator.geolocation.getCurrentPosition(showinfo, showerror);

}

function
showinfo(position) {

var location =
document.getElementById("location");

var data =
"";

data +=
"Latitude: " + position.coords.latitude + "<br>";

data +=
"Longitude: " + position.coords.longitude + "<br>";

data +=
"Accuracy: " + position.coords.accuracy + "mts.<br>";

location.innerHTML
= data;

}

function showerror(error) {

alert("Error: "
+ error.code + " " + error.message);

}

window.addEventListener("load",
initiate);

The error messages are intended for
internal use. The purpose is to provide a mechanism for the application to
acknowledge the situation and proceed accordingly. In the code in Listing 18-3,
we add the second parameter to the getCurrentPosition() method (another function) and
create this function, called showerror(), to show the values of the properties code and message. The value of code will be an integer between 0 and 3 according to the number of the
error (listed above).

The PositionError object is sent to the showerror() function and is represented by
the variable error. We could also check for the
errors individually (error.PERMISSION_DENIED, for example) and show an alert only if
that particular condition is true.

The third possible value of the getCurrentPosition() method is an object containing up to
three properties.

enableHighAccuracy—This is a
Boolean property that informs the system that the most accurate location is
required. When the value is set to true, the browser
tries to get information through systems like GPS, for example, to provide the
exact location of the device. These are high resource-consuming systems, and
their use should be limited to specific circumstances. For this reason, the
default value of this property is false.

timeout—This
property indicates the maximum time for the operation to take place. If the
information is not acquired at the deadline, the TIMEOUT error is
returned. Its value is in milliseconds.

maximumAge—The previous
locations are cached in the system. If we consider appropriate to get the last
information stored instead of retrieving a new location (to avoid draining the battery
or to get a quick response), this property can be set with a time limit. If the
last cached location is older than the value of this property, then a new
location will be retrieved from the system. Its value is in milliseconds.

The following code tries to get the most
accurate location of the device in no more than 10 seconds, but only if there
is no previous location in the cache captured less than 60 seconds ago (if
there is a previous location, that will be the information returned in the Position object).

Listing 18-4: Configuring
the Geolocation system

function initiate() {

var get =
document.getElementById("getlocation");

get.addEventListener("click", getlocation);

}

function
getlocation() {

var geoconfig = {

enableHighAccuracy:
true,

timeout: 10000,

maximumAge: 60000

};

navigator.geolocation.getCurrentPosition(showinfo, showerror, geoconfig);

}

function
showinfo(position) {

var location =
document.getElementById("location");

var data =
"";

data +=
"Latitude: " + position.coords.latitude + "<br>";

data +=
"Longitude: " + position.coords.longitude + "<br>";

data +=
"Accuracy: " + position.coords.accuracy + "mts.<br>";

location.innerHTML
= data;

}

function
showerror(error) {

alert("Error:
" + error.code + " " + error.message);

}

window.addEventListener("load",
initiate);

In our example, the object containing
the configuration values is stored in the geoconfig variable, and this variable is
declared as the third attribute of the getCurrentPosition() method. Nothing else changed
in the rest of the code from the previous example. The showinfo() function will show the
information on the screen independently of its origin (i.e., if it is cached or
new).

From this code, we can see the real
purpose of the Geolocation API and what the API was intended for. The most
useful features are geared towards mobile devices. For instance, the value true for the enableHighAccuracy attribute suggests the browser to use systems like GPS to
get the most accurate location, which are only available in these types of
devices. Also, the methods watchPosition() and clearWatch(), which we will see next, work with
location updates, and this is only useful, of course, when the device running
the application is mobile (and it is moving). This brings up two important
issues. First, most of our codes will have to be tested in a mobile device to
know exactly how they will perform in a real situation. And second, we have to
be responsible when using this API. GPS and other location systems consume
resources, and in most cases, devices will run out of battery if we are not
careful.

Watching the Location

Similar to getCurrentPosition(), the watchPosition() method takes three attributes
and performs the same task: to get the location of the device that is accessing
the application. The only difference is that the getCurrentPosition() method performs a one-time operation
while watchPosition() automatically offers new data
every time the location changes. The method is watching all the time and
sending information to a function when there is a new location to show until we
cancel the process with the clearWatch() method.

The watchPosition() method is implemented the same way than
the getCurrentPosition() method, as illustrated in the
following example.

Listing 18-5: Testing the watchPosition() method

function initiate() {

var get =
document.getElementById("getlocation");

get.addEventListener("click", getlocation);

}

function
getlocation() {

var geoconfig = {

enableHighAccuracy: true,

maximumAge: 60000

};

control =
navigator.geolocation.watchPosition(showinfo, showerror, geoconfig);

}

function
showinfo(position) {

var location =
document.getElementById("location");

var data =
"";

data +=
"Latitude: " + position.coords.latitude + "<br>";

data +=
"Longitude: " + position.coords.longitude + "<br>";

data +=
"Accuracy: " + position.coords.accuracy + "mts.<br>";

location.innerHTML
= data;

}

function
showerror(error) {

alert("Error:
" + error.code + " " + error.message);

}

window.addEventListener("load",
initiate);

We will not notice any change running
this example on a static desktop computer, but on a mobile device, new
information will be shown every time there is a change in the device’s
location. How often the information is sent to the showinfo() function is determined by the maximumAge property. If the new location
is retrieved 60 seconds (60000 milliseconds) after the previous one, it is
shown; otherwise, the showinfo() function will not be called.

Notice that the value returned by the
watchPosition() method is stored in the control variable. This variable is like the id of this operation.
If later we want to cancel the process, we have to execute the instruction clearWatch(control) and watchPosition() will stop updating the
information.

Google Maps

In the previous examples, we have shown
the location data on the screen exactly the way we receive it. However, these
values usually mean nothing to regular people. We cannot immediately state our
current locations’ latitude and longitude, let alone identify a location in the
world from these values. We have two alternatives: use this information
internally to calculate position, distance and other variables that let us
offer concrete results to the users (such as products or restaurants in the
area) or directly show the information retrieved by the Geolocation API in a
more comprehensible way, like a map.

Earlier in this book, we mentioned
the Google Maps API. This is an external JavaScript API provided by Google that
has nothing to do with HTML5 but is used in modern websites and applications.
It offers a variety of ways to work with interactive maps and even real views
of locations through the StreetView technology.

The following is a simple example
that applies one part of the API called Static Maps API. With this API,
we can build a URL with the information of the location, and an image of the
selected area in a map is returned.

Listing 18-6: Representing
the location on a map using the Google Maps API

function initiate() {

var get =
document.getElementById("getlocation");

get.addEventListener("click", getlocation);

}

function
getlocation() {

navigator.geolocation.getCurrentPosition(showinfo, showerror);

}

function
showinfo(position) {

var location =
document.getElementById("location");

var mapurl =
"http://maps.google.com/maps/api/staticmap?center=" +
position.coords.latitude + "," + position.coords.longitude +
"&zoom=12&size=400x400&sensor=false&markers=" +
position.coords.latitude + "," + position.coords.longitude;

location.innerHTML =
'<img src="' + mapurl + '">';

}

function
showerror(error) {

alert("Error:
" + error.code + " " + error.message);

}

window.addEventListener("load",
initiate);

The application is simple. We use the
getCurrentPosition() method and send the
information to the showinfo() function as usual, but now in this function the values of the Position object are added to a Google URL, and
then the address is inserted as the source of an <img> element to show the image returned on the
screen.

Figure 18-1: Image returned by the
Google Maps API

Do It Yourself: Test the code in Listing 18-6
in your browser using the document of Listing 18-1. You should see a map with
your location on the screen.
Change the values of the attributes zoom and size in the URL to modify the map returned by
the API. Go to the
Google Maps API's web page to find other alternatives: https://developers.google.com/maps/.

Chapter 19 - History API

19.1 History

The browser history is a list of all the web pages (URLs) visited by
the user in one single session. It is what makes navigation possible. Using the
navigation buttons on the left or right of the navigation bar in every browser,
we can move through the list back and forward and load previous documents.

Navigation

With the browser's arrows, we can load a web page we visited before or
go back to the last one, but sometimes it may be useful to navigate through the
browsing history from inside the document. For this purpose, browsers include
the History API. This API provides properties and methods to manipulate the
history and manage the list of URLs it contains. The following are the
properties and methods available to simulate the navigation buttons from JavaScript
code.

length—This property
returns the number of entries in the browsing history (the total of URLs on the
list).

back()—This method takes the browser one step back in the browsing
history (emulating the left arrow).

forward()—This method
takes the browser one step forward in the browsing history (emulating the right
arrow).

go(steps)—This method takes the browser
back or forward the specified steps in the browsing history. The attribute may
be a negative or positive value according to the direction we choose.

The API is defined by the History object, accessible from a property of the Window
object called history. Every time we want to read the API's properties or
call its methods, we have to do it from this property, as in window.history.back() or history.back() (the window property may be ignored, as we explained in Chapter 6).

Listing 19-1: Navigating back in the browser
history

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>History API</title>

<script>

function goback() {

window.history.back();

}

</script>

</head>

<body>

<section>

<button type="button"
onclick="goback()">Go to the previous page</button>

</section>

</body>

</html>

The document of Listing 19-1 illustrates how simple it is to
implement these methods. The document defines a button that calls the goback() function when the user clicks on it. In this function,
we call the back() method of the History API to take the user back to
the previous page.

Do It Yourself: Create a new HTML file with the
document of Listing 19-1. Visit a website you know and then load the document
in your browser. Click on the button. The browser should load the previous
website and show it on the screen.

URLs

As we will see in Chapter 21, it is common
practice to program small applications that retrieve information from a server
and show it within the current document without refreshing the page or loading
a new one. Users interact with websites and applications from the same URL,
receiving information, entering data, and getting the results printed on the
same page. However, the way browsers keep track of the user’s activity is
through URLs. URLs are the data inside the browsing history, the addresses that
indicate where the user is located. Because new web applications avoid the use
of URLs to point to the user’s location on the website, important steps are
lost in the process. Users can update data on a web page dozens of times but
leave no trace in the browser history to indicate the steps followed and help
them go back. To solve this issue, the History API includes properties and
methods that modify the URL in the location bar as well as the browser history.
The following are the most frequently used.

state—This property returns the value of the state of the
current entry.

pushState(state, title, url)—This method creates a new entry in the browser
history. The state attribute declares a value for the state of the
entry. It is useful to identify the entry later, and it may be specified as a
string or as a JSON object. The title attribute is the title of the
entry, and the url attribute is the URL of the entry we are generating
(this value will replace the current URL in the navigation bar).

replaceState(state, title, url)—This method works exactly like pushState(), but it does not generate a new entry. Instead, it
replaces the information of the current one.

If we want to create a new entry in
the browser history and change the URL inside the navigation bar, we have to
use the pushState() method. The following example
shows how it works.

Listing 19-2: Creating a document to
experiment with the History API

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>History API</title>

<link rel="stylesheet" href="history.css">

<script src="history.js"></script>

</head>

<body>

<section id="maincontent">

<p>This content is never refreshed</p>

<p><span id="url">page
2</span></p>

</section>

<aside id="databox"></aside>

</body>

</html>

In this document, we have
included permanent content inside a <section> element identified as maincontent, a text that we will turn into a
link to generate the second page of the website, and the usual databox for the content. The following are
the styles for the document.

Listing 19-3: Defining the styles for the
boxes and the <span> elements

#maincontent {

float: left;

padding: 20px;

border: 1px solid #999999;

}

#databox {

float: left;

width: 500px;

margin-left: 20px;

padding: 20px;

border: 1px solid #999999;

}

#maincontent span {

color: #0000FF;

cursor: pointer;

}

The
Basics: The cursor property is
a CSS property used to specify the graphic that represents the mouse's pointer.
The system changes this cursor automatically according to the content behind
the pointer. For general content, like structural elements or images, the
pointer is represented as an arrow, for text as a vertical bar, and for links
as a little hand. In Listing 19-3, we turned the pointer into a hand to show
the user that the content is clickable. There are several values available for
this property. For more information, visit our website and follow the links for
this chapter.

What we are going to do in this
example is to add a new entry with the pushState() method and update the content
without refreshing the page or loading another document.

Listing 19-4: Generating a new URL and
content

function initiate() {

databox = document.getElementById("databox");

url = document.getElementById("url");

url.addEventListener("click", changepage);

}

function changepage() {

databox.innerHTML = "The url is page2";

history.pushState(null, null, "page2.html");

}

window.addEventListener("load", initiate);

In the initiate() function in Listing 19-4, we create
the proper reference to the databox and add a listener for the click event to the <span> element. Every time the user clicks
on the text inside <span>, the changepage() function is called. This function
performs two tasks: it updates the content of the page with new information and
inserts a new URL in the browser history. After the function is executed, the databox shows the text "The url is
page2", and the URL of the main document is replaced in the location bar
by the URL page2.html.

Do It Yourself: Create a new HTML file with the
document of Listing 19-2, a CSS file called history.css with the styles in Listing 19-3,
and a JavaScript file called history.js with the code in Listing 19-4.
Upload all the files to your server or local server and open the document in
your browser. Click on the text "page 2" and check how the URL in the
location bar changes to the one generated by the code.

IMPORTANT: The URLs generated from these methods are fake URLs in the sense
that browsers never check for the validity of these addresses and the existence
of the document that they are pointing to. It is your responsibility to make
sure that these fake URLs are in fact valid and useful.

The state Property

What we have
done so far is to manipulate the browser history. We made the browser believe
that the user visited a URL that, at this point, does not exist. After the
"page 2" link is clicked, the fake URL page2.html was shown in the navigation
bar, and new content was inserted in the databox, everything
without refreshing or loading another page. But at this point the browser does
not consider the new URL a real document. If we go back in our browsing history
using the navigation buttons, the URL changes from the new one to the one
corresponding to the main document, but the content of the document does not
change at all. We need to detect when the fake URLs are revisited and perform
the right modifications in the document to show the corresponding state.

We
mentioned before the existence of the state property.
The value of this property may be set during the generation of a new URL, and
this is the way we can later identify what is the current URL. The API provides
the following event to work with this property.

popstate—This event is fired when a URL is revisited or when the
document has been loaded. It provides the state property with the value of the state
declared when the URL was generated with the pushState() or replaceState() methods. This value is null when the URL is real unless we changed it before through the replaceState() method, as we will see next.

In the following example, we
improve the previous code by implementing the popstate event and the replaceState() method to detect which URL the user
is currently requesting.

Listing 19-5: Keeping track of the user’s
location in the browser history

function initiate() {

databox = document.getElementById("databox");

url = document.getElementById("url");

url.addEventListener("click", changepage);

window.addEventListener("popstate", newurl);

history.replaceState(1, null);

}

function changepage() {

showpage(2);

history.pushState(2, null, "page2.html");

}

function newurl(event) {

showpage(event.state);

}

function showpage(current) {

databox.innerHTML = "The url is page " + current;

}

window.addEventListener("load", initiate);

There are two things we have to
do in our application to have full control over the situation. To begin with,
we must declare a state value for every URL we are going to use, the fakes and
the real ones, and then, we must update the content of the document according
to the current URL.

In the initiate() function in Listing 19-5, a
listener is added for the popstate event. Every time a URL is
revisited, the newurl() function is executed. This function
updates the content of the databox according to the current URL. It takes
the value of the state property and sends it to the showpage() function to be shown on the screen.

This works for every fake URL,
but as we explained before, the real URLs do not have a state value by default.
By using the replaceState() method at the end of the initiate() function, we change the information
of the current entry (the real URL of the main document) and declare the value
1 for its state. Now, every time the user revisits the main document, we are
able to detect it by checking this value.

The changepage() function is the same except this
time it uses the showpage() function to update the document’s
content and declare the value 2 for the state of the fake URL.

The application works as
follows: when the user clicks on the "page 2" link, the message
"The url is page 2" is shown on the screen, and the URL in the navigation
bar is changed to page2.html (including the full path, of course). This is what
we had done so far, but here is where things get interesting. If the user
clicks on the left arrow in the navigation bar, the URL in this bar changes to
the previous in the browsing history (that is, the real URL of our document),
and the popstate
event is fired. This event calls the newurl() function that reads the value of
the state
property and sends it to the showpage() function. Now the state’s value is
1 (the value we declared for this URL using the replaceState() method), and the message shown on
the screen is "The url is page 1". If the user goes back to the fake
URL using the right arrow in the navigation bar, the state’s value will be 2, and the message shown on the
screen will again be "The url is page 2".

The value of the state property may be any value we want
to control which URL is the current one and to adapt the document’s content to
the situation.

Do It Yourself: Use the files with the codes in
Listings 19-2 and 19-3 for the HTML document and CSS styles. Copy the code in
Listing 19-5 into the history.js file, and upload the files to your
server or local server. Open the document in your browser and click on the text
"page 2". The URL and the content of the databox should change according to the
corresponding URL. Click the back and forward buttons on the navigation bar
several times to see the URL changing and the content associated with that URL
updated on the screen.

Real-Life Application

The following
is a more practical application. We are going to use the History API to load a
set of four images from the same document. Each image is associated with a fake
URL that can later be used to retrieve it from the server.

The main document is loaded with
an image by default. This image will be associated with the first of four links
that are part of the permanent content. All these links will point to fake URLs
referencing a state, not a real document, including the link of the main
document that will be changed to page1.html to be consistent with the rest of
the URLs.

Listing 19-6: Creating the main document for
our application

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>History API</title>

<link rel="stylesheet" href="history.css">

<script src="history.js"></script>

</head>

<body>

<section id="maincontent">

<p>This content is never refreshed</p>

<p><span id="url1">image
1</span></p>

<p><span id="url2">image
2</span></p>

<p><span id="url3">image
3</span></p>

<p><span id="url4">image
4</span></p>

</section>

<aside id="databox">

<img id="image" src="monster1.gif">

</aside>

</body>

</html>

The only difference between
this new application and the previous one is the number of links and new URLs
we are generating. In the code in Listing 19-5, there were two states: state 1
corresponding to the main document and state 2 for the fake URL (page2.html)
generated by the pushState() method. In this case, we have to
automate the process to generate a total of four fake URLs corresponding to
each image available.

Listing 19-7: Manipulating history

function initiate() {

for (var f = 1; f < 5; f++) {

url = document.getElementById("url" + f);

url.addEventListener("click", function(x){

return function() {

changepage(x);

};

}(f));

}

window.addEventListener("popstate", newurl);

history.replaceState(1, null, "page1.html");

}

function changepage(page) {

showpage(page);

history.pushState(page, null, "page" + page +
".html");

}

function newurl(event) {

showpage(event.state);

}

function showpage(current) {

if (current != null) {

var image = document.getElementById("image");

image.src = "monster" + current + ".gif";

}

}

window.addEventListener("load", initiate);

In this example, we are using
the same functions, but with a few obvious changes. First, the replaceState() method in the initiate() function has the url attribute set to page1.html. We
decided to program our application this way, declaring the state of the main
document as 1 and the URL as page1.html
(independently of the real URL of the document), because this allows us to define
the names of all the URLs using the same name and the values of the state property. We can see this in
practice in the changepage() function. Every time the user
clicks on one of the links in the document, this function is executed, and the
fake URL is built with the value of the page variable and added to the history.
The value received by the function was previously set in the for loop at the beginning of the initiate() function. This value is set to 1 for the "image 1" link, 2 for the "image 2" link,
and so on.

Every time a URL is visited,
the showpage() function is executed to update the image
according to the current URL. Because the popstate event sometimes is fired when the state property is null (like after the main document is
loaded for the first time), we check the value received by the showpage() function before doing anything
else. If the value is different than null, it means that the state property is defined for that URL
and the image corresponding to that state has to be shown on the screen.

The images used for this
example are named monster1.gif, monster2.gif,
monster3.gif
and monster4.gif, following the same order as the
values of the state property. Thus, using this value,
we can select the image to be shown.

Do It Yourself: To test the last example, use the
HTML document of Listing 19-6 with the CSS code in Listing 19-3. Copy the code in
Listing 19-7 into the history.js file. Download the files monster1.gif,
monster2.gif,
monster3.gif
and monster4.gif from our website and upload all the files to your server
or local server. Open the document in your browser and click on the links.
Navigate through the URLs you selected using the navigation buttons. The images
on the screen should change according to the URL in the navigation bar.

The
Basics: The for loop used in the code in Listing
19-7 to add a listener for the click event to every <span> element in the document, takes
advantage of a technique described in the example of Listing
6-161,
Chapter 6. To send the current value of the f variable to the function, we have
to use two anonymous functions. The first function is executed when the addEventListener() method is executed. This function
receives the current value of the f variable (see the parentheses at
the end) and stores that value in the x variable. Then, the function
returns a second anonymous function with the value of the variable x. This second function is the one
that will be executed when the event is fired.

Chapter 20 - Page Visibility API

20.1 Visibility

Web applications are getting more
sophisticated and are demanding computer resources as never before. Web pages
are no longer static documents; JavaScript has turned them into full
applications, capable of executing complex processes without interruption, and
even without the user’s intervention. But there are moments when these
processes should be canceled or paused to effectively distribute the resources for
a better user experience. To produce applications that are conscious of its own
state, browsers include the Page Visibility API. This API informs the
application about the current visibility state of the document, reporting when
the tab is hidden, or the window is minimized, so our code can decide what to
do while nobody is watching.

State

The API provides a property to report the
current state and an event to let the application know that something changed.

visibilityState—This property returns the current visibility state of
the document. The possible values are hidden or visible (optional values such as prerender and unloaded may also be implemented by some browsers).

visibilitychange—This event is fired when the value of the visibilityState property changes.

The API is part of the Document object, and therefore it is accessible
from the document property of the Window object. The following document implements the property and
the event provided by the API to detect when the user moves to a different tab,
and it modifies the content of the page to report the new state.

Listing 20-1: Reporting the visibility state

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Page Visibility
API</title>

<script>

function initiate() {

document.addEventListener("visibilitychange", showstate);

}

function showstate() {

var element =
document.getElementById("application");

element.innerHTML +=
"<br>" + document.visibilityState;

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section
id="application">

<p>Move to another tab
or minimize this window to change the visibility state</p>

</section>

</body>

</html>

In the code in Listing 20-1, the showstate() function is set to handle the visibilitychange event. When the event is fired, the function shows
the value of the visibilityState property on the screen to inform the
current state of the document. When the tab is replaced with another tab or the
window is minimized, the value of the visibilityState property changes to hidden, and when the tab or the window is restored, the value is set back to visible.

Do It Yourself: Create a new HTML file with the document of Listing
20-1 and open the document in your browser. Open a new tab or move to another
tab already opened. Move back to the tab that contains the application. You
should see the words hidden and visible printed on the screen (the document was
hidden when you move to another tab and is visible again when you come back).

One of the reasons for the implementation of this
API was the high consumption of resources in modern applications, but the API
was also designed as a way to enhance the user experience. The next example
shows how to achieve the latter with just a few lines of code.

Listing 20-2: Responding to the visibility
state

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Page Visibility
API</title>

<script>

var video;

function initiate() {

video =
document.getElementById("media");

document.addEventListener("visibilitychange", showstate);

video.play();

}

function showstate() {

var state = document.visibilityState;

switch(state) {

case "visible":

video.play();

break;

case "hidden":

video.pause();

break;

}

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<video id="media"
width="720" height="400">

<source
src="trailer.mp4">

<source
src="trailer.ogg">

</video>

</body>

</html>

The code in Listing 20-2 plays a
video while the document is visible and pauses it when it is not. We use the
same functions from the previous example, except this time the value of the visibilityState property is checked by a switch instruction and the play() or pause() methods are executed to play or pause the video according
to the current state.

Do It Yourself: Update the code in your
HTML file with the document of Listing 20-2. Download the trailer.mp4 and
trailer.ogg files from our website. Open the new document in your browser and
alternate between tabs to see the application in action. The video should pause
when the document is not visible and resume when it is visible again.

Full Detection System

Browsers do not change the value of the visibilityState property when the user opens a new
window. The API is only capable of detecting the change in visibility when the
tab is hidden by another tab, or the window is minimized. To determine the
visibility state in any circumstance, we can complement the API with the
following events provided by the Window object.

blur—This event is fired when the window loses focus (it
is also fired by independent elements).

focus—This event is fired when the window regains focus (it
is also fired by independent elements).

With the addition of a small function, we can combine all
the tools available to build a better detection system.

Listing 20-3: Combining the blur, focus and visibilitychange events

<!DOCTYPE
html>

<html lang="en">

<head>

<meta
charset="utf-8">

<title>Page Visibility
API</title>

<script>

var state;

function initiate() {

window.addEventListener("blur", function() {

changestate("hidden");

});

window.addEventListener("focus", function() {

changestate("visible");

});

document.addEventListener("visibilitychange", function() {

changestate(document.visibilityState);

});

}

function
changestate(newstate) {

if (state != newstate) {

state = newstate;

showstate();

}

}

function showstate() {

var element =
document.getElementById("application");

element.innerHTML +=
"<br>" + state;

}

window.addEventListener("load", initiate);

</script>

</head>

<body>

<section
id="application">

<p>Move to another
window to change the visibility state</p>

</section>

</body>

</html>

In the initiate() function in Listing 20-3, listeners for
the three events are added. The changestate()
function was defined to respond to the events and process the value
corresponding to the current state. This value is determined by each event. The
blur event sends the value hidden; the focus event sends the value visible, and the visibilitychange event sends the current value of the visibilityState property. In consequence, the changestate() function receives the right value, no
matter how the document was hidden (by another tab, window or program). By
using the state variable, the function compares the previous value with the new one, stores the
new value in the variable and calls the showstate()
function to show the state on the screen.

This process is a little more complicated than
the previous one but considers every possible situation in which the document
may be hidden, and the state changed.

Do It Yourself: Update the code in your
HTML file with the document of Listing 20-3. Open the new document in your
browser, and alternate between the browser window and another window or
program. You should see the words hidden and visible printed on the screen
reflecting the change in states.

Chapter 21 - Ajax Level 2

21.1 The XMLHttpRequest Object

In the old web paradigm, websites
and web applications accessed the server and provided all the information at
once. If new information was required, the browser had to access the server
again and replace the old information with the new one. This prompted the use
of the word Pages to describe HTML documents. Documents were replaced by
other documents like the pages of a book.

This was the standard until
someone found a better use for an old object, first introduced by Microsoft and
later improved by Mozilla, called XMLHttpRequest. This object provides a way to
access the server and retrieve information from JavaScript code without refreshing
the document. In an article written in 2005, the name Ajax was coined
for this procedure.

Because of its relevance in
modern applications, browsers include the XMLHttpRequest Level 2 API for the
development of Ajax applications. The API incorporates features like
cross-origin communication and events to control the evolution of the request.
These improvements simplify the codes and provide new alternatives, such as
interacting with several servers from the same application or working with
small pieces of data instead of entire files.

The most important element of
this API is, of course, the XMLHttpRequest object. The following constructor is
included to create this object.

XMLHttpRequest()—This constructor returns an XMLHttpRequest object from which we can initiate a request and
listen to the events to control the communication process.

The object created by the XMLHttpRequest() constructor provides a few methods
to initiate and control the request.

open(method, url, async)—This method configures a pending request. The method
attribute specifies the HTTP method used to open the connection (GET or POST), the url attribute declares the
location of the code that is going to process the request, and the async
attribute is a Boolean value that declares the type of connection, synchronous
(false) or asynchronous (true). The method can also include values for
the user and password when necessary.

send(data)—This
method initiates the request. There are several versions of this method in an XMLHttpRequest object to process different kinds of data. The data attribute
may be omitted, declared as an ArrayBuffer, a blob, a document, a string, or a FormData
object, as we will see later.

abort()—This method cancels the request.

The following example gets a
text file from the server using the GET method.

Listing 21-1: Creating a document to process
Ajax requests

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Ajax Level 2</title>

<link rel="stylesheet" href="ajax.css">

<script src="ajax.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<button type="button" id="button">Press
Here</button>

</form>

</section>

<section id="databox"></section>

</body>

</html>

The following are the styles we
need to differentiate the form from the box where we are going to print the
information retrieved from the server.

Listing 21-2: Defining the styles to shape
the boxes on the screen

#formbox {

float: left;

padding: 20px;

border: 1px solid #999999;

}

#databox {

float: left;

width: 500px;

margin-left: 20px;

padding: 20px;

border: 1px solid #999999;

}

Do It Yourself: Create a new HTML file with the
document of Listing 21-1, a CSS file called ajax.css with the rules in Listing 21-2, and
a JavaScript file called ajax.js for the codes introduced below. To test the
examples in this chapter, you will have to upload all the files, including the JavaScript
file and the file to which the request is made, to your server or local server.

The code of our first example
reads a file on the server and shows its content on the screen. No data is sent
to the server; we just perform a GET request and show the information
retrieved.

Listing 21-3: Reading a file

var databox;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("button");

button.addEventListener("click", read);

}

function read() {

var url = "textfile.txt";

var
request = new XMLHttpRequest();

request.addEventListener("load", show);

request.open("GET", url, true);

request.send(null);

}

function show(event) {

var data = event.target;

if (data.status == 200) {

databox.innerHTML = data.responseText;

}

}

window.addEventListener("load", initiate);

In this example, the initiate() function creates a reference to the
databox and adds a listener for the click event to the button. When the
button is pressed, the read() function is executed. In this
function, the URL of the file to be read is declared, and the object is created
with the XMLHttpRequest() constructor and assigned to the request variable. This variable is used to
add an event listener for the load event to this object, and call its open() and send() methods to configure and start the
request. Since no data will be sent in this request, the send() method is empty (null), but the open() method needs its attributes to
configure the request; we have to declare the request as GET, specify the URL of the file to be
read, and set the type of operation (true for asynchronous).

When we use an asynchronous
operation, the browser continues processing the rest of the code while the file
is being downloaded. The end of the operation is informed through the load event. When the file has been loaded,
this event is fired, and the show() function is called. This function
checks the status of the operation through the value of the status property and then inserts the value
of the responseText property into the databox to show the content of the file on
the screen.

Do It Yourself: To test this example, download the
textfile.txt from our website or provide your own. Upload this file and the
rest of the files created with the codes in Listings 21-1, 21-2 and 21-3 to
your server or local server, and open the document in your browser. After
clicking the button, the content of the text file is shown on the screen.

The Basics: Servers return information to report the status of the
request. This information is called HTTP status code and includes a
number and a message describing the status. The load event sends a ProgressEvent object to the function to report
the progress and the state of the request. This object includes two properties
to get the status of the request: status and statusText. The status property returns the number of the
status, and the statusText property returns the message. The
value 200, used in the example of Listing 21-3, is one of several available. It
indicates that the request was successful (the message for this status is
"OK"). If the resource is not found, the value returned is 404. For a
full list of HTTP status codes, visit our website and follow the links for this
chapter.

Properties

The XMLHttpRequest object includes some properties to
configure the request. The following are the most frequently used.

responseType—This property sets the format of the data received.
It may take the values text, arraybuffer, blob, document, or json.

timeout—This property determines the maximum time allowed for the request to
be processed. It takes a value in milliseconds.

There are also three different
types of properties we can use to get the information returned by a request.

response—This is a general-purpose property. It returns the response to the request
in the format specified by the value of the responseType property.

responseText—This property returns the response to the request as
text.

responseXML—This property returns the response to the request as
XML data.

The most useful of these properties is response. This property returns the data in the format declared
previously by the responseType property. The following example shows how
to retrieve an image from the server using these properties.

Listing 21-4: Reading an image from the
server

var databox;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("button");

button.addEventListener("click", read);

}

function read() {

var url = "myimage.jpg";

var request = new XMLHttpRequest();

request.responseType = "blob";

request.addEventListener("load", show);

request.open("GET", url, true);

request.send(null);

}

function show(event) {

var data = event.target;

if
(data.status == 200) {

var
image = URL.createObjectURL(data.response);

databox.innerHTML = '<img src="' + image + '">';

}

}

window.addEventListener("load", initiate);

In Listing 21-4, the responseType property of the request is set to blob. Now, the data received will be processed as a blob that we can use to
store in a file, to slice, to upload again to the server, etc. In this example,
we use it as the source for an <img> element to show the image on the screen.
To turn the blob into a URL that we can use as the source of the image, we
apply the createObjectURL() method introduced in Chapter 16.

Events

In addition to load, the API includes the following events for the XMLHttpRequest object.

loadstart—This event is fired when the request starts.

progress—This event is fired periodically while sending or loading data.

abort—This event is fired when the request is aborted.

error—This event is fired when an error occurs during the request.

load—This
event is fired when the request has been completed.

timeout—If a timeout value has been specified, this event will be fired
when the request cannot be completed in the specified period of time.

loadend—This event is fired when the request has been completed (either in
success or failure).

The most useful event of all is
progress. This event is fired approximately
every 50 milliseconds to inform the code about the state of the request. By
taking advantage of the progress event, we can notify the user about
every step of the process and create professional communication applications.

Listing 21-5: Displaying the progress of the
request

var databox;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("button");

button.addEventListener("click", read);

}

function read() {

var url = "trailer.ogg";

var request = new XMLHttpRequest();

request.addEventListener("loadstart", start);

request.addEventListener("progress", status);

request.addEventListener("load", show);

request.open("GET", url, true);

request.send(null);

}

function start() {

var progress = document.createElement("progress");

progress.value = 0;

progress.max = 100;

progress.innerHTML = "0%";

databox.appendChild(progress);

}

function status(event) {

if (event.lengthComputable) {

var per = Math.ceil(event.loaded / event.total * 100);

var progressbar = databox.querySelector("progress");

progressbar.value = per;

progressbar.innerHTML = per + '%';

} else {

console.log("The total size can't be calculated");

}

}

function show(event) {

var data = event.target;

if (data.status == 200) {

databox.innerHTML = "Done";

}

}

window.addEventListener("load", initiate);

In Listing 21-5, the code
listens to three events, loadstart, progress and load, to control the request. The loadstart event calls the start() function to show the progress bar
on the screen for the first time. While the file is being downloaded, the progress event executes the status() function. This function calculates
the progress from the values returned by the properties of the ProgressEvent object (introduced in Chapter 16).
If the lengthComputable property returns true, which means that the system was
able to determine the values, we calculate the percentage of progress with the
formula event.loaded / event.total * 100.

Finally, when the file has been
downloaded, the load event is fired, and the show() function shows the string
"Done" on the screen.

Do It Yourself: To see the progress bar working,
you may have to use large files. In the code in Listing 21-5, we load the video
trailer.ogg, used in previous chapters, but this file might load too fast for
the progress bar to be able to report the progress. Also, some servers do not
report the size of the file. In cases like this, the lengthComputable property returns false. In our example, we print a message
on the console when this happens, but you should provide a better response that
lets the user know what is going on.

Sending Data

The same way we can receive data
from the server using Ajax, we can also send it. Sending data with a GET method is as simple as adding the
values to the URL. We have to insert the values in the URL, as explained in
Chapter 2, and the values are sent along with the request (e.g., myfile.php?var1=25&var2=46).
But the GET
method presents limitations, especially on the maximum size allowed for the
data. A better alternative is to use the POST method. Unlike a request performed
with the GET
method, a POST
request includes the message's body that allows us to send any type of
information to the server of any length we need.

An HTML form is usually the
best way to provide this information, but for dynamic applications, this is
probably not the best option or the most appropriate. The API includes the FormData object to solve this issue. This
object creates a virtual form that we can fill with the values we want to send
to the server. The API includes a constructor to get this object and a method
to add data to it.

FormData(form)—This
constructor returns a FormData object. The form attribute is a reference to a
form and provides an easy way to include an entire HTML form into the object
(optional).

append(name, value)—This
method adds data to a FormData object. It takes a keyword/value pair as attributes.
The name attribute is the name we want to use to identify the value, and
the value attribute is the value itself (it can be either a string or a
blob). The data returned by this method represents a form field.

The following example creates a small
form with two text fields called name and lastname. The form is sent to the server,
processed, and the response is shown on the screen.

Listing 21-6: Sending a virtual form to the
server

var databox;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("button");

button.addEventListener("click", send);

}

function send() {

var data
= new FormData();

data.append("name", "John");

data.append("lastname", "Doe");

var url = "process.php";

var request = new XMLHttpRequest();

request.addEventListener("load", show);

request.open("POST", url, true);

request.send(data);

}

function show(event) {

var data = event.target;

if (data.status == 200) {

databox.innerHTML = data.responseText;

}

}

window.addEventListener("load", initiate);

When information is sent to a
server, it is with the purpose of processing it and producing a result.
Usually, this result is saved on the server, and some information is returned
to provide feedback. In Listing 21-6, we send the data to the process.php
file and show the information returned by the code in this file on the screen.
Files with the php extension contain PHP code, which is code that is executed
on the server. This book does not cover how to program an application that runs
on the server, but for the purpose of this example, we are going to create a
simple code that returns to the browser a document that contains the values
sent by the application.

Listing 21-7: Responding to a POST request (process.php)

<?php

print('Your name is: '.$_POST['name'].'<br>');

print('Your last name is: '.$_POST['lastname']);

?>

Let’s see first how the
information to be sent is prepared. In the send() function in Listing 21-6, the FormData() constructor is invoked, and the FormData object returned is stored in the data variable. Two keyword/value pairs
are added to this object with the names name and lastname using the append() method.

The initialization of the
request is exactly the same as in previous examples except this time the first
attribute of the open() method is POST instead of GET, and the attribute of the send() method is the FormData object we have just created.

When the button in the document
is pressed, the send() function is called, and the form
created by the FormData object is sent to the server. The process.php
file on the server reads the form's fields (name and lastname) and returns a document to the
browser that contains their values. When this information is received by our JavaScript
code, the show()
function is executed, and the content of that document received from the server
is shown on the screen.

Figure 21-1: Response from the server
received by the application

Do It Yourself: This example requires several files
to be uploaded to a server or a local server. We are going to use the same HTML
document and CSS styles of Listings 21-1 and 21-2. The JavaScript code in
Listing 21-6 replaces the previous one. You also have to create a new file
called process.php with the code in Listing 21-7 to
respond to the request. Upload all these files to your server, and open the
HTML document in your browser. After clicking the Press Here button, you should
see the text returned by the process.php file on the screen, as illustrated in
Figure 21-1.

Uploading Files

Uploading files to a server is one
of the major concerns for web developers nowadays. It is a feature required by
almost every application online but had not been considered by browsers before.
This API takes care of the situation by incorporating a new property that
returns an XMLHttpRequestUpload object. This object provides all
the properties, methods and events available in an XMLHttpRequest object, but it was designed to
control the process of uploading a file.

upload—This property returns an XMLHttpRequestUpload object. The property must be accessed from an
existing XMLHttpRequest object.

To demonstrate how to work with
this object, we are going to create a new HTML document with an <input> field to select the file to be
uploaded.

Listing 21-8: Creating a document to upload
files

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Ajax Level 2</title>

<link rel="stylesheet" href="ajax.css">

<script src="ajax.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<label for="myfiles">File to Upload:
</label>

<input type="file" name="myfiles"
id="myfiles">

</form>

</section>

<section id="databox"></section>

</body>

</html>

When we want to send a file to
a server, we have to send the File object that represents the file, so
we can use a FormData object for this purpose. The system
automatically detects the type of information added to the FormData object and creates the proper
headers for the request. The rest of the process is exactly the same as studied
before in this chapter.

Listing 21-9: Uploading a file with FormData()

var databox;

function initiate() {

databox = document.getElementById("databox");

var myfiles = document.getElementById("myfiles");

myfiles.addEventListener("change", upload);

}

function upload(event) {

var files = event.target.files;

var myfile = files[0];

var data = new FormData();

data.append("file", myfile);

var url = "process.php";

var request = new XMLHttpRequest();

request.addEventListener("loadstart", start);

request.addEventListener("load", show);

var
xmlupload = request.upload;

xmlupload.addEventListener("progress", status);

request.open("POST", url, true);

request.send(data);

}

function start() {

var progress = document.createElement("progress");

progress.value = 0;

progress.max = 100;

progress.innerHTML = "0%";

databox.appendChild(progress);

}

function status(event) {

if (event.lengthComputable) {

var per = parseInt(event.loaded / event.total * 100);

var progressbar = databox.querySelector("progress");

progressbar.value = per;

progressbar.innerHTML = per + '%';

} else {

console.log("The total size can't be calculated");

}

}

function show(event) {

var data = event.target;

if (data.status == 200) {

databox.innerHTML = "Done";

}

}

window.addEventListener("load", initiate);

The main function in the code in
Listing 21-9 is upload(). This function is called when the
user selects a new file from the <input> element in the document (when the change event is fired). The file selected
is received and stored in the myfile variable, exactly as we did in the
File API in Chapter 16, as well as in the Drag and Drop API in Chapter 17. Once
we have a reference to the file, the FormData object is created, and the file is
added to the object by the append() method. To send this form, we initiate
a POST request and set all the event listeners
for the request except progress. The progress event is used to check the upload
process, so we first have to read the upload property to get the necessary XMLHttpRequestUpload object. After this object is
obtained, we can add the listener for the progress event and send the request.

The rest of the code shows a
progress bar on the screen when the process starts and updates this bar
according to the progress of the process.

Do It Yourself: Create a new HTML file with the
document of Listing 21-8 and a JavaScript file called ajax.js with the code in
Listing 21-9. You will also need a CSS file called ajax.css with the styles of Listing
21-2. Upload
the files to your server or a local server. Open the document in your browser
and press the button to select a file. Load a large file to be able to see the
progress bar working.

IMPORTANT: Some servers establish a limit on the size of the files
to be uploaded. By default, in most cases, this size is just 2 Megabytes. Files
larger than that will be rejected. You can modify this behavior from the
server’s configuration files (e.g., php.ini).

Real-Life Application

Uploading one file at a time is
probably not what most web developers have in mind, nor is using the <input> element to select the files to be
uploaded. Usually, every programmer wants their applications to be as intuitive
as possible, and what better way to do this than combining techniques and
methods the users are already familiar with. By taking advantage of the Drag
and Drop API, we are going to create an application to upload several files to
the server at the same time by dropping them into an area on the screen. The
following is the document with the drop box.

Listing 21-10: Defining the dropping area to
upload files

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Ajax Level 2</title>

<link rel="stylesheet" href="ajax.css">

<script src="ajax.js"></script>

</head>

<body>

<section id="databox">

<p>Drop Files Here</p>

</section>

</body>

</html>

The JavaScript code for this
example combines two APIs and organizes the code with anonymous functions. We
have to take the files dropped into the databox element and list them on the
screen, prepare the form with the file to be sent, make an upload request for
every file, and update each progress bar while the files are being uploaded.

Listing 21-11: Uploading files one by one

var databox;

function initiate() {

databox = document.getElementById("databox");

databox.addEventListener("dragenter", function(event) {

event.preventDefault();

});

databox.addEventListener("dragover", function(event) {

event.preventDefault();

});

databox.addEventListener("drop", dropped);

}

function dropped(event) {

event.preventDefault();

var files = event.dataTransfer.files;

if (files.length) {

var list = "";

for (var f = 0; f < files.length; f++) {

var file = files[f];

list += "<div>File: " + file.name;

list += '<br><span><progress value="0"
max="100">0%</progress></span>';

list += "</div>";

}

databox.innerHTML = list;

var count = 0;

var upload = function() {

var myfile = files[count];

var data = new FormData();

data.append("file", myfile);

var url = "process.php";

var request = new XMLHttpRequest();

var xmlupload = request.upload;

xmlupload.addEventListener("progress", function(event) {

if(event.lengthComputable){

var child = count + 1;

var per = parseInt(event.loaded / event.total * 100);

var progressbar =
databox.querySelector("div:nth-child(" + child + ") > span
> progress");

progressbar.value = per;

progressbar.innerHTML = per + "%";

}

});

request.addEventListener("load", function() {

var child = count + 1;

var elem = databox.querySelector("div:nth-child(" +
child + ") > span");

elem.innerHTML = "Done!";

count++;

if (count < files.length) {

upload();

}

});

request.open("POST", url, true);

request.send(data);

};

upload();

}

}

window.addEventListener("load", initiate);

The code in Listing 21-11 is
not easy to follow, but it will be easier if we go step by step. As usual,
everything starts from our initiate() function called as soon as the
document has been loaded. This function creates a reference to the databox that will be our drop box for this
example and adds listeners for three events to control the drag and drop
operation (see the Drag and Drop API in Chapter 17). The dragenter event is fired when the files that
are being dragged enter the area of the drop box, the dragover event is fired periodically while
the files are over the drop box, and the drop event is fired when the files are
dropped into the drop box. We do not have to do anything for the dragenter and dragover event in this example, so these events
are just canceled to prevent the browser’s default behavior. The only event we respond
to is drop.
The dropped()
function, set as the listener for this event, is executed every time something
is dropped into the databox.

The first line in the dropped() function also uses the
preventDefault() method
to do what we want with the files and not what the browser would do by default.
Now that we have absolute control over the situation, it is time to process the
files. First, we get the files from the dataTransfer object. The value returned is an
array of File
objects that we store in the files variable. To ensure that only files
and not another type of elements have been dropped into our drop box, we check
the value of the length property. If this value is
different than 0 or null, it means that one or more files
have been dropped, and we can proceed.

Now it is time to work with the
files we have received. With a for loop, we navigate through the files array and create a list of <div> elements containing the file’s name
and a progress bar within <span> elements. Once the list is finished,
the result is inserted in the databox element to be shown on the screen

It looks like the dropped() function does all the work, but
inside this function, we create another function called upload() to handle the upload process for
each file. Inside this function, we select a file from the array using the count variable as an index. This variable
is initialized to 0, so the first time the upload() function is called, the first file
on the list is selected and uploaded.

Every file is uploaded using the
same method from previous examples. A reference to the file is stored in the myfile variable, a FormData object is created using the FormData() constructor, and the file is added to
the object with the append() method.

This time, we only listen to
two events to control the process: progress and load. Every time the progress event is fired, an anonymous
function is called to update the status of the progress bar of the file that is
being uploaded. The <progress> element that corresponds to the
file is identified with the querySelector() method and the :nth-child() pseudo-class. The index for the
pseudo-class is calculated using the value of the count variable. This variable has the
number of the index of the files array, but this index starts at 0, and the index used for the list of
children accessed by :nth-child() starts at the value 1. To get the corresponding index
value to find the right <progress> element, we add 1 to the value of count, store the result in the child variable, and use this variable as
index.

Every time the previous process
finishes, we have to inform the situation and move to the next file in the files array. For this purpose, in the
anonymous function executed when the load event is fired, we increase the
value of count
by 1, replace the <progress> element with the string
"Done!", and call the upload() function again in case there are
more files to be processed.

Notice that the upload() function is called for the first
time at the end of the dropped() function. Because the value of count was previously initialized to 0, the first file to be processed is
the first one in the files array. When the upload process of
this file is completed, the load event is fired, and the anonymous
function that is called to handle this event increases the value of count by 1 and executes the upload() function again to process the next
file in the array. In the end, every file dropped in the drop box is uploaded
to the server, one by one.

Chapter 22 - Web Messaging API

22.1 Cross-Document Messaging

The Web Messaging API allows
applications from different origins to communicate with each other. The process
is called Cross-Document Messaging. Applications running in different
frames, tabs or windows can now communicate using this technology.

Posting a Message

The procedure is simple: we post a message from one document and read
it from the target document. The API provides the following method to post
messages.

postMessage(message, target)—This method sends a message to another document. The message
attribute is a string representing the message to be transmitted, and the target
attribute is the domain of the target document (host name or a port, as we will
see later). The target may be declared as a specific domain, as any document
with the * character, or as the same as the origin using the /
character. The method can also include an array of ports as a third attribute.

The API includes the following
event to listen to the messages coming from other documents.

message—This event is fired when a message is received.

The message event sends an object of type MessageEvent to the listener, which includes a
few properties to return the information about the message.

data—This
property returns the content of the message.

origin—This property returns the host name of the document that sent the
message. This value can be used to send a message back.

source—This property returns an object to identify the source of the message.
This value can be used as a reference of the sender to answer the message, as we
will see later.

To provide an example of this
API, we have to consider that the communication process occurs between
different windows (windows, frames or tabs), so we must create documents for
each side. Our example includes an HTML document with an iframe and the proper JavaScript
codes for the main document and the document to be loaded inside the iframe. The
following is the HTML code for the main document.

Listing 22-1: Including an iframe inside a
document for testing the Web Messaging API

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Cross Document Messaging</title>

<link rel="stylesheet" href="messaging.css">

<script src="messaging.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<label for="name">Your name: </label>

<input type="text" name="name"
id="name" required>

<button type="button"
id="button">Send</button>

</form>

</section>

<section id="databox">

<iframe id="iframe" src="iframe.html"
width="500" height="350"></iframe>

</section>

</body>

</html>

In this example, there are two <section> elements as in previous documents,
but this time the databox element includes an <iframe> element that is loading the iframe.html
file.

The Basics: The <iframe> element allows us to insert a
document inside another document. The document for the <iframe> is declared in the element's src attribute. Anything inside an
iframe responds as if it were located in its own window.

The following are the styles
required by our document to create the boxes on the screen.

Listing 22-2: Defining the boxes (messaging.css)

#formbox {

float: left;

padding: 20px;

border: 1px solid #999999;

}

#databox {

float: left;

width: 500px;

margin-left: 20px;

padding: 20px;

border: 1px solid #999999;

}

The JavaScript code for the
main document has to take the value of the name input from the form and send it to
the document inside the iframe using the postMessage() method.

Listing 22-3: Sending a message to the
iframe (messaging.js)

function initiate() {

var button = document.getElementById("button");

button.addEventListener("click", send);

}

function send() {

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

var iframe = document.getElementById("iframe");

iframe.contentWindow.postMessage(name, "*");

}

window.addEventListener("load", initiate);

In the code in Listing 22-3,
the message is composed of the value of the name input. The * character is used as a target to
send this message to every document running inside the iframe, regardless of
its origin.

The Basics: The postMessage() method belongs to the Window object. To get the Window object of an iframe from the main
document, we have to use the contentWindow property.

When the Send button is clicked
in the main document, the send() function is called, and the value
of the input field is sent to the content of the iframe. Now it is time to take
this message from the iframe and process it. We are going to create a short
HTML document for the iframe to show this information on the screen.

Listing 22-4: Creating a document for the
iframe (iframe.html)

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>iframe Window</title>

<script src="iframe.js"></script>

</head>

<body>

<section>

<div><b>Message from main window:</b></div>

<div id="databox"></div>

</section>

</body>

</html>

This document has its own databox that we can use to show the message
on the screen and the following JavaScript code to process the message.

Listing 22-5: Processing messages in the
target (iframe.js)

function initiate() {

window.addEventListener("message", receiver);

}

function receiver(event) {

var databox = document.getElementById("databox");

databox.innerHTML = "Message from: " + event.origin +
"<br>";

databox.innerHTML += "message: " + event.data;

}

window.addEventListener("load", initiate);

As we explained before, to
listen to messages, the API provides the message event and the properties of the MessageEvent object. In the code in Listing
22-5, the receiver()
function is set as the function to be called when this event is fired. This function
shows the content of the message using the data property and information about the
document that sent the message using the value of the origin property.

Remember that this JavaScript
code belongs to the HTML document of the iframe, and not the main document of
Listing 22-1. These are two different documents with their own scope and JavaScript
codes; one is in the main browser window, and the other is inside the iframe.

Do It Yourself: There is a total of five files that
have to be created and uploaded to your server to test the example above.
First, create a new HTML file with the code in Listing 22-1 for the main
document. This document requires the messaging.css file with the
styles in Listing 22-2 and the messaging.js file with the JavaScript code in
Listing 22-3. The document of Listing 22-1 has an <iframe> element with the iframe.html
file as its source. You will need to create this file with the HTML code in
Listing 22-4 and the corresponding iframe.js file with the JavaScript code in
Listing 22-5. Upload all the files to your server or local server, open the
main document in your browser, and send your name to the iframe using the form.

Filters and Cross-Origin

So far, our code has not followed
what is considered best practice. The JavaScript code in the main document is
sending a message to the iframe but is not controlling which document is
allowed to read it (any document inside the iframe will be able to read the
message). On the other side, the code in the iframe is not controlling the
origin and is processing every message received. Both parts of the
communication process must be improved to prevent abuse.

In the next example, we are
going to correct this situation and show how to answer a message from the
target using another property of the MessageEvent object called source. The following is an update of the
main document.

Listing 22-6: Communicating with specific
origins/targets

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Cross Document Messaging</title>

<link rel="stylesheet" href="messaging.css">

<script src="messaging.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<label for="name">Your name: </label>

<input type="text" name="name"
id="name" required>

<button type="button"
id="button">Send</button>

</form>

</section>

<section id="databox">

<iframe id="iframe"
src="http://www.domain2.com/iframe.html" width="500"
height="350"></iframe>

</section>

</body>

</html>

In the document of Listing
22-6, we not only provide the URL of the source of the iframe as we did before
but also declare the entire path to be in a different location (www.domain2.com).
To prevent abuse, we have to declare these locations and determine who is able
to read a message and from where. The JavaScript code for the main document now
considers this situation.

Listing 22-7: Communicating with specific
origins (messaging.js)

function initiate() {

var button = document.getElementById("button");

button.addEventListener("click", send);

window.addEventListener("message", receiver);

}

function send() {

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

var iframe = document.getElementById("iframe");

iframe.contentWindow.postMessage(name, "http://www.domain2.com");

}

function receiver(event) {

if (event.origin == "http://www.domain2.com") {

document.getElementById("name").value = event.data;

}

}

window.addEventListener("load", initiate);

The postMessage() method in the send() function in Listing 22-7 now
declares the target for the message (www.domain2.com). Only documents inside
the iframe and from that origin will be able to read the message.

In the initiate() function, we also add a listener
for the message
event. The purpose of the receiver() function is to listen to the answer
sent by the document in the iframe (this will make sense later).

The JavaScript code for the
iframe has to process messages only from authorized origins and send a response.
The following implementation only accepts messages that come from
www.domain1.com.

Listing 22-8: Responding to the main
document (iframe.js)

function initiate() {

window.addEventListener("message", receiver);

}

function receiver(event) {

var databox = document.getElementById("databox");

if (event.origin == "http://www.domain1.com") {

databox.innerHTML = "Valid message: " + event.data;

event.source.postMessage("Message received", event.origin);

}else{

databox.innerHTML = "Invalid origin";

}

}

window.addEventListener("load", initiate);

The filter for the origin is as
simple as comparing the value of the origin property with the domain we want to
read messages from. Once the origin proves to be valid, the message is shown on
the screen, and then an answer is sent back using the value of the source property. The origin property is also used to declare
this answer, which is only available for the window that has sent the message
(see how the receiver() function processes this answer in
Listing 22-7).

Do It Yourself: This example is a bit complicated.
We are using two different origins, so you need two separate domains (or
subdomains) to test the codes. Replace the domains in the codes with yours,
then upload the codes for the main document to one domain and the codes for the
iframe to the other, and you will be able to see how these two documents from
different domains communicate with each other.

Chapter 23 - WebSocket API

23.1 Web Sockets

The WebSocket API provides support
for faster and more efficient bi-directional communication between browsers and
servers. The connection is established through a TCP socket without sending
HTTP headers, thus reducing the size of data transmitted in every call. The
connection is also persistent, allowing servers to keep clients updated without
a previous request, which means we do not have to call the server for updates.
Instead, the server itself automatically sends us information about the current
situation.

WebSocket may look like an
improved version of Ajax, but it is an entirely different way of communication
that allows the construction of real-time applications in a scalable platform,
such as multiplayer video games, chat rooms, etc.

The API is simple; a few
methods and events are included to open and close the connection and send and
listen to messages. However, no server is configured to provide this service,
and the response has to be customized to our needs, so we have to install our
own WS server (WebSocket server) to establish the communication between the
browser and the server that allocates our application.

WebSocket Server

Although we can build our own WS
server, there are several programs available to configure a server and have it
ready to process requests. Depending on our preferences, we can opt for codes
written in PHP, Java, Ruby or other languages. For the purpose of this chapter,
we are going to use a PHP server. There are several versions available in this
language, but the easier to install and configured is a server called phpws, developed by Chris
Tanaskoski.

IMPORTANT: The phpws server requires at least PHP version 5.3 to
run correctly, and your hosting account has to include shell access to be able
to communicate with your server and run the PHP code (you can ask your hosting
provider to enable shell access if you do not have it by default).

The phpws server includes
several files to create the classes and methods we need to run the server. The
library is available at https://github.com/Devristo/phpws/, but for testing purposes, we have
included a package on our website already configured to install the WS server.
The package includes a file called demo.php that contains a function called onMessage() where all the messages received by
the server are processed. If we want to provide our own response, we have to
modify this function. The following is a version of the function we have
developed for the examples of this chapter.

Listing 23-1: Adapting the onMessage() function to our application (demo.php)

public function onMessage(IWebSocketConnection $user,
IWebSocketMessage $msg){

$msg = trim($msg->getData());

switch($msg){

case 'hello':

$msgback = WebSocketMessage::create("hello human");

$user->sendMessage($msgback);

break;

case 'name':

$msgback = WebSocketMessage::create("I don't have a
name");

$user->sendMessage($msgback);

break;

case 'age':

$msgback = WebSocketMessage::create("I'm old");

$user->sendMessage($msgback);

break;

case 'date':

$msgback = WebSocketMessage::create("today is
".date("F j, Y"));

$user->sendMessage($msgback);

break;

case 'time':

$msgback = WebSocketMessage::create("the time is
".date("H:iA"));

$user->sendMessage($msgback);

break;

case 'thanks':

$msgback = WebSocketMessage::create("you're welcome");

$user->sendMessage($msgback);

break;

case 'bye':

$msgback = WebSocketMessage::create("have a nice day");

$user->sendMessage($msgback);

break;

default:

$msgback = WebSocketMessage::create("I don't
understand");

$user->sendMessage($msgback);

break;

}

}

Once we have all these files on
our server, it is time to run the WS server. WebSocket uses a persistent
connection, so the WS server has to be running all the time, receiving and
sending messages to users. To execute the PHP file, we have to access our
server using an SSH connection, find the folder where the demo.php file is located
and insert the command php
demo.php.

Do It Yourself: Download the ws.zip
file from our website, decompress
it, and upload the ws folder and all of its
files to your server. Inside this folder, you will find the demo.php file with the onMessage() function already updated with the code of Listing 23-1. Connect to your server
with SSH using your favorite program (Terminal, Putty, etc.), find the ws
folder, and execute the command php
demo.php
to run the WS server.

IMPORTANT: You can also use a public WS server, such as
ws://echo.websocket.org/ (more information at http://websocket.org/echo.html). Usually, these servers do not
include any built-in commands and return the message received to the same user,
but are useful for testing purposes.

The
Basics: SSH is a
network protocol (Secure Shell) that you can use to access your server and
control it remotely. It allows you to work with folders and files on your
server and run programs. The most popular applications that provide a console
for Shell access are Terminal for Apple and PuTTY for Windows (available
for free at www.chiark.greenend.org.uk/~sgtatham/putty/).

Connecting to the Server

With the server running, we now have
to program the JavaScript code that will connect to it. For this purpose, the
API offers an object called WebSocket with a few properties, methods, and
events to set up the connection. The following is the object's constructor.

WebSocket(url)—This constructor initiates a connection between the
application and the WS server targeted by the url attribute. It returns
a WebSocket object referencing the connection. A second attribute
may be specified to provide an array with communication sub-protocols.

The connection is initiated by
the constructor, so there are only two methods to work with it.

send(data)—This
method sends a message to the WS server. The data attribute is a string
with the information to be transmitted.

close()—This method closes the connection.

A few properties inform about
the connection’s configuration and status.

url—This
property returns the URL of the file to which the application is connected.

protocol—This property returns the sub-protocol used in the connection.

readyState—This property returns a number representing the state
of the connection: 0 meaning the connection has not yet been established, 1
meaning the connection is opened, 2 meaning the connection is being closed, and
3 meaning the connection was closed.

bufferedAmount—This property reports the amount of data requested by
the connection but not yet sent to the server. The value returned helps us
regulate the amount of data and frequency of the requests in order not to
saturate the server.

The API provides the following
events to know the status of the connection and listen to messages sent by the
server.

open—This
event is fired when the connection is opened.

message—This event is fired when there is a message from the server available.

error—This event is fired when an error occurs.

close—This event is fired when the connection is closed.

The demo.php
file we prepared for these examples has a method called onMessage() that processes a small list of
predefined commands and sends back the proper answer (see Listing
23-1). For
testing purposes, we are going to use a form to insert and send these commands
to the server.

Listing 23-2: Creating the document to
insert commands

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>WebSocket</title>

<link rel="stylesheet" href="websocket.css">

<script src="websocket.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<label for="command">Command:
</label><br>

<input type="text" name="command"
id="command"><br>

<button type="button"
id="button">Send</button>

</form>

</section>

<section id="databox"></section>

</body>

</html>

We will also need a CSS file
with the following styles to design the boxes on the page.

Listing 23-3: Defining the styles for the
boxes

#formbox {

float: left;

padding: 20px;

border: 1px solid #999999;

}

#databox {

float: left;

width: 500px;

height: 350px;

overflow: auto;

margin-left: 20px;

padding: 20px;

border: 1px solid #999999;

}

As always, the JavaScript code
is responsible for the entire process. The following example establishes a
simple communication with the server to test the API.

Listing 23-4: Sending messages to the server

var databox, socket;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("button");

button.addEventListener("click", send);

socket =
new WebSocket("ws://YOUR_IP_ADDRESS:12345/ws/demo.php");

socket.addEventListener("message", received);

}

function received(event) {

var list = databox.innerHTML;

databox.innerHTML = "Received: " + event.data +
"<br>" + list;

}

function send() {

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

socket.send(command);

}

window.addEventListener("load", initiate);

IMPORTANT: Replace YOUR_IP_ADDRESS with the IP of your server. You can
always use your domain instead, but using the IP eludes DNS translation. You
should always use this technique to access your application to avoid the time
spent by the network translating the domain into the corresponding IP address.
Your server has to have the port 12345 open to use the JavaScript codes and the
WS server provided for this chapter. If you are not sure how to open this port,
ask your hosting provider.

In the initiate() function in Listing 23-4, the WebSocket object is constructed and stored in
the socket
variable. The url attribute declared in the constructor points to the
location of the demo.php file in our server. This URL
includes the port of connection. Usually, the host is specified with the IP
number of the server, and the value of the port is 12345, but that depends on
our needs, the configuration of our server, the ports available, the location
of the file, etc.

After we get the WebSocket object, we add a listener for the message event. Every time the WS server
sends a message to the browser, the message event is fired, and the received() function is called to respond. As
in previous APIs, the object sent by this event to the function includes the data property that contains the content
of the message. In the received() function, we use that property to
show the message on the screen.

The send() function is included to send
messages to the server. The value of the <input> element is taken by this function
and sent to the WS server using the send() method.

Figure 23-1: Application talking with
the WS server

Do It Yourself: Create a new HTML file with the
document of Listing 23-2, a CSS file called websocket.css with the code in
Listing 23-3, and a JavaScript file called websocket.js with the code in
Listing 23-4. Open the document in your browser, insert the command hello
in the input field, and press the Send button. You should see a message on the
right box with the response from the server, as illustrated in Figure 23-1
(your WS server has to be installed and running, as explained in the previous
section of this chapter).

IMPORTANT: The onMessage() function we prepared for these
examples checks the message received and compares its value with a list of
predefined commands (see Listing 23-1). The commands available are hello, name, age, date, time, thanks and bye.

The last
example illustrates how the communication process works for this API. The
connection is started by the WebSocket() constructor, the send() method sends every message we want
to be processed to the server, and the message event informs the application of
the arrival of new messages from the server. However, we did not close the
connection, check for errors or even detect when the connection was ready to
work. The following example listens to all the events provided by the API to
inform the user about the status of the connection at every step of the
process.

Listing 23-5: Informing the user about the
state of the connection

var databox, socket;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("button");

button.addEventListener("click", send);

socket = new
WebSocket("ws://YOUR_IP_ADDRESS:12345/ws/demo.php");

socket.addEventListener("open", opened);

socket.addEventListener("message", received);

socket.addEventListener("close",
closed);

socket.addEventListener("error", showerror);

}

function opened() {

databox.innerHTML = "CONNECTION OPENED<br>";

databox.innerHTML += "Status: " + socket.readyState;

}

function received(event) {

var list = databox.innerHTML;

databox.innerHTML = "Received: " + event.data +
"<br>" + list;

}

function closed() {

var list = databox.innerHTML;

databox.innerHTML = "CONNECTION CLOSED<br>" + list;

var button = document.getElementById("button");

button.disabled = true;

}

function showerror() {

var list = databox.innerHTML;

databox.innerHTML = "ERROR<br>" + list;

}

function send() {

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

if (command == "close") {

socket.close();

}else{

socket.send(command);

}

}

window.addEventListener("load", initiate);

There are a few improvements in
the code in Listing 23-5 from the previous example. Listeners for all the
events available in the WebSocket object are added, and the proper
functions to handle the events are created. We also show the status when the
connection is opened using the value of the readyState property, close the connection
using the close()
method when the "close" command is sent from the form, and disable
the Send button when the connection is closed (button.disabled
= true).

Figure 23-2: Connection closed

Do It Yourself: This last example requires the HTML
document and CSS styles of Listings 23-2 and 23-3. Open the document in your
browser, insert a command in the form, and click the Send button. You should
get answers from the server according to the command inserted (hello, name, age, date, time, thanks or bye). Send the command
"close" to close the connection.

IMPORTANT: Remember that your WS server has to
be running all the time to process requests. As we have discussed, if you wish,
you may test these examples with a public WS server, such as ws://echo.websocket.org/.
This server sends back to the browser the same text inserted in the form.

Chapter 24 - WebRTC API

24.1 Web Paradigms

WebRTC means Web Real-Time Communication. It is not just about
communication but rather a whole new kind of communication for the Web. This
API lets developers create applications that connect users to each other
without intermediaries. Applications implementing WebRTC can stream video, audio,
and data directly from one user to another, creating an effective peer-to-peer
connection.

This is a significant shift in paradigms. In the current paradigm,
users are only able to share information on the Internet through a server.
Servers are huge repositories of content accessible by a domain or an IP
address. Users have to connect to these servers, download or upload
information, and wait for other users to do the same on the other side. If we
want to share a picture with a friend, for example, we have to upload the picture
to a server and wait for our friend to connect to the same server and download
the picture to his computer. The process is illustrated in Figure 24-1.

Figure 24-1: Current paradigm

There is no way for User 1 to send information to User 2 without
using a server. Every process requires a server to store the information sent
by one user and provide it (serve it) to another. Outside the Web, there are numerous
applications connecting users directly with each other, allowing them to send
instant messages or make video calls, but browsers were incapable of doing this
until they implemented the WebRTC API.

WebRTC brings a new paradigm to the Web. The API provides the
technology to turn peer-to-peer applications for the Web a reality. The process
uses a signaling server to establish the connection, but the information is
exchanged between browsers without further intervention, as shown in Figure
24-2.

Figure 24-2: New paradigm for the Web

Servers are still necessary, but they are not the content providers
anymore. The server now sends signals to start the connection, but the content
is transmitted directly from the computer of one user to another. With this
technology, video and audio streaming between users, video calling, instant
messaging and data sharing are features now available for applications running
in the browser.

ICE Servers

In the current paradigm, servers are publically accessible. They are
assigned an IP address and usually a domain is created to represent it. Servers
are not only easy to find, but they are accessible all the time, from anywhere.
If a server changes the IP, its domain is translated to the new IP almost
instantly (it takes a few hours for the new information to propagate through
the network, but the change is instantaneous). User’s computers, on the other
hand, cannot have a unique IP. They have private IPs that are later translated
into public ones by a system called NAT (Network Address Translator).
This system sets routes towards users’ computers that are usually complex to
follow and vary in each case. As if this were not complicated enough, user’s
computers are also hidden behind firewalls, have different ports through which
the data is transferred, or even become inaccessible without warning.
Connecting one user to another requires information that browsers are incapable
of providing.

Considering this situation, the WebRTC API was developed to work
with servers that check and return the information necessary for the
application to access the personal computers. These servers work under a
structure called ICE (Interactive Connectivity Establishment) to find
the best way to connect one user to another. ICE is the name of a process that
gets the information through different servers and systems. Figure 24-3 shows
how the final structure is set up.

Figure 24-3: ICE Servers

ICE servers are usually STUN and TURN servers. A STUN server discovers and returns the public
IP of the user’s computer as well as the configuration of the NAT for that
computer, while a TURN server provides a connection using an IP in the cloud
when the other options are not available. The systems at both ends of the
communication process decide which connection is more efficient and proceed
through that route.

The structure described in Figure 24-3 works as
follows: the browsers of User 1 and 2 access the ICE servers to get the
information that describes how each computer is seen on the Internet. Both
applications access the signaling server and send their descriptions to each
other. Once the information arrives and both sides agree on the route to follow,
the peer-to-peer connection is established and the users are connected without
requiring intervention from servers anymore (unless they are disconnected, and
the connection has to be established again).

Peer Connection

The first step we have to take to create the peer-to-peer connection is
to provide the browser with the information about the connection, the data to
be shared, and the ICE servers to use. This is done by the properties, methods,
and events provided by the RTCPeerConnection object. The API provides the following constructor to
get this object.

RTCPeerConnection(configuration)—This constructor returns an RTCPeerConnection object. The object is used to provide the browser with the information
necessary to establish a peer-to-peer connection. The configuration
attribute is an object that specifies the information to find the ICE servers.

Users have to have the possibility to start and end a peer-to-peer
connection. RTCPeerConnection objects include the following method to terminate the
connection.

close()—This method changes the state of the RTCPeerConnection object to closed and terminates any active stream and ICE processing,
closing the connection.

ICE Candidate

When the ICE process finds a way to
communicate with a computer, the information retrieved is called ICE
Candidate. Candidates are managed by an object of type RTCIceCandidate. The API offers the following
constructor to get these objects.

RTCIceCandidate(candidateInitDict)—This constructor returns an RTCIceCandidate object. The candidateInitDict attribute provides the information to initialize the
object (this is usually the information sent by the remote peer).

The following are the property and method
available in the API to control and add the candidate to the connection.

iceState—This property returns the state of the ICE process for the current
peer connection.

addIceCandidate(candidate)—This method adds a remote ICE Candidate to the peer
connection. The candidate attribute is the object returned by the RTCIceCandidate()
constructor.

Offer and Answer

The communication between peers is
started by two processes called offer and answer. When an
application wants to initiate a connection, it sends an offer to the
application on the other side through the signaling server. If this offer is
accepted, the second application sends back an answer. The following are the
methods provided by the API to generate offers and answers.

createOffer(success, error)—This method is used to create an offer. It generates a blob containing
the description of the local computer (called Session Description). The
description contains the media streams, codecs negotiated for the session, the
ICE candidates, and also a property describing its type (in this case offer).
The Session Description obtained by this method is sent to the function
specified by the success attribute to be processed. This function is
responsible for sending the offer to the remote peer.

createAnswer(success, error)—This method is used to create an answer. It generates a blob
containing the description of the local computer (called Session Description).
The description contains the media streams, codecs negotiated for the session,
the ICE candidates, and also a property describing its type (in this case answer).
The Session Description obtained by this method is sent to the function specified
by the success attribute to be processed. This function is
responsible for sending the answer to the remote peer.

Session Description

As we have discussed, the
description of each computer, including the media streams, codecs negotiated
for the session and the ICE candidates, is called Session Description.
Session Descriptions are sent from one user to another through the signaling server and then
provided to the peer connection using the following methods.

setLocalDescription(description, success, error)—This method provides the Session Description of the
local computer to the peer connection. The description attribute is the object returned by the createOffer()
and createAnswer() methods. The success and error attributes are functions to be called in case of success
or failure.

setRemoteDescription(description, success, error)—This method provides the Session Description of the
remote computer to the peer connection. The description attribute is an
object returned by the RTCSessionDescription()
constructor and the information received from the remote peer. The success
and error attributes are functions to be called in case of success or failure.

Media Streams

A peer-to-peer connection is created
to share media streams and data. The process to transmit data involves the
creation of data channels, as we will see later, but to add or remove media
streams to a peer connection, we have to apply the following methods.

addStream(stream)—This
method adds a media stream to the peer connection. The stream
attribute is a reference to the media stream (returned by methods such as getUserMedia(), for example).

removeStream(stream)—This
method removes a media stream from the peer connection. The stream
attribute is a reference to the media stream (returned by methods such as getUserMedia(), for example).

Events

The process of setting the
peer-to-peer connection and getting information from one computer or another is
asynchronous. Once the RTCPeerConnection object is created, we have to
listen to events and wait for results. The following is the list of the events
available.

negotiationneeded—This event is fired when a new negotiation is required
(e.g., a new offer has to be made).

icecandidate—This event is fired when a new ICE Candidate is
available.

statechange—This event is fired when the state of the peer
connection changes.

addstream—This event is fired when a media stream is added by the remote peer.

removestream—This event is fired when a media stream is removed by
the remote peer.

gatheringchange—This event is fired when the state of the ICE process
changes.

icechange—This event is fired when the state of the ICE changes (a new server
was set, for example).

datachannel—This event is fired when a new data channel is added
by the remote peer (we will study data channels later).

24.2 Configuration

As we explained in the introduction
of this chapter, the structure necessary to generate a peer-to-peer connection
on the Web involves not only a JavaScript application on each end but also servers
to coordinate the connection. The API leaves in the hands of developers how to
configure the process, the technologies to use, and the type of servers and
network to be included, so there is plenty to do besides programming the
application.

IMPORTANT: The examples of this chapter are
based on a procedure we consider appropriate for the circumstance, but how the
connection is performed, how the signaling is made and how the users are
identified may change completely in different contexts. You have to decide
whether is right for your application to implement this procedure or not.

Configuring the Signaling Server

A signaling server is not the same as a content server.
Signaling servers have to establish persistent connections to inform the
application when an offer or an answer is received. Figure 24-3 illustrates
this process. When User 1 wants to connect to User 2, the application on the User
1's computer has to send an offer to the signaling server requesting the
connection, and the server has to inform User 2 that a request was made. This
process is only possible if a persistent connection was previously set by the
server and both users. Therefore, a signaling server not only has the purpose
of receiving and sending signals (offers and answers) but also has to keep
users informed by establishing a persistent connection before the peer-to-peer
connection is configured (for example, a video call system would not be
possible if users were not notified of an incoming call).

The work of a signaling server does not stop
there. It also has to control who is authorized to create a connection and who
is able to connect with whom. WebRTC does not provide a standard method to do
this; it is all in the hands of the developer. The good news is that there are
several options available. We already studied how to create persistent
connections in Chapter 23 using WebSockets. The WebSocket API is one alternative
but not the only one. Google also offers the Google Channel API; an API
developed to create a persistent connection between applications and Google
servers. And open-source servers that implement a technology called SIP
(Session Initiation Protocol) are already available for free.

For our example, we decided to implement the same
WebSocket server used in Chapter 23. The onMessage() function of the demo.php file has to be modified to receive and
send signals back to the right connection.

Listing 24-1: Responding to offers and
answers from the WebSocket server (demo.php)

public function onMessage(IWebSocketConnection $user,
IWebSocketMessage $msg){

$thisuser = $user->getId();

$msg = trim($msg->getData());

$msgback = WebSocketMessage::create($msg);

foreach($this->server->getConnections() as $user){

if($user->getId() != $thisuser){

$user->sendMessage($msgback);

}

}

}

The server for our example is not controlling anything. It is just
taking the message received from one user and sends it to the other. It is not
useful for a professional application, but it is enough to test our small
application and understand how the process works.

Do It Yourself: As well as in Chapter 23, we
prepared a file with the WebSocket server ready to install and with the demo.php
file already adapted for the application. Go to our website, download the
package webrtc.zip and upload the ws
folder inside this package to your server. Access your server with SSH using a
program like PuTTY (available at www.chiark.greenend.org.uk/~sgtatham/putty/), navigate to the ws
folder, and type the command php
demo.php to run
the WebSocket server. For more information, see Chapter 23.

Configuring the ICE Servers

ICE servers are specified during the
construction of the peer connection. The API provides a property to declare an
array with the configuration of ICE servers for the RTCPeerConnection() constructor.

iceServers—This property is used to specify the STUN and TURN
servers available to be used by the ICE process.

The value of this
property is defined as an array of objects containing the following properties.

urls—This
property declares the URL of the STUN or TURN server.

credential—This property is used to define a credential for a
TURN server.

The syntax to
introduce this information is the following: {"iceServers":
[{"urls": "stun: stun.domain.com:12345"}]}, where stun.domain.com is the domain of a STUN server,
and 12345 is the port in which the server is available.

IMPORTANT: We have to provide our own ICE
servers to work with our application. The creation and configuration of STUN
and TURN servers is beyond the scope of this book. In the examples in this
chapter, we are going to work with a STUN server provided by Google for testing
purposes. For more information on ICE servers, visit our website and follow the
links for this chapter.

24.3 Implementing WebRTC

The most common use of a
peer-to-peer connection is to make video calls, so we are going to create an
application that connects two users to each other. The document for this
example has to include two <video> elements to show the video from the
local camera and the video from the remote camera (the person we call).

Listing 24-2: Creating a document to make
video calls

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>WebRTC API</title>

<style>

#caller, #receiver {

float: left;

margin: 5px;

padding: 5px;

border: 1px solid #000000;

}

</style>

<script src="webrtc.js"></script>

</head>

<body>

<section id="caller">

<video id="callermedia" width="150"
height="150"></video>

<br><input type="button"
id="callbutton" value="Call">

</section>

<section id="receiver">

<video id="receivermedia" width="500"
height="500"></video>

</section>

</body>

</html>

The JavaScript code has to take
the video and audio streams from the local camera and microphone, assign them
to the <video> element with the callermedia id, and send the stream to the
other user. When a media stream from the other end of the line arrives, the
code has to assign that remote stream to the second <video> element identified as receivermedia, to establish the communication.

To perform these tasks, the code must set the peer-to-peer
connection, communicate with ICE servers, get the Session Description, send the
offer and the answer, and add the local and remote media streams to the
connection. But we are going to do this step by step. First, we need to create
the persistent connection with the signaling server (in this case, a WebSocket
server) and then capture the video stream from the local webcam and the audio
stream from the local microphone.

Listing 24-3: Connecting with the WebSocket
server and getting the media streams

var user, socket;

function initiate() {

var callbutton = document.getElementById("callbutton");

callbutton.addEventListener("click", makecall);

socket =
new WebSocket("ws://YOUR_IP_ADDRESS:12345/ws/demo.php");

socket.addEventListener("message", received);

var
promise = navigator.mediaDevices.getUserMedia({video: true});

promise.then(setwebcam);

promise.catch(showerror);

}

In the initiate() function in Listing 24-3, we start
by declaring the makecall() function as the listener of the click event. Every time the Call button
is clicked, this function is executed to send an offer and initiate the call.
Next, the application creates a persistent connection with the WebSocket server
and adds a listener for the message event to listen to messages coming
from this server. This is useful on both sides of the line: the receiver will
know when it is being called, and the caller will be ready to listen to the
answer.

Finally, the media streams from
the local webcam and microphone are captured by the getUserMedia() method (see Chapter 9). In case of
success, we call the setwebcam() function, and in case an error
occurs, the showerror() function is executed (the access to
the webcam was denied, for example). The following are the implementation of
these functions.

Listing 24-4: Starting the peer-to-peer
connection

function setwebcam(stream) {

var video = document.getElementById("callermedia");

video.srcObject = stream;

video.play();

var
servers = {"iceServers": [{"urls": "stun: stun.l.google.com:19302"}]};

user =
new RTCPeerConnection(servers);

user.addStream(stream);

user.addEventListener("addstream", setremotestream);

user.addEventListener("icecandidate", setice);

}

function showerror() {

console.log("Error");

}

The showerror() function will be called by several
methods, so we only use it to show an error message on the console, but the setwebcam() function has more work to do. We
have to set the peer-to-peer connection and assign the media streams from the
webcam and the microphone to the <video> element. At the beginning of this
function, the video and audio streams captured by the getUserMedia() method are assigned to the
corresponding <video> element (the small video on the
left of the screen) and then played by the play() method. The peer-to-peer connection is
initiated next by declaring the ICE servers available and creating the RTCPeerConnection object with this information. A
STUN server provided by Google is set as our ICE server for this example.

The RTCPeerConnection object provides the browser with
the information necessary to establish the connection. Some of this information
is immediately available, such as the local media stream, but the rest of the
process is asynchronous, so we have to listen to events to capture that
information when it becomes available. For this reason, after adding the local
media stream to the object with the addStream() method, we add listeners to the addstream and icecandidate events. The addstream event will be fired when a media
stream is added by the remote peer, and the icecandidate event will be fired when an ICE
candidate is determined by the local peer.

For all this to happen, the
peer-to-peer connection has to be established in the first place. The following
is the function we use to create the offer and initiate the call.

Listing 24-5: Getting the Session
Description

function makecall() {

user.createOffer(setdescription, showerror);

}

When the video and audio from
the local webcam and microphone are already playing on the screen on both ends,
we are ready to make the call. This process begins when one of the users clicks
the Call button, and the makecall() function is executed. This function
generates the Session Description of type offer and calls the setdescription() function with this information if the
operation is successful. The setdescription() function receives the Session
Description, sets that information as the local description, and sends it to
the remote peer to initiate the connection.

Listing 24-6: Sending an offer to a remote
peer

function setdescription(sessionDescription) {

user.setLocalDescription(sessionDescription, function() {

sendmsg(sessionDescription);

});

}

The setLocalDescription() method is used in the function in
Listing 24-6 to provide the browser with the information describing the local
peer. In case of success, this information is sent by the sendmsg() function to the remote peer. This
is the function in charge of sending messages to the WebSocket server to
establish the connection.

Listing 24-7: Sending signals to the remote
peer

function sendmsg(message) {

var msg = JSON.stringify(message);

socket.send(msg);

}

Before sending JavaScript
objects to the server, we have to convert them to JSON strings. In the function
in Listing 24-7, we do this using the JSON.stringify() method.

The Basics: JSON (JavaScript Object
Notation) is a data format developed specifically to share data online. A JSON
value is text with a specific format that can be sent as a regular string but
transformed into useful objects by almost any programming language. JavaScript
provides two methods to work with JSON: JSON.stringify() to convert JavaScript
objects to JSON strings and JSON.parse() to convert
JSON strings to JavaScript objects.

The sendmsg() function is in charge of the
signaling process. Every time an offer is made, an answer is sent, or ICE
candidates are shared by peers, the information is transmitted to the WebSocket
server through messages sent by this function. The format of these messages and
how these signals are sent and processed is decided by the developer. For our
application, we are going to use the type property sent by Session
Descriptions to check the type of message received. The following is the
function that receives and processes the signals.

Listing 24-8: Processing the signals

function received(event) {

var msg = JSON.parse(event.data);

switch (msg.type) {

case
"offer":

user.setRemoteDescription(new RTCSessionDescription(msg),
function() {

user.createAnswer(setdescription, showerror);

});

break;

case
"answer":

user.setRemoteDescription(new RTCSessionDescription(msg));

break;

case
"candidate":

var candidate = new RTCIceCandidate(msg.candidate);

user.addIceCandidate(candidate);

}

}

The received() function in Listing 24-8 is called
every time the message event of the WebSocket object is fired. This means that every
time the WebSocket server sends a message to the application, this function is
executed. Here, we check for the value of the type property in every message and proceed
according to each case. If the value of the type property is "offer", it means that the application
received an offer from a caller. In this case, we have to set the Session
Description for the remote peer and, in case of success, send an answer with
the createAnswer() method. On the other hand, if the signal message is of
type "answer", which means that the call was
accepted, we set the description of the remote peer, and the connection is
established. The last case of the switch instruction checks when a signal
message is of type "candidate". In this case, the ICE candidate
sent by the remote peer is added to the RTCPeerConnection object by the addIceCandidate() method.

IMPORTANT: The procedure we have
described is the one we have decided to use for this example. The WebRTC API
does not define any standard procedure to process messages or even to send them
(the use of the WebSocket server was our choice, as well). You have to decide whether is right
for your application to implement this procedure or not.

When the offer is accepted, and
the answer received, both computers share the media streams and information
about the ICE servers to be used to establish the connection. This process
fires the addstream
and icecandidate events, executing the setremotestream() and the setice() functions at both ends.

Listing 24-9: Responding to the addstream and icecandidate events

function setremotestream(event) {

var video = document.getElementById("receivermedia");

video.srcObject = event.stream;

video.play();

}

function setice(event){

if (event.candidate) {

var message = {

type: "candidate",

candidate: event.candidate,

};

sendmsg(message);

}

}

As soon as the RTCPeerConnection object is created, the browser
starts negotiating with the ICE servers to get the information required to
access the local computer. When this information is finally retrieved, the
application is informed by the icecandidate event. In the setice() function in Listing 24-9, the
information provided by the event is used to generate a signal message of type
candidate and send it to the remote peer. Notice that this time we had to
explicitly declare the type property because the candidate object does not include it (This is
a procedure we have to follow to be consistent with the rest of the signaling
process we created before in the received() function).

After the peers agree on which
route to follow for the connection, the streams are shared. The addition of a
new media stream by one peer is informed to the other peer through the addstream event. When this event is fired,
the remote stream must be assigned to a <video> element, the same way we did before
for the local media stream coming from the webcam and the microphone. To do
this, the setremotestream() function in Listing 24-9 creates
the corresponding URL to point to the remote stream and assigns it as the
source of the <video> element identified with the name receivermedia. This is done at both ends of the
connection, so each user has their own image on the left side of the screen and
the image of the other person on the right.

The last thing we need to do
for our application to be ready is to listen to the load event and execute the initiate() function as soon as the document is
loaded.

Listing 24-10: Listening to the load event to start the application

window.addEventListener("load", initiate);

Do It Yourself: Create a new HTML file with the
document of Listing 24-2. Create a file called webrtc.js,
and copy every JavaScript code from Listing
24-3 to
Listing 24-10 into this file. Upload both files to your server, run the
WebSocket server as explained in Chapter 23, and open the document in your
browser. To connect with another person, you will have to open the document on
different computers, but they can be on the same network.

IMPORTANT: Remember to replace the YOUR_IP_ADDRESS
string with the IP of your server. For testing purposes, it is also possible to
use your domain, but in a professional application, it is better to use the IP
to avoid the time spent by the DNS translation process.

24.4 Data Channels

The most revolutionary feature of
the API is probably not the transmission of media streams but the transmission
of data. This is done through data channels. These channels are created over an
existing peer connection and allow users to share any kind of data, which we
can turn into files or content at the other end. The API includes the RTCDataChannel object to represent a data channel
and the following method to create it.

createDataChannel(label, configuration)—This method returns a RTCDataChannel object. The label attribute identifies the channel, and the configuration
attribute provides an object with configuration parameters. The remote peer is
informed of the creation of the channel by the datachannel
event mentioned before.

The RTCDataChannel object includes the
following properties, methods, and events to configure and work with data
channels.

label—This property returns the label assigned to the channel when it was
created.

reliable—This property returns true if the channel was set as reliable when it was
created, or false otherwise.

readyState—This property returns the state of the channel.

bufferedAmount—This property reports the data requested but not yet
sent to the remote peer. The value returned by this property may be used to
regulate the amount of data and frequency of the requests in order not to
saturate the connection.

binaryType—This property sets the format of the data. It can
take two values: blob or arraybuffer.

send(data)—This method sends the value of the data
attribute to the remote peer. The data must be specified as a string, a blob,
or an ArrayBuffer.

close()—This method closes the data channel.

message—This
event is fired when a new message is received through a data channel (new data
was sent by the remote peer).

open—This
event is fired when the channel is opened.

close—This event is fired when the channel is closed.

error—This event is fired when an error occurs.

To demonstrate how data channels work, we are going to add a chat
room below the videos from the previous example so users can send messages
while they talk. The new document includes two small boxes at the top for the
videos, an input field and a button to write and send the messages, and a box
below to show the conversation.

Listing 24-11: Creating a document to test data channels

<!DOCTYPE html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>WebRTC API</title>

<style>

#caller,
#receiver {

float:
left;

margin:
5px;

padding: 5px;

border:
1px solid #000000;

}

#buttons
{

clear:
both;

width:
528px;

text-align: center;

}

#databox
{

width:
526px;

height:
300px;

margin:
5px;

padding: 5px;

border:
1px solid #000000;

}

</style>

<script
src="webrtc.js"></script>

</head>

<body>

<section
id="caller">

<video
id="callermedia" width="250"
height="200"></video>

</section>

<section
id="receiver">

<video
id="receivermedia" width="250"
height="200"></video>

</section>

<nav
id="buttons">

<input
type="button" id="callbutton" value="Call">

<input
type="text" id="myinput" size="60" required>

<input
type="button" id="sendbutton" value="Send">

</nav>

<section
id="databox"></section>

</body>

</html>

The JavaScript code is similar
to the previous example but adds all the functions necessary to create the data
channel and transfer messages from one peer to another.

Listing 24-12: Creating the peer-to-peer connection with
a data channel

var user, socket, channel, channelopen;

function
initiate() {

var
callbutton = document.getElementById("callbutton");

callbutton.addEventListener("click", makecall);

var sendbutton =
document.getElementById("sendbutton");

sendbutton.addEventListener("click", senddata);

socket =
new WebSocket("ws://YOUR_IP_ADDRESS:12345/ws/demo.php");

socket.addEventListener("message", received);

var promise
= navigator.mediaDevices.getUserMedia({video: true});

promise.then(setwebcam);

promise.catch(showerror);

}

function
showerror(event) {

console.error(event);

}

function
setwebcam(stream) {

var servers
= {"iceServers": [{"urls":
"stun:stun.l.google.com:19302"}]};

user = new
webkitRTCPeerConnection(servers);

user.addEventListener("addstream", setremotestream);

user.addEventListener("icecandidate", setice);

user.ondatachannel =
function(event) {

channel =
event.channel;

channel.onmessage =
receivedata;

channelopen = true;

};

user.addStream(stream);

var video =
document.getElementById("callermedia");

video.srcObject = stream;

video.play();

}

function received(event)
{

var msg =
JSON.parse(event.data);

switch
(msg.type) {

case
"offer":

user.setRemoteDescription(new RTCSessionDescription(msg), function() {

user.createAnswer(setdescription, showerror);

});

break;

case
"answer":

user.setRemoteDescription(new RTCSessionDescription(msg));

break;

case
"candidate":

var
candidate = new RTCIceCandidate(msg.candidate);

user.addIceCandidate(candidate);

}

}

function
sendmsg(message) {

var msg =
JSON.stringify(message);

socket.send(msg);

}

function
makecall() {

channel =
user.createDataChannel("data");

channel.onopen =
function() {

channelopen = true;

};

channel.onmessage =
receivedata;

user.createOffer(setdescription, showerror);

}

function
setdescription(sessionDescription) {

user.setLocalDescription(sessionDescription, function() {

sendmsg(sessionDescription);

});

}

function
setremotestream(event) {

var video =
document.getElementById("receivermedia");

video.srcObject = event.stream;

video.play();

}

function
setice(event) {

if
(event.candidate) {

var
message = {

type:
"candidate",

candidate: event.candidate,

};

sendmsg(message);

}

}

function senddata() {

var databox =
document.getElementById("databox");

if (!channelopen) {

databox.innerHTML =
"DataChannel is not ready, please try again";

} else {

var message =
document.getElementById("myinput").value;

var chatroom =
databox.innerHTML;

databox.innerHTML =
"Local Says: " + message + "<br>";

databox.innerHTML +=
chatroom;

channel.send(message);

}

}

function receivedata(event)
{

var databox =
document.getElementById("databox");

var chatroom =
databox.innerHTML;

databox.innerHTML =
"Remote Says: " + event.data + "<br>";

databox.innerHTML +=
chatroom;

}

window.addEventListener("load",
initiate);

The data channel has to be created by one of the peers using the createDataChannel() method. The procedure can only be done after the peer
connection is ready and before an offer is sent. One way to do this is to
create the data channel when a call is initiated. For this reason, we decided
to set up the data channel for this example in the makecall() function. When a user clicks the Call button, the
data channel is created and assigned to the channel variable. This process is asynchronous; the channel
is added to the peer connection, and the browser informs the application when
the channel is ready through the open event. To notify the rest of the application when the
channel is ready to be used, we listen to this event and change the value of
the channelopen variable to true when it is fired. We also listen to the message
event to check for messages coming from the other peer through this channel.

When a channel is created by one peer, the other peer detects this
action by listening to the datachannel event. A listener for this event was registered in
the initiate() function. The event returns an object with the channel
property, which provides a reference to the new channel. This reference is
stored in the channel variable and a listener for the message
event is also registered on this side to check for messages coming through that
channel.

Now, both peers have the data channel opened, a handler for the message
event registered, and are ready to listen and send messages. Two functions were
created for this purpose: senddata() and receivedata().  These functions take the messages generated by the
local and remote peers and insert them into the databox
element of each application. The senddata()
function is executed every time the Send button is clicked. This function reads
the channelopen variable to check whether or not the channel is
already opened, and, if it is opened, takes the value of the input field myinput,
shows it on the screen, and sends it to the other peer with the send()
method. When the message is received by the other peer, the message
event is fired, and the receivedata()
function is called. This function takes the value of the data
property of the object returned by the event and shows it on the screen.

Do It Yourself: Update your HTML file with the
document of Listing 24-11 and copy the code in Listing 24-12 in the webrtc.js file. Upload both files to your
server, run the WebSocket server as we explained before, and open the document
in your browser. Insert a message in the input field and press the Send button.
Remember to replace the YOUR_IP_ADDRESS string with the IP of your server.

Chapter 25 - Web Audio API

25.1 Audio Structure

The Web Audio API provides the tools to develop audio production
applications and game audio engines for the Web. To that end, the API uses a
modular organization where everything is a node (called Audio Nodes). The nodes represent every part of the audio system,
from raw sound to the speakers, and they are connected to each other to form
the final structure that produces the sound.

Figure 25-1: Basic node organization

Figure 25-1 represents the most basic structure possible: a node
containing the source of the audio and a node representing the destination
(speakers, headphones or whatever in the system is configured to make the
actual sound). This is the most basic structure, a node producing the sound and
a node making the sound audible, but we might have more nodes with audio
sources, filters, volume controls, delays, effects, etc. The structure may be
as complex as our application requires.

Figure 25-2: Complex audio system

The first audio source node in Figure 25-2 is
connected to the volume node (called GainNode), which in turn is connected
to the compressor node (called DynamicsCompressorNode), which is finally
connected to the destination node (speakers, headphones, etc.). The volume of
this audio source is modified by the volume node, then compressed,
automatically mixed with the rest of the sounds, and finally sent to the
destination node and reproduced by the device’s audio output. The sources for
audio 2 and 3 of this figure follow a similar path, but they pass through the
filter node (called BiquadFilter) and the effect node (called ConvolverNode)
before reaching the compressor node and destination.

Most nodes have an input and output connection to
receive sound from a previous node, perform some kind of process and send the
result to the next one. Our work is to create the nodes, provide the
information each one requires to work properly and connect them.

Audio Context

The structure of nodes is set in an audio context. This is similar to
the context created for the <canvas> element. The context, in this case, is represented by
an object of type AudioContext. The Web Audio API provides the following constructor
to get this object.

AudioContext()—This
constructor returns an AudioContext object. The object provides methods to create every
type of node available as well as some properties to configure the context and
set the destination node to access the device’s audio output.

The following are the
properties provided by the AudioContext object to configure the context and
the audio system.

destination—This
property returns an AudioDestinationNode that represents the destination node.
This is the last node of an audio system, and its function is to provide access
to the device’s audio output.

currentTime—This
property returns the time in seconds since the context was created.

activeSourceCount—This property returns the number of the AudioBufferSourceNode currently playing.

sampleRate—This
property returns the rate (frames per second) at which the audio is being
processed.

listener—This
property returns an AudioListener object with properties and methods to calculate the
position and orientation of the listener in a 3D scene.

Audio Sources

The most important nodes are the audio source nodes, depicted as the
starting point of every path in the previous figures. Without these primary
sources of audio, we would not have any sound to send to the speakers. These
types of nodes may be created from different sources: audio buffers in memory,
media streams, or media elements. The API provides the following methods to get
the objects to represent each source.

createBufferSource()—This method returns an AudioBufferSourceNode object. The object is created from an audio buffer in
memory and provides its own properties and methods to play and configure the
audio source.

createMediaStreamSource(stream)—This
method returns a MediaStreamAudioSourceNode object. The object is created from a media stream.
The stream attribute is a stream generated by methods like getUserMedia() (see
Chapter 9).

createMediaElementSource(element)—This
method returns a MediaElementAudioSourceNode object. The object is created from a media element.
The element attribute is a reference to an <audio>
or <video> element.

The createMediaStreamSource() and createMediaElementSource() methods work with streams and media elements that
have their own controls to play, pause or stop the media, but the createBufferSource() method returns an AudioBufferSourceNode object, which provides its own properties and methods
to play and configure the source.

loop—This
property sets or returns a Boolean value that determines if the sound assigned as
the source of the node is going to be played in a loop.

buffer—This
property declares an audio buffer in memory as the source of the node.

start(time,
offset, duration)—This method
starts playing the sound assigned as the source of the node. The time
attribute determines when the action will take place (in seconds). The offset and
duration attributes are optional. They determine which part of
the buffer must be played, starting from offset and as long as determined by duration
(in seconds).

stop(time)—This method stops playing the sound assigned as the
source of the node. The time attribute determines when the action will take place
(in seconds).

The object returned by the createBufferSource() method does not work directly with audio files. The
files have to be downloaded from the server, stored in memory as audio buffers,
and assigned to the audio source node with the buffer
property. The API provides the following method to turn the sounds from an
audio file into a buffer in memory.

decodeAudioData(arraybuffer, success, error)—This method creates an audio buffer from binary data
asynchronously. The arraybuffer attribute is the binary data from the audio file
(ArrayBuffer type). The success attribute is the function that will receive and
process the buffer, and the error attribute is the function that will handle errors.

Information from audio buffers may be retrieved by the following
properties.

duration—This
property returns the duration of the buffer in seconds.

length—This
property returns the length of the buffer in sample frames.

numberOfChannels—This property returns the number of channels available in the buffer.

sampleRate—This
property returns the sample rate of the buffer in samples per second.

Connecting Nodes

There is one more thing we need to study before creating our first
audio structure, and that is how to connect the nodes. Audio nodes have output
and input connections that allow us to create a path for the sound to follow.
The API provides two methods to build and manage this network of nodes.

connect(node)—This method connects the output from one node to the
input of another. The syntax is output.connect(input), where output is a reference to the node that provides the output,
and input is a reference to the node that receives the audio from that output.

disconnect()—This
method disconnects the node's output. The syntax is output.disconnect(), where output is a reference to the node to be disconnected.

25.2 Audio Applications

Usually, sounds are part of complex JavaScript
codes, like those necessary to create a 3D video game, but for didactic
purposes, we are going to use a simple document to demonstrate how to create
and implement audio nodes.

Listing 25-1: Creating a document to play sounds

<!DOCTYPE html>

<html
lang="en">

<head>

<meta
charset="utf-8">

<title>Web Audio API</title>

<script
src="audio.js"></script>

</head>

<body>

<section>

<button
type="button" id="button"
disabled="true">Play</button>

</section>

</body>

</html>

As we explained before, a
basic node structure requires a node for the source and another for the
destination. The destination node is returned by the context’s destination
property, but the audio source requires some work. We have to download the
audio file from the server, convert the content to an audio buffer, use the
buffer as the source of the node, and finally connect both nodes to get the audio
in the speakers.

For our example, we are going to download a WAV file using Ajax and the XMLHttpRequest object (see Chapter 21).

Listing 25-2: Playing an audio buffer

var context;

function
initiate() {

var
mybuffer;

var button
= document.getElementById("button");

button.addEventListener("click", function() {

play(mybuffer);

});

context = new
AudioContext();

var url =
"gunshot.wav";

var request
= new XMLHttpRequest();

request.responseType = "arraybuffer";

request.addEventListener("load", function() {

if
(request.status == 200) {

context.decodeAudioData(request.response, function(buffer) {

mybuffer = buffer;

button.disabled =
false;

});

}

});

request.open("GET", url, true);

request.send();

}

function play(mybuffer) {

var sourceNode =
context.createBufferSource();

sourceNode.buffer =
mybuffer;

sourceNode.connect(context.destination);

sourceNode.start(0);

}

window.addEventListener("load",
initiate);

As before, we have our initiate() function to run the application. The function begins
by getting a reference to the button in the document and adding a listener for
the click event to execute the play() function every time the button is pressed.
Immediately after, the audio context is created with the AudioContext() constructor and stored in the context variable. The gunshot.wav
audio file we want to play is downloaded next using Ajax.

The downloading process of the file is the same we applied in the
examples in Chapter 21, except this time the response type is set as arraybuffer
because we need this kind of data to create the buffer in memory. Once the file
has been completely downloaded and the status property of the request returns 200 (OK), the buffer is created asynchronously from the
value of the response property using the decodeAudioData() method. After the data is converted into an audio buffer, the method
calls a function to report the result. In this function, we assign the buffer
produced by the method to the mybuffer variable and enable the Play button to let the user
play the audio.

The whole audio system, including nodes and connections, has to be
built every time we want to play a sound. The initiate()
function takes care of downloading the file and turning its content into an
audio  buffer in memory, but the rest is done by the play()
function. Every time the Play button is clicked, the audio system is created by
this function. First, the audio source node is created by the createBufferSource()
method, and then the audio buffer is assigned as the value of the buffer
property of this node. This process links the node to the audio buffer in
memory. After the node is ready, it is connected to the destination node by the
connect() method and the sound represented by the node is finally
played with the start() method.

Notice that the button is initially disabled, but it is enabled in
the initiate() function after the data necessary to create the audio
system is ready. This is the process we decided to follow to simplify the
example. Once enabled, every time the button is clicked, the audio system is
created again, and the sound from the gunshot.wav audio file is played.

Do It Yourself: Create a new HTML file with the
document of Listing 25-1 and a JavaScript file called audio.js with the code in
Listing 25-2. Download the gunshot.wav file from our website. Upload all
the files to your server and open the document in your browser. Click the Play
button to play the sound.

Loops and Times

The start() method at the end of the play()
function in the last example was called with the value 0. When
this value is set to 0 or is less than the value of the context’s current
time, the sound starts playing immediately. Once it is played, the start()
method cannot be called again to play the same audio source node; we have to
recreate the whole audio system. This is the reason why we constructed the audio
system in a separate function. Despite this restriction, there are different
ways of playing a sound several times without having to call the play()
function again. One alternative is to generate a loop for the audio source node
setting the loop property to true.

Listing 25-3: Playing the audio source node
in a loop

function play(mybuffer) {

var sourceNode = context.createBufferSource();

sourceNode.buffer = mybuffer;

sourceNode.loop = true;

sourceNode.connect(context.destination);

sourceNode.start(0);

sourceNode.stop(context.currentTime + 3);

}

The loop will run indefinitely until the stop()
method is called. In the code in Listing 25-3, we use this method to stop the
loop after 3 seconds. The time is set using the value returned by the currentTime
property. By adding the value 3 to the current time, we order the browser to
stop playing the sound 3 seconds from now.

Do It Yourself: Replace the play() function in Listing
25-2 with
the new function in Listing 25-3. Upload the audio.js
file to your server and open the document of Listing
25-1 in your
browser.

Audio Nodes

Every node in the audio system is called Audio Node. The basic
nodes we need to produce sound are the audio source node and the destination
node created in previous examples, but these are not the only nodes available.
The API provides methods to build a variety of audio nodes to process, analyze,
and even create sounds.

createAnalyser()—This method creates an AnalyserNode. These types of
nodes provide access to information of the audio source. The object returned
includes several properties and methods for this purpose. It is often used to
visualize the audio stream.

createGain()—This method creates a GainNode. These types of nodes
set the gain of the audio signal (the volume). The object returned provides the
gain property to set the volume to values between 0.0 and 1.0.

createDelay(maxDelayTime)—This method creates a DelayNode. These types of nodes set a delay for
the audio source. The object returned provides the delayTime
property to set the delay in seconds. The attribute maxDelayTime
is optional and declares the maximum time for the delay in seconds.

createBiquadFilter()—This method creates a BiquadFilterNode. These types
of nodes apply filters to the audio signal. The object returned provides
properties, methods, as well as several constants to set the characteristics of
the filter.

createWaveShaper()—This method creates a WaveShaperNode. These types of
nodes apply a distortion effect based on a shaping curve. The object returned
provides the curve property for declaring the shaping curve
(Float32Array type).

createPanner()—This method creates a PannerNode. These types of
nodes are used to determine the position, orientation, and speed of the sound
in a three-dimensional space. The effect produced depends on the current values
set for the listener. The object returned provides several methods and
properties to configure the parameters of the node.

createConvolver()—This method creates a ConvolverNode. These types of
nodes apply a convolution effect to the audio signal based on an impulse
response. The object returned provides two properties: buffer and
normalize. The buffer property is necessary to set the buffer to be used as
the impulse response, and the normalize property receives a Boolean value to set whether or
not the impulse response will be scaled.

createChannelSplitter(numberOfOutputs)—This method creates a ChannelSplitterNode. These
types of nodes generate different outputs for each channel of the audio stream.
The numberOfOutputs attribute declares the number of outputs to be
generated.

createChannelMerger(numberOfInputs)—This method creates a ChannelMergerNode. These types
of nodes combine channels from multiple audio streams into one. The numberOfInputs attribute declares the number of inputs to be merged.

createDynamicsCompressor()—This method creates a DynamicsCompressorNode. These
types of nodes apply a compression effect to the audio signal. The object
returned provides several properties to configure the effect.

createOscillator()—This method creates an OscillatorNode. These types of
nodes generate waveforms for audio synthesis. The object returned provides
several properties and methods to configure the waveform.

IMPORTANT: The application of some of these
methods require knowledge of audio engineering. The topic is beyond the scope
of this book, but below we will study how to implement the methods necessary
for the construction of common audio applications as well as 2D and 3D video
games.

AudioParam

Besides the common properties and methods every audio node object provides to configure the node,
there is an additional object defined by the API to set specific parameters.
The AudioParam object is like a control unit linked to a node and ready
to adjust its values at any time.

Figure 25-3: Nodes controlled by
AudioParam properties and methods

This object can set values immediately or be
configured to do it later following a schedule. Listed below are the properties
and methods provided for this purpose.

value—This property declares the value of the parameter as soon as it is
defined.

setValueAtTime(value, startTime)—This method sets the value of the parameter at a given time. The value and startTime
attributes declare the new value and the time in seconds, respectively.

linearRampToValueAtTime(value, endTime)—This method gradually changes the value of the
parameter from the previous value to the one specified in the attributes. The value and endTime
attributes declare the new value and the end time of the process in seconds,
respectively.

exponentialRampToValueAtTime(value, endTime)—This method changes the current value of the
parameter exponentially to the value specified by the attributes. The value and endTime
attributes declare the new value and the ending time of the process in seconds,
respectively.

setTargetAtTime(target, startTime, timeConstant)—This method changes the value of the parameter
exponentially to the value of the target attribute. The startTime attribute declares the starting time of the process,
and timeConstant sets the rate at which the previous value will approach
the new one.

setValueCurveAtTime(values, startTime, duration)—This method changes the value of the parameter by
arbitrary values selected from the array declared by the values
attribute (Float32Array type). The startTime and duration attributes declare the starting time of the process
and the duration in seconds, respectively.

cancelScheduledValues(startTime)—This method cancels any change previously scheduled
at the time specified by the startTime attribute.

GainNode

The first thing we usually want to do when we are playing
a sound is to turn the volume up or down. For this purpose, we are going to
introduce a GainNode between the audio source node and the destination node
created in previous examples.

Figure 25-4: GainNode controlling volume

When we add a new node to an audio system, the
basic structure remains the same; we just have to create the new node, provide
the configuration values and connect it to the rest of the structure. In this
case, the audio source node has to be connected to the GainNode and the
GainNode to the destination.

Listing 25-4: Turning down the volume using
a GainNode

function play(mybuffer) {

var sourceNode = context.createBufferSource();

sourceNode.buffer = mybuffer;

var volumeNode = context.createGain();

volumeNode.gain.value = 0.2;

sourceNode.connect(volumeNode);

volumeNode.connect(context.destination);

sourceNode.start(0);

}

The new play() function in Listing 25-4 introduces a GainNode to our
audio system. The node is created by the createGain() method and then, using the AudioParam’s
value property, a value of 0.2 is assigned to the node’s gain
property. The possible values for this property range from 0.0 to 1. By
default, the value of gain is 1, so the volume in this example is reduced to 20%.

The connections made at the end of the code include the new
GainNode. First, the audio source node is connected to the GainNode (sourceNode.connect(volumeNode)), and then the GainNode is connected to the
destination node (volumeNode.connect (context.destination)).

The value of the gain property for this example was set to a fixed value by
the value property because the audio file contains only a short sound of a
gunshot, but we could have used any of the AudioParam methods instead to increase or reduce the volume at
different times (like at the end of a song, for example). Methods such as exponentialRampToValueAtTime() may be
used in combination with the duration property of the buffers to mix songs, fading out one
track and fading in the next one.

Do It Yourself: Replace the play() function in Listing
25-2 with
the new function in Listing 25-4. Upload the audio.js
file, the HTML document of Listing 25-1 and the audio file to your server,
open the document in your browser, and press Play.

DelayNode

The purpose of a DelayNode is to delay the sound for
the time specified by the delayTime property (and the AudioParam object). In the following example, we are going to
introduce this node to play the sound of the shotgun one second later.

Listing 25-5: Introducing delay

function play(mybuffer) {

var sourceNode = context.createBufferSource();

sourceNode.buffer = mybuffer;

delayNode = context.createDelay();

delayNode.delayTime.value = 1;

sourceNode.connect(delayNode);

delayNode.connect(context.destination);

sourceNode.start(0);

}

The play() function in Listing 25-5 replaces the
GainNode in the previous example with a DelayNode. The delay is declared as 1
second by the AudioParam’s value property, and the connections are set
following the same previous path: the audio source node is connected to the
DelayNode, and this node is connected to the destination node.

A DelayNode produces better results when it is
combined with other nodes, as in the following example.

Listing 25-6: Getting an echo effect with
the DelayNode

function play(mybuffer) {

var sourceNode = context.createBufferSource();

sourceNode.buffer = mybuffer;

delayNode = context.createDelay();

delayNode.delayTime.value = 0.3;

volumeNode = context.createGain();

volumeNode.gain.value = 0.2;

sourceNode.connect(delayNode);

delayNode.connect(volumeNode);

volumeNode.connect(context.destination);

sourceNode.connect(context.destination);

sourceNode.start(0);

}

In the play() function in Listing 25-6, a GainNode is
added in the middle to reduce the volume of the delayed sound and generate an
echo effect. Two paths are set for the audio source. In one path, the audio
source node is connected to the DelayNode, the DelayNode to the GainNode, and
the GainNode to the destination node. The sound on this path will have a low
volume and will be played with a delay of 0.3 seconds. The second path for the source is
a direct connection to the destination node. The sound on this path will be
played immediately at full volume.

Figure 25-5: Two paths for the same audio
source

Do It Yourself: Replace the play() function in Listing
25-2 with
the new functions in Listings 25-5 and 25-6 to test each example. Upload the
files to your server, open the document in your browser, and press Play. You
should hear two sounds, one after another, simulating an echo effect.

BiquadFilterNode

The BiquadFilterNode lets us apply common filters to
the audio signal. The node is created by the createBiquadFilter() method, and the type of filter is selected by the type property. The possible values
for this property are "lowpass", "highpass",
"bandpass", "lowshelf", "highshelf", "peaking",
"notch", and "allpass". The node also includes the frequency,
Q
and gain properties to configure the filter. The effect produced by the values
of these properties on the filter depends on the type of filter applied.

Listing 25-7: Adding a filter with the BiquadFilterNode

function play(mybuffer) {

var sourceNode = context.createBufferSource();

sourceNode.buffer = mybuffer;

var
filterNode = context.createBiquadFilter();

filterNode.type = "highpass";

filterNode.frequency.value = 1000;

sourceNode.connect(filterNode);

filterNode.connect(context.destination);

sourceNode.start(0);

}

In Listing 25-7, we declare the
type of the filter as "highpass" and set the cutoff frequency as 1000. This will cut off some frequencies
of the spectrum making our gun sound like a toy gun.

The value of the frequency property is set by the AudioParam’s value property. This may be replaced with
any AudioParam’s
method to vary the frequency over time.

Do It Yourself: Replace the play() function in Listing
25-2 with
the new function in Listing 25-7. Upload the files to your server, open the
document in your browser, and press Play.

IMPORTANT: The effect produced by the values
of the frequency,
Q and gain properties depends on the filter
selected. Some values will not produce any effect at all. For more information,
read the API’s specification. Links are available on our website.

DynamicsCompressorNode

Compressors smooth the audio, reducing the volume of
loud sounds and raising the volume of quiet sounds. These types of nodes are
usually connected at the end of the audio system with the purpose of
coordinating the audio levels of the sources to make them sound unified. The
Web Audio API includes the DynamicsCompressorNode to produce this effect on the
audio signal. The node has several properties to limit and control the compression:
threshold (decibels), knee (decibels), ratio (values from 1 to 20), reduction (decibels), attack (seconds) and release (seconds).

Listing 25-8: Adding a dynamic compressor

function play(mybuffer) {

var sourceNode = context.createBufferSource();

sourceNode.buffer = mybuffer;

var compressorNode = context.createDynamicsCompressor();

compressorNode.threshold.value = -60;

compressorNode.ratio.value = 10;

sourceNode.connect(compressorNode);

compressorNode.connect(context.destination);

sourceNode.start(0);

}

The code in Listing 25-8 shows how to create and configure the DynamicsCompressorNode, but this node is
not frequently used this way. These types of nodes are usually implemented at the
end of elaborated audio systems that include several audio sources.

Do It Yourself: Replace the play() function in Listing
25-2 with
the new function in Listing 25-8. Upload the files to your server, open the
document in your browser, and press Play.

ConvolverNode

The ConvolverNode applies convolution effects to an
audio signal. It is commonly used to simulate different acoustic spaces and
achieve complex distortions, like a voice on a telephone, for example. The
effect is achieved by calculating an impulse response using a second audio
file. This audio file is usually a recording from the real acoustic space we
are trying to simulate. Because of this requirement, to implement the effect,
we have to download at least two audio files, one for the source and another for
the impulse response, as in the following example.

Listing 25-9: Applying a convolution effect

var context;

var mybuffers = [];

function initiate() {

var button = document.getElementById("button");

button.addEventListener("click", function() {

play();

});

context = new AudioContext();

loadbuffers("gunshot.wav", 0);

loadbuffers("garage.wav", 1);

var control = function() {

if (mybuffers.length >= 2) {

button.disabled = false;

} else {

setTimeout(control, 200);

}

};

control();

}

function loadbuffers(url, id) {

var request = new XMLHttpRequest();

request.responseType = "arraybuffer";

request.addEventListener("load", function(){

if (request.status == 200) {

context.decodeAudioData(request.response, function(buffer) {

mybuffers[id] = buffer;

});

}

});

request.open("GET", url, true);

request.send();

}

function play() {

var sourceNode = context.createBufferSource();

sourceNode.buffer = mybuffers[0];

var
convolverNode = context.createConvolver();

convolverNode.buffer = mybuffers[1];

sourceNode.connect(convolverNode);

convolverNode.connect(context.destination);

sourceNode.start(0);

}

window.addEventListener("load", initiate);

In the code in Listing 25-9, we recreate the entire JavaScript
code to include a small loader to download the two audio files necessary for
the effect (gunshot.wav and garage.wav). This time we had to move the Ajax code to a new
function to download these files one by one and create the control()
function to control the downloading process.

At the beginning of the initiate() function, the loadbuffers()
function is called twice to download each file. A buffer is created from every
file and stored in the mybuffers array. This process is checked by the control()
function, and when the length of the array is equal or greater than 2, the
Play button is enabled.

The construction of the audio system in the play()
function is similar to previous examples, except this time two nodes require
audio buffers, so we assign the corresponding item of the mybuffers
array to the buffer property of each node, and finally connect all the
nodes.

Do It Yourself: Copy the code in Listing 25-9 into
the audio.js file, upload all the files to your
server, open the document of Listing 25-1 in your browser, and press Play.

PannerNode and 3D Sound

Three-dimensional graphic libraries are incredible tools nowadays. The
recreation of real objects on a computer screen has reached a high level of
perfection and realism, and because of WebGL, this amazing experience is now
available on the Web. But graphics are not enough to recreate the real world;
we also need sounds that change along with the changes in the position of the source
and vary according to the speed of the source and its orientation. PannerNode
is a node designed specifically to simulate these effects. This node is similar
to other nodes, but the sounds must be configured considering the parameters of
a 3D world. The object representing the node includes the following properties
and methods for this purpose.

panningModel—This property specifies the algorithm to use to
position the audio in the 3D scene. The possible values are "equalpower"
and "HRTF".

distanceModel—This property specifies the algorithm to use to
reduce the volume of the sound according to the movement of the source. The possible
values are "linear", "inverse", and
"exponential".

refDistance—This property specifies a reference value to
calculate the distance between the source and the listener. It may be useful to
adapt the node to the scale of our 3D scene. The default value is 1.

maxDistance—This property specifies the maximum distance between
the source of the sound and the listener. Over this limit, the sound will keep
its current values.

rolloffFactor—This property specifies the rate at which the volume
is reduced.

coneInnerAngle—This property specifies the angle for directional
audio sources. Inside this angle, the volume is not reduced.

coneOuterAngle—This property specifies the angle for directional
audio sources. Outside this angle, the volume is reduced to a value determined
by the coneOuterGain property.

setPosition(x, y, z)—This
method sets the position of the audio source relative to the listener. The x, y and z
attributes declare the value of each coordinate.

setOrientation(x, y, z)—This
method sets the direction for a directional audio source. The x, y and z
attributes declare a direction vector.

setVelocity(x, y, z)—This
method sets the speed and direction of the audio source. The x, y and z
attributes declare a direction vector to represent the direction and also the
speed of the source.

Sounds in a 3D scene are relative to the listener. Because there
is only one listener, its characteristics are defined by the audio context and
not by a particular node. The object representing the listener is accessible
through the listener property of the context and provides the following
properties and methods for configuration.

dopplerFactor—This property specifies a value to configure the
pitch shift for the Doppler effect.

speedOfSound—This property specifies the speed of the sound in
meters per second. It is used to calculate the Doppler shift. The default value
is 343.3.

setPosition(x, y, z)—This
method establishes the position of the listener. The x, y and z
attributes declare the value of each coordinate.

setOrientation(x, y, z, xUp, yUp, zUp)—This method establishes the direction in which the
listener is oriented. The x, y and z attributes declare a direction vector for the front
of the listener, and the xUp, yUp and zUp attributes declare a direction vector for the top of
the listener.

setVelocity(x, y, z)—This
method establishes the speed and direction of the listener. The x, y and z
attributes declare a direction vector to represent the direction and also the
speed of the listener.

We will see how to apply all this in a 3D application using the
Three.js library studied in Chapter 12. The document we have been using so far
in this chapter has to be modified to include the three.min.js
file and the <canvas> element.

Listing 25-10: Creating a document
to test 3D sound

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Web Audio API</title>

<script src="three.min.js"></script>

<script src="audio.js"></script>

</head>

<body>

<section>

<canvas id="canvas" width="500"
height="400"></canvas>

</section>

</body>

</html>

The code for this example draws a cube on the screen that we can
move backward and forward using the mouse wheel.

Listing 25-11: Calculating the position of the sound in a
3D scene

var context, pannerNode, renderer, scene, camera, mesh;

function
initiate() {

var canvas
= document.getElementById("canvas");

var width =
canvas.width;

var height
= canvas.height;

renderer =
new THREE.WebGLRenderer({canvas: canvas});

renderer.setClearColor(0xFFFFFF);

scene = new
THREE.Scene();

camera =
new THREE.PerspectiveCamera(45, width/height, 0.1, 10000);

camera.position.set(0, 0, 150);

var
geometry = new THREE.BoxGeometry(50, 50, 50);

var
material = new THREE.MeshPhongMaterial({color: 0xCCCCFF});

mesh = new
THREE.Mesh(geometry, material);

mesh.rotation.y = 0.5;

mesh.rotation.x = 0.5;

scene.add(mesh);

var light =
new THREE.SpotLight(0xFFFFFF, 1);

light.position.set(0, 100, 250);

scene.add(light);

context =
new AudioContext();

context.listener.setPosition(0, 0, 150);

var url =
"engine.wav";

var request
= new XMLHttpRequest();

request.responseType = "arraybuffer";

request.addEventListener("load", function() {

if
(request.status == 200) {

context.decodeAudioData(request.response, function(buffer) {

play(buffer);

canvas.addEventListener("mousewheel", move, false);

renderer.render(scene, camera);

});

}

});

request.open("GET",
url, true);

request.send();

}

function
play(mybuffer) {

var
sourceNode = context.createBufferSource();

sourceNode.buffer = mybuffer;

sourceNode.loop = true;

pannerNode =
context.createPanner();

pannerNode.refDistance =
100;

sourceNode.connect(pannerNode);

pannerNode.connect(context.destination);

sourceNode.start(0);

}

function
move(event) {

mesh.position.z += event.wheelDeltaY / 5;

pannerNode.setPosition(mesh.position.x, mesh.position.y, mesh.position.z);

renderer.render(scene, camera);

}

window.addEventListener("load",
initiate);

The initiate() function in Listing 25-11 initializes all the
elements of the 3D scene and downloads the engine.wav
file with the sound for our cube. Notice that after creating the audio context
with the audioContext() constructor, the position of the listener is set with
the setPosition() method. This is done in the initialization process
because in this example the listener keeps the same position the whole time,
but this will probably have to be changed in other applications.

After the file is downloaded and the audio buffer is created, a
listener for the mousewheel event is added to execute the move()
function every time the wheel of the mouse is rotated. This function updates
the position of the cube on the z axis according
to the direction in which the wheel was moved, and then it sets the new
coordinates of the audio source. The effect makes the cube look like the real
source of the sound.

In the play() function, the PannerNode is created and included in
the audio system. We use the refDistance property to set the values of the node relative to
the scale of our 3D scene.

Do It Yourself: Create a new HTML file with the
document of Listing 25-10, copy the code in Listing 25-11 into the audio.js
file, and download the engine.wav file from our website. Upload all the files
to your server, open the document in your browser, and rotate the wheel in your
mouse to move the cube. You should hear the sound changing as the cube moves
away or comes closer to you.

AnalyserNode

It would be difficult to implement all the tools provided by the Web
Audio API in a professional application if we were not able to see the results
on the screen. To do this, the API includes the AnalyserNode. This node applies
an algorithm called FFT (fast Fourier transform) to turn the waveform of
the audio signal into an array of values representing magnitude versus
frequency for a given period of time. The values returned may be used to
analyze the signal or create charts to show its values on the screen. The
AnalyserNode provides the following properties and methods for retrieving and
processing the information.

fftSize—This property specifies the size of the FFT (the size of the block of
data to be analyzed). The value must be a power of 2 (e.g., 128, 256, 512, 1024, etc.).

frequencyBinCount—This property returns the number of frequency values
provided by the FFT.

minDecibels—This property specifies the minimum value of decibels
for the FFT.

maxDecibels—This property specifies the maximum value of decibels
for the FFT.

smoothingTimeConstant—This property declares a period of time in which the
analyzer will get an average value of the frequencies. It takes a value from 0 to 1.

getFloatFrequencyData(array)—This method retrieves the current frequency data from the audio signal
and stores it in the array attribute as floating-point values. The attribute is
a reference to a floating-point array already created.

getByteFrequencyData(array)—This method retrieves the current frequency data from the audio signal
and stores it in the array attribute as values of type unsigned-byte. The
attribute is a reference to an unsigned-byte array already created.

getByteTimeDomainData(array)—This method retrieves the current waveform data from the audio signal
and stores it in the array attribute as floating-point values. The attribute is
a reference to a floating-point array already created.

The data produced by these properties and methods may be used to
update a chart on the screen and show the evolution of the audio signal. To see
how to implement an AnalyserNode, we are
going to use a <canvas> element and draw the sound coming from a <video>
element.

Listing 25-12: Creating a document to experiment with the
AnalyserNode

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Web Audio API</title>

<style>

section {

float: left;

}

</style>

<script src="audio.js"></script>

</head>

<body>

<section>

<video id="media" width="483"
height="272" controls>

<source src="trailer.mp4">

<source src="trailer.ogg">

</video>

</section>

<section>

<canvas id="canvas" width="500" height="272"></canvas>

</section>

</body>

</html>

As we explained before, when the source is a media element, the
audio source node has to be created with the createMediaElementSource() method. In the following code, we apply this and the createAnalyser() method to get the nodes we need for our audio system.

Listing 25-13: Drawing the chart on a <canvas> element

var canvas, context, analyserNode;

function initiate() {

var video = document.getElementById("media");

var elem = document.getElementById("canvas");

canvas = elem.getContext("2d");

context = new AudioContext();

var
sourceNode = context.createMediaElementSource(video);

analyserNode = context.createAnalyser();

analyserNode.fftSize = 512;

analyserNode.smoothingTimeConstant = 0.9;

sourceNode.connect(analyserNode);

analyserNode.connect(context.destination);

showgraphic();

}

function showgraphic() {

var data
= new Uint8Array(analyserNode.frequencyBinCount);

analyserNode.getByteFrequencyData(data);

canvas.clearRect(0, 0, 500, 400);

canvas.beginPath();

for (var
f = 0; f < analyserNode.frequencyBinCount; f++) {

canvas.fillRect(f * 5, 272 - data[f], 3, data[f]);

}

canvas.stroke();

requestAnimationFrame(showgraphic);

}

window.addEventListener("load", initiate);

In this example, the size of the FFT is set as 512 and a
period of time of 0.9 is
established with the smoothingTimeConstant property to limit the size of the data retrieved and
smooth the chart on the screen. After the nodes are created, configured and
connected, the audio system is ready to provide information from the audio
signal. The showchart()
function is in charge of processing this data. This function creates an empty
array of type Uint8Array and the size determined by the frequencyBinCount property, and then it calls the getByteFrequencyData() method
to populate the array with values from the audio signal. Because of the type of
array, the values stored will be integers from 0 to 256 (8-bit
integers). In the for loop, next, we use these values to calculate the size
of the bars that represent the corresponding frequencies and draw them on the
canvas.

Figure 25-6: Audio chart for the video
created with the AnalyserNode

© Copyright 2008, Blender Foundation / www.bigbuckbunny.org

Do It Yourself: Create a new HTML file with the
document of Listing 25-12, copy the code in Listing 25-13 into the audio.js
file, open the document in your browser, and play the video. You can download
the trailer.mp4 and trailer.ogg videos from our website.

The
Basics: JavaScript
variables are usually defined without taking into consideration the type of
data they are going to work with, but some methods take only values provided in
a specific type. The Uint8Array() constructor,
implemented in the example of Listing 25-13, is just one of the constructors
introduced by the language to provide new array types to satisfy the
requirements of some JavaScript APIs. For more information on the subject,
visit our website and follow the links for this chapter.

Chapter 26 - Web Workers API

26.1 Background Processing

JavaScript has become the primary tool for building successful
applications on the Web, but since its creation, the language was intended to
be processed one code at a time. The absence of multithreading (processing
multiple codes simultaneously) reduces efficiency and limits the scope of the
technology, turning the emulation of desktop applications on the Web into an
impossible task. Web Workers is an API designed with the purpose of turning JavaScript
into a multithreading language and solving this problem.

Workers

A worker is a JavaScript code that performs processes in the background
while the rest of the application is still running and responsive. The mechanism to create the
worker and connect it with the main application is simple; the worker is built
in a separate JavaScript file, and the codes communicate with each other
through messages. The following is the constructor provided by the API to
create a Worker object.

Worker(scriptURL)—This constructor returns a Worker object. The scriptURL attribute is the URL of the file with the
code (worker) that will be processed in the background.

Sending and Receiving Messages

The message sent to the worker from the
main code is the information we want to be processed, and the messages sent
back by the worker is the result of this processing. To send and receive these
messages, the API takes advantage of techniques implemented in the Web Messaging
API (see Chapter 22). The following are the method and event required to
perform this process.

postMessage(message)—This method
sends a message to or from the worker. The message attribute may be any JavaScript value, such as a
string or a number, as well as binary data, such as a File
object or an ArrayBuffer, representing the message to be transmitted.

message—This event is fired when messages
are received from the other code. As with the postMessage() method, it can be applied to the worker
or to the main code. It returns an object with the data property to retrieve
the content of the message.

The following is a simple document we are going to use to send our
name to a worker and print the answer. Even a basic example like this one
requires at least three files: the main document, the main code, and the file
with the code for the worker.

Listing 26-1: Creating a document to
experiment with Web Workers

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Web Workers API</title>

<link rel="stylesheet"
href="webworkers.css">

<script src="webworkers.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<label for="name">Name: </label><br>

<input type="text" name="name"
id="name"><br>

<button type="button"
id="button">Send</button>

</form>

</section>

<section id="databox"></section>

</body>

</html>

The document of Listing 26-1 requires
the following rules to design the form and a box on the side to show the
messages.

Listing 26-2: Defining the styles for the
boxes (webworkers.css)

#formbox {

float: left;

padding: 20px;

border: 1px solid #999999;

}

#databox {

float: left;

width: 500px;

margin-left: 20px;

padding: 20px;

border: 1px solid #999999;

}

The JavaScript code for the
document has to send the information we want to be processed to the worker.
This code also has to listen for the answer.

Listing 26-3: Loading the worker (webworkers.js)

var worker, databox;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("button");

button.addEventListener("click", send);

worker =
new Worker("worker.js");

worker.addEventListener("message", received);

}

function send() {

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

worker.postMessage(name);

}

function received(event) {

databox.innerHTML = event.data;

}

window.addEventListener("load", initiate);

Listing 26-3 presents the code for
our document (the one inside the webworkers.js file). After the creation of the
references for the databox and the button in the initiate() function, the Worker object is constructed. The Worker() constructor takes the worker.js file as the file with the code
for the worker and returns a Worker object with this reference.
Every interaction with this object will be an interaction with the code in this
particular file.

After we get the proper object, a
listener for the message event is added to listen for
messages coming from the worker. When a message is received, the received() function is called, and the value of the data property (the message) is shown on the screen.

The other part of the communication
is conducted by the send() function. When the user clicks
the Send button, the value of the name input is taken and sent to the
worker using the postMessage() method.

With the received() and send() functions in charge of
communications, we are ready to send messages to the worker and process its
answers. Now it is time to prepare the worker.

Listing 26-4: Creating the worker (worker.js)

addEventListener("message", received);

function received(event) {

var answer = "Your name is " + event.data;

postMessage(answer);

}

The same way the code in Listing 26-3
is listening for messages coming from the worker, the code for the worker also has
to listen for messages coming from the main code. For this reason, the first
line in Listing 26-4 adds a listener to the worker for the message event. Every time this event is fired (a message is
received), the received() function is executed. In this function,
the value of the data property is added to a
predefined string and sent back to the main code using the postMessage() method again.

Do It Yourself: Compare the codes in Listings
26-3 and 26-4 (the main code and the worker). Study how the communication
process works and how the same method and event are applied for this purpose in
both codes. Create the files using Listings 26-1, 26-2, 26-3 and 26-4, upload
them to your server, open the document in your browser, write your name in the
field, and press the button. You should see the message sent back by the worker
on the screen.

This worker is, of course,
elementary. Nothing is processed; the only processing performed is the
construction of a string from the message received that is immediately sent
back as the answer. However, this example is useful to understand how the codes
communicate with each other and how to take advantage of this API.

IMPORTANT: Despite their simplicity, there are a few important things
you must consider before creating your own workers. Messages are the only
possible way to communicate directly with workers. Also, workers cannot access
the document or manipulate any HTML element, and JavaScript functions and
variables from the main code are not accessible to workers. Workers are like
canned codes, only allowed to process information received through messages and
send the result back using the same mechanism.

Errors

Workers are powerful processing units. We
can use functions, native methods, and entire APIs from inside a worker.
Considering how complex a worker may become, the Web Workers API incorporates an
event to check for errors and return as much information as possible about the
situation.

error—This event is fired by the Worker object in the main code every time an error occurs in the worker. It
creates an object with three properties to provide information about the error:
message, filename, and
lineno. The message property returns the error message. It is
a string that tells us what went wrong. The filename property returns the name of the file with
the code that caused the error. This is useful when external files are loaded
by the worker, as we will see later. And the lineno property returns the line number in which the error occurred.

The following code shows errors
returned by a worker.

Listing 26-5: Listening to the error event (webworkers.js)

var worker, databox;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("button");

button.addEventListener("click", send);

worker = new Worker("worker.js");

worker.addEventListener("error", error);

}

function send() {

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

worker.postMessage(name);

}

function
error(event) {

databox.innerHTML = "ERROR: " + event.message +
"<br>";

databox.innerHTML += "Filename: " + event.filename +
"<br>";

databox.innerHTML += "Line Number: " + event.lineno;

}

window.addEventListener("load", initiate);

The JavaScript code in Listing 26-5
is similar to the main code in Listing 26-3. It constructs a worker, but it
only uses the error event because we do not want
to listen for answers from the worker this time but rather check for errors. It
is useless, of course, but it shows how the errors are returned and what kind
of information is provided in these situations. To test this code, we generate
an error from the worker by calling a non-existent function.

Listing 26-6: Producing an error (worker.js)

addEventListener("message", received);

function received(event){

test();

}

When a message is received by the
code in Listing 26-6, the received() function is executed, and the
non-existent test() function is called, generating
an error. As soon as the error occurs, the error event is fired in the main
code, and the error() function is executed, showing
the values on the screen of the three properties provided by the event.

Do It Yourself: For this example, we are using
the HTML document and CSS rules in Listings 26-1 and 26-2. Copy the code in
Listing 26-5 in the webworkers.js file and the code in Listing
26-6 into the worker.js file. Open the document of Listing 26-1 in your browser and send any
random string to the worker using the form to activate the process. The error
returned by the worker will be shown on the screen.

Terminating Workers

Workers are special units of code that
always run in the background, waiting for information to be processed. Usually,
their services will not be necessary or required all the time, and it is good
practice to stop or terminate their processing if we no longer need them. The
API provides two different methods for this purpose.

terminate()—This method terminates the worker from the main code.

close()—This method terminates the worker from inside the
worker.

When a worker is terminated, any
process running at that moment is aborted, and any task in the event loop is
discarded. To test both methods, we are going to create a small application
that works exactly like our first example, but it also responds to two
commands: "close1" and "close2". If the strings
"close1" or "close2" are sent from the form, the worker is
terminated by the main code or the code of the worker using terminate() or close(), respectively.

Listing 26-7: Terminating the worker from
the main code (webworkers.js)

var worker, databox;

function initiate() {

databox = document.getElementById("databox");

var button = document.getElementById("button");

button.addEventListener("click", send);

worker = new Worker("worker.js");

worker.addEventListener("message", received);

}

function send() {

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

if (name
== "close1") {

worker.terminate();

databox.innerHTML = "Worker Terminated";

} else {

worker.postMessage(name);

}

}

function received(event) {

databox.innerHTML = event.data;

}

window.addEventListener("load", initiate);

The only difference between the code in
Listing 26-7 and the one in Listing 26-3 is the addition of the if instruction to check for the insertion of the "close1"
command. If this command is inserted in the form, the terminate() method is executed, and a message is
shown on the screen indicating the termination of the worker. Otherwise, if the
string is different from the expected command, it is sent as a message to the worker.

The code for the worker performs a
similar task. If the message received is equal to "close2", the
worker terminates itself using the close() method or sends a message back
otherwise.

Listing 26-8: Terminating the worker from
the code of the worker

addEventListener("message", received);

function received(event) {

if
(event.data == "close2") {

postMessage("Worker Terminated");

close();

} else {

var
answer = "Your name is " + event.data;

postMessage(answer);

}

}

Do It Yourself: Use the same HTML document and
CSS rules of Listings 26-1 and 26-2. Copy the code in Listing 26-7 into the webworkers.js file and the code in Listing
26-8 into the worker.js file. Open the document in
your browser, and using the form, send the commands "close1" or
"close2". After this, the worker will no longer respond.

Synchronous APIs

Workers may have limitations in working
with the main document and accessing its elements, but when it comes to
processing and functionality, as we mentioned before, they are ready for the
task. For instance, we can use conventional methods such as setTimeout() or setInterval(), load additional information
from servers using Ajax, as well as accessing other APIs. The last possibility
is the most promising of all, but it has a catch: we have to learn a different
implementation of the APIs available for workers.

When we studied some APIs in earlier
chapters, the implementation presented was the one called asynchronous.
Most APIs have asynchronous and synchronous versions available. These different
versions of the same API perform the same tasks but use specific methods
according to the way they are processed. Asynchronous APIs are useful when the
operations performed are time-consuming and require resources the main document
needs at the moment. The asynchronous operations are carried out in the
background while the main code continues running without interruption. Because
workers are new threads running at the same time as the main code, they are
already asynchronous, and these types of operations are no longer necessary. In
consequence, workers have to apply the synchronous versions of the APIs.

IMPORTANT: Several APIs have synchronous versions available, like the
File API and the IndexedDB API, but currently most of them are under
development or are unstable. Visit the links on our website for more
information on this subject.

Importing JavaScript Code

Something worth mentioning is the
possibility of loading external JavaScript files from a worker. A worker may
contain all the code necessary to perform any task we need, but because several
workers may be created for a single document, there is a chance that some parts
of this code may become redundant. We can select these parts, put them into a
single file, and load that file from every worker that requires it with the
following method.

importScripts(file)—This method loads an external JavaScript file to incorporate
additional code into a worker. The file attribute indicates the path of the file
to be included.

The way the importScripts() method works is similar to methods from
other languages, like include() from PHP, for example. The
code in the file is incorporated into the worker as if it were part of the worker’s
file. To use this method, we have to declare it at the beginning of the worker.
The code for the worker will not be ready until these files have been loaded.

Listing 26-9: Loading external JavaScript
codes for the worker

importScripts("morecodes.js");

addEventListener("message",
received);

function
received(event) {

test();

}

The code in Listing 26-9 is
non-functional but is an example of how to use the importScripts() method. In this hypothetical situation,
the file morecodes.js containing the function test() is loaded as soon as the worker’s file is loaded. After
this, the test() function (and any other
function inside the morecodes.js file) becomes available for
the rest of the code in the worker.

Shared Workers

The worker we have seen so far is called Dedicated
Worker. This type of worker only responds to the main code from which it
was created. There is another kind of worker called Shared Worker, which
responds to multiple documents from the same origin. Working with multiple
connections means that we can share the same worker from different windows,
tabs or frames, and keep everything updated and synchronized. The API provides
an object to represent Shared Workers called SharedWorker. The following is the constructor we need
to create these objects.

SharedWorker(scriptURL)—This constructor replaces the Worker() constructor used for Dedicated Workers. As before,
the scriptURL attribute declares the path of the JavaScript file
with the code for the worker. An optional second attribute may be added to
specify a name for the worker.

The connections are made through
ports, and these ports can be saved inside the worker for future reference. To
work with Shared Workers and ports, this part of the API incorporates new
properties, events, and methods.

port—When the SharedWorker object is constructed, a new port is
created for this document and assigned to the port property. This property will be used later to reference the port and
communicate with the worker.

connect—This event checks for new connections from inside the
worker. The event is fired every time a document starts a connection with the worker.
It is useful to keep track of all the connections available for the worker (to
reference all the documents that are using this worker).

start()—This method is available for MessagePort objects (one of the objects returned in the
construction of a shared worker), and its function is to begin dispatching
messages received through a port. After the construction of the SharedWorker object, this method must be called to start the
connection.

The SharedWorker() constructor returns a SharedWorker object and a MessagePort object with the value of the port used to
establish the connection. The communication with the shared worker must be done
through the port referenced by the value of the port property.

The document for our example includes
an iframe to load another document in the same window. The main document and
the document within the iframe will share the same worker.

Listing 26-10: Creating a document to
experiment with shared workers

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>Web Workers</title>

<link rel="stylesheet"
href="webworkers.css">

<script src="webworkers.js"></script>

</head>

<body>

<section id="formbox">

<form name="form">

<label for="name">Name: </label>

<input type="text" name="name" id="name">

<button type="button"
id="button">Send</button>

</form>

</section>

<section id="databox">

<iframe id="iframe" src="iframe.html"
width="500" height="350"></iframe>

</section>

</body>

</html>

The document for the iframe must include
an element to show the information and a <script> tag to include the iframe.js file containing the code to
connect with the worker.

Listing 26-11: Creating the document for the
iframe (iframe.html)

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>iframe Window</title>

<script src="iframe.js"></script>

</head>

<body>

<section id="databox"></section>

</body>

</html>

Each document has its own JavaScript
code to start the connection with the worker and process its answers. These
codes have to construct the SharedWorker object and use the port
referenced by the value of the port property to send and receive
messages. The following is the code corresponding to the main document.

Listing 26-12: Connecting the main document
to the worker (webworkers.js)

var worker;

function initiate() {

var button = document.getElementById("button");

button.addEventListener("click", send);

worker =
new SharedWorker("worker.js");

worker.port.addEventListener("message", received);

worker.port.start();

}

function received(event) {

alert(event.data);

}

function send() {

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

worker.port.postMessage(name);

}

window.addEventListener("load", initiate);

Every document that wants to work
with a shared worker has to create the SharedWorker object and set a connection
with the worker. In the code in Listing 26-12, the object is constructed using
the worker.js file, and then the
communication is established through the corresponding port using the port property.

After a listener for the message event is added to listen to the answers from the worker,
the start() method is called to begin dispatching
messages. The connection to a shared worker is not established until this method
is executed.

Notice that the send() function is similar to previous examples, except this time
the communication is made through the value of the port property.

The code for the iframe is very
similar.

Listing 26-13: Connecting to the worker from
the iframe (iframe.js)

function initiate() {

var worker = new SharedWorker("worker.js");

worker.port.addEventListener("message", received);

worker.port.start();

}

function received(event) {

var databox = document.getElementById("databox");

databox.innerHTML = event.data;

}

window.addEventListener("load", initiate);

In both codes, the SharedWorker object is constructed referencing the
same file (worker.js), and the connection is
established using the port property (albeit through
different ports). The only noticeable difference between the code for the main
document and the code for the iframe is how the response from the worker is
processed. In the main document, the received() function pops up an alert message
(see Listing 26-12), while within the iframe, the answer is printed as a simple
text inside the databox (see Listing 26-13).

Now it is time to see how the shared
worker handles every connection and sends messages back to the proper document.
Remember that there is only one worker for both documents (hence shared
worker). Every request to connect to the worker has to be differentiated and
saved for future reference. For this purpose, we are going to store the
references to the ports of each document in an array called myports.

Listing 26-14: Responding from a shared
worker (worker.js)

var myports
= new Array();

addEventListener("connect",
connect);

function connect(event)
{

myports.push(event.ports[0]);

event.ports[0].onmessage = send;

}

function send(event) {

for (var f = 0; f
< myports.length; f++) {

myports[f].postMessage("Your Name is " + event.data);

}

}

This procedure is similar to the one
for Dedicated Workers. This time we have to consider which document we are
going to answer, because several may be connected to the worker at the same
time. For this purpose, the connect event provides the ports array with the value of the newly created port (the array
only contains this value located at index 0).

Every time a code requests a
connection to the worker, the connect event is fired. In the code in
Listing 26-14, this event calls the connect() function. In this function, we
perform two operations. First, the value of the port is taken from the ports property (index 0) and stored in the myports array (initialized at the beginning of the worker), and
second, the onmessage event property is defined for
this particular port, and the send() function is set as the
handler. In consequence, every time a message is sent to the worker from the
main code, no matter from which document, the send() function in the worker is executed. In this function, we
use a for loop to retrieve all the ports opened for
this worker and send a message to each document connected. The process is the
same as for Dedicated Workers, but this time several documents are answered
instead of just one.

Do It Yourself: To test this example, you have
to create several files and upload them to your server. Create an HTML file
with the document of Listing 26-10. This document will load the same webworkers.css file used throughout this
chapter, the webworkers.js file with the code in Listing
26-12 and the iframe.html file for the iframe with the
code in Listing 26-11. You also have to create a file called worker.js for the worker with the code in
Listing 26-14. Once all these files are saved and uploaded to the server, open the
main document in your browser. Use the form to send a message to the worker,
and see how both documents (the main document and the document in the iframe)
process the response.

For Masterminds

Book Series

for more books visit

www.formasterminds.com