W3Cx Front-End Web Developer

W3C logo.

W3Cx-3of5-HTML5.1x - Coding Essentials and Best Practices

W3Cx HTML 5.1x logo.

Course Information

Module 1: HTML5 Basics

1.1 History of HTML: From HTML1.0 to HTML5.0

1.2 HTML5 Structural elements

1.3 A Blog example: Mixing All Elements Together

1.4 The Details and Summary elements

1.5 Microdata

Module 2: HTML5 Multimedia

2.1 HTML5 Multimedia: Streaming with Video and Audio Elements

2.2 Subtitles and closed captions

2.3 Enhanced HTML5 media players and frameworks

2.4 Webcam, microphone: the getUserMedia API

Module 3: HTML5 Graphics

3.1 Introduction - Module 3: HTML5 Graphics

3.2 Basics of HTML5 canvas

3.3 Immediate drawing mode: rectangles, text, images

3.4 Path drawing mode: lines, circles, arcs, curves and other path drawing methods

3.5 Colors, gradients, patterns, shadows, etc.

Module 4: HTML5 Animation

4.1 Introduction - Module 4: Animations

4.2 Basic animation techniques

4.3 Canvas and user interaction (keyboard, mouse)

4.4 A glimpse of advanced canvas functionalities

Module 5: HTML5 Forms

5.1 Introduction - Module 5: Forms

5.2 Elements and Attributes

5.3 Accessible Forms

5.4 <input> Types

5.5 Forms Attributes

5.6 Forms Elements

5.7 Form Validation API

Module 6: HTML5 Basic APIs

6.1 Introduction - Module 6: HTML5 Basic APIs

6.2 The Web Storage API

6.3 The File API

6.4 The Geolocation API

About W3C and the Web

Course Information

Welcome to “HTML5 Coding Essentials and Best Practices”

W3C has designed a “Front-End Web Developer” (FEWD) Professional Certificate  where you learn all of the necessary skills needed to build interactive and responsive user experiences on the Web. This program deepens your knowledge of the 3 foundational languages that power the Web: HTML5, CSS and JavaScript.
The W3C FEWD program is composed of 5 courses:

  1. CSS Basics
  2. 5.0x HTML5 and CSS Fundamentals
  3. This course: 5.1x HTML5 Coding Essentials and Best Practices
  4. 5.2x HTML5 Apps and Games
  5. JavaScript Introduction

This course is a natural follow up to the CSS Basics and  HTML5 & CSS Fundamentals courses.

As such, required prerequisites are:

During this course, you will:

The HTML5 course team is thrilled to guide you in your learning experience. We are committed to teach you how to code Web pages, and how to do it the correct way. We encourage you to create Web pages and apps and share them in the discussion forums. Have fun!

W3C Tools - Online Code Editors:

Web Editors:

While any text editor, like NotePad or TextEdit, can be used to create Web pages, they don’t necessarily offer a lot of help towards that end. Other options offer more facilities for error checking, syntax coloring and saving some typing by filling things out for you.

Here are a few possibilities for Web editors:

Online Editors:

To help you practice during the whole duration of the course, we use the following interactive online editors. Pretty much all the course’s examples actually use these tools.

JSBin logo.

JS Bin is an open source collaborative Web development debugging tool. Most of the examples that are in this course are on JSBin.

Tutorials can be found on the Web such as this one or on YouTube.

The tool is really simple, just open the link to the provided examples, look at the code, look at the result, etc.

And you can modify the examples as you like, you can also modify / clone / save / share them.

Keep in mind that it's always better to be logged in (it's free) if you do not want to lose your contributions/personal work.

CodePen.io logo.

CodePen is an HTML, CSS, and JavaScript code editor that previews/showcases your code bits in your browser. It helps with cross-device testing, real-time remote pair programming and teaching.

Here's an article of interest if you use CodePen: Things you can do with CodePen [Brent Miller, February 6, 2019].

There are many other handy tools such as JSFiddle, and Dabblet. Please share your favorite tool on the discussion forum, and explain why! Share also your own code contributions, such as a nice canvas animation, a great looking HTML5 form, etc.

Browser Compatibility:

The term browser compatibility refers to the ability of a given Web site to appear fully functional on the browsers available in the market.

The most powerful aspect of the Web is what makes it so challenging to build for: its universality. When you create a Web site, you’re writing code that needs to be understood by many different browsers on different devices and operating systems!

To make the Web evolve in a sane and sustainable way for both users and developers, browser vendors work together to standardize newfeatures, whether it’s a new  HTML elementCSS property, or JavaScript API. But different vendors have different priorities, resources, and release cycles — so it’s very unlikely that a new feature will land on all the major browsers at once. As a Web developer, this is something you must consider if you’re relying on a feature to build your site.

We are then providing references to the browser support of HTML5 features presented in this course using 2 resources: Can I Use and  Mozilla Developer Network (MDN) Web Docs.

Can I use

Can I Use  provides up-to-date tables for support of front-end Web technologies on desktop and mobile Web browsers. Below is a snapshot of what information is given by CanIUse when searching for “CSS3 colors”.

Example of a CanIUse browser support table (using CSS3 colors);

Example CSS in CanIUse.

MDN Web Docs

Logo of MDN Web Docs.

To help developers make these decisions consciously rather than accidentally,  MDN Web Docs provides browser compatibility tables in its documentation pages, so that when looking up a feature you’re considering for your project, you know exactly which browsers will support it.

External resources:

W3C Validators:

For over 15 years, the W3C has been developing and hosting  free and open source tools used every day by millions of Web developers and Web designers.
All the tools listed below are Web-based, and are available as downloadable sources or as free services on the  W3C Developers tools site.

W3C Validator

The W3C validator checks the  markup validity of various Web document formats, such as HTML. Note that you are automatically directed to the Nu Html Checker when validating an HTML5 document.

CSS Validator

The CSS validator checks Cascading Style Sheets (CSS) and (X)HTML documents that use CSS stylesheets.

Laptop showing unicorn validator.

Unicorn

Unicornis W3C’s unified validator, which helps people improve the quality of their Web pages by performing a variety of checks. Unicorn gathers the results of the popular HTML and CSS validators, as well as other useful services, such as Internationalization, RSS/Atom feeds and http headers.

Internationalization Checker

The W3C Internationalization Checker provides information about various internationalization-related aspects of your page, including the HTTP headers that affect it. It will also report a number of issues and offer advice about how to resolve them.

Link Checker

The W3C Link Checker looks for issues in links, anchors and referenced objects in a Web page, CSS style sheet, or recursively on a whole Web site.

For best results, it is recommended to first ensure that the documents checked use valid  (X)HTML Markup and CSS.

W3C Cheatsheet

The W3C cheatsheet provides quick access to useful information from a variety of specifications published by W3C. It aims at giving in a very compact and mobile- friendly format a compilation of useful knowledge extracted from W3C specifications, completed by summaries of guidelines developed at W3C, in particular Web accessibility guidelines, the Mobile Web Best Practices, and a number of internationalization tips.

W3C Cheat Sheet snapshot image.

Its main feature is a lookup search box, where one can start typing a keyword and get a list of matching properties/elements/attributes/functions in the above-mentioned specifications, and further details on those when selecting the one of interest.

The W3C cheatsheet is only available as a  pure Web application.

Help Build the Web Platform

Most of the technologies you use when developing Web applications and Web sites are designed and standardized in W3C in a completely open and transparent process.

In fact, all W3C specifications are developed in public GitHub repositories, so if you are familiar with GitHub, you already know how to contribute to W3C specifications! This is all about raising issues (with feedback and suggestions) and/or bringing pull requests to fix identified issues.

Contribute

Contributing to this standardization process might be a bit scary or hard to approach at first, but understanding at a deeper level how these technologies are built is a great way to build your expertise.

Github (the octocat) logo.

If you’re looking to an easy way to dive into this standardization processes, check out which issues in the W3C GitHub repositories have been marked as “good first issue” and see if you find anything where you think you would be ready to help.

Shape the future

W3C Web Incubator Community Group logo.

Another approach is to go and bring feedback ideas for future technologies: the W3C Web Platform Community Incubator Group was built as an easy place to get started to provide feedback on new proposals or bring brand-new proposals for consideration.

Happy Web building!

What is W3C?

As steward of global Web standards, W3C’s mission is to safeguard the openness, accessibility, and freedom of the World Wide Web from a technical perspective.

W3C’s primary activity is to develop protocols and guidelines that ensure long- term growth for the Web. The widely adopted Web standards define key parts of what actually makes the World Wide Web work.

A few history bits

Tim Berners-Lee at his desk in CERN, Switzerland, 1994.
Tim Berners-Lee at his desk in CERN, 1994

Tim Berners-Lee wrote a proposal in 1989 for a system called the World Wide Web. He then created the first Web browser, server, and Web page. He wrote the first specifications for URLs, HTTP, and HTML.

In October 1994, Tim Berners-Lee founded the World Wide Web Consortium (W3C) at the Massachusetts Institute of Technology, Laboratory for Computer Science [MIT/LCS] in collaboration with CERN, where the Web originated (see information on the original CERN Server, with support from DARPA and the European Commission.

In April 1995, Inria became the first European W3C host, followed by Keio University of Japan (Shonan Fujisawa Campus) in Asia in 1996. In 2003, ERCIM took over the role of European W3C Host from Inria. In 2013, W3C announced Beihang University as the fourth Host.

In addition to these four Host locations that employ W3C staff, there are  W3C Offices around the globe that support the developer communities in their regions and organize local events. Find the one next to your place!

A few figures

As of June 2020, W3C:

The Web is Amazing!

People often use the words “Internet” and “Web” interchangeably, but this usage is technically incorrect.

The Web is an application of the Internet.

The Web is the most popular way of accessing the Internet, but other applications of the Internet are e-mail and ftp for example.

One analogy equates the Internet to a road network where the Web is a car, the email is a bicycle, etc.
Read this article for more details about the difference between Internet and the Web.

Internet

Web

The internet is a global network of billions of servers, computers, and other  hardware devices. Each device can connect with any other device as long as both are connected to the internet using a valid  IP address. The internet makes the information sharing system known as the web possible.

The web, which is short for World Wide Web, is one of the ways information is shared on the internet (others include  email, File Transfer Protocol (FTP), and instant messaging services. The web is composed of billions of connected digital documents that are viewed in a web browser, such as Chrome,  Safari, Microsoft Edge, Firefox, and others.

Think of the internet as a library. Think of the books, magazines, newspapers, DVDs,  audiobooks, and other media it contains as websites.

Both the internet and the web serve unique purposes but work hand in hand to provide information, entertainment, and other services to the public.

The internet really is the information superhighway. It passes through various kinds of network traffic including, FTP, IRC, and the World Wide Web. Without it, we wouldn’t have our favorite and most common way to access websites.

The internet was born in the 1960s under the name ARPAnet. It was an experiment by the U.S. military to find ways to maintain communications in the case of a nuclear strike. With a decentralized network, communications could be maintained even if parts were taken offline. ARPAnet eventually became a civilian effort, connecting university mainframe computers for academic purposes.

As personal computers became mainstream in the 1980s and 1990s and the internet was opened to commercial interests, it grew exponentially. More and more users plugged their computers into the massive network through dial-up connections, then through faster connections such as ISDN, cable, DSL, and other technologies. Today, the internet has grown into a public spiderweb of interconnected devices and networks.

No single entity owns the internet, and no single government has absolute authority over its operation. Some technical rules, and its hardware and software standards, are agreed upon by invested organizations, groups, businesses, and others. These groups help the internet remain functional and accessible. However, for the most part, the internet is a free and open broadcast medium of networked hardware with no single owner.

Most consumers are familiar with and comfortable with the World Wide Web. With its easy-to-use interface, it’s the best way to get information in a few clicks.

The World Wide Web was born in 1989. Interestingly enough, the web was built by research physicists so that they could share research findings with one another’s computers. Today, that idea has evolved into the greatest collection of human knowledge in history.

The credited inventor of the World Wide Web is Tim Berners-Lee.

You have to access the internet to view the World Wide Web and the web pages or other content it contains. The web is the collective name for all the pages, sites, documents, and other media that are served to visitors.

The web consists of digital documents, referred to as web pages, that are viewable through web browser software on devices like smartphones, tablets, and computers. These pages contain many types of content, including static content like encyclopedia pages, but also dynamic content like eBay sales, stocks, weather, news, and traffic reports.

A collection of connected web pages that are publicly accessible and under a single domain name is referred to as a website.

Web pages are connected using  Hypertext Transfer Protocol (HTTP), the coding language that allows you to visit any public web page. By clicking a  hyperlink or entering a  Uniform Resource Locator (URL), the browser uses this unique address to find and access a web page. Search engines like Google make it easy to filter the billions of web pages now populating the web by locating the articles, videos, and other media you want to find based on your search criteria.

Final Verdict: You Can’t Have the Web Without the Internet

Plain and simple, the internet allows access to the World Wide Web. Without it, we have no way of accessing the thousands of websites out there. For most online needs, however, the web is the easiest to use. Each serves an important purpose.

Why Accessibility is Important

The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect.

Tim Berners-Lee, W3C Director and inventor of the World Wide Web

The Web has become an essential aspect of our daily lives, and everyone should have access to this technology. Web accessibility focuses on ensuring equivalent access for people with disabilities. It is increasingly important to many organizations and governments from around the world, and has many business benefits. Access to information, including on the Web, is also recognized by the UN Convention on the Rights of Persons with Disabilities (CRPD).

Who is impacted?

Web accessibility addresses all disabilities, including hearing, learning and cognitive, neurological, physical, speech, and visual disabilities. Some examples of Web accessibility features include:

Web accessibility benefits people with and without disabilities

Web accessibility features also benefit many more users, such as:

The Web is an increasingly important resource in many aspects of life: education, employment, government, commerce, health care, recreation, and more. When Web pages, Web technologies, Web tools, or Web applications are badly designed, they can create barriers that exclude people from using the Web. More information is available in the  W3C Accessibility overview.

First Steps in Web Accessibility

There are many simple Web accessibility improvements that you can implement and check right away, even when you are new to this topic.

Remember that when developing or redesigning a website or Web application, it is best to evaluate accessibility early and throughout the development process to identify accessibility problems early, when it is easier to address them.

Two examples are provided below but you can find more tips and information in these 2 resources:

Example #1: page title

Good page titles are particularly important for orientation — to help people know where they are and move between pages open in their browser. The first thing screen readers say when the user goes to a different Web page is the page title. In the Web page markup, they are the words <title> within the <head>.

Check #1: There is a title that adequately and briefly describes the content of a page, and it distinguishes the page from other Web pages.

Example:

<head>
  ...
  <title>Web Accessibility Initiative (WAI) - home page</title>
  ...
</head>

Example 2: image text alternatives (“alt text”)

Text alternatives (“alt text”) are a primary way of making visual information accessible, because they can be rendered through any sensory modality (for example, visual, auditory or tactile) to match the needs of the user. Providing text alternatives allows the information to be rendered in a variety of ways by a variety of user agents. For example, a person who cannot see a picture can have the text alternative read aloud using synthesized speech.

Check #2: Every image has alt with appropriate alternative text.

Example: See the W3C logo below. It contains a link that points to the W3C Web site. The text alternative is going to be a brief description of the link target.

W3C logo.

W3C Home

<a href="https://w3.org">
  <img src="https://w3.org/Icons/w3c_home.png" width="72" height="48" 
    alt="World Wide Web Consortium">
</a>
HTML5 logo.
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Page Title</title>
  <link rel="stylesheet" href="style.css">
  <script src="script.js"></script>
</head>
<body>
  ... <!-- The rest is content -->
Some of the new structural elements introduced by HTML5: section, article, etc.

1.1 History of HTML: From HTML1.0 to HTML5.0

As Web site layouts evolve, HTML5 structural elements such as lists, paragraphs, tables, etc. show their limits.

Today, many Web sites offer navigation menus, tabbed panels, headers, footers, and so on.

The way these “parts”’ are implemented relies heavily on <div>  and <span> elements with different id and class attributes, lots of CSS and lots of JavaScript code to apply custom styles and behaviors.

However, there are some issues with this approach:

Even if differences exist between ids, classes and css/js implementations, they also share common behaviors, layouts, and “ways of doing things” that could be guessed at first glance by a human.

So various studies have been conducted in order to identify the most popular ids, class names, widgets, etc. used on the Web:

Quoting from this article: “During the creation of HTML5, Ian Hickson used Google’s tools to mine data from over a billion Web pages, surveying what ids and class names are most commonly used on the real world Web. Opera did a similar study of 3.5 million URLs, calling it MAMA (”Metadata Analysis and Mining Application”). MAMA, as structural Web-paged search engine, had a smaller URL set, but looked at a larger and wider variety of Web page statistics”.

New elements added to the HTML5 set

The results of these surveys led to the addition of new structural elements in HTML5. For example, the very popular <div class=“header”> led to the creation of a <header> element, <div class=“aside”> to a <aside> element, etc.

Finally, the 20 most popular ids and class names found in Hickson’s and Opera’s surveys gave birth to these new elements (click on the element’s name to go to the W3C specification about this element):

1.2 HTML5 structural elements with description

HTML5 element Description
<header> Introduction of “sectioning elements”: an article, a section, the entire document (header page). Typically the header of a Web site that appears on the top of each page, or a header of a long <article> or of a long <section>.
<footer> Contains the footer of a site, a long <article>, or a long <section>.
<nav> Section that contains the main navigation links (within the document or to other pages).
<article> Independent content, which can be individually extracted from the document and syndicated (RSS or equivalent) without penalizing its understanding. Typically a blog post.
<section> Generic section used to group different articles for different purposes or subjects, or to define the different sections of a single article. Generally used with a header.
<time> Used for marking up times and dates.
<aside> Section whose content is not necessarily directly related to the main content that surrounds it, but can provide additional information.
<figure> and <figcaption> Used to encapsulate a figure as a single item, and contains a caption for the figure, respectively.
<main> The main element represents the main content of the body of a document or application. The main content area consists of content that is directly related to or expands upon the central topic of a document or central functionality of an application. There can be only one <main> element in a document.

And there is no <content> element even though the <div class=“content”> was very popular. Instead, the HTML5 group decided that anything not embedded in one of the elements from the above table is “default content”.

If the content is of a type that corresponds to one of the elements from the table, i.e. if the content is an article, it should be embedded between <article> and  </article>.

Read also at the end of this section about the new <main> element. This element is part of the HTML5 recommendation  and an integral part of the HTML document structure.

External resources:

1.3 A Blog Example: Mixing All Elements Together

A blog example that uses the structural elements

Let’s study an example we put on JsBin (all examples we have cooked up are available on the jsbin.com Web site and can be modified freely: you can save your own version using the “Bins/create milestone” menu, share your version with others in the forums, etc. Don’t  hesitate to play with the source code, you will never break anything).

Use a <header> at the top of the blog

Image of the header element at the top of the blog.

This is an example of one way to organize a blog. Here, we have designed the HTML page using a <header> element that contains the “Simple HTML5 blog” text that appears on top of the page.

HTML code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Simple HTML5 blog</title>
</head>
<body>
  <header>
    <h1>Simple <span>HTML5</span> blog</h1>
  </header>
...

The CSS rules we used:

header {
  color: #007e99;
  font-size: 2.5em;
  padding: 20px 50px
}
header span {
  color: #722
}

Use a <nav> for the navigation menu just below the header

Image of the navigation menu.

The navigation menu just below the header is a <nav> element. For the purpose of this example we haven’t provided any value for the hyperlinks…

HTML code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8"/>
5.   <title>Simple HTML5 blog</title>
6. </head>
7. <body>
8.   <header>
9.     <h1>Simple <span>HTML5</span> blog</h1>
10.   </header>
11.   <nav>
12.     <ul>
13.       <li><span>Blog</span></li>
14.       <li><a href="">About</a></li>
15.       <li><a href="">Contact</a></li>
16.     </ul>
17.   </nav>

And here is the CSS we used in this example for the <nav> element:

Click to expand!
1. nav {
2.   font-size: 1.5em;
3.   margin: 5px 0;
4.   padding: 20px 50px
5. }
6. nav li {
7.   display: inline;
8.   margin: 0 15px
9. }
10. nav li:first-child {
11.   margin-left: 0
12. }
13. * html nav ul {
14.   margin-left: -15px
15. }
16. nav span, nav a {
17.   padding: 3px 15px 4px
18. }
19. nav span {
20.   background: #722;
21.   color: #fff
22. }

A <section> for each month and an <article> for each post in the blog

Now, we have one big <section> element that contains a set of <article> elements…

Image of figure and figcaption that embed an img. Image of the navigation menu. Image of sections that contain articles.

HTML code:

<section>
  <article>
    ...
  </article>
  <article>
    ...
  </article>
  <article>
    ...
  </article>
</section>

And here is the CSS:

Click to expand!
1. section {
2.   float: left;
3.   padding: 35px 0;
4.   position: relative;
5.   width: 70%
6. }
7. section article {
8.   margin: 0 50px 40px;
9.   padding: 25px 0 0;
10.   position: relative
11. }
12. section header {
13.   font-size: 1em;
14.   padding: 0;
15. }
16. section h2 {
17.   font-size: 2.3em;
18. }

Note that the H2, article, article header, etc. will be styled using these rules.

Add a <header> at the beginning of each <article>

Image of the header at the top of each article.

Next, in each article in the section we have a header (to display the article title), paragraphs (article content), and so on.

Example for the first blog article:

Click to expand!
1. <section>
2.   <article>
3.     <header>
4.       <h2><a href="">Information about this example</a></h2>
5.     </header>
6.     <p>Try to move the mouse on different elements. The structure will be
7.       highlighted and you will be able
8.       to see the different inclusions of elements one in each other. If you
9.       move the cursor to this sentence, it will be highlighted in dark grey,
10.       showing the presence of an <article> element, surrounded by a
11.       <section> element (light grey), etc. So we have some articles in
12.       a single section element. The page title at the top is a <header>
13.       element, while the tag cloud on the right is a <aside> element. The
14.       main menu on top (with Blog, About, Contact) is a <nav> element.</p>
15.     <figure>
16.       <img src="HTML5-tags.png"
17.         alt="Example of HTML5 structural tags" />
18.         <figcaption>
19.           Fig. 1 : an example of how new structural elements could
20.           be used. This page put a <nav> on top, and does not have
21.           headers and footer for each article, like in this figure,
22.           but it could... By the way this is a
23.         <figcaption> inside a <figure> element...
24.         </figcaption>
25.     </figure>
26.   </article>
27.   ...
28. </section>

Use <figure> and <figcaption> and embed <img> inside

Also note the way we included a figure using the new “HTML5” method, using a <figure>..<figure> element that embedded a <img src=…> element together with a <figcaption> element.

Image of figure and figcaption that embed an img.

Here is the CSS for the <figcaption> element we have used in the example (we did not apply any style to the <figure> element):

HTML code:

<figure>
  <img src="HTML5-tags.png"
  alt="Example of HTML5 structural tags" />
  <figcaption>
    Fig. 1 : an example of how .....
  </figcaption>
</figure>

CSS code:

figcaption {
  font-style:italic;
  font-size: 0.8em;
  width: 100%
}

Use an <aside> element to display a tag cloud on the… side of the main content

After the long <section> element that contains all the blog articles displayed in the page, we added the HTML code for the tag cloud that is displayed on the right of the page, “aside”! This is done using - you already guessed it - an <aside> element:

image of the tag cloud defined as an aside element.
Click to expand!
1. <section>
2.   .... all <article>... </article> here....
3. </section>
4. <aside>
5.   <h2>Tag cloud</h2>
6.   <ul class="tag-cloud">
7.     <li><a href="" rel="tag" class="w2">ajax</a></li>
8.     <li><a href="" rel="tag" class="w8">apple</a></li>
9.     <li><a href="" rel="tag" class="w3">css</a></li>
10.     ...
11.   </ul>
12. </aside>
13. ...

We are not going to show the complete CSS here as it uses some tricks to display the list as a “real tag cloud” that uses JavaScript for handling events, etc. Those who are curious can look at the code of the online example.

Here is the CSS for the <aside> element:

Click to expand!
1. aside {
2.   float: right;
3.   padding: 70px 0 30px;
4.   position: relative;
5.   width: 25%
6. }
7. aside h2 {
8.   color: #888;
9.   font-size: 1.8em
10. }
11. aside .tag-cloud {
12.   padding: 15px 35px 10px 0;
13.   text-align: center
14. }
15. ...

We used a float:right CSS rule to put the tag cloud on the right… In a following section we will provide several examples that explain how to make a nice layout with the new structural elements, using simple CSS rules.

Here is the result:

The aside tag cloud on the right.

Add a <footer> at the end of the blog

Image of the blog footer.

Finally, we added a <footer> element (lines 12-14 below) after the tag cloud definition, to display a page footer:

Click to expand!
1. <html>
2. ...
3. <body>
4.   ...
5.   <section>
6.     ...
7.   </section>
8.   <aside>
9.     ...
10.   </aside>
11.   <footer>
12.     <p>&copy; 2009 Some blog</p>
13.   </footer>
14. </body>
15. </html>

With this CSS rule:

footer {
  clear: both;
  color: #777;
  padding: 10px 50px
}

And here is the result at the bottom of the page:

The resulting footer at the bottom of the page.

1.3.4 More on <article> and <section>

Can an <article> contain a <section>?

Picture of 'eisher' hands.

It may not be clear whether a <section> may contain one or several <article> elements or if an <article> may contain one or several <section> elements.

An <article> may be cut into different <section> elements!

Example of a blog post defined as a long <article>, that is in turn cut into smaller <section> elements:

Click to expand!
1. <article id="id1">
2.   <section id="id1part1">
3.     <h2>Introduction</h2>
4.   </section>
5.   <section id="id1part2">
6.     <h2>My travel to India</h2>
7.   </section>
8.   <section id="id1part3">
9.     <h2>Return to France</h2>
10.   </section>
11. </article>

The blog example from the previous part of the course, on the other hand, uses a single <section> that contains several <article> elements.

Indeed, we can also have a <section> that regroups all blog posts per month, each one being an <article> element.

A <section> may be cut into different <article> elements, too!

Can you put a <nav> in an <article>?

Yes, you can, in case you would like to propose some navigation links with each blog post, for example:

Click to expand!
1. <article>
2.   <header>
3.     <h1>Blog post title</h1>
4.     <p>Author: Michel</p>
5.   </header>
6.   <nav>
7.     <ul>
8.       <li><a href="...">Next post</a></li>
9.       <li><a href="...">Previous post</a></li>
10.       <li><a href="...">Contact author</a></li>
11.     </ul>
12.   </nav>
13.   <p>Content...</p>
14.   <footer>
15.     <p>Posted by Michel, the <time datetime="2012-02-02">February 2,
16.     2012</time> </p>
17.   </footer>
18. </article>

In that case, the <nav> element proposes navigation links to the next or previous blog post, as well as a link to contact the author of the blog post.

Also note that we used in that example a <footer> element in the blog post.

What about the <div> element? Is it still useful?

The new elements have been primarily designed to better structure the code of HTML pages such as those generated by blog or CMS software, however do not forget that they add new semantics and will be taken into account by:

You can use <div> elements in all cases where the proposed structural elements do not fit your needs: for defining some content that should be styled, for example.

This chart from the HTML5 Doctor Web site may help you decide whether or not to use a <div>:

Flow chart about using header, H1, etc.

1.3.5 Headings and Structural Elements

We will now present some best practices for starting to use <section>, <article>, <nav>, <aside>, in particular concerning the use of headings (h1, h2, h3, h4, h5 and h6).

Use <h1>…<h6> for the headings

Since the very beginning, HTML has had heading elements: <h1>…<h6>. These elements are used to display headings with different sizes by default, when no CSS is used.  The following example shows 6 sentences that are surrounded by <h1>, <h2>, <h3>, <h4>, <h5> and <h6>:

This is a H1 heading

This is a H2 heading

This is a H3 heading

This is a H4 heading

This is a H5 heading
This is a H6 heading

These headings define a hierarchy, as shown by the default sizes given by the browser. This hierarchy can also be used to define an outline of the document. To illustrate this, we have used a browser extension. Here is the result for the previous example:

Outliner in action from the previous example.

In the above outline, note that we have only used H1… H6 elements, without any new HTML5 structural elements such as <section> or <article>.

Here is a list of browser extensions you can try, for visualizing the outline of a document: table-of-contents-crx Chrome extension or this Firefox extension.

Using headings and new sectioning elements (section, article, aside, nav)

Definition of heading content and sectioning content

The <section>, <article>, <nav> and <aside> elements are called “sectioning elements”. They cut a document into slices we call “sections”.

The HTML5 specification says that “each sectioning element potentially has a heading and has also an outline associated”.

<h1> … <h6>

are called headings, and define the header of a section (whether explicitly marked up using sectioning content elements, or implied by the heading content itself). This means that:

<body>
  <h1>Title of my document</h1>
  ...
</body>

… defines the header of a section implicitly, while:

<body>
  ...
  <section>
    <h1>Title of my section</h1>
    ...
  </section>
</body>

… defines the heading of the explicit section (its parent element <section>)

Use multiple headings of different rank with sectioning content

The first element of a heading content in an element of sectioning content represents the heading for that section (the <section><h1>…</h1></section> in the above example).

Subsequent headings of equal or higher rank start new (implied) sections, headings of lower rank start implied subsections that are part of the previous one. In both cases, the element represents the heading of the implied section.

Let’s clarify this by looking at some example code:

Click to expand!
1. <body>
2. <section>
3.   <h1>This H1 is the heading of an explicit section</h1>
4.   ...
5.   <h2>This H2 is a subheading, part of the same section
6.     (lower rank)</h2>
7.   ....
8.   <h1>This H1 starts an implicit new section in the explicit
9.     section (equal or higher rank)</h1>
10.  ...
11.  <h2>This is a H2 heading in the new section that has
12.    just started</h2>
13.  ...
14. </section>
15. </body>

The corresponding outline is:

Outline of previous example.

In the above example, please note two things:

  1. The outline shows an “Untitled body” at the root of the hierarchy,
  2. The default size for the H1 and H2 is the same (!). Indeed, when we start a <h1> inside a <section> the browser lowers its default size automatically, as if a new hierarchy level has been added artificially. We will discuss this further in the following sections, as we introduce some best practices.

1.3.6 Best Practices When Using Sectioning Elements

Best practice #1: always add a heading to explicit sectioning content

It’s always better - mainly for accessibility reasons - to include a heading (a <h1>, <h2>…<h6>) in each sectioning element (<section>, <article>, <nav>, <aside>), but also after the <body> element (called a “sectioning root”).

Here are some examples:

Good (heading in each explicit section):

<section>
  <h1>Blog post of April 2020</h1>
  ...
</section>

Good (heading in a <header> does not change anything):

<section>
  <b><header></b>
  <b><h1>Blog post of April 2020</h1></b>
  <b><p>Posted by Michel Buffa...</p></b>
  <b></header></b>
...
</section>

Bad (there is no Hx after the <section> - no heading):

<section>
  <header>
    <p class="article title">Blog post of April 2020</p>
    <p>Posted by Michel Buffa...</p>
  </header>
  ...
</section>

The last example is bad for accessibility reasons. A screen reader that vocalizes the page will just say “Entering section”, while in the previous two good examples it would say “entering section with heading Blog Posts of April 2020”. You can also check if your headings and sectioning elements are ok by using a browser extension that displays the outline of the document (just search for “html5 outliner” in your browser’s extension search engine).

UPDATE: For the course screenshots, we used the Google Chrome HTML5 outliner extension that is no more available (it has been removed by its developer), but you can use any other equivalent extension such as table-of-contents-crx for Chrome or Outline sidebar for Firefox.

The outline of the last example looks like this;

Outline of last example.

Notice that <body> is also a sectioning element. It’s called a “sectioning root”, and would also need a heading.

Final good version:

<body>
  <b><h1>Example Blog</h1></b>
  <section>
    <header>
      <b><h2>Blog post of April 2020</h2></b>
      <p>Posted by Michel Buffa...</p>
    </header>
    <p>Content of the blog post...</p>
  </section>
</body>

The sectioning root (<body>) and the sectioning elements (<section> here…), each have a heading.

To sum up:

More about the <header> element

The <header> element is just a container. It is not taken into account for defining new sections of a document nor does it affect the hierarchy levels.

You can use heading elements <h1>…<h6> in a <header> but be careful if you use more than one, as the rules explained in the previous part of the course will apply and may generate implicit “sections” in the header.

This example has two headings in the <header>:

1. <section>
2.   <header>
3.     <h1>Some text in a h1 in a header of a section</h1>
4.     <h2>This a h2 in the header...</h2>
5.   </header>
6. </section>

Here is the resulting table of contents, notice the two subsections that appear, one for the H1, one for the H2:

Example structural elements.

Indeed, HTML does not have a dedicated mechanism for marking up subheadings, alternative titles or taglines. 

If you do not want the subtitles to be included in the table of contents, just use standard markup, for example <p> elements, as shown in the next example. Of course, CSS rules can be applied to change colors, sizes, etc.

1. <header>
2.   <h1>HTML 5.1 Nightly</h1>
3.   <p>A vocabulary and associated APIs for HTML and XHTML</p>
4.   <p>Editor's Draft 9 May 2013</p>
5. </header>

Best practice #2: try not to rely on implicit sectioning, use <section>, <article>, etc. instead of just <h1>…<h6>

The example below defines several implicit “sections” by using <Hx> directly (at lines 7 and 9):

Ok version (no explicit sections everywhere):

Click to expand!
1.  <body>
2.  <h4>Apples</h4>
3.  <p>Apples are fruit.</p>
4.  <section>
5.    <h2>Taste</h2>
6.    <p>They taste lovely.</p>
7.    <h6>Sweet<h6>
8.    <p>Red apples are sweeter than green ones.</p>
9.    <h1>Color</h1>
10.   <p>Apples come in various colors.</p>
11. </section>
12. </body>

Better version (best practice):

Click to expand!
1.  <body>
2.  <h1>Apples</h1>
3.  <p>Apples are fruit.</p>
4.  <section>
5.    <h2>Taste</h2>
6.    <p>They taste lovely.</p>
7.    <section>
8.      <h3>Sweet</h3>
9.      <p>Red apples are sweeter than green ones.</p>
10.   </section>
11. </section>
12. <section>
13.   <h2>Color</h2>
14.   <p>Apples come in various colors.</p>
15. </section>
16. </body>
Both of the examples above are semantically identical and produce the same outline:
Best Practice example of section.

1.3.7 Embedding a Table of Contents

Here we propose a small piece of JavaScript code you can use in your documents to display an embedded table of contents. 

This example is a simple document, with a hyperlink that, once clicked, displays the table of contents in an <aside> element in the main <section>. Just look at the source code and copy/paste the link into your own HTML documents.

Online example at JsBin.

Extract of source code:

Click to expand!
1.  <body>
2.  <h1>This is an example of embedded table of content</h1>
3.  <section>
4.    <header>
5.      <h1>First section of the document (this is a h1)</h1>
6.        This is a subheading...
7.    </header>
8.    <h2>First subsection of the first section (a h2)</h2>
9.    <p>Blah Blah...</p>
10. </section>
11. <section>
12.   <h1>Second section of the document (a h1)</h1>
13.   <h2>First subsection (a h2)</h2>
14. </section>
15. <aside>
16.   <h3>Table of contents</h3>
17.   <a href="javascript:(function(){...})();"
18.     title="TableDeMatiere">
19.       Click here to display the table of contents!
20.   </a>
21. </aside>
22. </body>

Best practice: visualizing the table of contents is useful for debugging the structure of your page, and checking the presence of headings after sectioning content.

Indeed, tools that generate the table of contents are a good way to debug the structure of your page. Is the hierarchy correct? Is it what I wanted when I designed my page?

They are also useful for checking the presence of headings in each sectioning content. If some headings are missing, the table of contents will display some “untitled entries”. Remember that having a heading after each sectioning content is a good practice in terms of accessibility.

1.3.8 The <main> Element

Main element, Earth, Water, Fire, Space & Air.

If you use <nav> / <header> / <footer> etc. to structure your document, you can also use <main> to identify the main content of the document. Doing so provides a navigable document structure for assistive technology users as well as styling hooks for devs.

We have seen the different sectioning elements of HTML5, so why didn’t we talk about the <main> element earlier in this part of the course? Shouldn’t <main>…</main> be used in place of <div class=“main”>…</div>?

The <main> element is supported by major modern browsers (see the corresponding support table on CanIUse and MDN’s brower compatibility page.

This element is subject to some constraints:

And finally, here are some examples (from the HTML5 specification) that mix the <main> element with the other sectioning elements already seen in the course:

Click to expand!
1.  <!-- other content -->
2.  
3.  <main>
4.  
5.    <h1>Skateboards</h1>
6.    <p>The skateboard helps kids to get around.</p>
7.  
8.    <article>
9.      <h2>Longboards</h2>
10.     <p>Longboards are a type of skateboard with a longer
11.       wheelbase and larger, softer wheels.</p>
12.     <p>... </p>
13.     <p>... </p>
14.   </article>
15. 
16.   <article>
17.     <h2>Electric Skateboards</h2>
18.     <p>These no longer require the propelling of the skateboard by means of the feet; rather an electric motor propels the board, fed by an electric battery.</p>
19.     <p>... </p>
20.     <p>... </p>
21.   </article>
22. 
23. </main>
24.  
25. <!-- other content -->

Here is another example (also from the specification). Here the <main> element contains a <nav> element consisting of links to subsections of the main content:

Click to expand!
1. <!DOCTYPE html>
2.   <html lang="en">
3.     <head>
4.       <meta charset="utf-8"/>
5.       <title>Graduation Ceremony Summer 2022</title>
6.     </head>
7.   <body>
8.     <header>The Lawson Academy:
9.       <nav>
10.         <h2>Click these links to navigate...</h2>
11.         <ul>
12.           <li><a href="courses.html">Courses</a></li>
13.           <li><a href="fees.html">Fees</a></li>
14.           <li><a>Graduation</a></li>
15.         </ul>
16.       </nav>
17.     </header>
18.     <main>
19.       <h1>Graduation</h1>
20.       <nav>
21.         <h2>Please choose:</h2>
22.         <ul>
23.           <li><a href="#ceremony">Ceremony</a></li>
24.           <li><a href="#graduates">Graduates</a></li>
25.           <li><a href="#awards">Awards</a></li>
26.         </ul>
27.       </nav>
28.       <h2 id="ceremony">Ceremony</h2>
29.         <p>Opening Procession</p>
30.         <p>Speech by Valedictorian</p>
31.         <p>Speech by Class President</p>
32.         <p>Presentation of Diplomas</p>
33.         <p>Closing Speech by Headmaster</p>
34.       <h2 id="graduates">Graduates</h2>
35.       <ul>
36.         <li>Eileen Williams</li>
37.         <li>Andy Maseyk</li>
38.         <li>Blanca Sainz Garcia</li>
39.         <li>Clara Faulkner</li>
40.         <li>Gez Lemon</li>
41.         <li>Eloisa Faulkner</li>
42.       </ul>
43.       <h2 id="awards">Awards</h2>
44.         <ul>
45.         <li>Clara Faulkner</li>
46.         <li>Eloisa Faulkner</li>
47.         <li>Blanca Sainz Garcia</li>
48.       </ul>
49.     </main>
50.     <footer>Copyright 2023 B.Bauska</footer>
51.   </body>
52. </html>

Best practice

For accessibility matters, a best practice is to split your page content into “regions” defined by the five 5 elements (aside, footer, header, main and nav) learned this week. 

We recommend this article written by Steve Faulkner: “Easy content organisation with HTML5” (24 September 2015). Steve explains in details how to organize an HTML document into “regions” based on the semantic markup elements we have seen so far during Module 1 of this course.

External resources:

1.3.9 The Blog Example, Applying Best Practices

Let’s go back to our blog example and see what can be improved:

The blog example is online at JsBin:  let’s see below what the Google Chrome HTML5 Outliner extension showed.

Image of Blog Table of Contents, with untitled nav entry.

Also note that in this example, we used H1s after each sectioning element, and we still get a hierarchy, some H1s are inside an <article> that is in a <section> (this corresponds to the third example given in the “heading and sectioning elements” part of the course):

Click to expand!
1. <section>
2.   <header>
3.     <b><h1>Blog posts for April 2012</h1></b>
4.   </header>
5.   <article>
6.     <header>
7.       <b><h1><a href="">Information about this example</a></h1></b>
8.         This example is a modified version of <a href="https://example.com/blog/index.html">https://example.com/blog/index.html</a>
9.     </header>
10.      ...
11.   </article>
12. </section>

With this technique, parts of the document can be moved more easily, or integrated inside an RSS stream, without the need to renumber the headings.

Beware that this technique will require you to use some CSS styling, and may confuse some screen readers that do not yet take into account this way of computing the heading hierarchy. A simple fix is to use an H1 right after the <body> and use only H2…H6 inside <section>, <article>, <nav> and <aside>.

Let’s fix the missing heading

We need to add a heading in the <nav> element. This will both fix the outline of the document by removing the untitled entry, and will also make screen readers happy as they will better vocalize the structure of the page (it will say “entering nav” followed by the vocalization of the heading content).

1. <nav>
2.   <header>
3.     <h1>Navigation menu</h1>
4.     <b>
5.   </header></b>
6.   <ul>
7.     <li><span>Blog</span></li>
8.     <li><a href="">About</a></li>
9.     <li><a href="">Contact</a></li>
10.   </ul>
11. </nav>

Here is the fixed result:

Good outline without the untitled nav.

A common remark from Web designers is: “we do not want a heading content displayed systematically after a <nav>, or an <aside> element…”

BEST PRACTICE #1: In order to NOT display the heading content on screen the recommended technique is described in this article by Steve Faulkner.  Do not use display:none or visibility:hidden in your CSS stylesheet, as in that case the heading content will never be vocalized by screen readers, and more generally by assistive technologies.

As an illustration of the recommended technique, see  this JSBin version of the blog example  that hides the <h2>Navigation menu</h2> from the <nav>…</nav>  element, using the CSS technique explained in the above link.

BEST PRACTICE #2: It is not advised to include interactive content (links, controls etc) that is hidden offscreen it is in fact a violation of the  W3C WCAG 2.0 Guidelines. All interactive content must have a visible focus indicator (and be on screen when focused).

Embedding a table of contents and adding a <main> element

In the previous section, we saw how to embed a table of contents using some JavaScript code borrowed from the Google Chrome HTML5 outliner extension.

Let’s add this piece of code (we removed the JS details from this extract):

<aside>
  <h1>
    <a href="javascript:(function(){...});"
      title="TableOfContents">
      Click here to display the table of contents!
    </a>
  </h1>
</aside>

We also added a <main> element to identify the main content of the page composed of the big section with all blog posts:

<main>
  <section>
    <header>
      <h2>Blog posts for April 2012</h2>
    </header>
    ...
</main>

Use H1 as top level headings only, use H2…H6 in sectioning content

As explained in the article HTML5 Document Outline and in the W3C HTML Wiki, it is risky to use nested H1s, as browsers do not correctly implement the “outline algorithm”.

The blog example uses nested H1’s. If you check it with the W3C conformance checker, it issues a warning: “Consider using the h1 element as a top-level heading only (all h1 elements are treated as top-level headings by many screen readers and other tools).

While this is just a warning, we do prefer to use H1s only as top level elements, and replace the H1s we had after <section>, <article>, <nav> and <aside> elements respectively by a H2s and H3s.

Extract from source code:

<nav>
  <header>
    <h2>Navigation menu</h2>
  </header>
  ...
</nav>

Finally, the fixed example

Blog with embedded table of contents.

1.3.10 Examples of Page Layouts

In this section, we show some “classic” CSS layout techniques for designing an HTML page that uses the new sectioning elements.

We embed examples from this very good post about “Positioning content”. This is a recommended reading as it details how to use the CSS float property to layout a Web page.

The 4 examples below are given “as is” to give you some hints. There are lots of other possibilities on using CSS to position element.

Example #1: a <section> on the left and an <aside> on the right, using the float and width CSS properties

This example uses the following HTML structure (notice that we use the “HTML entity syntax” for displaying “<” or “>”. For example, “<” displays a “<” character).

Click to expand!
1. <header>
2.   <code><header></code>
3. </header>
4.   
5. <section>
6.   <code><section> <br> float: left;</code>
7. </section>
8.   
9. <aside>
10.   <code> right;</code>
11. </aside>
12.   
13. <footer>
14.   <code><footer></code>
15. </footer>

Here we use the CSS rule float:left for the <section> and the CSS rule float:right for the <aside>. When an element floats, it goes out of the normal flow of the HTML element. Then by default it floats to the edge of its parent; and its size depends on the elements it contains. In order to fill the whole horizontal space, we prefer here to “force the width” by setting the CSS width property with a percentage.  We took width: 63% for the <section> on the left and width:30% for the <aside> on the right.

You can look at the complete CSS code in the interactive example below (click on the CSS or HTML text in the menu bar below, or click “edit on codepen” to change the code and see the results):

Header with section float left and aside float right.

Also available online at JSBin.

Michel Buffa home page: 3 sections centered.

Example #2: three sections centered, of equal size, also using the float and  width CSS properties

Here we show how to make a 3 column layout using the CSS float property.

HTML code:

Click to expand!
1. <header>
2.   <code><header></code>
3. </header>
4. 
5. <section>
6.   <code><section> <br> float: left;</code>
7. </section>
8. 
9. <section>
10.   <code><section> <br> float: left;</code>
11. </section>
12. 
13. <section>
14.   <code><section> <br> float: left;</code>
15. </section>
16. 
17. <footer>
18.   <code><footer></code>
19. </footer>

Instead of having one element with a float:left and one element with a  float:right property, we instead use float:left for all three of them, and we give a width:30% CSS property value to each <section>. We also set a small margin so that the colums have a gap between them.

Look at the CSS code in the example below:

Header with three float sections.

Example #3: same result using the CSS flex property

This example uses the CSS flex property to achieve a result similar to the one shown in Example 2. There are many articles on Flexbox and we recommend those from Rachel Andrew on Smashing Magazine: “ Use cases for Flexbox”, “Flexbox: how big is that flexible box”, etc.

Same example using the CSS Flex property.

Example #4: another example written by a student, that uses the flex property

This example also uses all the structuring elements we saw: main, article, section, etc. It uses only the simplest parts of the FlexBox CSS module, so it should be easy to understand, even for CSS beginners:

Another example using the flex property.

External resources

1.4 The <details> and <summary> Elements

These elements have been introduced for displaying a foldable zone in an HTML document.

In the screenshot below, taken from the W3C specification page, the text next to the horizontal arrow is a <summary> element, and the text displayed when we click on the summary part, is the <details> element. This is a sort of “accordion” with foldable content.

Example of summary details elements from the W3C specification.

The <details> element generates a simple widget to show/hide element contents, optionally by clicking on its child <summary> element.

Here is an example of what can be done using these elements: see the online version on JSBin:

Example of folded summary details.

And here is what is displayed after clicking on the small arrow-shaped icon to the left of the summary:

Example of summary details unfolded.

Here is the code of this example:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en"> ...
3.   <body>
4.     <details>
5.       <summary>
6.         How to beat the boss...spoiler alert !
7.       </summary>
8.       <p> Just aim to the red spots near his eyes</p>
9.       <p>Keep shooting at these spots until the eyes open, then hit quickly both eyes with your laser beam.</p>
10.     </details>
11.   </body>
12. </html>

The <summary>…</summary> is inside a <details>…</details> element. By clicking on the icon at the left of the summary, the content of the <details> value is displayed/hidden.

<details> blocks can be embedded inside one another, like in this example:

Step 1: all folded:
Other example, unfolded.
Step 2: click on top level summary icon, the first “hidden” part appears…
The unfolded content contains in turn a summary details folded.
Step3: click on embedded summary icon inside the part that has been previously unfolded
We unfolded the summary details inside the previous summary details. Recursive accordeons!
Source code of this example, see the summary/details inside another one:
Click to expand!
1. <details>
2.   <summary>
3.     How to beat the boss...spoiler alert !
4.   </summary>
5.   <p> Just aim to the red spots near his eyes</p>
6.   <p>Keep shooting at these spots until the eyes open, then hit quickly both eyes with your laser beam.</p>
7.   <details>
8.     <summary>
9.       Bonus and spoiler No 2: get a new weapon by cutting the tail of the boss.
10.     </summary>
11.     <p>Before finishing him, try to cut his trail, you will get a new weapon</p>
12.     <p>Just try to stay behind him as long as you can, hitting his tail with your melee weapon, 
13.       after a few hits the trail will fall and you will get a new bonus weapon, then finish the boss.</p>
14.   </details>
15. </details>

CSS pseudo classes for styling summary icons

There are CSS pseudo classes to style this icon when it is in the open or closed state. Support for these is still incomplete as of June 2020 (works on Google Chrome, Opera, Safari, not in FF).

Example1 (see online example):
Styling the open/close icon.

The color and background of the icon on the left are specified by the following CSS rule, which uses the pseudo class ::-webkit-details-marker

In this example: red arrow, white background.

summary::-webkit-details-marker {
  color:#FF0000;
  background:#FFFFFF;
}
Styled summary details icon, unfolded state.

Once opened, the selector details [open] can style the icon when <details>  is unfolded. In this example: blue arrow, turquoise background.

Here is the corresponding CSS rule:

details[open] summary::-webkit-details-marker {
  color:#0000FF;
  background:#00FFFF;
}

It is also possible to change the icon itself using the CSS pseudo class :after

Example 2 (see it  online):
'A+' as a custom open icon for summary. 'A-' as a custom closed icon.
CSS rules used in this example:

Use a “+” shaped icon, pink, bold, etc… :

summary:after {
  content: "+";
  color: #FF00FF;
  float: left;
  font-size: 1.5em;
  font-weight: bold;
  margin: -5px 5px 0 0;
  padding: 0;
  text-align: center;
  width: 20px;
}

Use a “-” shaped icon, white, when details are displayed:

details[open] summary:after {
  content: "-";
  color: #FFFFFF
}

Current browser support

1.4.2 The <time> and <mark> Elements

Picture of a clock with the words 'it's about time'.

The <time> element

The <time> element is useful for marking a time or a duration in a document.

It provides both a human readable part (the part between <time> and </time>) and a machine readable part contained within a datetime attribute. Dates are expressed as YYYY-MM-DD.

The machine readable part adds semantics that can be used by search engines for indexing, by browsers or by browser extensions, or by JavaScript code. Useful scenarios include generating alerts for birthdays, automatically adding dates or events that contain <time> elements in a calendar, etc.

Examples:

1.  We open at <time>10:00</time> every morning.
2.  I have a meeting the <time datetime="2020-02-14">Monday 14/02/2020.</time>.
3.  Blog posts from the year <time datetime="2020">2020</time>.
4.  Archives, blog posts for <time datetime="2020-04">April 2020</time>
5.  This recipe was published by Michel the <time datetime="2020-04-16">April 16, 2020</time>.

The datetime attribute

The datetime attribute can be used for indicating a date/time or a duration.

Date/time values

Supports different specifications of time such as “a year”, “a month in a year”, “a week in a year”, “a time”, etc… 

Here are some examples:

Different syntaxes of the datetime attribute

datetime attribute values Interpretation
<time datetime=“2020”> The year 2020
<time datetime=“2020-11”> November 2020
<time datetime=“11-13”> November 13th (any year)
<time datetime=“2020-W21”> Week 21 from year 2020
<time datetime=“2020-11-13 09:00”> November 13th year 2020, time = 9:00
<time datetime=“2020-11-13TO9:00”> Same as prev ex, both syntaxes are supported, with and without the “T” between date and time
<time datetime=“09:00%”> 9:00 in the morning, GMT
<time datetime=“09:00-05”> 9:00 in the morning, GMT minus 5 hours
<time datetime=“09:00+05:45”> 9:00 in the morning, GMT plus 5 hours 45 mins, (for example, Nepal is 5:45 ahead of GMT

Duration values

Duration values use the prefix “P” for “period” as in <time datetime=“P4D”> (period = four days)…

Funny picture duration values.

So you start the attribute string value with a “P”, followed by a duration value that ends with another letter indicating the unit used: “D” for “days”,  “H” for hours, “M” for minutes and “S” for seconds. 

You can separate the different elements “P”, value and unit with spaces, but this is optional. So <time datetime="P4D"> is a duration of 4 days, as is <time datetime="P 4 D">.

Using a “T” after the “P” marker allows you to indicate a more accurate duration time: <time datetime=“PT4H 6M 12.55S”> is a duration of 4 hours, 6 minutes and 12.55 seconds.

Alternatively, you could use also a duration time component.

From Bruce Lawson’s article : “Whichever you choose, it’s represented internally as a number of seconds. Because of this, you can’t specify a duration in terms of months, because a month isn’t a precise number of seconds; a month can last from 28 to 31 days. Similarly, a year isn’t a precise number of seconds; it’s 12 months and February sometimes has an extra day.

You still can’t represent dates before the Christian era, as years can’t be negative. Neither can you indicate date ranges. To mark up From “21/02/2012 to 25/02/2012″, use two separate <time> elements.”

Examples:

<h2>Recipe:</h2>
<ul>
  <li> Preparation time: <time datetime="PT30M">30 minutes</time> </li>
  <li> Cooking time:     <time datetime="PT10M">10 minutes</time> </li>
</ul>

The <time> element with no attributes

Used without attributes, the value between the opening <time> and closing <time> should follow the syntax given by the specification so that machines can understand it (same syntax as the one presented for the datetime attribute in the previous section). However it is recommended to use a datetime attribute, as it gives more freedom in the way you can display the date/time/duration in a human-readable form.

External resources:

The <mark> element

Picture of a yellow hightlighter pen.

The HTML <mark> tag is used for indicating text as marked or highlighted for reference purposes, due to its relevance in another context.

Some use cases:

Example 1:

Source code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset=utf-8 />
    <title>JS Bin</title>
  </head>
  <body>
    <p>Project is due in <b><mark>.zip format</mark></b> next monday.</p>
  </body>
</html>

Example 2:

Another example for marking code, yellow hightlighted background.

Source code:

<body>
  <pre>
    <code><mark>var</mark> i = 3;</code>
  </pre>
  <p>The var keyword is used to declare a variable in JavaScript.</p>
</body>

Change the default style of the <mark> element

If you don’t like the default yellow background, you may use CSS to change the style of the <mark> element:

For example:

Style the mark element with CSS, highlighted green background.

… comes with this CSS rule:

mark {
  background-color: green;
  color: yellow;
}

External resources:

1.4.3 The Download and Translate Attributes

The old way to download files using HTML and HTTP

Everyone knows the classic way to make hyperlinks, using <a href="...">some text. What happens when you click on the hyperlink depends on the MIME type received by the browser. If you link to a file the browser knows how to render (an html page, a gif, jpg, or png image, etc.) there is a good chance that the MIME type received by the browser will be something like this:

Content-type: text/html, text/plain, image/gif, image/jpg, etc.

For example, HTML code such as this:

<a href="toto.jpg">
  please right click this link to download
  the toto.jpg picture</a>

…will ask the remote HTTP server to send back the toto.jpg file. The browser will receive in the response HTTP header from the server (and by default the browser will display the image in a new tab):

1.  ...
2.  Content-type: image/jpg
3.  ...

However, if the link points to some PHP code,  Java servlet code, or any kind of script/application on the server side, this remote server code can send in its HTTP response a Content-type that may force the browser to download the image instead of rendering it.

It may also propose a name for the file to be downloaded that may be different from the one that appears in the URL of the href attribute. This can be done by generating, in addition to the Content-type line in the response HTTP header, a Content-Disposition line that looks like this:

Content-Disposition: attachment; filename="MyImage.png";

Here are some extracts from a Java Servlet that generate a zip file and forces the browser to propose downloading it using a specified name:

Click to expand!
1. protected void doGet(HttpServletRequest request, HttpServletResponse response)
2. throws ServletException, IOException {
3.   try {
4.     // Build the zip file
5.     String path = getServletContext().getRealPath("data");
6.     File directory = new File(path);
7.     String[] files = directory.list();
8.     if (files != null && files.length > 0) {
9.       byte[] zip = zipFiles(directory, files);
10.       ServletOutputStream sos = response.getOutputStream();
11. 
12.       // generate a HTTP response that forces the download
13.       [response.setContentType("application/zip");]
14.       [response.setHeader("Content-Disposition",]
15.       ["attachment; filename="DATA.ZIP"");]
16.         sos.write(zip); sos.flush();
17.     }
18.   } catch (Exception e) {
19.       e.printStackTrace();
20.     }
21. }

The above example will cause the browser that invoked this server-side code to start the download of a file named “DATA.ZIP”.

To download a file using an arbitrary name: the download attribute

Picture of a road sign with the words 'New Way'.

HTML5 proposes the use of a new attribute named download to download resources rather than navigating to them. The example below shows how to trigger the download of an image by the browser (instead of rendering it, which is the default behavior) with a name different from the name of the resource.

<a href="normal.gif" [download]=["MichelBuffa.gif"]>
  download a picture of Michel Buffa
</a>

This will indeed force the download of an image with a filename different from its original filename on the server side. Here is a screen capture of the Web browser while downloading the picture. We can see in the status bar the name of the link (the image is “normal.gif”) and the downloaded file is “MichelBuffa.gif”:

Image saved with another 'new' name, thanks to the download attribute.

WARNING: since 2015, and for security reasons, the image should be located on the same domain as the HTML page that contains the link (using a relative URL works well, for example, but linking a page on another domain will not work - it will keep its original name).

Interesting applications: serverless download

No server picture.

Serverless download demo (by E.Bilderman)

This demo shows the use of the download attribute together with the HTML5 File, FileSystem and FileWriter APIs (to be studied later in this course) for generating on-the-fly content from JavaScript code, and proposing downloading it to a file.

We won’t detail this demo here, but take a look if you are curious to see what  can be done with this new download attribute. As the FileWriter and FileSystem APIs are still supported only by Google Chrome (other browsers need polyfills), you will need Google Chrome to try it.

We have also put the simplified source code of this demo on JSBin.com for you to play with.

Serverless download demo: type text in a text area, press download, enter 
    a filename and voilà! you can download the textarea content into a file, 
	without any server.

External resources:

The HTML5 translate attribute

HTML5 gives us a new translate attribute. This attribute is used to limit the impact of  translation tools such as Google Translate by prohibiting the translation of certain content. In many cases some parts of a document should not be translated.

Use cases include:

Both  Google translate and Microsoft online translation services already offer the ability to prevent translation of content by adding markup to your content, although they do it in (multiple) different ways. Hopefully, the new attribute will help significantly by providing a standard approach.

Principle: give hints to translating tools

The specification about the translate attribute tells us that “The translate attribute is an enumerated attribute that is used to specify whether an element’s attribute values and the values of its Text node children are to be translated when the page is localized, or whether to leave them unchanged.

The attribute’s keywords are the empty string, yes, and no. The empty string and the yes keyword map to the yes state. The no keyword maps to the no state. In addition, there is a third state, the inherit state, which is the missing value default (and the invalid value default).”

Example illustrating how to specify parts of an HTML element that should not be translated:

<span <b>[translate="no"]</b> class="author">[Michel Ham]</span>

In the above example, a <span> element defines an author (of a blog, for example) who is named Michel Ham. However, his family name is the same as pork and would be translated to “Michel Jambon” in French, or Michel Jamón in Spanish…

Using the translate=“no” attribute should prevent this behavior…

<span <b>[translate="no"]</b> class="author">[Michel Ham]</span> is a professor from the University of Nice, France.

Will be correctly translated into French by:

"[Michel Ham] est un professeur de l'Université de Nice, France."

…where all of the end of the sentence has been translated except the author’s name.

Inheritance between elements

When you define an element as not being translatable, its children inherit this behavior and are themselves not translatable. The reverse is also true.

<p translate="no">This is a text in a paragraph element, that should not be translated: the p element has a translate="no" attribute.<span> 
  This part that is in a span element embedded within the paragraph. 
  It does not have a translate attribute but inherits the translation-mode of the p and will not be translated
too</span>. This is the end of the paragraph...</p>

External resources:

1.5 Microdata

There are several ways to provide machine-readable content embedded in a classical Web document: HTML+RDFamicroformatsJSON-LD, HTML5 microdata. In this section, we focus on microdata.

Adding microdata to Web pages helps search engines to better understand the pages’ content, their topics, etc. The main purpose of microdata is Search Engine Optimization(SEO).

This information is not visible to humans: it is pure semantic information. Popular kinds of microdata are events, a person’s profile, the description of an organization, the details of a recipe, a product description, a geographical location, etc.

Quick example of microdata that describes a person

Click to expand!
1.  <section [itemscope itemtype="https://schema.org/Person"]>
2.      <h1>Contact Information</h1>
3.      <dl>
4.          <dt>Name</dt>
5.        <dd [itemprop="name"]>Michel Buffa</dd>
6.          <dt>Position</dt>
7.        <dd><span [itemprop="jobTitle"]>
8.            Professor/Researcher/Scientist</span> for
9.          <span [itemprop="affiliation"]>
10.           University of Côte d'Azur, France
11.         </span>
12.       </dd>
13.     </dl>
14.     <!-- SURFACE ADDRESS GOES HERE -->
15.     <h1>My different online public accounts</h1>
16.   <ul>
17.     <li><a href="https://www.twitter.com/micbuffa"
18.       [itemprop="url"]>Twitter profile</a></li>
19.     <li><a href="https://www.blogger.com/micbuffa"
20.       [itemprop="url"]>Michel Buffa's blog</a></li>
21.   </ul>
22. </section>

We can also add another embedded data item in the middle, such as the person’s address:

Click to expand!
1.  ...
2.  </dl>
3.  
4.  <!-- SURFACE ADDRESS GOES HERE -->
5.  
6.  <dd [itemprop=]"address"[ itemscope]
7.    itemtype="https://schema.org/PostalAddress">
8.      <span [itemprop=]"streetAddress">10 promenade des anglais</span><br>
9.      <span [itemprop=]"addressLocality">Nice</span>,
10.     <span [itemprop=]"addressRegion">Alpes maritimes, France</span>
11.     <span [itemprop=]"postalCode">06410</span><br>
12.     <span [itemprop=]"addressCountry"[ itemscope]
13.   [itemtype=]"https://schema.org/Country">
14.     <span [itemprop=]"name">France</span>
15.   </span>
16. </dd>
17. 
18. <h1>My different online public accounts</h1>
19. 
20. ...

In the following sections, we look more closely at the itemprop, itemscope and itemtype attributes.

Data that can be processed, organized, structured, or presented in a given context

Different use cases:

Note: For advanced users, Microdata is very similar to microformats, which use HTML classes, or to RDFa, which doesn’t validate in HTML4 or HTML5. Because RDFa was considered to be too hard for authors to write, microdata is HTML5’s answer to help embed semantics into html documents.

External resources

1.5.2 Testing Tools

Introduction

After seeing the principle of embedding microdata in an HTML page, we now present some structured data test tools you can use to check if your data are correct.

Picture of diverse tools used by workers.

One of the most popular resources for testing microdata (as well as microformats and RDFa) is this Google page about understanding how structured data works. This page contains a link to a structured data testing tool that you can use to see how Google recognizes the semantic data you embed in your HTML code.

Testing a real interactive example with an “about page” for Michel Buffa

Let’s have a look now at a (small) example of an about page. It renders as a very simple paragraph that explains who Michel Buffa is… But we embedded Microdata, so it’s interesting to see how a search engine sees it, and how it may produce “augmented search results”.

Online example at JsBin

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset=utf-8 />
5.    <title>Michel Buffa</title>
6.  </head>
7.  <body>
8.    <div [itemscope itemtype="https://schema.org/Person"]>
9.      My name is <span itemprop="name">Michel Buffa</span>,
10.     And I'm a <span itemprop="jobTitle">professor/researcher</span> at
11.      <a href="https://www.i3s.unice.fr/" itemprop="affiliation">I3S
12.     Laboratory</a> in the south of France, near the city of Nice. My
13.     email
14.     is : <span itemprop="email">[email protected]</span>.
15.     I live in the city of
16.     <span itemprop="address" itemscope
17.       [itemtype="https://schema.org/PostalAddress"]>
18.       <span itemprop="addressLocality">Biot</span>, in a region named
19.       <span itemprop="addressRegion">Alpes Maritimes</span>
20.     </span>
21.   </div>
22. </body>
23. </html>

Rendering of the page in a browser:

Rendering of Michael Buffa's home page.

Here is what Google sees of the page. We just entered its  URL in the Google page about rich snippets and structured data:

Microdata of the example, as see by Google.

Note that the address is a fully featured embedded object in the Person’s description.

Live Microdata

The Live Microdata Web site is a bit similar to the previous one except that it shows the extracted metadata as JSON objects:

Example of live microdata from the previous example. Microdata are 
    displayed as json objects.

And the JSON view of the microdata:

JSON view of the microdata.

1.5.3 Adding Microdata to an HTML Page

Basic steps

Adding microdata to an HTML page is a really simple task and requires only three  attributes: itemscope, itemtype and itemprop.

1 - Define a container element by adding an itemscope attribute

First, you need to add an itemscope attribute to an HTML element. This will define the “global object” for which we will define properties. This element can be of different types that we will describe later, but for now let us keep looking at the same example we used in previous sections:

  <section itemscope itemtype="https://schema.org/Person">
  ...
  </section>

We will look at the itemtype attribute later. Now that we have defined a global wrapper object/element (a Person in this case), we can add properties inside this element to define the first name, last name, etc.

2 - Specify the vocabulary used for your microdata with the itemtype attribute of the container element

HTML5 proposes semantic elements for representing sections, articles, headers, etc, but it does not propose any specific elements or attributes to describe an address, a product, a person, etc.

We need a special vocabulary to represent a person or a physical address. With microdata you can define your own vocabulary or better, reuse one of the existing popular vocabularies, such as schema.org.

Microdata works with properties defined as name/value pairs. The names are defined in the corresponding vocabulary. For example, the vocabulary for representing a Person defines a set of property names.

As you can see in this small extract from the vocabulary (also called a “schema”), a Person can have a name (some text), an Address (the type is defined by another vocabulary named PostalAddress), an affiliation (defined by another vocabulary named Organization) and so on.

We notice that one property, such as the address of a Person, may use another vocabulary. Yes, a vocabulary may link to another vocabulary! There is also inheritance between vocabularies! The above screenshot shows that the Person vocabulary inherits from a Thing vocabulary, and the five first properties of the table come from this vocabulary that describes things.

If you are a developer and if you are familiar with object oriented programming, think of properties as class attributes and think of vocabularies as classes.

Vocabularies are meant to be shared

Picture with words 'time to share'.

If one of the existing vocabularies available at the schema.org Web site fits your needs, you should reuse it, as the most popular vocabularies are becoming de facto standards and will be taken into account by Web crawlers, browsers, and browser extensions.

However, if you do not find a vocabulary corresponding to your needs, keep in mind that anyone can define a microdata vocabulary and start embedding custom properties in their own Web pages. You need to define a namespace and put a description of your vocabulary in a Web page that has the name of your vocabulary.

3 - Add properties using the itemprop attribute in HTML elements inside the container

Basics:

Now that you have defined a container element, you may add properties to the HTML inside:

Click to expand!
1.  <section itemscope itemtype="https://schema.org/Person">
2.    <h1>Contact Information</h1>
3.    <dl>
4.      <dt>Name</dt>
5.      <dd [itemprop="name"]>Michel Buffa</dd>
6.        <dt>Position</dt>
7.        <dd><span [itemprop="jobTitle"]>
8.          Professor/Researcher/Scientist
9.        </span> for
10.       <span [itemprop="affiliation"]>University of Nice,
11.          France
12.       </span>
13.     </dd>
14.   </dl>
15.   <h1>My different online public accounts</h1>
16.   <ul>
17.     <li><a href="https://www.twitter.com/micbuffa"
18.       [itemprop="url"]>Twitter profile</a></li>
19.     <li><a href="https://www.blogger.com/micbuffa"
20.       [itemprop="url"]>Michel Buffa's blog</a></li>
21.   </ul>
22. </section>

In this example, the container is a <section> that corresponds to a Person (we have one clue here: the name of the vocabulary given by the itemtype attribute), and each property defined inside this section is identified by the value of the itemprop attribute of sub-elements.

The line:

<dd itemprop="name"Michel Buffa</dd>

…defines a property called “name” that has a value of “Michel Buffa” (the text value between the opening and closing tags of the <dd> element).

Nesting microdata items

As we saw with the Person/Address example at the beginning of this chapter, it is possible to nest microdata items inside one another.
Give an element inside a microdata container its own itemscope attribute with the recommended itemtype attribute for indicating the name of the vocabulary used by the nested microdata.

Again, look at the Person/Address example:

Click to expand!
1.   ...
2.   </dl>
3.  
4.   <!-- SURFACE ADDRESS GOES HERE -->
5.  
6.   <dd itemprop="address"[ itemscope]
7.     [itemtype="https://schema.org/PostalAddress"]>
8.     <span itemprop="streetAddress">10 promenade des anglais</span><br>
9.     <span itemprop="addressLocality">Nice</span>,
10.    <span itemprop="addressRegion">Alpes maritimes, France</span>
11.    <span itemprop="postalCode">06410</span><br>
12.    <span itemprop="addressCountry" itemscope
13.      [itemtype="https://schema.org/Country"]>
14.      <span itemprop="name">France</span>
15.    </span>
16.  </dd>
17. 
18.  <h1>My different online public accounts</h1>
19. 
20.  ...

The properties at lines 8-12 refer to the address nested microdata (they are defined in the Address vocabulary, not the Person vocabulary), and “France” (line 14) is a property that refers to the Country vocabulary.

Several properties with the same name but different values

It is possible to use the same property name several times in one microdata object, but with different values:

1.   ...
2.   <h1>My different online public accounts</h1>
3.   <ul>
4.   <li><a href="https://www.twitter.com/micbuffa"[ itemprop="url"]>Twitter
5.     profile</a></li>
6.   <li><a href="https://www.blogger.com/micbuffa"[ itemprop="url"]>Michel
7.     Buffa's blog</a></li>
8.   </ul>

This defines the fact that Michel Buffa has two online accounts, and the two properties have the name url, each with its own value.

It is possible to set more than one property at once, with the same value

Here are some microdata that represent a song. In this example, at line 5 we set two different properties: genre and keywords with the same value (see the  MusicRecording schema definition):

1.  <div itemscope itemtype="https://schema.org/MusicRecording">
2.    <h2>The song I just published</h2>
3.    <ul>
4.      <li>Name: <span itemprop="name">Please buy me on itunes, 
          I need money!</span></li>
5.      <li>Band: <span [itemprop="genre keywords"]>[Punk, Ska]</span></li>
6.    </ul>
7.  </div>

And so on…
Now, let’s see what elements are compatible with the itemprop attribute and where the values of the properties are located, depending on each element type.

The HTML elements compatible with the itemprop attribute

If the itemprop attribute appears on a:

Elements that can be associated with microdata

HTML5 elements microdata value associated
<a>, <area>, <audio>, <embed>, <iframe>, <img>, <link>, <object>, <source>, or <video> element The data is the url in the element’s href, src, or data attribute, as appropriate. For example, an image element inside a container of personal contact information can be recognized as that person’s photo and downloaded accordingly.
<time> element The data is the time in the element’s datetime attribute. This lets you, for example, just say “last week” in your text content but still indicate exact date and time.
<meta> element The data is whatever appears in the content attribute of the <meta> element. This is used when you need to include some data that isn’t actually in the text of your page.
anything else The data is whatever is in the text of the element.

For example, the value of a property defined in an <img> element will be the value of the src attribute:

<img itemprop="image" src="MichelBuffa.png" alt="A great professor">

Or, for a <time>, it will be the value of the datetime attribute:

<time itemprop="birthday" datetime="1965-04-16">April 16, 1965</time>

Or, for an <a> element, the value will be the value of the href attribute:

<a href="https://www.twitter.com/micbuffa" itemprop="url">profile</a>

1.5.4 Microdata Tools

There are many tools available (most are free) that you can use for generating, visualizing and debugging microdata. We list some of them in this page, but feel free to share the tools you find / like in the forums.

Microdata generators

To automatically generate microdata for describing persons, restaurants, movies, products, organizations, etc., there is a wide variety of microdata generators such as these listed below (but do not hesitate to search for “microdata generators” using your favorite search engine, and you will find lots!):

Example:

Example of generator. I entered my name, job, city in a form and a text area 
  next to it shows the corresponding HTML microdata.

1.5.5 Examples of Well Structured Documents with Microdata

Here, we propose a few links to Web pages that were created by students of previous editions of this course).

The students had to create a Web page to introduce themselves, with some information including: name, job, employer, location, etc., and of course enrich the page with microdata. They also had to follow the best practices concerning the new structural elements, headings, etc.

Click on these pages and look at the source code…

Example #1

Visit the example #1 online.

Structure:

Picture of the first 'about me' page example. Shows the table of contents.

Microdata:

Microdata from the example page.

Example #2:

View the example #2 online.

Example page, shows table of contents.

Module 2: HTML5 Multimedia - An Introduction

2.1 HTML5 Multimedia - Streaming with <video> & <audio> Elements

Until 2012, it was only possible to integrate an audio or video player using the proprietary Flash technology, marketed by the company Macromedia (later acquired by Adobe). The <video> element of HTML5 is one of the three “Flash killers” (the others being <audio> for the sound and <canvas> for drawing and animation). (Note that Adobe no longer supports Flash Player  since December 31, 2020)

Check the HTML code of the following CodePen:

Codepen snapshot: simple HTML5 video player.

Please note that:

You will learn more about the different attributes of the <video> element  later on in the course.

Current browser support for the <video> element

The <video> element is supported by all major browsers. See the  support table from CanIUse.

Restriction: you cannot embed a YouTube or DailyMotion video using the  <video> element

Help! <video src=“my youtube video URL”></video> does not work! 

BEWARE: you cannot directly embed videos from most of the popular Web sites such as YouTube, Dailymotion, Vimeo, etc. For commercial reasons, and because advertising is automatically added to the videos, these Web sites do not allow “regular” embedding of their videos.

While they use HTML5 to render their videos, these hosting sites (YouTube, etc.) use rather complex techniques in order to prevent you from using them with the <video>element. Instead, you often need to embed an <iframe> that will render the HTML5 videos in your Web site, and of course, the advertising that comes along with them.

Usually you have an “embed” button close to the videos that prompts you with some HTML code that you can copy and paste for embedding.

An example using YouTube:

Here is the HTML code you need to copy and paste in order to embed avideo:

  <iframe width="560" height="315" src="https://www.youtube.com/embed/WMFXg-kni0U" title="YouTube video player"
  frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope;
  picture-in-picture" allowfullscreen></iframe>

The YouTube video embedded in this page by the above code: it’s HTML5 but it’s not a  <video> element directly inserted in the HTML of this page, it’s an <iframe>.

Web of things (WoT) explainer video snapshot.

Support for different video CODECS (mp4, ogG, avi, etc.)

This is one of the main problems encountered in recent years: codec support was not the same from one browser to another, for commercial/economic reasons. For example, between 2010 and 2013, Firefox only supported the ogg/oggm format. It did not support mp3/mp4 encoding for audio/video, while Internet Explorer only supported H.264 encoding. Since 2012, things have changed with browser updates and today most popular formats are supported.

The recommended CODEC, which works in all popular browsers: H264/mp4.

External resources

2.1.2 The <audio> Element

HTML5 audio is composed of several layers:

This course will focus on the <audio> element. We present the  Web Audio API and other advanced HTML5 features in the  W3Cx HTML5 Apps and Games course.

The attributes, event set and JavaScript API of the <audio> element are just a “reduced” version of the ones from the <video> element, and here we will only address the differences and peculiarities.

The <audio> element, basic usage

Here is a simple example (also available online example from JSBin):

Press play to stream the neigh of a horse: (does not work in git)

As you can see, the code is very similar to the basic <video> element usage.

Click to expand!
1.   <!DOCTYPE html>
2.   <html lang="en">
3.     <head>
4.       <meta charset="utf-8"/>
5.       <title>horse song</title>
6.  
7.     </head>
8.     <body>
9.       <audio controls="controls">
10.        <source src="https://mainline.i3s.unice.fr/mooc/horse.ogg" 
             type="audio/ogg" />
11.        <source src="https://mainline.i3s.unice.fr/mooc/horse.mp3" 
             type="audio/mp3" />
12.        Your browser does not support the audio element.
13.        Download the audio/video in
14.        <a href="https://mainline.i3s.unice.fr/mooc/horse.ogg">OGG</a>
15.        or <a href="https://mainline.i3s.unice.fr/mooc/horse.mp3">MP3</a>
16.        format.
17.      </audio>
18. 
19.    </body>
20.  </html>

In this example, just as for the <video> element, we used the controls attribute in order to render the play/stop, time, volume and progress widgets.

Notice the other similarities: between the <audio>…<audio> tags, we added a text message that is displayed if the Web browser doesn’t support the  <audio> element, and we used several <source>…</source> elements that link to different audio formats for the same file. The browser will use the first format it recognizes.

External resources

2.1.3 Attributes of <video> and <audio>

Most useful attributes of the <video> element

Here are the most common attributes you can use with the <video> element. They are self explanatory…

Be careful if you target mobile applications or if you have multiple videos on the same page

The autoplay attribute is not recommended if your Web site targets mobile applications (actually, it is often ignored by mobile browsers), as it may consume bandwidth even if the user is not interested in watching the proposed video. If you target mobile devices, we recommend using preload=none as well, as the default value for this attribute is auto.

Best practice: do not use autoplay and add preload=“none” if you target mobile devices or if you have multiple audio/video files on the same page.  For example,  this page contains many audio elements and it does not make sense to have them preload or autoplay.

About the poster attribute

If the poster attribute is missing, usually the first non-blank frame of the video will be used as the image that is shown when the video is not playing.

About the autoplay attribute for general use

Do not abuse of the autoplay attribute. We talked earlier about mobile applications, but even on desktop applications it’s usually a bad idea to use it (except for WebCams and for some animations with small video loops, without sound, or for sites like YouTube, with just videos).

Best practice: think twice before using the autoplay attribute, even for desktop applications.

Attributes of the <audio> element

The attributes you can use with the <audio> element are a subset of those available for the <video> element. Except for the poster attribute, they are all recognized and have the expected meanings:

As with the <video> element, the same best practice in regard to preload and autoplay attributes should be followed.

2.1.4 Styling Media Players with CSS

The <video> and <audio> elements are just like other HTML elements, so CSS can be used for styling, including CSS transitions, animations, etc.

An example of an audio player with some style

You can try this example online at JSBin.

To add some styling to the basic example we saw when we introduced the <audio>  element, we just add a <figure> with two children: an <img> and a  <figcaption>. Inside the <figcaption> we add the <audio> element from the previous example.

Please move the mouse pointer over this player’s elements: (does not work in git)

HTML Click to expand!
1.   <figure id="figaudio1">
2.     <img id="imghorse" width="200"
3.       src="https://upload.wikimedia.org/wikipedia/commons/d/d4/Nokota_Horses.jpg"
4.       alt = "a horse"/>
5.     <figcaption id="figcptionaudio1"> Press Play to hear the horse!
6.       <audio controls="controls">
7.         <source src="https://mainline.i3s.unice.fr/mooc/horse.ogg"
8.                 type="audio/ogg" />
9.         <source src="https://mainline.i3s.unice.fr/mooc/horse.mp3"
10.                type="audio/mp3" />
11.        Your browser does not support the audio element.
12.        Download the audio/video in
13.        <a href="https://mainline.i3s.unice.fr/mooc/horse.ogg">OGG</a>
14.        or <a href="https://mainline.i3s.unice.fr/mooc/horse.mp3">MP3</a>
15.          format.
16.      </audio>
17.    </figcaption>
18.  </figure>
Two gery-black horses in a field.

Press Play to hear the horse ! (does not work in git)

HTML click to expand!
1.   <figure id="figaudio1">
2.     <img id="imghorse" width="200"
3.       src="https://upload.wikimedia.org/wikipedia/commons/d/d4/Nokota_Horses.jpg"
4.       alt = "a horse"/>
5.     <figcaption id="figcptionaudio1"> Press Play to hear the horse!
6.       <audio controls="controls">
7.         <source src="https://mainline.i3s.unice.fr/mooc/horse.ogg"
8.           type="audio/ogg" />
9.         <source src="https://mainline.i3s.unice.fr/mooc/horse.mp3"
10.          type="audio/mp3" />
11.        Your browser does not support the audio element.
12.        Download the audio/video in
13.        <a href="https://mainline.i3s.unice.fr/mooc/horse.ogg">OGG</a>
14.          or <a href="https://mainline.i3s.unice.fr/mooc/horse.mp3">MP3</a>
15.          format.
16.      </audio>
17.    </figcaption>
18.  </figure>

CSS click to expand!
1.    #figaudio1 {
2.      width : 420px;;
3.      text-align:center;
4.      padding : 6px;
5.      background : white;
6.      margin : 0 11px 0px 0;
7.      border :solid 1px #888888;
8.      border-radius : 8px ;
9.    }
10.  
11.   #figcptionaudio1 {
12.     font-size : .8em;
13.     padding : 6px 8px;
14.     background : #dddddd;
15.     display :block;
16.     text-align :center;
17.     font-family : georgia, serif;
18.     font-style : italic;
19.     border-radius : 7px ;
20.   }
21.  
22.   #figaudio1 > img {
23.     background : #eeeeee;
24.     padding : 5px;
25.     border : solid 1px #444444;
26.   }
27.  
28.   /* For audio and img transitions/animation */
29.     audio, #figaudio1 > img {
30.     transition:all 0.5s;
31.   }
32.  
33.   #figaudio1 > img:hover {
34.     box-shadow: 15px 15px 20px rgba(0,0, 0, 0.4);
35.     transform: scale(1.05);
36.   }
37.  
38.   audio:hover, audio:focus, audio:active {
39.     box-shadow: 15px 15px 20px rgba(0,0, 0, 0.4);
40.     transform: scale(1.05);
41.   }

Changing the size of a video on the fly using CSS transforms

Resizing and rotating a video as the mouse pointer comes over it

See this example online (where you can modify the code on the fly) or just play the following video, and move the mouse pointer in and out of the video while it’s playing.

Tilted screen example using pseudo css class: hover.

This example uses the pseudo CSS class :hover in order to track the mouseover event. On mouseover, it uses a CSS transition property that interpolates the changes in the scale and orientation of the video element (done using a transform CSS property).

The corresponding HTML source code is:

1.  <video <b>id="w3devCampusVideo"</b> autoplay controls>
2.  
3.    <source src=https://mainline.i3s.unice.fr/mooc/samuraiPizzacat.webm
4.      type=video/webm>
5.    <source src=https://mainline.i3s.unice.fr/mooc/samuraiPizzacat.ogg
6.      type=video/ogg>
7.    <source src=https://mainline.i3s.unice.fr/mooc/samuraiPizzacat.mp4
8.      type=video/mp4>
9.  </video>

… and the CSS source code is as follows:

1.  #w3devCampusVideo {
2.    width: 300px;
3.    <b>transition: all 0.5s ease-in-out;</b>
4.  }
5.   
6.  #w3devCampusVideo</b>:hover</b> {
7.    width:400px;
8.    transform:rotate(-5deg);
9.  }

Fullscreen video that resizes and maintains ratios

This is a trendy way of displaying videos.

Below you will find two examples that show how to do this trick. The first is for a “regular” video, using the <video> and <source> elements. This technique can also be used on any YouTube embedded videos (see Example #2 below).

The interesting part is that we use a 100% standard (and really small and simple) JavaScript code here to handle the window resize events and we just set regular CSS properties width and height of the video element, to resize the video.

Example #1: with a regular video

Full width, resizable, borderless video, just using plain CSS and JS DOM events.

Full width video like paypal site.

Here is the HTML code. It’s really simple, just notice the <body onload=“init();”> which calls the JavaScript init() function right after the page is loaded.

HTML click to expand!
1.   <!DOCTYPE html>
2.   <html lang="en">
3.   <head>
4.     <meta charset="utf-8">
5.     <title>Full width video like PayPal site</title>
6.   </head>
7.   <body onload="init();">
8.     <video id="myVideo" autoplay>
9.       <source
10.        src=https://mainline.i3s.unice.fr/mooc/samuraiPizzacat.webm
11.        type=video/webm>
12.      <source
13.        src=https://mainline.i3s.unice.fr/mooc/samuraiPizzacat.ogg
14.        type=video/ogg>
15.      <source
16.        src=https://mainline.i3s.unice.fr/mooc/samuraiPizzacat.mp4
17.        type=video/mp4>
18.    </video>
19.  </body>

Here is the CSS (remove margins, remove padding, hide parts that could overflow from the <body>):

body {
  margin:0;
  padding:0;
  overflow:hidden;
}

And now the JavaScript code:

1. var video;
2. function init() {
3.   // function called when the page is loaded
4.   video = document.querySelector("#myVideo");
5.   // For initial value
6.   video.width = window.innerWidth;
7.   video.height = window.innerHeight;
8.   // For dealing with window resize
9.   window.onresize = function() {
10.     video.width = window.innerWidth;
11.     video.height = window.innerHeight;
12.   };
13. }

Example #2: with a YouTube video

Full width, resizable, borderless YouTube video. To do this: just 100% standard CSS + DOM manipulation using JavaScript.

Example video cropping.

The CSS and JavaScript codes for this example are exactly the same as in Example #1.

Full screen video, pure CSS approaches

  1. Let’s use the video from the PayPal Web site, played full screen using only very simple CSS.

In this example, the video does not rescale; it’s just cropped if the browser window is resized. Enlarge your browser and you’ll see a man with a phone on the right. Resize your browser and you’ll see only part of the video.

Full screen video.

CSS code:

body {
  margin:0;
  padding:0;
  overflow:hidden;
}
 
video {
  width:100%;
  height:auto;
}
  1. Full screen video with CSS effects

This time the video is zoomed in so that it’s much bigger than the browser’s window. When we resize the browser, the part of the video that is visible adapts itself. It’s not “real resize” of the video. Try this example and read the explanation in this article by Dudley Storey.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Full screen video with CSS effects </title>
6. </head>
7. <body>
8.   <header>
9.     <video autoplay loop=""
10.       poster="https://mainline.i3s.unice.fr/mooc/polina.jpg"
11.       id="bgvid">
12.       <source src="https://mainline.i3s.unice.fr/mooc/polina.webm"
13.         type="video/webm">
14.       <source src="https://mainline.i3s.unice.fr/mooc/polina.mp4"
15.         type="video/mp4">
16.     </video>
17.   </header>
18.   <section>
19.     <h1>Full screen video with CSS effects</h1>
20.   </section>
21. </body>
22. </html>

CSS code:

CSS click to expand!
1. html, body{
2.   color:white;
3.   height: 100%;
4. }
5. header{
6.   height: 100%;
7.   background-image: url('https://mainline.i3s.unice.fr/mooc/dots.png'), url('#');
8.   background-repeat: repeat, no-repeat;
9.   background-size: auto, cover;
10.   background-position: center center, top left;
11.   font-family: sans-serif;
12.   color: #051a00;
13. }
14. header video {
15.   position:fixed;
16.   top:50%;
17.   left:50%;
18.   min-width:100%;
19.   min-height:100%;
20.   width:auto;
21.   height:auto;
22.   z-index:-100;
23.   transform:translateX(-50%) translateY(-50%);
24. }

The trick here is that:

the video is in the header, and the header has a plotted transparent background image (“dots.png”) that is repeated in X and Y (see lines 8 and 9).

  1. The video is positioned so that it’s origin (top left corner) is away from the visible surface (line 25), while it is set to take 100% of the surface (lines 20 and 21).

Full screen video that resizes and keeps its ratio, using the viewport units.

Full screen video that resized and keeps its ratio using viewport units.

This time we obtain the same result as with the first example that used JavaScript and a resize event. The video resizes correctly and keeps its ratio.

CSS Code

CSS click to expand!
1. body {
2.    margin: 0;
3. }
4.  
5. video {
6.    position: absolute;
7.    width: 100vw;
8.    height: 100vh;
9.    object-fit: cover;
10.    object-position: center center;
11. }

Discussion: why can’t we achieve perfect resizing with only CSS and the use of properties width=100% and height=100%?

Let’s use the same video to compare the different approaches again:

  1. Original approach, using JavaScript. This solution works on any browser, so we will focus on the two following methods, based on pure CSS.
  2. Using CSS 100% width and height properties (no JavaScript).
  3. Using CSS viewport units for width and height (no JavaScript).

Resizing the browser window shows that #1 (JavaScript) and #3 (viewport units) behave in the same way: the width or height of the video always fills the window (whichever is smaller), and we always see the whole video.

Conclusion: we can get full size video without JavaScript by using viewport units (vw and vh), unless we need to support some old browsers (see their current support on CanIUse).

Setting the video to 100% width and height results in different behavior:

2.1.5 Control Players From JavaScript

The <video> and <audio> elements have methods, properties/attributes and events that can be manipulated with JavaScript. Using the DOM API, it’s possible to manipulate an audio or video element as a JavaScript object that has:

The set of properties/attributes/methods of the <audio> and <video> elements is called an “API” (Application Programming Interface). For example, we will speak here of the “media API” to talk about the associated API.

Like any HTML element, the <video> element can be manipulated/created using the DOM JavaScript API. Here is an example of programmatically creating a <video> element:

1.  var video = document.createElement('video');
2.  video.src = 'video.mp4';
3.  video.controls = true;
4.  document.body.appendChild(video);

This will create a complete video player for the file “video.mp4”, with control buttons, and will add it to the <body> element of the page.

Example that shows how to call play/pause or rewind a video

Please look at this interesting example:

How to call the play/pause/rewind functions.

Note that in order to play the video, you must click on the “vid.play()” text. To pause it, you click on the “vid.pause()” text, and so on. Notice the text at the top of the video, as well as the transparency. The text can be selected, since all the elements displayed are pure DOM objects. You can zoom the page in and out, etc. This was not possible with the Flash technology.

Conclusion:  you can very easily change the look and feel of the standard video player by using custom CSS and designing your own control widgets. We can find many examples of such video players that offer extended functionalities on the Web. We will present some of them later in the course, but before that, let’s see a little more of what we can do using the JavaScript API of the <video> element (it will be an identical approach for the <audio> element, since it shares the same API by a very small margin).

2.1.6 The JavaScript API

Methods, properties, and events

The JavaScript API gives you powerful tools to manipulate the <video> element, as the video object provides many properties, methods and events.

The complete list of events can be found in the HTML5 living standard specification.

The list of properties can be found at the W3C HTML5 Video Events and API page. This page is interesting for Web developers because it shows an interactive view of the different values and events changing over time while the video is playing within the page.

Try the direct link, and play with the different buttons and look at the table of events and properties that will change in real time. The displayed names show the properties, events, and methods from the API.

HTML5 video events and API.

Here is a table that shows the most interesting methods, properties, and events provided by the <video> element API

We provide this as a quick reminder - keep in mind that the complete list is much longer!

Methods Properties Events
play() currentSrc play
pause() currentTime pause
load() startTime (readonly) progress
canPlayType() videoWidth error
videoHeight timeupdate
duration (readonly) ended
ended (readonly) abort
error empty
paused (readonly) emptied
muted waiting
seeking loadedmetadata
volume
height
width
seekable (readonly)
played (readonly)

In the next pages, let’s see, through a set of examples, how to use these most important properties, methods, and events…

2.1.7 The <video> Element JavaScript API

The JavaScript API is useful for implementing playlists, making custom user interfaces and many other interesting things. The “enhanced HTML5 multimedia players” lesson presented further on the course relies heavily on this API.

Example #1: how to use external buttons to control the player’s behavior

This example gives the first steps towards writing a custom video player. It shows basic usage of the JavaScript API for adding custom buttons to play/pause the video or to go back to the beginning by setting the currentTime property to zero.

Try it online:

This example gives the first steps towards writing a custom video player. Example of custom controls.

Source code extract:

Click to expand!
1.  <video id="vid" controls>
2.    <source src=https://mainline.i3s.unice.fr/mooc/samuraiPizzacat.webm
3.      type=video/webm>
4.  </video>
5.  <p>Example of custom controls:</p>
6.  <button onclick="playVideo();" style="cursor: pointer;">Play</button>
7.  <button onclick="pauseVideo();" style="cursor: pointer;">Pause</button>
8.  <button onclick="rewindVideo();" style="cursor: pointer;">
9.    Back to beginning</button>
10. <script>
11.   vid = document.querySelector("#vid");
12.   function playVideo() {
13.     vid.play();
14.   }
15.   function pauseVideo() {
16.     vid.pause();
17.   }
18.   function rewindVideo() {
19.     vid.currentTime = 0;
20.   }
21. </script>

Explanations:

Example #2: how to detect the end of a video and start another one

This example listens to the ended event, and calls a callback function when the video is ended.

This example listens to the ended event, and calls a callback function when the video is ended.

Source code extract:

Click to expand!
1. <video src="video.ogv" id="myVideo">
2.   video not supported
3. </video>
4. <script type='text/javascript'>
5.   var vid = document.querySelector('#myVideo');
6.   vid.addEventListener('ended', playNextVideo, false);
7.   function playNextVideo(e) {
8.     // Whatever you want to do after the event, change the src attribute
9.     // of the video element, for example, in order to play another video
10.   }
11. </script>

Example #3: how to manage playlists - sequential movies

This example detects the end of a video, then loads the next video, changes the src attribute of the video element and plays the video.

Check the online example below: use the progress cursor to go near the end of the first video that is being played, and see how it continues with the next video.

Manage playlists.

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.    <head>
4.      <meta charset="utf-8"/>
5.      <title>Sequential Movies</title>
6.      <script>
7.        var myVideo;
8.        var currentVideo = 0;
9.        var sources = [
10.         "https://mainline.i3s.unice.fr/mooc/samuraiPizzacat.mp4",
12.         "https://www.archive.org/download/AnimatedMechanicalArtPiecesAtMit/P1120973_512kb.mp4"
12.       ];
13.       // Set the src of the video to the next URL in the playlist
14.       // If at the end we start again from beginning (the modulo
15.       // source.length does that)
16.       function loadNextVideo() {
17.         myVideo.src = sources[currentVideo % sources.length]
18.         myVideo.load();
19.         currentVideo++;
20.       }
21.       // listener plays the video
22.       function loadAndplayNextVideo() {
23.         console.log("playing " + sources[currentVideo % sources.length])
24.         loadNextVideo();
25.         myVideo.play();
26.       }
27.       // Called when the page is loaded
28.       function init(){
29.         // get the video element using the DOM api
30.         myVideo = document.querySelector("#myVideo");
31.         // Defines a callback function called each time a video ended
32.         myVideo.addEventListener('ended', loadAndplayNextVideo, false);
33.         // Loads the first video when the page is loaded
34.         loadNextVideo();
35.       }
36.     </script>
37.   </head>
38. <body onload="init()">
39.   <video id="myVideo"
40.     controls>
41.   </video>
42. </body>
43. </html>

Explanations:

2.1.8 Advanced Extended Examples

In this section, we propose five extended examples that use more JavaScript and more complex CSS manipulation. They might be a little hard to understand if you are a JavaScript beginner, but don’t be afraid to try and test them, look at the code, etc.

Some examples are given “as is”, such as the custom video player that uses SVG (at the end of the page); if you are interested, you may view the code.

Example #1: a player showing the use of every type of CSS3 transformation

Please see this example online, originally written by Chris Heilmann, and tuned by us ;).

2d transformations: scale, translate & rotate.

Don’t forget to click the JavaScript and CSS tabs of the CodePen in order to display the JavaScript code that creates the buttons on the right of the video, and the CSS that processes the different clicks and applies CSS3 transforms.

This example shows a lot:

Example #2: how to track all possible events and manipulate many properties

This example also shows how to handle failures. See the code and play with this example below:

Example #2. How to track all possible events.

Below is a piece of code for handling errors during video playback:

Click to expand!
1. ...
2. vid.addEventListener('error', function(evt) {
3.   logEvent(evt,'red');
4. }, false);
5. ...
6. function logEvent(evt, color) {
7.   switch (evt.type) {
8.     ...
9.         case 'error':
10.         var error = document.querySelector('video').error;
11.         switch (error.code) {
12.         case error.MEDIA_ERR_ABORTED:
13.           note.innerHTML = "fetching aborted at the user's request";
14.           break;
15.         case error.MEDIA_ERR_NETWORK:
16.           note.innerHTML = "a network error caused the browser to stop fetching the media";
17.           break;
18.         case error.MEDIA_ERR_DECODE:
19.           note.innerHTML = "an error occurred while decoding the media";
20.           break;
21.         case error.MEDIA_ERR_SRC_NOT_SUPPORTED:
22.           note.innerHTML = "the media indicated by the src
23.             attribute was not suitable";
24.           break;
25.         default:
26.           note.innerHTML = "an error occurred";
27.           break;
28.       }
29.     break;
30.   }
31.   ...
32. }

Example #3: how to display a percentage of buffering when using a slow connection

See the example online here too.

Example #3. How to display a percentage of buffering.

Note that on mobile phones, the video does not start until the user presses the play control or clicks on the video picture. Using the “canplaythrough” event is a trick to call a function that starts the video player as soon as the page is loaded on desktop. This event is not supported by mobile devices, so if you try this example on a mobile, the video will not start automatically.

As explained by the Apple Developer Web site:  “The buffered property is a TimeRanges object: an array of start and stop times, not a single value. Consider what happens if the person watching the media uses the time scrubber to jump forward to a point in the movie that hasn’t loaded yet—the movie stops loading and jumps forward to the new point in time, then starts buffering again from there. So the buffered property can contain an array of discontinuous ranges. The example simply seeks the end of the array and reads the last value, so it actually shows the percentage into the movie duration for which there is data.”

Source code extract:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <title>JavaScript Progress Monitor</title>
5.   <meta charset="utf-8"/>
6.   <script>
7.     function getPercentProg() {
8.       var myVideo = document.getElementsByTagName('video')[0];
9.       var endBuf = myVideo.buffered.end(0);
10.       var soFar = parseInt(((endBuf / myVideo.duration) * 100));
11.       document.getElementById("loadStatus").innerHTML = soFar + '%';
12.     }
13.     // Will be called as soon as the page is ready on desktop computer,
14.     // Only when a user clicks on play control or image on mobile
15.     function myAutoPlay() {
16.       var myVideo = document.getElementsByTagName('video')[0];
17.       myVideo.play();
18.     }
19.     function addMyListeners(){
20.       var myVideo = document.getElementsByTagName('video')[0];
21.       myVideo.addEventListener('progress', getPercentProg, false);
22.       // Calls autoplay only if the device is adapted
23.       myVideo.addEventListener('canplaythrough', myAutoPlay, false);
24.     }
25.   </script>
26. </head>
27. <body onload="addMyListeners()">
28.   <h1>Check progression of buffering before playing a movie. Useful withy
29.     slow connexion (3G, etc.)</h1>
30.   <div>
31.     <video controls>
32.       <source src=https://html5doctor.com/demos/video-canvas-magic/video.webm
33.         type=video/webm>
34.       <source src=https://html5doctor.com/demos/video-canvas-magic/video.ogg  
35.         type=video/ogg>
36.       <source src=https://html5doctor.com/demos/video-canvas-magic/video.mp4
37.         type=video/mp4>
38.     </video>
39.     <p id="loadStatus">Buffering...</p>
40.   </div>
41. </body>
42. </html>

Example #4: how to use SVG elements as external controllers

This is the ultimate way of doing a real custom player: redesign your own controls using SVG shapes! This example (try it online)is given “as is” for those of you who may be curious.

Example #4. Snapshot of the Online Example of a Custom XHTML5/SVG Video 
    Player.  Image Shows a Cup of Coffee on a Table.

Example #5: a custom video player written by a previous student

This is more an example than a tutorial. Maurice, a student who followed the precursor version of this MOOC, had the assignment to write a custom video player with playlist, video thumbnails, custom play/pause/next/previous/volume controls, and present it in a Web page that used a nice layout based on the HTML5 structuring elements studied previously.

Here is the online example. We recommend that you look at the source code:

2.3.1 Enhanced HTML5 Media Players and Frameworks

This section introduces the HTML5 <track> element, useful for adding closed captions, subtitles, descriptions, and metadata to your videos. It comes with a new JavaScript API.

The WebVTT format used for describing a track file is also presented in this chapter.

Most of the major desktop browsers support HTML5 captioning

Please check the browser support related to the <track> element support by browsers.

Some definitions


The accessibility features of TV programs often propose both options for people with hearing deficiencies.

Typical use: add a subtitle/caption track to a <video> element

Important warning!!

The <track> element cannot be used with a file:// URL. Please use https:// and a Web server. Your server must use a special MIME format for the .vtt files: text/vtt;charset=utf-8 (set by default on most servers now).

Examples of the lines to add to an Apache Web server:

1.  <Files mysubtitle.vtt>
2.    ForceType text/vtt;charset=utf-8
3.  </Files>

It is worth mentioning that most browsers work well with WebVTT, even if the MIME type is not defined.

Here is an example of a video element that includes a <track> element in the .vtt (WebVTT) format (line 9 in the source code shown below):

Example using a track element in the .vtt format.

The example uses a <track> element to insert basic captions to the video: sounds and music are described, in addition to standard subtitles that correspond to what the different movie characters say.

<video height="272" width="640"
  poster="https://mainline.i3s.unice.fr/mooc/q1fx20VZ-640.jpg"
  crossorigin="anonymous"
  controls>
<source src="https://mainline.i3s.unice.fr/mooc/sintel.mp4"
  type="video/mp4">
<source src="https://mainline.i3s.unice.fr/mooc/sintel.webm"
  type="video/webm">
<track src="https://mainline.i3s.unice.fr/mooc/sintel-captions.vtt"
  kind="captions" label="Closed Captions" default>
</video>

Notes:

Multiple tracks may be included in a video element

Multiple tracks are needed to support different langages, video captions for the hearing-impaired, subtitles, etc.

Below is an example (from the specification) that includes multiple <track> elements (subtitles for three languages and captions only for English):

Click to expand!
1. <video src="brave.webm">
2.   <track kind=subtitles src=brave.en.vtt
3.     srclang=en
4.     label="English">
5.   <track kind=captions src=brave.en.hoh.vtt
6.     srclang=en
7.     label="English for the Hard of Hearing">
8.   <track kind=subtitles src=brave.fr.vtt
9.     srclang=fr
10.     lang=fr 
11.     label="Français">
12.   <track kind=subtitles src=brave.de.vtt
13.     srclang=de
14.     lang=de
15.     label="Deutsch">
16. </video>

Note the use of some new attributes in the <track> element:

External resources

2.3.2 The WebVTT Format

The ";WebVTT: The Web Video Text Tracks Format " defines files that contain text for captions and subtitles, and much more… The WebVTT files are used with the src attribute of the <track> element, that can be used inside a <video>…</video>.

In the interactive example presented before, we used a file called sintel-captions.vtt:

<video height="272" width="640"
  poster="https://mainline.i3s.unice.fr/mooc/q1fx20VZ-640.jpg"
  crossorigin="anonymous"
  controls>
  ...
<track src="https://mainline.i3s.unice.fr/mooc/sintel-captions.vtt"
  kind="captions" label="Closed Captions" default>
</video>

And here is an extract of the corresponding sintel-captions.vtt file:

Click to expand!
1.  WEBVTT
2.  
3.  00:00:01.000 --> 00:00:02.042
4.  (drumbeat)
5.  
6.  00:00:07.167 --> 00:00:12.025
7.  (plaintive violin solo playing)
8.  
9.  00:00:15.000 --> 00:00:18.183
10. (wind whistling)
11. 
12. 00:00:24.167 --> 00:00:27.025
13. (orchestra music swells)
14. 
15. 00:00:43.033 --> 00:00:43.192
16. (weapons clash)
17. 
18. 00:00:44.000 --> 00:00:44.175
19. (gasps)
20. 
21. 00:00:44.183 --> 00:00:45.158
22. (grunts)
23. 
24. 00:00:45.167 --> 00:00:47.058
25. (groaning)
26. 
27. 00:00:54.192 --> 00:00:55.150
28. (blade rings)
29. 
30. 00:00:55.158 --> 00:00:57.008
31. (bellowing)
32. 
33. 00:00:57.017 --> 00:00:58.067
34. (grunting)
35. 
36. 00:00:59.075 --> 00:01:00.133
37. (panting)
38. 
39. 00:01:05.108 --> 00:01:06.125
40. (cries out in agony)
41. 
42. 00:01:08.050 --> 00:01:09.058
43. (panting)
44. 
45. 00:01:12.092 --> 00:01:13.142
46. (panting)
47. 
48. 00:01:14.017 --> 00:01:18.125
49. (orchestra plays ominous low notes)
50. 
51. 00:01:31.058 --> 00:01:35.133
52. (plaintive violin solo returns)
53. 
54. 00:01:46.158 --> 00:01:49.058
55. This blade has a dark past.
56. 
57. 00:01:51.092 --> 00:01:54.108
58. It has shed much innocent blood.
59. 
60. 00:01:57.083 --> 00:02:00.000
61. You're a fool for traveling alone
62. so completely unprepared.
63. 
64. 00:02:01.100 --> 00:02:03.033
65. You're lucky your blood's still flowing.
66. 
67. 00:02:04.183 --> 00:02:06.075
68. Thank you.

This format is rather simple, but we still recommend reading this excellent article from Mozilla Developer Network that explains in detail all the different options.

Each “element” in this file has a starting and ending time, plus a value (the text that will be displayed), followed by a blank line (blanklines are separators between elements).

Each element is called “a cue”, and may optionally have an ID that will be useful when using the track element JavaScript API, inparticular the getCueById() method of TextTrack objects.

Example of a .vtt file with numeric IDs:

9
00:00:21.000 --> 00:00:22.000
to hear from you

10
00:00:22.500 --> 00:00:25.000
We want to hear what inspires you as a developer

IDs may also be defined as strings, and values can use HTML as well:

Opening
00:00:00.000 --> 00:00:30.000
Welcome to our <i>nice film</i>

The displayed text can span multiple lines, but blank lines are not allowed, as they would be interpreted as a separator:

00:01:57.083 --> 00:02:00.000
<p>You're a fool for traveling alone</p>
<p>so completely unprepared.</p>

External resource:

2.3.3 Adding Subtitles to a Video

Let’s look at a simple example. First, you need a video on one of the formats/codecs supported by the browsers you target. A recommended codec is mp4/H264, but other formats, such as webm, may have some advantages if the browser supports them. For example, webm allows the video to start playing after a much shorter buffering time. In other words, try if possible to provide the video encoded with more than one codec.

For this, use any sort of open source, free or commercial video encoding software, such as Handbrake (free, open source) or Super (free). There are also online video encoding services, and you can even upload your video to YouTube, let it encode your video in several resolutions and codecs, and use a browser extension such as Video DownloadHelper (for Firefox) or JDownloader, to download the video in your chosen formats.

So, let’s suppose you have a video like the one below (we included it on YouTube for practical reasons). This video has subtitles (you can activate them in the YouTube player), but the goal of this lesson is to explain how we made them without using the YouTube embedded tools, which do not allow export the subtitle file to be exported in the webVTT format.

Picture guitar picking.

And if you’ve also got it in mp4/H264 and in webm formats, here is how you can embed it in your page using the video element:

<video id="myVideo" width=500 controls>
  <source
    src="videos/SameOldSongAndDanceRogerLathaudPart1MidRes.mp4"
    type="video/mp4">
  <source
    src="videos/SameOldSongAndDanceRogerLathaudPart1MidRes.webm"
    type="video/webm">
  <track src="videos/subtitles.vtt"
    kind="subtitles" srclang="en" default>
</video>

At line 9, we added a <track> element to add English subtitles, as the guitar teacher there is speaking in French. We will now explain how we created this subtitle track.

Adding subtitles to the video

Now, we need to create a WebVTT file for this video. How can we synchronize an English translation of what the guitar teacher says in French?

Many tools - both free and commercial - are available to add subtitles to a video. Most are native applications you need to install on your computer. However, a free and very practical tool is available for doing this 100% in a Web browser: amara.

Go to the above Web site, click on the “subtitle a video” link, then follow the different tutorials/instructions. It will ask for a YouTube URL, so it’s better to first upload your video to YouTube (even in private mode). Once you have entered the URL of your video, you will have an online subtitles/caption editor. Enter your subtitles and sync them until you are happy with the results.

Example subtitles and captions.

Once your subtitles/captions are ok, you will be able to upload them to YouTube, or -this is what we wanted first- download them as WebVTT format:

Youtube make subtitles.

Note that YouTube can also help you “make subtitles” with its speech recognition tool, but you will only be able to export in .srt format afterwards. You will have to convert this format afterwards to .vtt.

Try your subtitled/captioned video

Example guitar picking subtitle captions.

2.3.4 Styling Captions

In this section, we will look at different possibilities for styling and positioning  the text displayed as captions/subtitles while playing a video.

The example below shows how we can do that (play the video for 40s, look at the positions and styles of the subtitles and captions, look at the HTML):

Blank CodePen.

The WebVTT file is shown below. Notice the new attributes that have been added on the right end of the duration values:

Click to expand!
1.  WEBVTT
2.  
3.  00:00:01.000 --> 00:00:05.000
4.  These captions test some features of the WebVTT formats
5.  
6.  00:00:06.000 --> 00:00:10.000 line:5%
7.  This cue is positioned at the top of the video
8.  
9.  00:00:11.000 --> 00:00:15.000 position:5% align:start
10. This cue is positioned at the left side of the video.
11. 
12. 00:00:16.000 --> 00:00:20.000 position:95% align:end
13. And this one ate the right side.
14. 
15. 00:00:21.000 --> 00:00:25.000 size:33%
16. This cue is only a third of the width of the video, hence the multiple line breaks.
17. 
18. 00:00:26.000 --> 00:00:30.000
19. This cue contains <b>bold</b> text.
20. 
21. 00:00:31.000 --> 00:00:35.000
22. This cue contains <i>italic</i> text.
23. 
24. 00:00:36.000 --> 00:00:40.000
25. This cue contains <u> </u> text.
26. 
27. 00:00:41.000 --> 00:00:45.000
28. This cue contains <b><i><u>bold, italic, 
  underlined</u></i></b> text.
29. 
30. 00:00:46.000 --> 00:00:50.000
31. <c.myclass>This cue contains the class "myclass".
32. Browsers that support ::cue CSS should make it red.</c>
33. 
34. 00:00:51.000 --> 00:00:55.000
35. The following cue contains two voices.
36. Tarzan should be blue and Jane green.
37. 
38. 00:00:56.000 --> 00:01:00.000
39. <v Tarzan>Me Tarzan...
40. <v Jane>That would make me Jane!
41. 
42. bigtext
43. 00:01:01.000 --> 00:01:05.000
44. This cue has a unique id.
45. Using CSS, its font size should be 150%.
46. 
47. 00:01:06.000 --> 00:01:10.000
48. The <00:01:06.333>text <00:01:06.666>in <00:01:07.000>this 
  <00:01:07.333>cue <00:01:07.666>should <00:01:08.000>grow
49. <00:01:08.333>one <00:01:08.666>word <00:01:09.000>at 
  <00:01:09.333>a <00:01:09.666>time
50. 
51. 00:01:11.000 --> 00:01:15.000
52. That's it! For now...

How to position the subtitles

Example video: positioning subtitles.

The video example tests nearly all the possibilities for positioning subtitles/captions, styling (using HTML element wrapping with <b>, <i>,  etc.), voicing (subtitles corresponding to different characters will be displayed in different colors) and CSS styling.

It is possible to locate the cues in the video viewport using absolute or relative values. The attributes that position the text are located on the same line as the cue definition, like at line 9 of the previous WebVTT example file:

9.  00:00:11.000 --> 00:00:15.000<b> position:5% align:start</b>
10. This cue is positioned at the left side of the video.

There are several possible values:

And so on. Please look at the video as it is self-explanatory.

Use of <b>, <i>, <u> for styling subtitles / captions

One can use the HTML elements  <b>, <i>, <u> to modify the rendering of subtitles and captions, as illustrated in the example below:

This cue contains bold, italic and underline text.

Using CSS classes for styling

It is possible to style using CSS classes as part of a cue value, using the <c> element. You can specify the CSS class that should be applied by adding “.” followed by the name of your CSS class. Here is an example:

1. <c.myclass>This cue contains the class "myclass".
2. Browsers that support ::cue CSS should make it red.</c>
Image of a video frame showing cue containing the class 'myclass'.

CSS rules used in this example:

1.  <style type="text/css">
2.       ::cue(.myclass) { color: red; }
3.       ::cue(v[voice="Tarzan"]) { color: blue; }
4.       ::cue(v[voice="Jane"]) { color: green; }
5.       ::cue(#bigtext) { font-size: 150%; }
6.  </style>

The ::cue pseudo element selector is used to match “cues” in the webVTT file. You add parenthesis and a secondary CSS selector to match cues that have a particular id, or a particular CSS class, etc. Look at the CSS above and at the extract from the webVTT file, play the video, you will understand how the above CSS classes affect the rendering of the subtitles for Jane and Tarzan’s voices.

Support differs from one browser to another, see this compatibility table (from CanIuse). Note however that most of the enhanced players presented further on in the course provide full support.

Using voicing for styling: the <v> element

Here is an example that shows the voices of the different characters displayed with different colors:

v tag example.  Tarzan and Jane.

Using the <v> tag, you will distinguish different voices that should be displayed in different colors (depending on the HTML5 video player implementation). See the CSS presented in the previous section to see how to specify the colors for the different voices.

Example source code:

00:00:56.000 --> 00:01:04.000
2.  <v Tarzan>Me Tarzan...
3.  <v Jane>That would make me Jane!

2.3.5 Chapter

When you play a movie in DVD or Blu-Ray format, a menu appears on the screen: play a movie, choose subtitles, etc. Usually there is also a “chapters” menu that allows you to quickly access a part of the movie. With videos on the Web, one can also indicate the chapter breakdown, using WebVTT files and a <track> element/tag.

Adding chapters is very similar to adding subtitles/captions. Look at line 5 in the code below, where we use an extra <track> element with a kind=“chapters” attribute.

1.  <video poster="webvtt_talk.png" style="width:100%" preload="metadata">
2.    <source src="webvtt_talk.webm">
3.    <source src="webvtt_talk.mp4">
4.    <source src="webvtt_talk.ogv">
5.    <track id="nav" src="webvtt_talk_navigation.vtt" 
        <b>kind="chapters"<b> srclang="en">
6.    <track id="cc" src="webvtt_talk_captions.vtt" kind="captions"
7.      label="captions" srclang="en" default>
8.  </video>

Here is an example of WebVTT files with defined chapters. Each “CUE” at lines 3, 7, 11, … can bear any name. We use “Chapter 1, Chapter 2, Ending, etc.” but you are free to name them as you wish.

What makes them special is that the track has an attribute kind=“chapters”.

Often, the <video> elements rendered in standard browsers ignore chapters, but enhanced HTML5 players take them into account, and it’s not much of a stretch to make your own enhanced player with a nice chapter presentation, as we’ll see in a more advanced chapter of this course.  For example, we’ll generate a custom navigation menu, using the <track> JavaScript API (explained later in this section).

Example of a WebVTT file that defines chapters:

Click to expand!
1.  WEBVTT FILE
2.   
3.  Chapter 1
4.  00:00:00.000 --> 00:00:10.700
5.  Title Slide
6.   
7.  Chapter 2
8.  00:00:10.700 --> 00:00:47.600
9.  Introduction by Naomi Black
10.  
11. Chapter 3
12. 00:00:47.600 --> 00:01:50.100
13. Impact of Captions on the Web
14.  
15. Chapter 4
16. 00:01:50.100 --> 00:03:33.000
17. Requirements of a Video text format
18.  
19. Ending
20. 00:03:33.000 --> 00:04:57.766
21. Simple WebVTT file
22.  
23. Greetings 6
24. 00:04:57.766 --> 00:06:16.666
25. Styled WebVTT file

An example of what you can achieve using chapters, using the JW Player

Using the JW player.

2.3.6 Tools for Creating WebVTT Files

Many tools are available to make and edit HTML5 video and caption/subtitles:

We do not claim that these are the best tools, so feel free to share your discoveries in the discussion forum!

2.3.7 The <track> JavaScript API

Most “complex” HTML elements like forms, audio or video players, come with a JavaScript API that allows you to control them programmatically, customize them, etc.

The <track> element comes with a powerful API that is used to develop many interesting features such as:

Examples of use

Example #1: add a navigation menu to start playing the video at given chapters

This example shows a video with an enhanced progress bar that displays the different chapters as small “clickable” squares. Furthermore, using the JavaScript API of the <track> element, this Web site builds a navigation menu (on the right of the video):

Navigation menu using the track JavaScript API.

Example #2: sync video with Google Map and Google Street View

Check this demo (only on Chrome) by Sam Dutton: it shows a video that comes with a WebVTT file that contains longitudes and latitudes. When the video plays, JavaScript functions are called at given times and get the longitude and latitude. A Google Map and a Google Street views are updated in real time.

Video sync with map and street views.

Example #3: sync guitar tablatures and music score with a video

This example shows how we manage to render music scores in real time as the video plays.

Some JavaScript code listens to the ontimeupdate event while the video is playing. We use the currentTime property of the video to know exactly where we are in the video. Finally, we also rely on an external library to render in an HTML5 canvas the bars corresponding to the current video explanations. We render in real time guitar pro tablatures using the alphatab.net library.

We render in real time guitar pro tablatures using the alphatab.net library.

2.3.8 Enhanced HTML5 Video Players

There are numerous “enhanced” video players; most are free and open source, some are commercial. They offer lots of features, which are listed below. Not all of these features are available in every player, this list just illustrates what can be added to the standard <video> element.

We call them “HTML5 enhanced video players” because on top of being based on the <video> element, they come with custom features, custom look’n’feel, chapters, etc., based on a JavaScript API that makes such customization possible.

Accessible players

For those of you interested in this particular topic, here is a very good resource that compares most of the players presented in this section, in terms of accessibility. This resource has links to players designed especially for people with disabilities: accessible media players and resources.

Advantages and disadvantages of using a custom player

Advantages of enhanced video players:

Advantages of relying only on the <video> element rather than onan enhanced player:

Video.js: a framework for building your own custom video player

Open source, and made for developers, video.js comes with many plugins (chapters, thumbnails etc.).

Video JS Examples: Make your player yours.

Which should I use? the <video> element and my own customization or an out of the box enhanced player?

Either solution (basic player or enhanced player) is good and HTML5 compliant.

Popular players such as JWPlayer have many explanations and examples on their Web sites, and are either free of charge or come with free versions.

Interesting comparisons and reviews are available on the following Web sites:

Example screenshots

Scrub Bar thumbnails (JWPlayer)

Scrub bar thumbnails.

Custom look’n’feel and logo (Sublime video player):

Custom look and feel with logo.

Chapters and chapter thumbnails (JWPlayer):

Chapters and chapter thumbnails.

PayPal accessible player:

Paypal accessible player.

LeanBack (says “free for non-commercial use”, licensing is not very clear…):

Leanback player.

2.4.1 Webcam, microphone: the getUserMediaAPI

Introduction

HTML5 has introduced, with the getUserMedia and MediaDevices APIs, a way to control webcams and microphones programmatically. This allows, for example, to create an identification form where one can simply click on a button to capture his portrait from the Webcam of his computer or smartphone. It will also allow to record a video or audio extract (for example for a “telephone answering machine” type application), to create video conference applications running in the Web browser, or even musical applications if you plug a guitar into a sound card. The possibilities are numerous.

We will therefore study together how to access the audio and video streams of the hardware on which a Web application is going to run, and how to parameterize them (choice of camera, resolution, etc.).

The getUserMedia API

The getUserMedia API is useful for controlling a Webcam video stream.

This API is one component of the WebRTC specification. When using getUserMedia, to manage a webcam with its video stream, we will always use it in conjunction with the <video> element. We could also use it with the <audio> element if we are only interested in using a microphone (i.e. from a computer), or any sound input .

The getUserMedia API, when dealing with video streams, is always used in conjunction with the <video> element.

Screenshot of a simple Web camera webcam display in a Web page.

Typical use of the getUserMedia API with a Webcam

The main idea is to set the srcObject attribute of a <video> element to the live video stream object coming out of the Webcam. To get this stream, you’ll have to call the navigator.getUserMedia(params) method from the getUserMedia API, that returns an ES6 promise (ES stands for “ECMAScript” and is the scripting language that forms the basis of JavaScript). Do not panic if you do not know ES6’s promises! The syntax is very simple, and you’ll learn what you need from the provided examples.

The stream is passed as a parameter to the then() method returned by the promise, as in this typical example (you can run it and see the result by clicking on the “CodePen” logo at the top right):

Click to expand!
1. <video id="myVideo" autoplay>Fallback msg here.</video>
2. <script>
3.   if (navigator.mediaDevices.getUserMedia) {
4.     // request video and audio stream from the user's webcam
5.     navigator.mediaDevices.getUserMedia({
6.       audio: true,
7.       video: true
8.     }).then((stream) => {
9.       let video = document.querySelector('#myVideo');
10.       video.srcObject = stream;
11.       video.play();
12.     }).catch((error) => {
13.       console.log('navigator.getUserMedia error: ', error);
14.     });
15.   }
16. </script>
17. We can also simplify the code by using a writing with async/await of JavaScript:
18. <video id="myVideo" autoplay>Fallback msg here.</video>
19. <script>
20.   async function startWebCam() {
21.     // request video and audio stream from the user's webcam
22.     let stream = await navigator.mediaDevices.getUserMedia({
23.       audio: true,
24.       video: true
25.     });
26.     let video = document.querySelector('#myVideo');
27.     video.srcObject = stream;
28.     video.play();
29.   }
30.   startWebCam();
31. </script>

HTTPS is mandatory: for getUserMedia to work, it is mandatory to access the page that contains the JavaScript code through https://, for security reasons.

Support of getUserMedia/stream is very good in all modern browsers, including mobile ones. All the video conferencing applications you use that run in a Web browser are based on this API (Google Meet, Jitsi, Bigblue Button), and even Microsoft Teams is a web application in disguise.

2.4.2 More On getUserMedia

Let’s see some more examples of what we can do with the getUserMedia API: start/stop the Webcam, take a screenshot from the current video stream from the Webcam, and apply CSS effects in real time. Below, we give links to some cool examples available on the Web.

How to stop/release the Webcam

Resulting image of Michel Buffa using his WebCam, drinking from a ladle 
    with two custom controls below.
Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Webcam start/stop</title>
6.   <script>
7.     let webcamStream;
8.     function startWebcam() {
9.       // request video and audio stream from the user's webcam
10.       navigator.mediaDevices.getUserMedia({
11.       audio: true,
12.       video: true
13.       }).then((stream) => {
14.       let video = document.querySelector('#video');
15.       video.srcObject = stream;
16.       video.play();
17.       webcamStream = stream;
18.       }).catch((error) => {
19.       console.log('navigator.getUserMedia error: ', error);
20.       });
21.       }
22.     function stopWebcam() {
23.       webcamStream.getTracks()[0].stop(); // audio
24.       webcamStream.getTracks()[1].stop(); // video
25.       }
26.   </script>
27. </head>
28. <body >
29.   <video width=400 height=400 id="video" controls></video>
30.   <p>
31.     <button onclick="startWebcam();">Start WebCam</button>
32.     <button onclick="stopWebcam();">Stop WebCam</button>
33.   </p>
34. </body>
35. </html>

In order to stop the Webcam and make the hardware “unlock it”, you need to call the stop() method of the video stream.

Code source:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.  <meta charset="utf-8">
5.  <title>Webcam start/stop</title>
6.  <script>
7.    let webcamStream;
8.   
9.    function startWebcam() {
10.     // request video and audio stream from the user's webcam
11.     navigator.mediaDevices.getUserMedia({
12.       audio: true,
13.       video: true
14.     }).then((stream) => {
15.       let video = document.querySelector('#video');
16.       video.srcObject = stream;
17.       video.play();
18.  
19.       webcamStream = stream;
20.     }).catch((error) => {
21.         console.log('navigator.getUserMedia error: ', error);
22.     });
23.   }
24.  
25.   function stopWebcam() {
26.     webcamStream.getTracks()[0].stop(); // audio
27.     webcamStream.getTracks()[1].stop(); // video
28.   }
29. </script>
30. </head>
31. <body>
32.   <video width=400 height=400 id="video" controls></video>
33.   <p>
34.     <button onclick="startWebcam();">Start WebCam</button>
35.     <button onclick="stopWebcam();">Stop WebCam</button>
36.   </p>
37. </body>
38. </html>

Explanations:

Other examples that mix what we’ve seen in previous chapters, but this time with a live video stream

Applying CSS effects on a video element with a live webcam

Resulting image of Michel Buffa using his WebCam with css filter effects on live stream.

Try this example that shows how to use the getUserMedia API. Note the CSS effects (click on the video to cycle from one effect to another):

Click to expand!
//~~~~~~~~~~~~~~~~~~~~
// GET USER MEDIA CODE
//~~~~~~~~~~~~~~~~~~~~
let video;
let webcamStream;
function startWebcam() {
  // request video and audio stream from the user's webcam
    navigator.mediaDevices.getUserMedia({
      audio: true,
      video: true
    }).then((stream) => {
      let video = document.querySelector('#video');
      video.srcObject = stream;
      video.play();
      webcamStream = stream;
    }).catch((error) => {
      console.log('navigator.getUserMedia error: ', error);
    });
  }
  
function stopWebcam() {
    webcamStream.getTracks()[0].stop(); // audio
    webcamStream.getTracks()[1].stop(); // video
  }
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // CODE FOR CHANGING CSS FILTERS
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  let idx = 0;
  let filters = [
    'grayscale',
    'sepia',
    'blur',
    'brightness',
    'contrast',
    'hue-rotate', 'hue-rotate2', 'hue-rotate3',
    'saturate',
    'invert'
  ];
  
function changeFilter(el) {
  // Remove all CSS classes for element el
  el.className = '';
  // Choose a CSS class name
  console.log("toggling effect: " + filters[idx  filters.length]);
  let effect = filters[idx++ % filters.length];
  el.classList.add(effect);
}

CSS

Click to expand!
#output {
  width: 307px;
  height: 250px;
  background: rgba(255,255,255,0.5);
  border: 1px solid #ccc;
}
#screenshot-stream {
  width: initial;
  height: initial;
}
#screenshot {
  vertical-align: top;
}
.blur {
  filter: blur(3px);
}
.brightness {
  filter: brightness(5);
}
.contrast {
  filter: contrast(8);
}
.hue-rotate {
  filter: hue-rotate(90deg);
}
.hue-rotate2 {
  filter: hue-rotate(180deg);
}
.hue-rotate3 {
  filter: hue-rotate(270deg);
}
.saturate {
  filter: saturate(10);
}
.grayscale {
  filter: grayscale(1);
}
.sepia {
  filter: sepia(1);
}
.invert {
  filter: invert(1)
}

HTML

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Webcam and CSS effects</title>
</head>
<body>
  <h1>Example of CSS effects on a live video stream</h1>
  <p>
    Click the button "start webcam at the end of this page, then click on the video to toggle the 
    different effects.</p>
  </p>
  <video onclick="changeFilter(this);" width=400 height=400 id="video" controls autoplay></video>
  <p>
    <button onclick="startWebcam();">Start WebCam</button>
    <button onclick="stopWebcam();">Stop WebCam</button>
  </p>
</body>
</html>

Taking a snapshot from the live Webcam stream

The trick is to copy and paste the current image from the video stream into a <canvas> element.

Take a snapshot from the live Webcam stream.

JavaScript

Click to expand!
//~~~~~~~~~~~~~~~~~~~~
// GET USER MEDIA CODE
//~~~~~~~~~~~~~~~~~~~~
let video;
let webcamStream;
function startWebcam() {
  // request video and audio stream from the user's webcam
  navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true
  >
  }).then((stream) => {
    video = document.querySelector('#video');
    video.srcObject = stream;
    video.play();
    webcamStream = stream;
  }).catch((error) => {
    console.log('navigator.getUserMedia error: ', error);
  });
}

function stopWebcam() {
  webcamStream.getTracks()[0].stop(); // audio
  webcamStream.getTracks()[1].stop(); // video
}
  //~~~~~~~~~~~~~~~~~~~~~
  // TAKE A SNAPSHOT CODE
  //~~~~~~~~~~~~~~~~~~~~~
  var canvas, ctx;

function init() {
  // Get the canvas and obtain a context for
  // drawing in it
  canvas = document.getElementById("myCanvas");
  ctx = canvas.getContext('2d');
}
  
function snapshot() {
  // Draws current image from the video element into the canvas
  ctx.drawImage(video, 0,0, canvas.width, canvas.height);
}

CSS

canvas {
  border:1px solid black;
}
Click to expand!

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <title>WebCam screenshot</title>
  <meta charset="utf-8"/>
</head>
<body onload="init();">
  <h1>Take a snapshot of the current video stream</h1>
    Check the CSS and JavaScript tabs. Click on the Start WebCam button.
  <p>
    <button onclick="startWebcam();">Start WebCam</button>
    <button onclick="stopWebcam();">Stop WebCam</button>
    <button onclick="snapshot();">Prendre un screenshot</button>
  </p>
  <video onclick="snapshot(this);" width=200 height=200 id="video" controls autoplay></video>
  <p>
    Screenshots : </p>
  <canvas id="myCanvas" width="200" height="150"></canvas>
</body>
</html>

We will look at this example in greater detail in the next course section (related to the <canvas> element).

Impressive demonstrations available on the Web

2.4.3 Using the Microphone

Instead of using the getUserMedia API with: navigator.getUserMedia({video:true}, onSuccess, onError), it is also possible to use {audio:true} for the first parameter. In this case, only the microphone input will be captured. Notice that {video:true, audio:true} is also accepted, if you write a video conferencing system and need to capture both the audio and the video (this is often the case when writing WebRTC applications).

Apart from videoconferencing, microphone input will be used for music Web apps, from the WebAudio API. This API focuses on real time sound processing and music synthesis. This API is covered in the advanced W3Cx HTML5 course (HTML5 Apps and Games).

Check out the WebAudio demonstrations written by Chris Wilson, esp. the one called “Input effects”.

The image below is taken from one of the demonstrations, where the sound captured by the microphone is processed in real time. We get visualizations of the signal in real time as an animated waveform, animated frequencies or animated audiogram. If we connect an electric guitar to the sound card input, then this demonstration shows that we can recreate with WebAudio most of the classic effects used by guitarists (delay, reverb, distortion, chorus, etc.).

WebAudio Live Processing.

2.4.4 Webcam Resolution

It is possible to set “hints” for the preferred cam/resolution during video capture. This is done by using a "constraint" object that is passed as a parameter to the getUserMedia(…) method. It’s just the same object we passed in the basic example: navigator.getUserMedia({video:true}, success, error) except that this time this object is a little more complex by including new properties in addition to video:true or audio:true.

For more information, this article on MDN about the getUserMedia API gives great examples on how to set the camera resolution and/or to choose the front or back camera when using a mobile phone.

Typical use:

How to set camera resolution on mobile phone.

JavaScript

Click to expand!
var vgaButton, qvgaButton, hdButton, dimensions, video, stream;
function init() {
  vgaButton = document.querySelector('button#vga');
  qvgaButton = document.querySelector('button#qvga');
  hdButton = document.querySelector('button#hd');
  dimensions = document.querySelector('p#dimensions');
  video = document.querySelector('video');
    // Defines event listeners for the buttons that set the resolution
    qvgaButton.onclick = function() {
      getMedia(qvgaConstraints);
    };
    vgaButton.onclick = function() {
      getMedia(vgaConstraints);
    };
    hdButton.onclick = function() {
      getMedia(hdConstraints);
    };
    // Trick: check regularly the size of the video element and display it
    // When getUserMedia is called the video element changes it sizes but for
    // a while its size is zero pixels... o we check every half a second
    video.addEventListener('play', function() {
    setTimeout(function() {
      displayVideoDimensions();
    }, 500);
  });
}
  // The different values for the constraints on resolution
  var qvgaConstraints = {
    video: {
      width: { max: 320 },
      height: { max: 180 }
    }
  };
  var vgaConstraints = {
    video: {
      width: { max: 640 },
      height: { max: 360 }
    }
  };
  var hdConstraints = {
    video: {
      width: { min: 1280 },
      height: { min: 720 }
    }
  };
  // The function that is called when a button has been clicked: starts the video
  // with the preferred resolution

function getMedia(constraints) {
  if (!!stream) {
    video.srcObject = null;
    stream.getTracks()[0].stop();
  }
  navigator.mediaDevices.getUserMedia(constraints)
  .then((stream) => {
    video.srcObject = stream;
    video.play();
    window.stream = stream;
  })
  .catch((error) =>{
    console.log('navigator.getUserMedia error: ', error);
  });
}
  // util function that is called by the setInterval(...) every 0.5s, for
  // displaying the video dimensions
  function displayVideoDimensions() {
  dimensions.innerHTML = 'Actual video dimensions: ' + video.videoWidth +
  'x' + video.videoHeight + 'px.';
  }

CSS

video {
  border:1px solid;
}

HTML

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>getUserMedia constraints for Webcam resolutions</title>
</head>
<body onload="init();">
  <h1>Set the camera resolution</h1>
  Example adapted from:
  <a href="https://www.simpl.info/getusermedia/constraints/">
    https://www.simpl.info/getusermedia/constraints/
  </a>
  <br>
  <p>Click a button to call <code>getUserMedia()</code> with appropriate resolution. </p>
  <div id="buttons">
    <button id="qvga">QVGA</button>
    <button id="vga">VGA</button>
    <button id="hd">HD</button>
  </div>
  <p id="dimensions"></p>
  <video autoplay></video>
</body>
</html>
Call getUserMedia() function for appropriate resolution.

Code source extract related to the “constraint” object which specifies the desired resolutions:

Click to expand!
1.  var vgaConstraints = {
2.    video: {
3.      width: { max: 640 },
4.      height: { max: 360 }
5.    }
6.  };
7.  var hdConstraints = {
8.    video: {
9.      width: { min: 1280 },
10.     height: { min: 720 }
11.   }
12. };
13.  
14. let constraints = hdConstraints;
15. navigator.mediaDevices.getUserMedia(constraints)
16. .then((stream) => {...}

How to check which resolutions are supported by a browser?

Use this Web app that systematically tests a set of “preferred resolutions” and compared them to the actual resolutions returned by the browser. Remember that the requested resolution is a hint, and there is no real guarantee that your configuration will allow it.

Selecting the front or rear camera on smartphones

Here are some other constraints you can set. In particular, look at the ones for selecting the front or rear camera (smartphones):

Click to expand!
1.  // more on video resolution
2.  constraints = {
3.    video: {
4.      width: { min: 1024, ideal: 1280, max: 1920 },
5.      height: { min: 776, ideal: 720, max: 1080 }
6.    }
7.  }
8.  // Framerate
9.  constraints = { video: { frameRate: { ideal: 10, max: 15 } } };
10.  
11. // front and back camera (mobile), some examples
12. var front = false;
13. 
14. document.getElementById('flip-button').onclick = function() {
      front = !front;
    };
15. // toggle front and back camera (mobile) by clicking a button
16. constraints = { video: { facingMode: (front? "user" : "environment") } };
17.  
18. // prefer front camera
19. constraints = { audio: true, video: { facingMode: "user" } }
20.  
21. // require rear camera
22. constraints = { audio: true, video: { facingMode: { exact: "environment" } } }

Select input/output for audio and video streams

Webapp for selecting audio and video input/output.

Source code extract:

Click to expand!
1.  function gotDevices(deviceInfos) {
2.    for (var i = 0; i !== deviceInfos.length; ++i) {
3.      var deviceInfo = deviceInfos[i];
4.      console.log("device with id: " + deviceInfo.deviceId);
5.      // possible values: audioinput, audiooutput, videoinput
6.      console.log("device with kind: " + deviceInfo.kind);
7.      // 'speaker' or 'camera' for example
8.      console.log("device with label: " + deviceInfo.label);
9.      //... should build a menu, test kind/label and set
10.     // audioSource and videoSource variables
11.   }
12. }
13. // ...
14. var constraints = {
15.   audio: {deviceId: audioSource ? {exact: audioSource} : undefined},
16.   video: {deviceId: videoSource ? {exact: videoSource} : undefined}
17. };
18. 
19. navigator.mediaDevices.getUserMedia(constraints).
20. then(gotStream).then(gotDevices).catch(handleError)

2.4.5 The MediaRecorder API

This MediaRecoredr API allows to record / capture the audio or video stream. There are many sources for audio or video streams, but we will only consider here the streams coming from a WebCam or a sound input (i.e. microphone).

For example, the MediaRecorder API is used to record the video stream from a WebCam as a file saved on the hard disk. Below is a screenshot of an application allowing to record the WebCam. You will be able to run this example a little further, but for security reasons, it cannot run directly in this Web page.

Screenshot showing on the left the webcam video stream, and on the right the same stream recorded and playable in a HTML video element.

Let’s record, replay and download the video stream captured using a Webcam. You can test it below by clicking on “CodePen” at the top right:

JavaScript

Click to expand!
var mediaRecorder;
var recordedBlobs;

var gumVideo = document.querySelector('video#gum');
var recordedVideo = document.querySelector('video#recorded');

var recordButton = document.querySelector('button#record');
var playButton = document.querySelector('button#play');
var downloadButton = document.querySelector('button#download');
recordButton.onclick = toggleRecording;
playButton.onclick = play;
downloadButton.onclick = download;
// get stream using getUserMedia
navigator.mediaDevices.getUserMedia({ audio: true,video: true})
  .then((stream) => {
    recordButton.disabled = false;
    console.log('getUserMedia() got stream: ', stream);
    window.stream = stream;
    gumVideo.srcObject = stream;
  })
  .catch((error) => {
    console.log('navigator.getUserMedia error: ', error);
  });
function handleDataAvailable(event) {
  if (event.data && event.data.size > 0) {
    recordedBlobs.push(event.data);
  }
}

function handleStop(event) {
  console.log('Recorder stopped: ', event);
}

function toggleRecording() {
  if (recordButton.textContent === 'Start Recording') {
    startRecording();
  } else {
    stopRecording();
    recordButton.textContent = 'Start Recording';
    playButton.disabled = false;
    downloadButton.disabled = false;
  }
}
// create the media recorder
function startRecording() {
  recordedBlobs = [];
    
  try {
    mediaRecorder = new MediaRecorder(window.stream);
  } catch (e) {
    console.error('Exception while creating MediaRecorder: ' + e);
    return;
  }
  console.log('Created MediaRecorder', mediaRecorder);
  recordButton.textContent = 'Stop Recording';
  playButton.disabled = true;
  downloadButton.disabled = true;
  mediaRecorder.onstop = handleStop;
  mediaRecorder.ondataavailable = handleDataAvailable;
  mediaRecorder.start(10); // collect 10ms of data
  console.log('MediaRecorder started', mediaRecorder);
}

function stopRecording() {
  mediaRecorder.stop();
  console.log('Recorded Blobs: ', recordedBlobs);
  recordedVideo.controls = true;
}

function play() {
  var superBuffer = new Blob(recordedBlobs, {type: 'video/webm'});
  recordedVideo.src = window.URL.createObjectURL(superBuffer);
}

function download() {
  var blob = new Blob(recordedBlobs, {type: 'video/webm'});
  var url = window.URL.createObjectURL(blob);
  var a = document.createElement('a');
  a.style.display = 'none';
  a.href = url;
  a.download = 'test.webm';
  document.body.appendChild(a);
  a.click();
  setTimeout(function() {
    document.body.removeChild(a);
    window.URL.revokeObjectURL(url);
  }, 100);
}

CSS

Click to expand!
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
button {
  margin: 0 3px 10px 0;
  padding-left: 2px;
  padding-right: 2px;
  width: 99px;
}
button:last-of-type {
  margin: 0;
}
p.borderBelow {
  margin: 0 0 20px 0;
  padding: 0 0 20px 0;
}
video {
  height: 232px;
  margin: 0 12px 20px 0;
  vertical-align: top;
  width: calc(20em - 10px);
}
video:last-of-type {
  margin: 0 0 20px 0;
}
video#gumVideo {
  > margin: 0 20px 20px 0;
}
@media screen and (max-width: 500px) {
  button {
    font-size: 0.8em;
    width: calc(33% - 5px);
  }
}
@media screen and (max-width: 720px) {
  video {
    height: calc((50vw - 48px) * 3 / 4);
    margin: 0 10px 10px 0;
    width: calc(50vw - 48px);
  }
  video#gumVideo {
    margin: 0 10px 10px 0;
  }
}

HTML

Click to expand!
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>MediaRecorder API usage</title>
</head>
<body>
  <div id="container">
    <h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC example:</a> <span>MediaRecorder</span></h1>
    <p>For more info, see the <a href="https://w3c.github.io/mediacapture-record/MediaRecorder.html" 
      title="W3C MediaStream Recording API - Editor's Draft">MediaStream Recording API Editor's Draft</a>.</p>
      <video id="gum" autoplay muted></video>
      <video id="recorded" loop controls></video>
    <div>
      <button id="record" disabled>Start Recording</button>
      <button id="play" disabled>Play</button>
      <button id="download" disabled>Download</button>
    </div>
    <a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/record" title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
  </div>
</body>
</html>

Click “start recording”, then press the play button on the video element on the right of the app. You can also click the “download” button to download a .webm file, playable offline with a media player such as VLC or online in a Web page with the <video> element.

Five steps are needed to use the mediaRecorder object

1 - Create a mediaRecorder from a stream

Source code extract:

var options = {mimeType: 'video/webm; codecs=vp9'};
mediaRecorder = new MediaRecorder(stream, options);

… where stream is typically the object returned by the call to getUserMedia (see previous examples).

2 - Add a “data handler” and call the start() method of the mediaRecorder object

Source code extract:

1. var recordedChunks = []; // will hold the recorded stream
2. mediaRecorder.ondataavailable = handleDataAvailable;
3. mediaRecorder.start();
4 
5. function handleDataAvailable(event) {
6.   if (event.data.size > 0) {
7.     recordedChunks.push(event.data);
8.     } else {
9.   // ...
10. }

Explanations:

3 - When you’ve finished recording, tell the mediaRecorder to stop

When you’re done, you need to call the stop() method of the mediaRecorder object. This will end the periodic execution of the handleDataAvailable method, and stop the data capture.

mediaRecorder.stop();

4 - Create a BLOB (Binary Large Object) with the collected data, and use it to set the src attribute of an HTML5 video player

This piece of code creates a blob with the recordedChunks array. Use the URL.createObjectURL(recordedChunks) standard method to create another object that can be used as a value to set the src attribute of an HTML5 video element.

Like that, the recorded stream can be played using a standard HTML5 <video> element.

function play() {
  var superBuffer = new Blob(recordedChunks);
  videoElement.src =
  window.URL.createObjectURL(superBuffer);
}

5 - Download the captured stream

A trick consists in creating, on the fly, an invisible link with a download attribute (see Module 1) and a href attribute that points to the blob object containing the recorded stream encoded using a given codec, then generate programmatically a click event on the link. This will force the browser to download a file of type video/webm to the hard disk.

Click to expand!
1.  function download() {
2.    var blob = new Blob(recordedChunks, {
3.      type: 'video/webm'
4.    });
5.    var url = URL.createObjectURL(blob);
6.    var a = document.createElement('a');
7.    document.body.appendChild(a);
8.      a.style = 'display: none';
9.      a.href = url;
10.     a.download = 'test.webm';
11.     a.click();
12.   window.URL.revokeObjectURL(url);
13. }

3.1 HTML5 Graphics - Introduction

3.2 About JavaScript and HTML5

JavaScript logo.

HTML5 is composed of new elements, but it also comes with many JavaScript APIs for controlling video and sound, drawing and animating things in the new <canvas> element, for offline applications, persistence, geolocation, orientation, etc.

So yes, during this course, in particular in Modules 3 and 4, you will have to do a bit of JavaScript. But, DON’T PANIC!

Here we provide a basic introduction to JavaScript. If you want to learn more, many resources are available on the Web; this document is simply here to give you a head start. Remember that one great thing about these MOOCs courses is that everybody can help each other. Some students are very good in JavaScript and are usually very happy to help others when they encounter difficulties.

You will learn a lot by looking at examples, tweaking them, cloning and modifying them, etc. Many previous students who were real JavaScript beginners managed to do [all] the assignments (drawing and animating a monster with keyboard/mouse interaction)! And they did this by just studying the provided examples.

External resources

What do you need? How to debug? How to catch errors?

We will not look at the JavaScript syntax here, but more at “JavaScript in the browser”, how it works, how to start writing code, etc.

First of all, you need to find a way to debug your code and see errors. If your work does not produce any results, you must know why!

For that you will use the dev. tools of your browser. Press F12 in Windows or cmd-alt-i in Mac to open the dev. tools, then go to the console tab: this is where errors will be displayed, or messages of your own (use the console.log(string) JavaScript function in the JavaScript code embedded in your html page). In the console, you will be able to type any JavaScript command.

Let’s look at this example on JS Bin:

# HTML code
1 <!DOCTYPE html>
2 <html lang=“en”>
3 <head>
4 <meta charset=utf-8 />
5 <title>Web Audio API</title>
6 <script>
7 console.log(“Some JavaScript code has been executed”);
8 </script>
9 </head>
10 <body>
11 <h1>JavaScript debugging using the dev tool console</h1>
12 </body>
13 </html>

The simplest way to add JavaScript code in an HTML page, is by using the <script>…</script> element.

The code in this example is executed sequentially when the page is loaded: the JavaScript code is executed before the browser could see the rest of the page (as the <script></script> is located before the <body>).

The H1 element, for example, does not exist in the Document Object Model, and has not yet been displayed when the JavaScript code is executed. If we move the <script></script> at the end of the document, then the H1 would have been built before the JavaScript code is executed.

The only line of code we have is console.log(“Some JavaScript code has been executed”);

This means “display in the JavaScript console the message…”. If we open the console tab provided by jsbin.com in a dedicated tab (that redirects all console.log() messages), and re-execute the page (just type a space at the end of a line, this will re-render the page and display the message in the console), we see the message in the console tab, as well as in the dev. tools console. This is illustrated by the image below:

JavaScript debugging using the dev tool console.

It is also possible to use the “real dev. tool console”, and for this I recommend running the application in a single window, not in the JS Bin editor. Press the black arrow on the top right of the output window - this will render the page as a standalone Web page, then press F12. You should see:

View of the JavaScript Debug Devtool console.

Ok, now, let’s make an error: change console.log() into consollle.log(). Let’s see what happens:

View of the JavaScript console.

And if we run it standalone and use the dev. tool console:

Dev tool for error inspection.

And if we click on the line number in the right, the dev. tool shows the source code centered on the line that caused the error:

View of the JavaScript console. We can see an extract of the source code with 
  different tools for watching variable values over execution etc.

Without such tools, debugging JavaScript code is impossible. So you need to look at some basic tutorials on how to use the dev. tools of your browsers, since they differ from one another in the way they work - although the principles remain the same.

About the asynchronous nature of JavaScript

Some of you may not be used to “asynchronous programming”, “callbacks” etc. We recommend to read this article on WikiPedia and  this thread on StackOverflow.

3.2.2 The <canvas> Element

Canvas element logo.

The <canvas> tag was introduced into the HTML specification around 2010 as a “Flash killer.” At the time, popular video games used this proprietary technology and only a few browsers supported it. The HTML canvas allows drawing and animation at 60 frames per second, in 2D or 3D.

The canvas has been designed for pixel-based graphics, while SVG (Scalable Vector Graphics, another W3C standard) is for vector-based graphics.

Indeed, the canvas JavaScript drawing API supports different kind of shapes: lines, rectangles, ellipses, arcs, curves, text, images. Some drawing styles need to be specified that will affect the way shapes are drawn (color, drawing width, shadows, etc.). An alpha channel for drawing in transparent mode is also supported, as well as many advanced drawing modes and global filters (blur, etc.).

The canvas is also used to do animations at 60 frames per second (useful for games), to display videos with special effects, to display a webcam stream, and so on.

Examples

Here are some fun examples that show the interest of the HTML5 canvas.

Example #1

Foot Chinko is one popular free HTML5 games:

Foot chinko one of the best html5 2D game of 2015.

Example #2

Lots of data visualization tools and JavaScript libraries use the HTML5 canvas element for Data visualization:

html5 data visualization, bar graphs.

Example #3

A version of the arcade game Galaxian, that runs at 60 frames per second in an HTML5 canvas element:

A version of the game Galaxian that runs at 60 frames/s in a canvas.

Performance is  good and animation is generally very smooth, since most Web browsers (mobile and desktop) support hardware acceleration.

Note: 3D drawing using the WebGL API is also possible in a <canvas>, but will not be covered in this course. For the most curious among you, please have  a look at the two popular libraries for doing 3D drawing/animation in a <canvas>:  BabylonJS  and ThreeJS.

External resources

3.2.3 Canvas and Accessibility

The dynamic nature of the <canvas> element has made it difficult to use in applications that need to be accessible to people with disabilities. To be accessible, it must meet the following principles:

Read more on this topic:

3.2.4 HTML Canvas Cheatsheet

We recommend these 2 quick references (or cheatsheets) below. Do not hesitate to keep your favorite one open in a separate browser ta

  1. As a PDF file, this canvas cheatsheet:
Snapshot of an Canvas cheatsheet from skilled.com.
  1. Another resource, as an HTML file:
Snapshot of HTML5 Canvas cheatsheet API.

3.2.5 Coordinate System

The coordinate system used for drawing in canvases is similar to the one used by many drawing APIs like Java2D: the (0 , 0) is in the top left corner while the X axis is going to the right and the Y axis to the bottom, as  shown in the following picture:

Coordinate system.

3.2.6 Drawing Rectangles in a Canvas

Small errata about what I said in the above video: “So let’s get the canvas using the DOM API method document.getElementById() or better, use document.querySelector() that is a more recent method from the DOM API”..

The part is bold is not correct: querySelector, technically, comes from Selectors API. Just in case some people would like to check the specification.

Detailed explanation of the example shown in the above video

Here are the different steps, in a little more detail, of the example demonstrated in the above video:

1 - Add the <canvas> element into an HTML page

<canvas id="myCanvas" width="300" height="225">
  Fallback content that will be displayed in case the web browser
  does not support the canvas tag or in case scripting
  is disabled.
</canvas>

Place code similar to the above somewhere in an HTML page. This example defines an area of 300 by 225 pixels on which content can be rendered with JavaScript.

Normally you should see nothing as a result; by default canvases are “transparent”.  Make it visible using CSS! For example, you can add a border to the canvas (or change the background color, or put an image in the background).

The three lines of CSS will create a border around the canvas with id=“myCanvas”, of 1 pixel width, in black:

<style>
  #myCanvas {
    border:1px solid black;
  }
</style>

2 - Select the <canvas> element for use from JavaScript

We can have more than one <canvas> in a single page, and canvases will be manipulated with JavaScript like other elements in the DOM.

For example with:

var canvas = document.getElementById("myCanvas");

… or with the querySelector() method introduced by HTML5, that use the CSS selector syntax for selecting elements:

var canvas = document.querySelector("#myCanvas");

3 - Get a “2D context” associated with the canvas

This step is useful for drawing and setting drawing properties (color, etc.)

Once we have a pointer to the <canvas>, we can get a “context”. This particular object is the core of the canvas JavaScript API. It provides methods for drawing, like fillRect(x, y, width, height) for example, that draws a filled rectangle, and properties for setting the color, shadows, gradients, etc.

Let’s first get the context (do this only once):

var ctx=canvas.getContext('2d');

… then, set the color for drawing filled shapes:

ctx.fillStyle='red';

… and draw a filled rectangle:

ctx.fillRect(0,0,80,100);

Complete example that draws a filled rectangle in red

HTML Source Code

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <style>
    #myCanvas {
    border: 1px solid black;
    }
  </style>
<title>Canvas</title>
<meta charset="utf-8"/>
  <script>
    var canvas, ctx;
    function init() {
    // This function is called after the page is loaded
    // 1 - Get the canvas
    canvas = document.getElementById('myCanvas');
    // 2 - Get the context
    ctx=canvas.getContext('2d');
    // 3 - we can draw
    drawSomething();
    }
    function drawSomething() {
    // draw a red rectangle
    ctx.fillStyle='#FF0000';
    ctx.fillRect(0,0,80,100);
    }
  </script>
</head>
<body onload="init();">
  <canvas id="myCanvas" width="200" height="200">
    Your browser does not support the canvas tag.
  </canvas>
</body>
</html>

HTML Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.     <head>
4.        <style>
5.           #myCanvas {
6.              border: 1px solid black;
7.           }
8.        </style>
9.        <title>Canvas</title>
10.       <meta charset="utf-8"/>
11.       <script>
12.          var canvas, ctx;
13.          function init() {
14. // This function is called after the page is loaded
15. // 1 - Get the canvas
16.          canvas = document.getElementById('myCanvas');
17. // 2 - Get the context
18.          ctx=canvas.getContext('2d');
19. // 3 - we can draw
20.          drawSomething();
21.       }
22.       function drawSomething() {
23. // draw a red rectangle
24.          ctx.fillStyle='#FF0000';
25.          ctx.fillRect(0,0,80,100);
26.       }
27.       </script>
28.    </head>
29.    <body onload="init();">
30.      <canvas id="myCanvas" width="200" height="200">
31.       Your browser does not support the canvas tag.
32.      </canvas>
33.    </body>
34. </html>

Explanations

Only access elements when the DOM is ready:

Notice that we wrote an “init” function (line 13) that is called only when the page has been entirely loaded (we say “when the DOM is ready”). There are several ways to do this. In this example we used the <body onload=“init();”> method, at  line 29.

It’s good practice to have such a function, as we cannot access the elements of the page before the page has been loaded entirely and before the DOM is ready.

Another way is to put the JavaScript code at the end of the document (between  <script>…</script>), right before the </body>. In this case when the JavaScript code is executed, the DOM has already been constructed.

Start by getting the canvas and the context:

Before drawing or doing anything interesting with the canvas, we must first get its drawing “context”. The drawing context defines the drawing methods and properties we can use.

Good practice is to get the canvas, the context, the width and height of the canvas and other global objects in this “init” function.

After the context is set, we can draw, but first let’s set the current color for filled shapes:

The example shows the use of the fillStyle property at line 24 - useful for specifying the way shapes will be filled. In our case this line indicates the color of all the filled shapes we are going to draw:

ctx.fillStyle='#FF0000';

The context property named fillStyle is used here. This property can be set with a color, a gradient, or a pattern. We will see examples of these later on in the course.

The example says that all filled shapes will use the color “#FF0000”, which corresponds to a pure red color using the CSS RGB hexadecimal encoding (we could also have used ctx.fillStyle=‘red’);

Then we can draw:

ctx.fillRect(0,0,80,100);

This line is a call to the method fillRect(top left X coordinate, top left Y coordinate, width, height), which draws a filled rectangle.

The way the rectangle will be filled depends on the current value of several properties of the context, in particular the value of the fillStyle property. In our case, the rectangle will be red.

Summary of the different steps:

  1. Declare the canvas, remembering to add an id attribute, and fallback content:
<canvas id="myCanvas" width="200" height="200">
  ...fallback content...
</canvas>
  1. Get a reference to the canvas in a JavaScript variable using the DOM API:
var canvas=document.getElementById('myCanvas');
  1. Get the context for drawing in that canvas:
var ctx=canvas.getContext('2d');
  1. Specify some drawing properties (optional):  
ctx.fillStyle='#FF0000';
  1. Draw some shapes:
ctx.fillRect(0,0,80,100)

3.2.7 Drawing Principles

More about the “context” object

Before we go on, we should take some time to clarify the way we draw on HTML5 canvases. We already mentioned that we use a graphic context for all the main operations. Whenever a shape, a text, or an image is drawn, the current values of the different properties of the graphic context are taken into account. Some are relevant only for certain kinds of shapes or drawing modes, but you must be aware that it is always the current values of these drawing properties that are used.

Later on we’ll see that there are ways to save and restore this whole set of values, but for now, let’s examine in greater detail some of the properties and methods we’ve already encountered, and introduce new ones.

More about properties and methods of the context object

Its value can be one of the following:

The default value is the color black. Any kind of drawing in “fill mode” will use the value of this property to determine how to render the “filled part” of the drawing: any filled rectangle will be filled black by default, any filled circle will be filled in black, and so on.

As long as we don’t modify the value of this property, all drawing commands for filled shapes will use the current value.

Note that we will study in detail how to use colors, gradients and patterns later, but for now we introduce some properties and values so that you can understand the principles of canvas drawing.

fillStyle and the other context properties can be considered to be “global variables” of the context.

The two first parameters are the coordinates of the top left corner of the rectangle. This method uses the current value of the fillStyle property to determine how to fill the rectangle.

ctx.fillStyle='pink';
ctx.fillRect(10,10,200,200);

Produces this result:

Filled rectangle with pink color.

The possible values are the same as those for the fillStyle property: a color, a pattern, or a gradient. This property will be taken into account when wireframe shapes are drawn.

ctx.strokeStyle='blue';
ctx.strokeRect(10,10,200,200);

… gives this result:

Stroked rectangle - border is in blue.

Only the outline of the rectangle will be drawn, and it will be drawn using the value of the strokeStyle property.

Actually it draws it in a color called “transparent black” (!) that corresponds to the initial state of the rectangle as if no drawing had occurred.

ctx.fillStyle='pink';
ctx.fillRect(10,10,200,200);
ctx.clearRect(50, 50, 20, 20);

The result is:

The use of ClearRect draws a white rectangle against the pink background.

Let’s see some simple examples

Example #1: draw a wireframe red rectangle, width lineWidth = 3 pixels

Extract from the source code (the part that draws the rectangle):

1.  function drawSomething() {
2.    // draw a red rectangle, line width=3 pixels
3.    ctx.lineWidth=3;
4.    ctx.strokeStyle='red';
5.    ctx.strokeRect(10,10,80,100);
6.  }

Here, we used “stroke” instead of “fill” in the property and method names (lines 4 and 5): strokeStyle instead of fillStyle, strokeRect(…) instead of fillRect(…).

We also introduced a new property of the context, that applies only when drawing in “stroke” mode, the lineWidth property (line 3), that is used for setting the width of the shape outline. The value is in pixels.

Example #2: draw two filled red rectangles with a blue outline of 5 pixels and some text

Let’s continue with another example. This time we will draw several shapes that share the same colors - they will be filled in red, with a blue outline. We also show how to draw a text message with a given font.

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Drawing with outline</title>
  <meta charset="utf-8"/>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
  <script>
    var canvas, ctx;
    function init() {
      // This function is called after the page is loaded
      // 1 - Get the canvas
      canvas = document.getElementById('myCanvas');
      // 2 - Get the context
      ctx=canvas.getContext('2d');
      // 3 - we can draw
      drawSomething();
    }
  
    function drawSomething() {
    // set the global context values
      ctx.lineWidth=5;
      ctx.fillStyle='red';
      ctx.strokeStyle='blue'
      // font for all text drawing
      ctx.font = 'italic 20pt Calibri';
      // Draw the two filled red rectangles
      ctx.fillRect(10, 30, 70, 150);
      ctx.fillRect(110, 30, 70, 150);
      // Draw the two blue wireframe rectangles
      ctx.strokeRect(10, 30, 70, 150);
      ctx.strokeRect(110, 30, 70, 150);
      // Draw a message above the rectangles
      ctx.fillText("hello", 70, 22);
    }
  </script>
</head>
<body onload="init();">
  <canvas id="myCanvas" width="200" height="200">
    Your browser does not support the canvas tag.
  </canvas>
</body>
</html>

JavaScript code extract:

Click to expand!
1.  function drawSomething() {
2.       // set the global context values
3.      ctx.lineWidth=5;
4.      ctx.fillStyle='red';
5.      ctx.strokeStyle='blue'
6.      // font for all text drawing
7.      ctx.font = 'italic 20pt Calibri';
8.  
9.      // Draw the two filled red rectangles
10.     ctx.fillRect(10, 30, 70, 150);
11.     ctx.fillRect(110, 30, 70, 150);
12. 
13.     // Draw the two blue wireframe rectangles
14.     ctx.strokeRect(10, 30, 70, 150);
15.     ctx.strokeRect(110, 30, 70, 150);
16. 
17.     // Draw a message above the rectangles
18.     ctx.fillText("hello", 70, 22);
19. }

This example shows the “global” nature of the context properties. Once you set the filled color to red, any shapes you draw in filled mode will be red. This is true for all the context properties. We set some of these properties in lines 3-7, and all following calls to context methods for drawing rectangles or text will depend on them. The two filled rectangles at lines 10-11 will be red, the two wireframe rectangles drawn at lines 14-15 will be blue, etc.

Line 18 shows how to draw a text message at an X position of 70 and a Y position of 22. The font is set at line 7 using the font property of the context.  The syntax is the same we use in CSS for using “system fonts”.

If you would like to draw the filled text message in green, for example, you should set the ctx.fillStyle property to “green” after you draw the rectangles and before you draw the text (i.e just before line 18).

Summary of what we’ve learned

3.2.8 Transformations

We now introduce the basics of 2D transformations, a powerful tool that will make things easier as soon as you have to:

Let’s start with some simple examples before looking at how we use 2D transforms.

Examples

If we draw three rectangles of size 100x200 in a 400x400 canvas, one at (0, 0) and another at (150, 0), and a third at (300, 0), here is the result and the corresponding code:

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Let's draw three rectangles!</title>
  <meta charset="utf-8"/>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
  <script>
    var canvas, ctx;
    function init() {
      // This function is called after the page is loaded
      // 1 - Get the canvas
      canvas = document.getElementById('myCanvas');
      // 2 - Get the context
      ctx=canvas.getContext('2d');
      // 3 - we can draw
      drawSomething();
    }
    function drawSomething() {
      // draw a red rectangle
      ctx.fillStyle='lightgreen';
      ctx.fillRect(0,0,100,200);
      ctx.fillRect(150,0,100,200);
      ctx.fillRect(300,0,100,200);
    }
  </script>
</head>
<body onload="init();">
  <canvas id="myCanvas" width="400" height="400">
    Your browser does not support the canvas tag.
  </canvas>
</body>
</html>

JavaScript code extract:

1.  function drawSomething() {
2.       ctx.fillStyle='lightgreen';
3.   
4.       ctx.fillRect(0,0,100,200);
5.       ctx.fillRect(150,0,100,200);
6.       ctx.fillRect(300,0,100,200);
7.  }

What if we wanted to draw these 3 rectangles at another position, as a group? We would like to draw all of them a little closer to the bottom, for example… Let’s add some parameters to the function:  the X and Y position of the rectangles.

HTML source code:

Click to expand!
<!DOCTYPE html>
<html>
<head lang="en">
  <title>Draw 3 rectangles at any X and Y position</title>
  <meta charset="utf-8"/>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
  <script>
    var canvas, ctx;
    function init() {
      // This function is called after the page is loaded
      // 1 - Get the canvas
      canvas = document.getElementById('myCanvas');
      // 2 - Get the context
      ctx=canvas.getContext('2d');
      // 3 - we can draw
      drawSomething(0, 100);
    }
    function drawSomething(x, y) {
       // draw a red rectangle
       ctx.fillStyle='lightgreen';
       ctx.fillRect(x,y,100,200);
       ctx.fillRect(x+150,y,100,200);
       ctx.fillRect(x+300,y,100,200);
    }
  </script>
</head>
<body onload="init();">
  <canvas id="myCanvas" width="400" height="400">
    Your browser does not support the canvas tag.
  </canvas>
</body>
</html>

JavaScript code extract:

Click to expand!
1.  var canvas, ctx;
2.  
3.  function init() {
4.      // This function is called after the page is loaded
5.      // 1 - Get the canvas
6.      canvas = document.getElementById('myCanvas');
7.      // 2 - Get the context
8.      ctx=canvas.getContext('2d');
9.      // 3 - we can draw
10.     drawSomething(0, 100);
11. }
12. 
13. function drawSomething(x, y) {
14.     // draw 3 rectangles
15.     ctx.fillStyle='lightgreen';
16.     ctx.fillRect(x,y,100,200);
17.     ctx.fillRect(x+150,y,100,200);
18.     ctx.fillRect(x+300,y,100,200);
19. }

At line 10, we called the drawSomething(…) function with 0 and 100 as parameters, meaning “please add an offset of 0 in X and 100 in Y directions to what is drawn by the function…

If you look at the code of the modified function, you will see that each call to fillRect(…) uses the x and y parameters instead of hard coded values. In this way, if we call it with parameters (0, 100), then all rectangles will be drawn 100 pixels to the bottom (offset in y). Here is the result:

Rectangles are drawn 100 pixels towards the bottom.

Now we can start having some fun… let’s draw a monster’s head using only rectangles:

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
<title>Monster's head with rectangles</title>
<meta charset="utf-8"/>
<style>
#myCanvas {
border: 1px solid black;
}
</style>
<script>
var canvas, ctx;
function init() {
// This function is called after the page is loaded
// 1 - Get the canvas
canvas = document.getElementById('myCanvas');
// 2 - Get the context
ctx=canvas.getContext('2d');
// 3 - we can draw
drawMonster(0, 0);
}
function drawMonster(x, y) {
// draw a red rectangle
// head
ctx.fillStyle='lightgreen';
ctx.fillRect(x,y,200,200);
// eyes
ctx.fillStyle='red';
ctx.fillRect(x+35,y+30,20,20);
ctx.fillRect(x+140,y+30,20,20);
// interior of eye
ctx.fillStyle='yellow';
ctx.fillRect(x+43,y+37,10,10);
ctx.fillRect(x+143,y+37,10,10);
// Nose
ctx.fillStyle='black';
ctx.fillRect(x+90,y+70,20,80);
// Mouth
ctx.fillStyle='purple';
ctx.fillRect(x+60,y+165,80,20);
}
</script>
</head>
<body onload="init();">
<canvas id="myCanvas" width="400" height="400">
Your browser does not support the canvas tag.
</canvas>
</body>
</html>

JavaScript source code:

Click to expand!
1.  function drawMonster(x, y) {
2.     // head
3.     ctx.fillStyle='lightgreen';
4.     ctx.fillRect(x,y,200,200);
5.  
6.     // eyes
7.     ctx.fillStyle='red';
8.     ctx.fillRect(x+35,y+30,20,20);
9.     ctx.fillRect(x+140,y+30,20,20);
10. 
11.    // interior of eye
12.    ctx.fillStyle='yellow';
13.    ctx.fillRect(x+43,y+37,10,10);
14.    ctx.fillRect(x+143,y+37,10,10);
15. 
16.    // Nose
17.    ctx.fillStyle='black';
18.    ctx.fillRect(x+90,y+70,20,80);
19. 
20.    // Mouth
21.    ctx.fillStyle='purple';
22.    ctx.fillRect(x+60,y+165,80,20);
23. }

As you can see, the code uses the same technique, becomes less and less readable. The Xs and Ys at the beginning of each call makes understanding the code harder, etc.

However, there is a way to simplify this => 2D geometric transformations! 

Geometric transformations: changing the coordinate system

The idea behind 2D transformations is that instead of modifying all the coordinates passed as parameters to each call to drawing methods like fillRect(…), we will keep all the drawing code “as is”. For example, if the monster of our previous example was drawn at (0, 0), we could just translate (or rotate, or scale) the original coordinate system.

Let’s take a piece of code that draws something corresponding to the original coordinate system, located at the top left corner of the canvas:

Click to expand!
1.  function drawMonster(x, y) {
2.     // head
3.     ctx.fillStyle='lightgreen';
4.     ctx.fillRect(0,0,200,200);
5.  
6.     // eyes
7.     ctx.fillStyle='red';
8.     ctx.fillRect(35,30,20,20);
9.     ctx.fillRect(140,30,20,20);
10. 
11.    // interior of eye
12.    ctx.fillStyle='yellow';
13.    ctx.fillRect(43,37,10,10);
14.    ctx.fillRect(143,37,10,10);
15. 
16.    // Nose
17.    ctx.fillStyle='black';
18.    ctx.fillRect(90,70,20,80);
19. 
20.    // Mouth
21.    ctx.fillStyle='purple';
22.    ctx.fillRect(60,165,80,20);
23. 
24.    // coordinate system at (0, 0)
25.    drawArrow(ctx, 0, 0, 100, 0, 10, 'red');
26.    drawArrow(ctx, 0, 0, 0, 100, 10, 'red');
27. }

This code is the just the same as in the previous example except that we removed all Xs and Yx in the code. We also added at the end (lines 25-26) two lines of code that draw the coordinate system. The drawArrow(startX, startY, endX, endY, width, color) function is a utility function that we will present later. You can see it in the JavaScript source code of the pen below:

JavaScript source code:

Click to expand!
// Borrowed and adapted from : http://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag
function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
//variables to be used when creating the arrow
var headlen = 10;
var angle = Math.atan2(toy-fromy,tox-fromx);
ctx.save();
ctx.strokeStyle = color;
//starting path of the arrow from the start square to the end square and drawing the stroke
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.lineWidth = arrowWidth;
ctx.stroke();
//starting a new path from the head of the arrow to one of the sides of the point
ctx.beginPath();
ctx.moveTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//path from the side point of the arrow, to the other side point
ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
//path from the side point back to the tip of the arrow, and then again to the opposite side point
ctx.lineTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//draws the paths created above
ctx.stroke();
ctx.restore();
}

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
<title>Monster's head drawn with 2D transformations</title>
<meta charset="utf-8"/>
<style>
#myCanvas {
border: 1px solid black;
}
</style>
<script>
var canvas, ctx;
function init() {
// This function is called after the page is loaded
// 1 - Get the canvas
canvas = document.getElementById('myCanvas');
// 2 - Get the context
ctx=canvas.getContext('2d');
// 3 - we can draw, try to change these values
drawMonster(0, 0);
}
function drawMonster(x, y) {
// head
ctx.fillStyle='lightgreen';
ctx.fillRect(0,0,200,200);
// eyes
ctx.fillStyle='red';
ctx.fillRect(35,30,20,20);
ctx.fillRect(140,30,20,20);
// interior of eye
ctx.fillStyle='yellow';
ctx.fillRect(43,37,10,10);
ctx.fillRect(143,37,10,10);
// Nose
ctx.fillStyle='black';
ctx.fillRect(90,70,20,80);
// Mouth
ctx.fillStyle='purple';
ctx.fillRect(60,165,80,20);
// coordinate system at (0, 0)
drawArrow(ctx, 0, 0, 100, 0, 10, 'red');
drawArrow(ctx, 0, 0, 0, 100, 10, 'red');
}
</script>

Note that the X and Y parameters are useless for now…

  Translation using ctx.translate(offsetX, offsetY)

Now, instead of simply calling drawMonster(0, 0), we will call first ctx.translate(100, 100), and look at the result below:

JavaScript source code:

Click to expand!
// Borrowed and adapted from : http://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag
function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
  //variables to be used when creating the arrow
  var headlen = 10;
  var angle = Math.atan2(toy-fromy,tox-fromx);
  ctx.save();
  ctx.strokeStyle = color;
  //starting path of the arrow from the start square to the end square and drawing the stroke
  ctx.beginPath();
  ctx.moveTo(fromx, fromy);
  ctx.lineTo(tox, toy);
  ctx.lineWidth = arrowWidth;
  ctx.stroke();
  //starting a new path from the head of the arrow to one of the sides of the point
  ctx.beginPath();
  ctx.moveTo(tox, toy);
  ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
  //path from the side point of the arrow, to the other side point
  ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
  //path from the side point back to the tip of the arrow, and then again to the opposite side point
  ctx.lineTo(tox, toy);
  ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
  //draws the paths created above
  ctx.stroke();
  ctx.restore();
}

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
<title>Translated monster's head</title>
<meta charset="utf-8"/>
<style>
  #myCanvas {
    border: 1px solid black;
  }
</style>
<script>
  var canvas, ctx;
  function init() {
    // This function is called after the page is loaded
    // 1 - Get the canvas
    canvas = document.getElementById('myCanvas');
    // 2 - Get the context
    ctx=canvas.getContext('2d');
    // 3 - we can draw, try to change these values
    ctx.translate(100, 100);
    drawMonster(0, 0);
  }

  function drawMonster(x, y) {
    // head
    ctx.fillStyle='lightgreen';
    ctx.fillRect(0,0,200,200);
    // eyes
    ctx.fillStyle='red';
    ctx.fillRect(35,30,20,20);
    ctx.fillRect(140,30,20,20);
    // interior of eye
    ctx.fillStyle='yellow';
    ctx.fillRect(43,37,10,10);
    ctx.fillRect(143,37,10,10);
    // Nose
    ctx.fillStyle='black';
    ctx.fillRect(90,70,20,80);
    // Mouth
    ctx.fillStyle='purple';
    ctx.fillRect(60,165,80,20);
    // coordinate system at (0, 0)
    drawArrow(ctx, 0, 0, 100, 0, 10, 'red');
    drawArrow(ctx, 0, 0, 0, 100, 10, 'red');
  }
</script>
</head>
<body onload="init();">
  <canvas id="myCanvas" width="400" height="400">
    Your browser does not support the canvas tag.
  </canvas>
</body>
</html>

JavaScript code extract:

ctx.translate(100, 100);
drawMonster(0, 0);

Line 1 changes the position of the coordinate system, line 2 draws a monster in the new translated coordinate system. All subsequent calls to drawing methods will be affected and will work in this new system too.

There are other transformations available:

Here is the previous example, but this time we translated the coordinate system, then rotated it with an angle equal to PI/4 , then we scaled it so that units are half as big:

JavaScript code extract:

Click to expand!
// Borrowed and adapted from : http://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag
function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
  //variables to be used when creating the arrow
  var headlen = 10;
  var angle = Math.atan2(toy-fromy,tox-fromx);
  ctx.save();
  ctx.strokeStyle = color;
  //starting path of the arrow from the start square to the end square and drawing the stroke
  ctx.beginPath();
  ctx.moveTo(fromx, fromy);
  ctx.lineTo(tox, toy);
  ctx.lineWidth = arrowWidth;
  ctx.stroke();
  //starting a new path from the head of the arrow to one of the sides of the point
  ctx.beginPath();
  ctx.moveTo(tox, toy);
  ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
  //path from the side point of the arrow, to the other side point
  ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
  //path from the side point back to the tip of the arrow, and then again to the opposite side point
  ctx.lineTo(tox, toy);
  ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
  //draws the paths created above
  ctx.stroke();
  ctx.restore();
}

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
<title>Translated, rotated, and scaled monster's head</title>
<meta charset="utf-8"/>
<style>
#myCanvas {
border: 1px solid black;
}
</style>
<script>
  var canvas, ctx;
  function init() {
    // This function is called after the page is loaded
    // 1 - Get the canvas
    canvas = document.getElementById('myCanvas');
    // 2 - Get the context
    ctx=canvas.getContext('2d');
    // 3 - we can draw, try to change these values
    ctx.translate(100, 100);
    ctx.rotate(Math.PI/4);
    ctx.scale(0.5, 0.5);
    drawMonster(0, 0);
  }

  function drawMonster(x, y) {
    // head
    ctx.fillStyle='lightgreen';
    ctx.fillRect(0,0,200,200);
    // eyes
    ctx.fillStyle='red';
    ctx.fillRect(35,30,20,20);
    ctx.fillRect(140,30,20,20);
    // interior of eye
    ctx.fillStyle='yellow';
    ctx.fillRect(43,37,10,10);
    ctx.fillRect(143,37,10,10);
    // Nose
    ctx.fillStyle='black';
    ctx.fillRect(90,70,20,80);
    // Mouth
    ctx.fillStyle='purple';
    ctx.fillRect(60,165,80,20);
    // coordinate system at (0, 0)
    drawArrow(ctx, 0, 0, 100, 0, 10, 'red');
    drawArrow(ctx, 0, 0, 0, 100, 10, 'red');
  }
</script>
</head>
<body onload="init();">
  <canvas id="myCanvas" width="400" height="400">
    Your browser does not support the canvas tag.
  </canvas>
</body>
</html>

And here is the code of the transformations we used, followed by the call to the function that draws the monster:

ctx.translate(100, 100);
ctx.rotate(Math.PI/4);
ctx.scale(0.5, 0.5);
 
drawMonster(0, 0);

If we draw two shapes at two different positions, they will be relative to this new coordinate system.

JavaScript code extract:

Click to expand!
// Borrowed and adapted from : http://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag
function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
//variables to be used when creating the arrow
var headlen = 10;
var angle = Math.atan2(toy-fromy,tox-fromx);
  ctx.save();
  ctx.strokeStyle = color;
  //starting path of the arrow from the start square to the end square and drawing the stroke
  ctx.beginPath();
  ctx.moveTo(fromx, fromy);
  ctx.lineTo(tox, toy);
  ctx.lineWidth = arrowWidth;
  ctx.stroke();
  //starting a new path from the head of the arrow to one of the sides of the point
  ctx.beginPath();
  ctx.moveTo(tox, toy);
  ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
  //path from the side point of the arrow, to the other side point
  ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
  //path from the side point back to the tip of the arrow, and then again to the opposite side point
  ctx.lineTo(tox, toy);
  ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
  //draws the paths created above
  ctx.stroke();
  ctx.restore();
}

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>2D transformation</title>
    <meta charset="utf-8"/>
    <style>
      #myCanvas {
        border: 1px solid black;
        }
    </style>
    <script>
      var canvas, ctx;
        function init() {
        // This function is called after the page is loaded
        // 1 - Get the canvas
        canvas = document.getElementById('myCanvas');
        // 2 - Get the context
        ctx=canvas.getContext('2d');
        // 3 - we can draw, try to change these values
        ctx.translate(100, 100);
        ctx.rotate(Math.PI/4);
        ctx.scale(0.5, 0.5);
        // Draw the monster at (0, 0)
        drawMonster(0, 0);
         // draw a filled rectagle at (250, 0)
        ctx.fillRect(250, 0, 100, 100);
        drawCoordinateSystem('red', 10);
        }
        function drawMonster(x, y) {
        // head
        ctx.fillStyle='lightgreen';
        ctx.fillRect(0,0,200,200);
        // eyes
        ctx.fillStyle='red';
        ctx.fillRect(35,30,20,20);
        ctx.fillRect(140,30,20,20);
        // interior of eye
        ctx.fillStyle='yellow';
        ctx.fillRect(43,37,10,10);
        ctx.fillRect(143,37,10,10);
        // Nose
        ctx.fillStyle='black';
        ctx.fillRect(90,70,20,80);
        // Mouth
        ctx.fillStyle='purple';
        ctx.fillRect(60,165,80,20);
        }
      function drawCoordinateSystem(color, width) {
      // coordinate system at (0, 0)
      drawArrow(ctx, 0, 0, 100, 0, width, color);
      drawArrow(ctx, 0, 0, 0, 100, width, color);
      }
    </script>
  </head>
  <body onload="init();">
    <canvas id="myCanvas" width="400" height="400">
      Your browser does not support the canvas tag.
    </canvas>
  </body>
</html>
  ctx.translate(100, 100);
  ctx.rotate(Math.PI/4);
  ctx.scale(0.5, 0.5);

// Draw the monster at (0, 0)
  drawMonster(0, 0);
// Draw a filled rectagle at (250, 0)
  ctx.fillRect(250, 0, 100, 100);

How can we reset the coordinate system, how can we go back to the previous one?

Aha, this is a very interesting question… the answer is in the next page!

3.2.9 Saving and Restoring the Context

There are two methods for saving and restoring the context properties: ctx.save() and ctx.restore().

What will be saved: fillStyle and strokeStyle, lineWidth, the previous coordinate system, etc. Meaning that ALL properties that affect drawing!

A call to ctx.save() will probably save the context property values in a hardware register on your graphics card. Multiple contexts can be saved consecutively and restored.

Multiple contexts can be backed up consecutively and restored. Contexts saved will be stacked, the last one that has been saved will be restored by the next call to restore(), so it is very important to have one restore for each save.

Best practice: save the context at the beginning of any function that changes the context, restore it at the end of the function!

Example of a function that changes the context and restores it after execution

JavaScript code extract:

Click to expand!
// Borrowed and adapted from : http://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag
function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
  //variables to be used when creating the arrow
  var headlen = 10;
  var angle = Math.atan2(toy-fromy,tox-fromx);
  ctx.save();
  ctx.strokeStyle = color;
  //starting path of the arrow from the start square to the end square and drawing the stroke
  ctx.beginPath();
  ctx.moveTo(fromx, fromy);
  ctx.lineTo(tox, toy);
  ctx.lineWidth = arrowWidth;
  ctx.stroke();
  //starting a new path from the head of the arrow to one of the sides of the point
  ctx.beginPath();
  ctx.moveTo(tox, toy);
  ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
  //path from the side point of the arrow, to the other side point
  ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
  //path from the side point back to the tip of the arrow, and then again to the opposite side point
  ctx.lineTo(tox, toy);
  ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
  //draws the paths created above
  ctx.stroke();
  ctx.restore();
}

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Saving and restoring the context</title>
  <meta charset="utf-8"/>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
  <script>
    var canvas, ctx;
    function init() {
    // This function is called after the page is loaded
    // 1 - Get the canvas
    canvas = document.getElementById('myCanvas');
    // 2 - Get the context
    ctx=canvas.getContext('2d');
    // 3 - we can draw, try to change these values
    drawMonster(200, 100, Math.PI/4, 'lightGreen', 'yellow');
    // draw another rectangle at (0, 0), you will see it's black
    // (default color), and at the top left corner of the canvas
    // (original position of the coordinate system)
    ctx.fillRect(0, 0, 80, 80);
    }  /* function init() */
  function drawMonster(x, y, angle, headColor, eyeColor) {
    // GOOD PRACTICE : SAVE CONTEXT AND RESTORE IT AT THE END
    ctx.save();
    // Moves the coordinate system so that the monster is drawn
    // at position (x, y)
    ctx.translate(x, y);
    ctx.rotate(angle)
    // head
    ctx.fillStyle=headColor;
    ctx.fillRect(0,0,200,200);
    // eyes
    ctx.fillStyle='red';
    ctx.fillRect(35,30,20,20);
    ctx.fillRect(140,30,20,20);
    // interior of eye
    ctx.fillStyle=eyeColor;
    ctx.fillRect(43,37,10,10);
    ctx.fillRect(143,37,10,10);
    // Nose
    ctx.fillStyle='black';
    ctx.fillRect(90,70,20,80);
    // Mouth
    ctx.fillStyle='purple';
    ctx.fillRect(60,165,80,20);
    // coordinate system at (0, 0)
    drawArrow(ctx, 0, 0, 100, 0, 10, 'red');
    drawArrow(ctx, 0, 0, 0, 100, 10, 'red');
    // GOOD PRACTICE !
    ctx.restore();
    }  /* function drawMonster () */
  </script>
</head>
<body onload="init();">
  <canvas id="myCanvas" width="400" height="400">
    Your browser does not support the canvas tag.
  </canvas>
</body>
</html>

We slightly modified the function that draws the monster:

Example of context save-restore: a monster drawn by a function that saves 
    and restored the context, then a rectangle is drawn, with context as it was previously.

Source code extract of this function: notice at lines 3 and 26  how we save/restore the context at the beginning/end. Right after saving the context, we modify the coordinate system (lines 7-8). The rest of the code is nearly the same as in the last version of the monster example.

function drawMonster

Click to expand!
1.  function drawMonster(x, y, angle, headColor, eyeColor) {
2.      // BEST PRACTICE: SAVE CONTEXT AND RESTORE IT AT THE END
3.      ctx.save();
4.  
5.      // Moves the coordinate system so that the monster is drawn
6.      // at position (x, y)
7.      ctx.translate(x, y);
8.      ctx.rotate(angle);
9.  
10.     // head
11.     ctx.fillStyle=headColor;
12.     ctx.fillRect(0,0,200,200);
13. 
14.     // eyes
15.     ctx.fillStyle='red';
16.     ctx.fillRect(35,30,20,20);
17.     ctx.fillRect(140,30,20,20);
18. 
19.     // interior of eye
20.     ctx.fillStyle=eyeColor;
21.     ctx.fillRect(43,37,10,10);
22.     ctx.fillRect(143,37,10,10);
23.  
24.     ...
25.     // BEST PRACTICE!
26.     ctx.restore();
27. }

3.3 Immediate Mode

In the previous sections, we learned how to draw filled or wireframe rectangles.

As soon as the ctx.strokeRect(x, y, width, height) or the ctx.fillRect(x, y, width, height)  method is called, a rectangle is indeed drawn immediately in the canvas.

While drawing rectangles with strokeRect or fillRect, drawing text or drawing images, all these shapes will be drawn in immediate mode.

Another mode called “path mode” or “buffered mode” will be seen later in this course, which will be useful for drawing lines, curves, arcs, and also rectangles. Rectangles are the only shapes that have methods for drawing them immediately  and also other methods for drawing them in “path/buffered mode”.

Example: drawing rectangles in immediate mode using best practices

Let’s give an example that draws several rectangles, filled or wireframe, with different colors and line widths:

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Immediate mode</title>
  <meta charset="utf-8"/>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
  <script>
    var canvas, ctx;
    window.onload = function () {
      canvas = document.getElementById('myCanvas');
      ctx = canvas.getContext('2d');
      // black rectangle, default color (black)
      ctx.fillRect(10, 10, 100, 100);
      // outlined rectangle, default color
      ctx.strokeRect(150, 10, 100, 100);
      // outlined rectangle filled in red, outline blue
      ctx.fillStyle = 'red';
      ctx.strokeStyle = 'lightBlue';
      ctx.lineWidth = 10;
      ctx.fillRect(100, 150, 150, 150);
      ctx.strokeRect(100, 150, 150, 150);
      // A function to automatize previous drawing
      var angle = Math.PI / 10;
      drawFilledRectangle(300, 150, 150, 150, 'pink', 'green', 10, angle);
      drawFilledRectangle(300, 150, 150, 150, 'yellow', 'purple', 10, angle + 0.5);
    };
    function drawFilledRectangle(x, y, w, h, fillColor, strokeColor, lw, angle) {
      // GOOD PRACTICE : save if the function change the context or coordinate
      // system
      ctx.save();
      // position coordinate system
      ctx.translate(x, y);
      ctx.rotate(angle);
      // set colors, line width...
      ctx.lineWidth = lw;
      ctx.fillStyle = fillColor;
      ctx.strokeStyle = strokeColor;
      // draw at 0, 0 as we translated the coordinate
      // system already
      ctx.fillRect(0, 0, w, h);
      ctx.strokeRect(0, 0, w, h);
      // GOOD PRACTICE : a restore for a save!
      ctx.restore();
      }
  </script>
</head>
<body>
  <canvas id="myCanvas" width="578" height="400">
  </canvas>
</body>
</html>

3.3.2 Drawing Text

The canvas API provides two main methods for drawing text: ctx.strokeText(message, x, y) and  ctx.fillText(message, x, y). It also provides a set of context properties for setting the character font and style, for laying out the text, etc.

Typical use

Look at the example below, and change the position where the text is drawn, change font attributes, etc.:

HTML source code:

Click to expand!
<!DOCTYPE html>
<html>
  <head>
    <title>Drawing text</title>
    <meta charset="utf-8"/>
  </head>
  <body>
    <canvas id="myCanvas" width=500 height=300>Your browser 
      does not support the canvas tag.</canvas>
    <script type="text/javascript">
      var canvas=document.getElementById('myCanvas');
      var context=canvas.getContext('2d');
      context.font = "60pt Calibri";
      context.lineWidth = 3;
      context.strokeStyle = "blue";
      context.fillStyle = "red";
      context.fillText("Hello World!", 10, 100);
      context.strokeText("Hello World!", 10, 100);
    </script>
  </body>
</html>

Source code extract:

1.  context.font = "60pt Calibri";
2.  // .. set color, lineWidth, shadow etc.
3.  
4.  // 10, 10 is the start of the baseline, bottom of left leg of the "H" in the
5.  // "Hello World" example.
6.  context.fillText("Hello World!", 10, 10);
7.  // Or
8.  context.strokeText("Hello World!", 10, 10);

Properties and methods useful for drawing text

Choosing the font: the context.font property

It is possible to draw text in a canvas using the font property of the context to specify the font style (plain, bold, italic), the size, and the font name. Other properties such as strokeStyle or fillStyle, as well as other properties that are detailed in the next pages, are also going to be taken into account.

The font property accepts values like: font-style,  font-weight, font-size, font-face.

Accepted values are:

Examples:

Drawing text in solid or wireframe: the fillText() or strokeText() methods

The fillText(message, x, y) or strokeText(message, x, y) methods from the context will actually draw a text message at the origin of the baseline position. In the “Hello World” example, this is located at the bottom of the left leg of the “H”.

There is a fourth optional parameter maxWidth that forces the text to fit into a given width, distorting it if necessary:

  context.strokeText("Hello World!", x, y [, maxWidth]);
  context.fillText("Hello World!", x, y [, maxWidth]);

Forcing a text not to exceed a certain width: the maxWidth property

Click to expand!
<!DOCTYPE html>
<html>
<head>
  <title>Drawing text: the maxWidth property</title>
  <meta charset="utf-8"/>
</head>
<body>
  <canvas id="myCanvas" width=500 height=300>Your browser 
    does not support the canvas tag.</canvas>
  <script type="text/javascript">
    var canvas=document.getElementById('myCanvas');
    var context=canvas.getContext('2d');
    context.font = "60pt Calibri";
    context.lineWidth = 3;
    context.strokeStyle = "blue";
    context.fillStyle = "red";
    context.fillText("Hello World!", 10, 100);
    context.strokeText("Hello World!", 10, 100);
    // Draw text with constrained width
    context.fillText("Hello World!", 10, 160, 250);
    context.strokeText("Hello World!", 10, 160, 250);
    context.fillText("Hello World!", 10, 220, 150);
    context.strokeText("Hello World!", 10, 220, 150);
  </script>
</body>
</html>

JavaScript code extract:

Click to expand!
1.  ...
2.  context.font = "60pt Calibri";
3.  context.lineWidth = 3;
4.  context.strokeStyle = "blue";
5.  context.fillStyle = "red";
6.  
7.  context.fillText("Hello World!", 10, 100);
8.  context.strokeText("Hello World!", 10, 100);
9.  
10. // Draw text with constrained width of 250 pixels
11. context.fillText("Hello World!", 10, 160, 250);
12. context.strokeText("Hello World!", 10, 160, 250);
13. 
14. // Constrain width to 150 pixels
15. context.fillText("Hello World!", 10, 220, 150);
16. context.strokeText("Hello World!", 10, 220, 150);

Measuring the width of a given text (bounding box) with the ctx.measureText()method

The ctx.measureText() method can be used to get the current width in pixels of a given text, taking into account the diverse properties involved such as font, size, shadow, lineWidth, etc.

HTML source code:

Click to expand!
<!DOCTYPE html>
<html>
<head>
  <title>Drawing text: the ctx.measureText() method</title>
  <meta charset="utf-8"/>
</head>
<body>
  <canvas id="myCanvas" width=500 height=300>Your browser 
    does not support the canvas tag.</canvas>
  <script type="text/javascript">
    var canvas=document.getElementById('myCanvas');
    var context=canvas.getContext('2d');
    context.font = "60pt Calibri";
    context.lineWidth = 3;
    context.strokeStyle = "blue";
    context.fillStyle = "red";
    context.fillText("Hello World!", 10, 100);
    context.strokeText("Hello World!", 10, 100);
    var textMetrics = context.measureText("Hello World!");
    var width = textMetrics.width;
    context.font = "20pt Arial";
    context.fillText("Width of previous text: " + width 
  + "pixels", 10, 150);
    // Draw the baseline of the given width
    context.moveTo(10, 100);
    context.lineTo(width+10, 100);
    context.stroke();
  </script>
</body>
</html>

JavaScript code extract from this example:

Click to expand!
1.  context.font = "60pt Calibri";
2.  context.lineWidth = 3;
3.  context.strokeStyle = "blue";
4.  context.fillStyle = "red";
5.  
6.  context.fillText("Hello World!", 10, 100);
7.  context.strokeText("Hello World!", 10, 100);
8.  
9.  var textMetrics = context.measureText("Hello World!");
10. var width = textMetrics.width;
11. 
12. // Draw a text that displays the width of the previous drawn text
13. context.font = "20pt Arial";
14. context.fillText("Width of previous text: " 
      + width + "pixels", 10, 150);
15. 
16. // Draw the baseline of the given width
17. context.moveTo(10, 100);
18. context.lineTo(width+10, 100);
19. context.stroke();

Changing the way the text is horizontally drawn: the ctx.textbaseline property

The textBaseline property of the context is used to specify the different ways one can position the baseline of a given text:

HTML source code:

Click to expand!
<!DOCTYPE html>
<html>
<head>
  <title>Drawing text: the ctx.textbaseline property</title>
  <meta charset="utf-8"/>
</head>
<body>
  <canvas id="myCanvas" width=500 height=300>Your browser does not support the canvas tag.</canvas>
  <script type="text/javascript">
    var canvas=document.getElementById('myCanvas');
    var context=canvas.getContext('2d');
    context.strokeStyle = "#000000";
    context.lineWidth = 1;
    context.beginPath();
    context.moveTo( 0, 75);
    context.lineTo(500, 75);
    context.stroke();
    context.closePath();
    context.font = "16px Verdana";
    context.fillStyle = "#000000";
    context.textBaseline = "top";
    context.fillText("top", 0, 75);
    context.textBaseline = "hanging";
    context.fillText("hanging", 40, 75);
    context.textBaseline = "middle";
    context.fillText("middle", 120, 75);
    context.textBaseline = "alphabetic";
    context.fillText("alphabetic", 200, 75);
    context.textBaseline = "ideographic";
    context.fillText("ideographic", 300, 75);
    context.textBaseline = "bottom";
    context.fillText("bottom-glyph", 400, 75);
  </script>
</body>
</html>

The example above shows the different possible values for this property and the corresponding results. The default value is “alphabetic” and corresponds to what has been used in the previous “Hello World” example.

Possible values for the textbaseline property:

textBaseline property Description
top The text is aligned based on the top of the tallest glyph in the text.
hanging The text is aligned based on the line the text seems to hang from. This is almost identical to top, and in many cases, you cannot see the difference.
middle The text is aligned according to the middle of the text.
alphabetic The bottom of vertically oriented glyphs, e.g. western alphabet like the Latin.
ideographic The bottom of horizontally oriented glyphs.
bottom The text is aligned based on the bottom of the glyph in the text, that extends furthest down in the text.

Typical use (taken from the example above):

1.  context.textBaseline = "top";
2.  context.fillText("top", 0, 75);
3.  context.textBaseline = "hanging";
4.  context.fillText("hanging", 40, 75);
5.  context.textBaseline = "middle";
6.  context.fillText("middle", 120, 75);

Setting the horizontal justification of a text with the textAlign property

The textAlign property of the context tells how the x parameter will be used when calling strokeText(“some text”, x, y) and fillText(“some text”, x, y). For example, with textAlign=“center”, the x parameter gives the position of the vertical center of the text, while in textAlign=“right”, x corresponds to the rightmost position of the text.

HTML source code:

Click to expand!
<!DOCTYPE html>
<html>
<head>
  <title>Drawing text: the textAlign property</title>
  <meta charset="utf-8"/>
</head>
<body>
  <canvas id="myCanvas" width=500 height=120>Your browser 
    does not support the canvas tag.</canvas>
  <script type="text/javascript">
    var canvas=document.getElementById('myCanvas');
    var context=canvas.getContext('2d');
    context.stokeStyle = "#000000";
    context.lineWidth = 1;
    context.beginPath();
    context.moveTo( 250, 0);
    context.lineTo( 250, 130);
    context.stroke();
    context.closePath();
    context.font = "16px Verdana";
    context.fillStyle = "#000000";
    context.textAlign = "center";
    context.fillText("center", 250, 20);
    context.textAlign = "start";
    context.fillText("start", 250, 40);
    context.textAlign = "end";
    context.fillText("end", 250, 60);
    context.textAlign = "left";
    context.fillText("left", 250, 80);
    context.textAlign = "right";
    context.fillText("right", 250, 100);
  </script>
</body>
</html>

Typical use (source code taken from the above example):

Click to expand!
1.  context.textAlign = "center";
2.  context.fillText("center", 250, 20);
3.  context.textAlign = "start";
4.  context.fillText("start", 250, 40);
5.  context.textAlign = "end";
6.  context.fillText("end", 250, 60);
7.  context.textAlign = "left";
8.  context.fillText("left", 250, 80);
9.  context.textAlign = "right";
10. context.fillText("right", 250, 100);

3.3.3 Drawing Images

Working with images is rather simple, except that we need the images to be fully loaded into memory before drawing them. Loading images is an asynchronous process we need to take care of. Working with multiple images might also be difficult for beginners. We present a multiple image loader later on in this course.

Let’s say it once again: To use an image in a canvas, make sure that the image has been loaded by the Web browser before drawing it!

It is also possible to draw images from a video stream, images corresponding to another canvas content, or images that are defined by <img> HTML elements in the page. We will see that as well in the following parts of this chapter.

But let’s start with a basic example!

Example #1: drawing an image in a canvas

HTML Source code:

Click to expand!
1.  <!DOCTYPE HTML>
2.  <html lang="en">
3.    <head>
4.      <meta charset="utf-8"/>
5.      <title>Simple image drawing in a canvas</title>
6.      <script>
7.        window.onload = function () {
8.        // Necessity to run this code only after the web page has been loaded.
9.        var canvas = document.getElementById("myCanvas");
10.       var context = canvas.getContext("2d");
11.       var imageObj = new Image();
12.       // Callback function called by the imageObj.src = .... line
13.       //located after this function
14.       imageObj.onload = function () {
15.       // Draw the image only when we have the guarantee
16.       // that it has been loaded
17.       context.drawImage(imageObj, 0, 0);
18.     };
19.  
20.     // Calls the imageObj.onload function asynchronously
21.     imageObj.src =
22.       "https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png";
23.     };
24.     </script>
25.   </head>
26.   <body>
27.     <canvas id="myCanvas" width="512" height="512"></canvas>
28.   </body>
29. </html>

Explanations:

  1. We have to create a JavaScript Image object (line 10),
  2. When we set the src attribute of this object with the URL of the image file, then an asynchronous request is sent in the background by the browser. Loading a big image may take some time, so the rest of the Ja vaScript code continues  running. This is why we call it “asynchronous”.
  3. When the image file has been loaded, the browser calls the onload callback associated with the image (line 14).
  4. We draw the image only from inside this callback, otherwise we have no guarantee that the image has been loaded and can be usable. The actual drawing here is done line 1.
  5. There are numerous variants of the drawImage(…) context method at line 17

See picture below:

Drawing Images with Subimages. Source image within a Destination Canvas.

Example #2: different variants of drawImage(…)

This example illustrates the use of the different variants of the drawImage method:

HTML source code:

Click to toggle!
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Drawing images: drawImage variants</title>
  <script>
    window.onload = function() {
    var canvas = document.getElementById("myCanvas");
    var context = canvas.getContext("2d");
    var imageObj = new Image();
    imageObj.onload = function() {
    // Try commenting/uncommenting the following lines to see the
    // effect of the different drawImage variants
    // Original, big image
    // context.drawImage(imageObj, 0, 10);
    // Original image drawn with size = 100x100 pixels
    context.drawImage(imageObj, 0, 10, 100, 100);
    // with size = 150x150
    context.drawImage(imageObj, 80, 10, 150, 150);
    // with size = 200x200
    context.drawImage(imageObj, 210, 10, 200, 200);
    // draw the sub image at 0, 0, width = 512, height = 100
    // at position 100, 250, with a width of 256 and a height of 50 
    context.drawImage(imageObj, 0, 0, 512, 100, 100, 250, 256, 50);
    };
    imageObj.src = "https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png";
    };
  </script>
</head>
<body>
  <canvas id="myCanvas" width="512" height="512"></canvas>
</body>
</html>

CSS source code:

#myCanvas {
  border:1px solid black;
}

JavaScript source code extract:

Click to expand!
1.  var imageObj = new Image();
2.   
3.  imageObj.onload = function() {
4.     // try commenting/uncommenting the following lines to see the
5.     // effect of the different drawImage variants
6.  
7.     // original, big image
8.     // context.drawImage(imageObj, 0, 10);
9.  
10.    // original image drawn with size = 100x100 pixels
11.    context.drawImage(imageObj, 0, 10, 100, 100);
12.    // with size = 150x150
13.    context.drawImage(imageObj, 80, 10, 150, 150);
14.    // with size = 200x200
15.    context.drawImage(imageObj, 210, 10, 200, 200);
16.  
17.   // draw the sub image at 0, 0, width = 512, height = 100
18.   // at position 100, 250, with a width of 256 and a height of 50
19.   context.drawImage(imageObj, 0, 0, 512, 100, 100, 250, 256, 50);
20. };
21. imageObj.src = "https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png";
22. };

Example #3: draw an image defined in the page by an <img src=“…”> element

Sometimes, you may want to draw an image that is already declared in the HTML document as an <img src="..."> element. Remember that when you add an <img>  in the document, the browser starts downloading it in background.

You could try drawing it using some code like this:

1.  <body>
2.    <canvas id="myCanvas" width="512" height="512"></canvas>
3.    <p>Original image as an <img> element:</p>
4.    <b><img id="logo"</b>
5.    <b>src="https://fc07.deviantart.net/fs70/f/2013/149/b/8/texture_85_by_voyager168-d670m68.jpg"></b>
6.    <script>
7.      canvas = document.getElementById("myCanvas");
8.      var ctx = canvas.getContext("2d");
9.      var logo = document.querySelector("#logo");
10.     <b>ctx.drawImage(logo, 0, 0, 100, 100);</b>
11.   </script>
12. </body>

Although you will find many examples on the Web that do it this way, they will only work most of the time with small images, or with images that are in the browser’s cache. Remember that you cannot draw an image that has not been fully loaded!

If you try to draw an image that is not loaded or partially loaded, you will have unexpected results!

Best practice: only draw an image that is fully loaded, use the onload callback!

The right way to do this is shown in this online example, that starts drawing only from the onload callback function:

CSS source code:

#myCanvas {
  border:1px solid black;
}

HTML source code:

Click to expand!
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Drawing an image with <img></title>
  <script>
    var canvas, context, imageObj;
    window.onload = function() {
    canvas = document.getElementById("myCanvas");
    context = canvas.getContext("2d");
    imageObj = document.querySelector("#logo");
    drawAllImages();
    };
	
    function drawAllImages() {
      console.log("image is already loaded, we draw it!");
      // Original image drawn with size = 100x100 pixels
      context.drawImage(imageObj, 0, 10, 100, 100);
      // with size = 150x150
      context.drawImage(imageObj, 80, 10, 150, 150);
      // with size = 200x200
      context.drawImage(imageObj, 210, 10, 200, 200);
      // draw the sub image at 0, 0, width = 512, height = 100
      // at position 100, 250, with a width of 256 and a height of 50
      context.drawImage(imageObj, 0, 0, 512, 100, 100, 250, 256, 50);
    }
  </script>
</head>
<body>
  <p>A canvas with an image that is further in the page, loaded by the 
    <img src=...> tag. This is not the recommended way to load images, 
      except if the image is already in your page. Use the onload callback to be sure that the 
      image is in the page. </p>
    <canvas id="myCanvas" width="512" height="512"></canvas>
    <p>Original image is an &lt;img> element:
  </p>
  <img id="logo" src="https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png" 
    alt="html5 logo">
</body>
</html>

With large image files, this will not break nor produce unexpected results:

CSS source code:

#myCanvas {
  border:1px solid black;
}

HTML

Click to expand!
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Using a large image</title>
  <script>
    let canvas, context;
    window.onload = () => {
    canvas = document.getElementById("myCanvas");
    context = canvas.getContext("2d");
    draw();
    };

    function draw() {
      var imageObj = document.querySelector("#logo");
      console.log("Image has been loaded, let's draw it!");
      // Original image drawn with size = 100x100 pixels
      context.drawImage(imageObj, 0, 10, 100, 100);
      // with size = 150x150
      context.drawImage(imageObj, 80, 10, 150, 150);
      // with size = 200x200
      context.drawImage(imageObj, 210, 10, 200, 200);
      // draw the sub image at 0, 0, width = 512, height = 100
      // at position 100, 250, with a width of 256 and a height of 50
      context.drawImage(imageObj, 0, 0, 512, 100, 100, 250, 256, 50);
    }
  </script>
</head>
<body>
  <p>It is possible that the drawing in the canvas below appears only after a few seconds, 
    because the image is very large!</p>
  <canvas id="myCanvas" width="512" 
    height="512"></canvas>
  <p>Very large original image, declared as an &lt;img> element:
  </p>
  <img id="logo" alt="logo" src="https://mainline.i3s.unice.fr/mooc/texture_85_by_voyager168-d670m68.jpg" 
    width="512" height="512">
</body>
</html>

The DOM Level 2 Events specification says: “The load event occurs when the DOM implementation finishes loading all content within a document, all frames within a FRAMESET, or an OBJECT element.

3.3.4 Drawing Images from a Video Stream

The drawImage(…) function can take a video element as its first parameter. The image that will be drawn is the one currently played by the video stream. This can be done at video frequency on most modern computers or mobile devices.

HTML source code:

Click to expand!
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Drawing images from a video stream</title>
  <style>
    body {
      margin: 10px;
      padding: 0px;
    }
    #myCanvas {
      border: 10px solid red;
    }
  </style>
  <script>
    var video;
    var canvas, ctx;
    var angle = 0;
    
    function init() {
      video = document.getElementById('sourcevid');
      canvas = document.getElementById('myCanvas');
      ctx = canvas.getContext('2d');
      setInterval("processFrame()", 25);
    }
  
    function processFrame() {
      // Uncomment next line for drawing 100% copy of the video content
      //ctx.drawImage(video, 0, 0);
      // Comment these 4 lines if you uncommented the previous one
      ctx.drawImage(video, 0, 0, 320, 180);
      drawRotatingVideo(480, 90);
      ctx.drawImage(video, 0, 180, 320, 180);
      ctx.drawImage(video, 320, 180, 320, 180);
    }

    function drawRotatingVideo(x, y) {
      // Clear thze zone at the top right quarter of the canvas
      ctx.clearRect(320, 0, 320, 180);
      // We are going to change the coordinate system, save the context !
      ctx.save();
      // translate, rotate and recenter the image at its "real" center,
      //not the top left corner
      ctx.translate(x, y);
      ctx.rotate(angle += 0.01);
      ctx.translate(-80, -45);
      ctx.drawImage(video, 0, 0, 160, 90);
      // restore the context
      ctx.restore();
    }
  </script>
</head>
<body onload="init()" >
  <p>This is a <code>&lt;video></code> element: </p>
  <video id="sourcevid" autoplay loop>
  <source src="https://mainline.i3s.unice.fr/mooc/BigBuckBunny_640x360.mp4" 
    type="video/mp4" />
  <source src="https://mainline.i3s.unice.fr/mooc/BigBuckBunny_640x360.ogv" 
    type="video/ogg"/>
  </video>
  <p>This is a <code>&lt;canvas></code> element: </p>
  <canvas id="myCanvas" width="620" height="360"></canvas>
</body>
</html>

This example shows:

JavaScript source code extract:

Click to expand!
1.  <script>
2.     var video;
3.     var canvas, ctx;
4.     var angle = 0;
5.   
6.  function init() {
7.     video = document.getElementById('sourcevid');
8.     canvas = document.getElementById('myCanvas');
9.     ctx = canvas.getContext('2d');
10.  
11.    setInterval("processFrame()", 25); // call processFrame each 25ms
12. }
13.  
14. function processFrame() {
15.     ctx.drawImage(video, 0, 0, 320, 180);
16.     drawRotatingVideo(480, 90);
17.     ctx.drawImage(video, 0, 180, 320, 180);
18.     ctx.drawImage(video, 320, 180, 320, 180);
19. }
20.  
21. function drawRotatingVideo(x, y) {
22.      // Clear the zone at the top right quarter of the canvas
23.     ctx.clearRect(320, 0, 320, 180);
24.  
25.     // We are going to change the coordinate system, save the context!
26.     ctx.save();
27.     // translate, rotate and recenter the image at its "real" center,
28.     //not the top left corner
29.     ctx.translate(x, y);
30.     ctx.rotate(angle += 0.01); // rotate and increment the current angle
31.     ctx.translate(-80, -45);
32.  
33.     ctx.drawImage(video, 0, 0, 160, 90);
34.  
35.     // restore the context
36.     ctx.restore();
37. }
38. </script>
39. </head>
40.  
41. <body onload="init()" >
42.   <p>This is a <video><video> element: </p>
43.   <video id="sourcevid" autoplay="true" loop="true">
44.   <source src="https://mainline.i3s.unice.fr/mooc/BigBuckBunny_640x360.mp4"
45.          type="video/mp4" />
46.   <source src="https://mainline.i3s.unice.fr/mooc/BigBuckBunny_640x360.ogv"
47.          type="video/ogg"/>
48.   </video>
49.   <p>This is a <canvas> element: </p>
50.   <canvas id="myCanvas" width="620" height="360"></canvas>
51. </body>

Explanations:

3.4 Immediate Mode vs. Path Mode

Immediate mode

As a reminder: an immediate mode means “executing a call to a drawing method means immediately drawing in the canvas”. The drawing appears as soon as the design instruction is executed.

Here is an example that draws 1000 random rectangles in a canvas, using immediate mode rectangle drawing calls:

JavaScript source code:

Click to expand!
var canvas, ctx, w, h;
function init() {
  canvas = document.getElementById('myCanvas');
  ctx = canvas.getContext('2d');
  w = canvas.width;
  h = canvas.height;
  console.time("time to draw");
  for(var i=0; i < 1000; i++) {
    var x = Math.random() * w;
    var y = Math.random() * h;
    var width = Math.random() * w;
    var height = Math.random() * h;
    ctx.strokeRect(x, y, width, height);
  }
  console.timeEnd("time to draw");
}

HTML source code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Canvas</title>
</head>
<body onload = init();>
  <canvas id="myCanvas" width="400" height =400>
    Your browser does not support the canvas tag.</canvas>
</body>
</html>
Click to expand!
1.  var canvas, ctx, w, h;
2.  
3.  function init() {
4.      canvas = document.getElementById('myCanvas');
5.      ctx = canvas.getContext('2d');
6.  
7.      w = canvas.width;
8.      h = canvas.height;
9.  
10.     console.time("time to draw");
11. 
12.     for(var i=0; i < 1000; i++) {
13.        var x = Math.random() * w;
14.        var y = Math.random() * h;
15.        var width = Math.random() * w;
16.        var height = Math.random() * h;
17. 
18.        ctx.strokeRect(x, y, width, height);
19.     }
20.     console.timeEnd("time to draw");
21. }

Lines 12-18 draw 1000 rectangles of random sizes in immediate mode. We also measure the time using the usual console.time(name_of_timer) and  console.timeEnd(name_of_timer) that will write in the browser console the time elapsed. Note that console.time(…) and console.timeEnd(…) display results only in the browser’s console, not in the JSBin console.

On a Mac Book Pro from 2015, the result is an average time of 4.034ms for drawing all these rectangles:

Image of the devtool console measuring average time to draw rectangles.

Path mode

There is another drawing mode called “path drawing mode” where you first send drawing orders to the graphics processor, and these orders are stored in a buffer. Then you call methods to draw the whole buffer at once. There are also methods to erase the buffer’s content.

Path drawing mode allows parallelism: if you need to draw 10,000 rectangles, it’s better to store the orders in the graphics card, then execute the drawing all at once, rather than doing 10,000 immediate calls to strokeRect(…) for example. With the buffered mode, the Graphic Processing Unit (GPU) of the graphics card hardware will be able to parallelize the computations (modern graphics cards can execute hundreds/thousands of things in parallel).

Same example as before, this time using the buffered mode for drawing rectangles:

JavaScript source code:

Click to expand!
var canvas, ctx, w, h;
function init() {
  canvas = document.getElementById('myCanvas');
  ctx = canvas.getContext('2d');
  w = canvas.width;
  h = canvas.height;
  console.time("time to draw");
  for(var i=0; i < 1000; i++) {
    var x = Math.random() * w;
    var y = Math.random() * h;
    var width = Math.random() * w;
    var height = Math.random() * h;
    ctx.rect(x, y, width, height);
  }
  ctx.stroke();
  console.timeEnd("time to draw");
}

HTML source code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Canvas</title>
</head>
<body onload = init();>
  <canvas id="myCanvas" width="400" height =400>
    Your browser does not support the canvas tag.</canvas>
</body>
</html>

Extract from source code (the part that draws the rectangles):

1.  for(var i=0; i < 1000; i++) {
2.    var x = Math.random() * w;
3.    var y = Math.random() * h;
4.    var width = Math.random() * w;
5.    var height = Math.random() * h;
6.    ctx.rect(x, y, width, height); // store a rectangle in path/buffer
7.  }
8.  ctx.stroke(); // draws the whole buffer (the 1000 rectangles) at once

Instead of calling strokeRect(…) or fillRect(…), we just call the rect(…) method of the context (line 7). This is how we can delay the drawing of the rectangles. The 1000 rectangles are stored in a buffer in the hardware.

The call to ctx.stroke() (line 9) or to its sister method ctx.fill() will draw the entire buffer contents in fill or stroke mode.

And here is what the timer gives: a slightly faster execution time. Changing 1000 to 100,000 will give even larger differences.

Path mode is faster than immediate mode! We have now an average time of 3.1ms

Image of the devtool console which shows time values, path vs immediate mode.

Reset the path mode buffer

A call to ctx.beginPath() will reset the buffer (empty its contents). We will see many more examples of using the path drawing mode in another further section.

1.  // start a new buffer / path
2.  ctx.beginPath();
3.  // all these orders are in a buffer/path
4.  ctx.moveTo(10, 10);
5.  ctx.lineTo(100, 100);
6.  ctx.lineTo(150, 70);
7.  // Draw the buffer
8.  ctx.stroke();

Summary of path mode principles

  1. Call drawing methods that work in path mode, for example call ctx.rect(…) instead of ctx.strokeRect(…) or ctx.fillRect(…),
  2. Call ctx.stroke() or ctx.fill() to draw the buffer’s contents,
  3. Beware that the buffer is never emptied, two consecutive calls to ctx.stroke() will draw the buffer contents twice! Instead, use ctx.beginPath() to empty it if needed,
  4. It is possible to empty the buffer by calling ctx.beginPath(),
  5. Path drawing is faster than immediate drawing (parallelization is possible).

3.4.2 A Warning

Warning: you do not need to spend too much time on each part of this sub-section.

You do not need to memorize or learn by heart all the examples in the following pages. They are given as references. There will be no quizzes about curves as they are not often done “by hand”, but are generated by tools such as Adobe Illustrator or online generators.

Also, do not forget to use your favorite HTML5 canvas cheatsheet (provided before in the last section). You will find it very helpful when you start playing with the canvas.

3.4.3 Drawing Lines

We have been drawing rectangles so far.

Now let’s go a bit further by introducing the notion of “path drawing”. This approach uses the ctx.moveTo(x, y) method of the context, in conjunction with other drawing methods that end in “To”, such as ctx.lineTo(x, y).

This makes it easier to draw multiple connected lines. Consecutive calls to ctx.lineTo(x, y) will store in the path/buffer a set of connected lines that we will draw altogether by a single call to ctx.stroke() or ctx.fill().

Here are the 5 different steps:

  1. Put the “pencil” somewhere with a call to ctx.moveTo(x1, y1). This will be the origin of the first line.
  2. Call the ctx.lineTo(x2, y2) method to draw a line from the previous position (previous step) to the position passed as parameters to the  lineTo(…) method. This position will serve as the origin for the next line to be drawn.
  3. Call lineTo(x3, y3) again to draw a line that goes from (x2, y2) to (x3, y3). This line will start at the end of the previous one.
  4. Repeat step 3 to draw more connected lines.
  5. Call the ctx.stroke() or the ctx.fill() methods to draw the path  defined by the different lines.

Note the call to ctx.stroke() or ctx.fill() will use the current values of the strokeStyle or fillStyle properties. It is possible to call ctx.moveTo(x, y) in the middle of steps 1 through 5 in order to move the pen somewhere else without connecting to the last drawn line.

Drawing a grid

See the example below:

Draw blue grid using ctx.stroke command.

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Drawing a grid</title>
</head>
<body>
  <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
  <script type="text/javascript">
    var canvas=document.getElementById('myCanvas');
    var ctx=canvas.getContext('2d');
    // Vertical lines
    for (var x = 0.5; x < 500; x += 10) {
      ctx.moveTo(x, 0);
      ctx.lineTo(x, 375);
    }
    // Horizontal lines
    for (var y = 0.5; y < 375; y += 10) {
      ctx.moveTo(0, y);
      ctx.lineTo(500, y);
    }
    // Draw in blue
    ctx.strokeStyle = "#0000FF";
    // Before the execution of the next line nothing as been drawn yet !
    ctx.stroke();
  </script>
</body>
</html>

JavaScript source code extract:

Click to expand!
1.  var canvas=document.getElementById('myCanvas');
2.  var ctx=canvas.getContext('2d');
3.  
4.  // Vertical lines
5.  for (var x = 0.5; x < 500; x += 10) {
6.      ctx.moveTo(x, 0);
7.      ctx.lineTo(x, 375);
8.  }
9.  
10. // Horizontal lines
11. for (var y = 0.5; y < 375; y += 10) {
12.     ctx.moveTo(0, y);
13.     ctx.lineTo(500, y);
14. }
15.  
16. // Draw in blue
17. ctx.strokeStyle = "#0000FF";
18.  
19. // Until the execution of the next line, nothing has been drawn!
20. ctx.stroke();

In this example, the entire grid is drawn during the execution of the last line of code, with the single call to ctx.stroke().

Mixing filled and wireframe shapes (and immediate and path modes)

Try this:

Mix filled and wireframe shapes using different methods.

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Canvas</title>
</head>
<canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
<script type="text/javascript">
  var canvas=document.getElementById('myCanvas');
  var ctx=canvas.getContext('2d');
  ctx.fillStyle='#FF0000';
  ctx.fillRect(0,0,80,100);
  ctx.moveTo(0,0);
  ctx.lineTo(100, 100);
  ctx.lineTo(100,0);
  ctx.strokeStyle = "#0000FF";
  ctx.stroke();
</script>
</body>
</html>

JavaScript source code:

Click to expand!
1.  var canvas=document.getElementById('myCanvas');
2.  var ctx=canvas.getContext('2d');
3.  
4.  // a filled rectangle in immediate mode
5.  ctx.fillStyle='#FF0000';
6.  ctx.fillRect(0,0,80,100);
7.  
8.  // two consecutive lines in path mode
9.  ctx.moveTo(0,0);
10. ctx.lineTo(100, 100);
11. ctx.lineTo(100,0);
12. 
13. // draws only the two lines in wireframe mode
14. ctx.strokeStyle = "#0000FF";
15. ctx.stroke();

This example shows that filled and wireframe shapes should be drawn differently (here a filled rectangle is drawn using a call to the fillRect(…) method while a wireframe set of connected lines is drawn using the stroke() method of the context).

Drawing a single path made with disconnected lines / parts

Try this:

Drawing single path made with disconnected lines.

JavaScript source code:

Click to expand!
1.  var canvas=document.getElementById('myCanvas');
2.  var ctx=canvas.getContext('2d');
3.  
4.  // first part of the path
5.  ctx.moveTo(20,20);
6.  ctx.lineTo(100, 100);
7.  ctx.lineTo(100,0);
8.  
9.  // second part of the path, moveTo(...) is used to "jump" to another place
10. ctx.moveTo(120,20);
11. ctx.lineTo(200, 100);
12. ctx.lineTo(200,0);
13. 
14. // indicate stroke color + draw the path
15. ctx.strokeStyle = "#0000FF";
16. ctx.stroke();

In this last example, we simply called the moveTo() method between each part of the path (lines 5 and 10). And we called stroke() (line 16) only once to draw the whole path.

3.4.4 Drawing Lines with Different Styles

Common mistake: drawing the same path twice

Let’s look at the drawing from the last example of the previous section:

Same drawing as in previous section. Example using moveto() and stroke() command functions.

HTML source code:

Click to expand!
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>Drawing disconnected lines</title>
</head>
<canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
<script type="text/javascript">
  var canvas=document.getElementById('myCanvas');
  var ctx=canvas.getContext('2d');
  // first part of the path
  ctx.moveTo(20,20);
  ctx.lineTo(100, 100);
  ctx.lineTo(100,0);
  // second part of the path, moveTo(...) is used to "jump" to another place
  ctx.moveTo(120,20);
  ctx.lineTo(200, 100);
  ctx.lineTo(200,0);
  // indicate stroke color + draw the path
  ctx.strokeStyle = "#0000FF";
  ctx.stroke();
</script>
</body>
</html>

Imagine that we would like to draw them with different styles and colors: the shape on the left will stay as it is now (blue, wireframe), while the shape on the right will be filled, colored in pink. Let’s look at how we can do this…

Drawing two paths with different styles: the WRONG and the right way!

First, the wrong way!

In this example, we will draw the two parts of the path with different styles: the first part in wireframe mode, and the second part in filled mode.

What we will try first is to call stroke() after the first half of the path, then call fill() after the second half of the path:

THE WRONG WAY! Drawing two paths with different styles.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8"/>
5.   <title>Drawing two paths with different styles: the wrong way!</title>
6. </head>
7. <body>
8. <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
9. <script type="text/javascript">
10.   var canvas=document.getElementById('myCanvas');
11.   var ctx=canvas.getContext('2d');
12.   // first part of the path
13.   ctx.moveTo(20,20);
14.   ctx.lineTo(100, 100);
15.   ctx.lineTo(100,0);
16.   // indicate stroke color + draw first part of the path
17.   ctx.strokeStyle = "#0000FF";
18.   ctx.stroke();
19.   // second part of the path
20.   ctx.moveTo(120,20);
21.   ctx.lineTo(200, 100);
22.   ctx.lineTo(200,0);
23.   // indicate stroke color + draw the path
24.   ctx.fillStyle = "pink";
25.   ctx.fill();
26. </script>
27. </body>
28. </html>

JavaScript source code:

Click to expand!
1.  var canvas=document.getElementById('myCanvas');
2.  var ctx=canvas.getContext('2d');
3.  
4.  // first part of the path
5.  ctx.moveTo(20,20);
6.  ctx.lineTo(100, 100);
7.  ctx.lineTo(100,0);
8.  
9.  // indicate stroke color + draw first part of the path
10. ctx.strokeStyle = "#0000FF";
11. ctx.stroke();
12. 
13. // second part of the path
14. ctx.moveTo(120,20);
15. ctx.lineTo(200, 100);
16. ctx.lineTo(200,0);
17. 
18. // indicate stroke color + draw the path
19. ctx.fillStyle = "pink";
20. ctx.fill();

Hey - it does not work! Weirdly, the two parts of the path are filled in pink! But we called stroke() after the first half of the path was drawn (lines 5-8). Then we called fill() only after the second part of the path was specified (lines 14-19)… so, what happened?

Remember that fill() or stroke() draws the whole path, even if it is disconnected, and even if it has already been drawn!

What happened is:

  1. The call to stroke() has drawn the path corresponding to the lines 5-7. Indeed, the first part of the path (on the left) has actually been drawn once in wireframe mode, and in blue.
  2. Then, the call to fill() at line 20 has drawn the whole path again, but in pink and in filled mode. But this time the path corresponds to lines 5-7 plus lines 14-16 that make up the second shape on the right. So the path that has been drawn this time is made of both of the triangles.

Important: If you do not want to draw parts of the same path several times, you need to draw two different paths, using the ctx.beginPath() method, as shown in the next example.

Now, the right way!

Draw two paths with different styles the RIGHT way.

HTML source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8"/>
5.    <title>Drawing two paths with different styles: the right way!</title>
6.  </head>
7.  <body>
8.  <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
9.  <script type="text/javascript">
10.   var canvas=document.getElementById('myCanvas');
11.   var ctx=canvas.getContext('2d');
12.   // first part of the path
13.   ctx.moveTo(20,20);
14.   ctx.lineTo(100, 100);
15.   ctx.lineTo(100,0);
16.   // indicate stroke color + draw first part of the path
17.   ctx.strokeStyle = "#0000FF";
18.   ctx.stroke();
19.   // start a new path, empty the current buffer
20.   ctx.beginPath();
21.   // second part of the path
22.   ctx.moveTo(120,20);
23.   ctx.lineTo(200, 100);
24.   ctx.lineTo(200,0);
25.   // indicate stroke color + draw the path
26.   ctx.fillStyle = "pink";
27.   ctx.fill();
28. </script>
29. </body>
30. </html>

JavaScript source code:

Click to expand!
1.  var canvas=document.getElementById('myCanvas');
2.  var ctx=canvas.getContext('2d');
3.  
4.  // first part of the path
5.  ctx.moveTo(20,20);
6.  ctx.lineTo(100, 100);
7.  ctx.lineTo(100,0);
8.  
9.  // indicate stroke color + draw first part of the path
10. ctx.strokeStyle = "#0000FF";
11. ctx.stroke();
12. 
13. // start a new path, empty the current buffer
14. ctx.beginPath();
15. 
16. // second part of the path
17. ctx.moveTo(120,20);
18. ctx.lineTo(200, 100);
19. ctx.lineTo(200,0);
20. 
21. // indicate stroke color + draw the path
22. ctx.fillStyle = "pink";
23. ctx.fill();

This time, in order to draw the two shapes differently, we defined two separate paths. The way to do this is just to call ctx.beginPath() to start a new path. In this example, the first path has been drawn in wireframe mode, then a new path has been started that is drawn in filled mode.

3.4.5 Drawing Lines in Immediate Mode

Sometimes, it might be useful to draw just one line.

It’s interesting to see how we can write a single “draw line” function that takes the start and end coordinates, the color, the line width, etc., and give the impression of being done in “immediate” mode.

Here is the code for this “utility” function that you may find useful:

Click to expand!
1.  function drawLine(x1, y1, x2, y2, color, width) {
2.      ctx.save();
3.  
4.      // set color and lineWidth, if these parameters
5.      // are not defined, do nothing (default values)
6.      if(color)
7.          ctx.strokeStyle = color;
8.  
9.      if(width)
10.         ctx.lineWidth = width;
11. 
12.     // start a new path
13.     ctx.beginPath();
14. 
15.     ctx.moveTo(x1, y1);
16.     ctx.lineTo(x2, y2);
17.     ctx.stroke();
18. 
19.     ctx.restore();
20. }

Notice the save/restore of the context at the beginning/end of the function. This is REALLY a best practice to avoid affecting other functions’ context.

Here is an example:

Drawing lines in immediate mode to avoid affecting other functions' context.

HTML source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8"/>
5.    <title>Drawing lines in immediate mode</title>
6.  </head>
7.  <body>
8.  <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
9.  <script type="text/javascript">
10.   var canvas=document.getElementById('myCanvas');
11.   var ctx=canvas.getContext('2d');
12.   drawLine(0, 0, 100, 100);
13.   drawLine(0, 50, 150, 200, 'red');
14.   drawLine(10, 100, 100, 10, 'green', 10);
15.   function drawLine(x1, y1, x2, y2, color, width) {
16.     ctx.save();
17.     // set color and lineWidth, if these parameters
18.     // are not defined, do nothing (default values)
19.     if(color)
20.       ctx.strokeStyle = color;
21.     if(width)
22.       ctx.lineWidth = width;
23.     // start a new path
24.     ctx.beginPath();
25.     ctx.moveTo(x1, y1);
26.     ctx.lineTo(x2, y2);
27.     ctx.stroke();
28.     ctx.restore();
29.   }
30. </script>
31. </body>
32. </html>

Source code extract:

drawLine(0, 0, 100, 100);
drawLine(0, 50, 150, 200, 'red');
drawLine(10, 100, 100, 10, 'green', 10);

3.4.6 Drawing Arrows

In this section, we present a function that draws arrows in a canvas.

You may find multiple implementations on the Web for drawing arrows in a canvas, but the one we are presenting has the advantage of being rather simple and enables you to set the color and line width of the arrows.

Examples

Example #1:

Click to expand!
1.  // Adapted from : https://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag
2.  function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
3.      //variables to be used when creating the arrow
4.      var headlen = 10;
5.      var angle = Math.atan2(toy-fromy,tox-fromx);
6.   
7.      ctx.save();
8.      ctx.strokeStyle = color;
9.   
10.     //starting path of the arrow from the start square to the end square
11.     //and drawing the stroke
12.     ctx.beginPath();
13.     ctx.moveTo(fromx, fromy);
14.     ctx.lineTo(tox, toy);
15.     ctx.lineWidth = arrowWidth;
16.     ctx.stroke();
17.  
18.     //starting a new path from the head of the arrow to one of the sides of
19.     //the point
20.     ctx.beginPath();
21.     ctx.moveTo(tox, toy);
22.     ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),
23.                toy-headlen*Math.sin(angle-Math.PI/7));
24.  
25.     //path from the side point of the arrow, to the other side point
26.     ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),
27.                toy-headlen*Math.sin(angle+Math.PI/7));
28.  
29.     //path from the side point back to the tip of the arrow, and then
30.     //again to the opposite side point
31.     ctx.lineTo(tox, toy);
32.     ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),
33.                toy-headlen*Math.sin(angle-Math.PI/7));
34.  
35.     //draws the paths created above
36.     ctx.stroke();
37. 
38.     ctx.restore();
39. }

Explanations:

Example #2

Function to draw arrows.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8"/>
5.   <title>Drawing arrows</title>
6. </head>
7. <body>
8.   <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
9.   <script type="text/javascript">
10.   var canvas=document.getElementById('myCanvas');
11.   var ctx=canvas.getContext('2d');
12. 
13. // Adapted from : http://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag
14. function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
15.   //variables to be used when creating the arrow
16.   var headlen = 10;
17.   var angle = Math.atan2(toy-fromy,tox-fromx);
18.   ctx.save();
19.   ctx.strokeStyle = color;
20.   //starting path of the arrow from the start square to the end square
21.   //and drawing the stroke
22.   ctx.beginPath();
23.   ctx.moveTo(fromx, fromy);
24.   ctx.lineTo(tox, toy);
25.   ctx.lineWidth = arrowWidth;
26.   ctx.stroke();
27.   //starting a new path from the head of the arrow to one of the sides of
28.   //the point
29.   ctx.beginPath();
30.   ctx.moveTo(tox, toy);
31.   ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),
32.   toy-headlen*Math.sin(angle-Math.PI/7));
33.   //path from the side point of the arrow, to the other side point
34.   ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),
35.   toy-headlen*Math.sin(angle+Math.PI/7));
36.   //path from the side point back to the tip of the arrow, and then
37.   //again to the opposite side point
38.   ctx.lineTo(tox, toy);
39.   ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),
40.   toy-headlen*Math.sin(angle-Math.PI/7));
41.   //draws the paths created above
42.   ctx.stroke();
43.   ctx.restore();
44. }
45.     drawArrow(ctx, 10, 10, 100, 100, 10, 'red');
46.     drawArrow(ctx, 100, 10, 140, 140, 3, 'black');
47.   </script>
48. </body>
49. </html>

Source code extract:

drawArrow(ctx, 10, 10, 100, 100, 10, 'red');
drawArrow(ctx, 100, 10, 140, 140, 3, 'black');

How to draw nicer arrows?

On the Web, you will find many different ways to draw arrows.

This article on drawing lines and arcs with arrow heads is worth reading. It details how to draw arrows with curved heads and different styles for the head. Note, however, that you will need to modify some parts if you want it to support different line widths, etc.

Screenshot from a demo available on the above Web site:

Round clock with nice arrows.

In a later part of the course dedicated to curve drawing in a canvas, we will also show how to draw curved arrows, with very simple code (much simpler than the one used for drawing the clock’s hands above).

3.4.7 Closing a Path

The ctx.closePath() method indicates that we would like a closed path: draw from the last point to the first.

Try this:

Example on closing a path in a drawing.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8"/>
5.   <title>Closing a path</title>
6. </head>
7. <body>
8.   <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
9.   <script type="text/javascript">
10.     var canvas=document.getElementById('myCanvas');
11.     var ctx=canvas.getContext('2d');
12.     // Path made of three points (defines two lines)
13.     ctx.moveTo(20,20);
14.     ctx.lineTo(100, 100);
15.     ctx.lineTo(100,0);
16.     // Close the path, try commenting this line
17.     ctx.closePath();
18.     // indicate stroke color + draw first part of the path
19.     ctx.strokeStyle = "blue";
20.     ctx.stroke();
21.   </script>
22. </body>
23. </html>

JavaScript source code:

Click to expand!
1.  var canvas=document.getElementById('myCanvas');
2.  var ctx=canvas.getContext('2d');
3.  
4.  // Path made of three points (defines two lines)
5.  ctx.moveTo(20,20);
6.  ctx.lineTo(100, 100);
7.  ctx.lineTo(100,0);
8.  
9.  // Close the path, try commenting this line
10. ctx.closePath();
11. 
12. // indicate stroke color + draw first part of the path
13. ctx.strokeStyle = "blue";
14. ctx.stroke();

Explanations:

Try commenting the line 10 in the online example and see the results!

3.4.8 Drawing Circles and Arcs

The ctx.arc(cx, cy, radius, startAngle, endAngle, drawInverse) method is useful for drawing arcs of circles. It takes the center of the circle/arc, its radius, the starting angle of the arc (turning clockwise), the ending angle of the arc, and an optional parameter we will talk about later.

Note: the figures in this page have been borrowed from the HTML5 Canvas Tutorials Web site.

HTML5 canvas arc diagram: drawing circle, coordinate system.

Typical usage

Typical usage for drawing an arc/circle/ellipse is:

1.  ctx.arc(centerX, centerY, radius, startAngle, endAngle); // clockwise drawing
2.  
3.  ctx.arc(centerX, centerY, radius, startAngle, endAngle, false);

The angles are in radians (between 0 and 2*Math.PI). The arc is drawn clockwise. Beware that this may not seem natural if you’re used to the trigonometric order.

The last parameter is optional and has a value of false by default. If true, instead of drawing an arc of circle that corresponds to the parameters, it will draw its complementary. See the examples below to see the difference.

Examples

Example #1: drawing an arc with radius = 50, starting angle = 0, end angle = PI/2

Try this example:

Example on drawing an arc with radius = 50, start angle = 0, end angle = pi/2.
Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8"/>
5.   <title>Drawing an arc</title>
6. </head>
7. <body>
8.   <canvas id="myCanvas" width="500">Your browser does not support the canvas tag.</canvas>
9.   <script type="text/javascript">
10.     var canvas=document.getElementById('myCanvas');
11.     var ctx=canvas.getContext('2d');
12.     ctx.beginPath();
13.     // try to set the last parameter to true or remove it
14.     ctx.arc(100,75,50,0,Math.PI/2);
15.     ctx.lineWidth=10;
16.     ctx.stroke();
17.   </script>
18. </body>
19. </html>

JavaScript source code extract:

1.  ctx.beginPath();
2.  // we ommited the last parameter
3.  ctx.arc(100, 75, 50, 0, Math.PI/2);
4.   
5.  ctx.lineWidth = 10;
6.  ctx.stroke();

And if we change the last parameter of the arc function call (line 3) to true (we omitted it, so it took a value of false by default) :

1.  ctx.beginPath();
2.  // we omitted the last parameter
3.  ctx.arc(100, 75, 50, 0, Math.PI/2, true);
4.   
5.  ctx.lineWidth = 10;
6.  ctx.stroke();

Then, the result is the “complementary” of the previous arc:

Example complementary of the previous arc.

Example #2: drawing a Full circle (filled + outlined)

Example draw a full circle.

JavaScript source code:

Click to expand!
1.  var canvas = document.getElementById("myCanvas");
2.  var ctx = canvas.getContext("2d");
3.  var centerX = canvas.width / 2;
4.  var centerY = canvas.height / 2;
5.  var radius = 70;
6.  
7.  ctx.beginPath();
8.  
9.  // Add to the path a full circle (from 0 to 2PI)
10. ctx.arc(centerX, centerY, radius, 0, 2*Math.PI, false);
11. 
12. // With path drawing you can change the context
13. // properties until a call to stroke() or fill() is performed
14. ctx.fillStyle = "lightBlue";
15. // Draws the filled circle in light blue
16. ctx.fill();
17. 
18. // Prepare for the outline
19. ctx.lineWidth = 5;
20. ctx.strokeStyle = "black";
21. 
22. // draws the path (the circle) AGAIN, this
23. // time in wireframe
24. ctx.stroke();
25. 
26. // Notice we called ctx.arc() only once ! And drew it twice
27. // with different styles

Notice that we called ctx.arc() only once! And drew it twice, with different styles, with calls to ctx.stroke() and ctx.fill(). Each call drew the defined path in wireframe and in filled mode!

Proposed projects

Project #1: modify the previous example in order to get:

Example of half circle.

Project #2: make a small program that draws a smiling head like this (or make something better!)

Example of smiling head.

3.4.9 Drawing Rounded Rectangles

There is another method called ctx.arcTo(x1, y1, x2, y2, radius), which is a bit complex to use, but very practical for drawing rounded rectangles.

In fact, the arcTo(…) method draws an arc of a circle depending on some tangents. Let’s look at these pictures for a better understanding:

There are three (3) diagrams, all drawn on canvas dynamically. The arcTo really uses carTo() with reduced alpha, and the rest of the diagram is calculated in JavaScript and drawn with lineTo and arc.

Typical use:

1.  ctx.moveTo(x0, y0);
2.  
3.  ctx.arcTo(x1, y1, x2, y2, radius);

This method can be confusing. It was defined mainly for drawing rounded shapes like rounded rectangles. We used an excerpt here from the excellent tutorial on the arcTo(…) method.

It works like this:

1.  Draw an imaginary line through (x0,y0) and (x1,y1), draw another imaginary line through (x1,y1) and (x2,y2),
2.  Take an imaginary circle of radius r, and slide it up between the two lines until it just touches both lines. The two points at which the circle touches the lines are called the tangent points.
3.  arcTo(x1, y1, x2, y2, r) will draw a line from the current point (x0,y0) to the first tangent point on the line from (x0,y0) to (x1,y1),
4.  It will also draw an arc from that tangent point to the other tangent point on the line from (x1,y1) to (x2,y2) along the circumference of the circle.
5.  Finally, it adds the tangent point where the arc ends up, on the line from (x1,y1) to (x2,y2) to the path as the new current point on the path.

Examples

Example #1: simple use

Simple use arcTo example #1.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3.   <head>
4.     <meta charset="utf-8"/>
5.     <title>Simple use - arcTo - Example #1</title>
6.   </head>
7.   <body>
8.     <canvas id="myCanvas" height = 400 width="800">Your browser does not 
9.   support the canvas tag.</canvas>
10.     <script type="text/javascript">
11.       var canvas=document.getElementById('myCanvas');
12.       var context=canvas.getContext('2d');
13.       context.beginPath();
14.       context.moveTo(0, 20);
15.       context.arcTo(100, 100, 200, 20, 50);
16.       context.lineWidth = 5;
17.       context.strokeStyle = "#0000ff";
18.       context.stroke();
19.     </script>
20.   </body>
21. </html>

JavaScript source code extract:

1.  context.beginPath();
2.  context.moveTo(0, 20);
3.  context.arcTo(100, 100, 200, 20, 50);
4.  
5.  context.lineWidth = 5;
6.  context.strokeStyle = "#0000ff";
7.  context.stroke();

Example #2: draw A rounded rectangle

Try this:

Draw a rounded rectange, example #2.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3.   <head>
4.     <meta charset="utf-8"/>
5.     <title>Canvas</title>
6.   </head>
7.   <body>
8.     <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
9.     <script type="text/javascript">
10.       var roundedRect=function(ctx,x,y,width,height,radius,fill,stroke)
11.       {
12.       ctx.beginPath();
13.       // draw top and top right corner
14.       ctx.moveTo(x+radius,y);
15.       ctx.arcTo(x+width,y,x+width,y+radius,radius);
16.       // draw right side and bottom right corner
17.       ctx.arcTo(x+width,y+height,x+width-radius,y+height,radius);
18.       // draw bottom and bottom left corner
19.       ctx.arcTo(x,y+height,x,y+height-radius,radius);
20.       // draw left and top left corner
21.       ctx.arcTo(x,y,x+radius,y,radius);
22.       if(fill){
23.         ctx.fill();
24.       }
25.       if(stroke){
26.         ctx.stroke();
27.         }
28.       }
29.       var canvas=document.getElementById('myCanvas');
30.       var ctx=canvas.getContext('2d');
31.       ctx.strokeStyle='rgb(150,0,0)';
32.       ctx.fillStyle='rgb(0,150,0)';
33.       ctx.lineWidth=7;
34.       roundedRect(ctx,15,15,160,120,20,true,true);
35.     </script>
36. </body>

JavaScript source code:

Click to expand!
1.   var roundedRect=function(ctx,x,y,width,height,radius,fill,stroke) {
2.      ctx.beginPath();
3.  
4.     // draw top and top right corner
5.     ctx.moveTo(x+radius,y);
6.     ctx.arcTo(x+width,y,x+width,y+radius,radius);
7.     // draw right side and bottom right corner
8.     ctx.arcTo(x+width,y+height,x+width-radius,y+height,radius);
9.     // draw bottom and bottom left corner
10.    ctx.arcTo(x,y+height,x,y+height-radius,radius);
11.    // draw left and top left corner
12.    ctx.arcTo(x,y,x+radius,y,radius);
13. 
14.    if(fill) {
15.       ctx.fill();
16.    }
17. 
18.    if(stroke){
19.       ctx.stroke();
20.    }
21.  }
22. 
23.  var canvas = document.getElementById('myCanvas');
24.  var ctx    = canvas.getContext('2d');
25. 
26.  ctx.strokeStyle = 'rgb(150,0,0)';
27.  ctx.fillStyle   = 'rgb(0,150,0)';
28.  ctx.lineWidth   = 7;
29. 
30.  roundedRect(ctx, 15, 15, 160, 120, 20, true, true);

In this example, each call to ctx.arcTo(…) draws a side plus a corner. This makes us suspect that the arcTo() method has been designed primarily for drawing rounded rectangles…

Example #3: comparison between lineTo and arcTo

This example at JS Bin is the same as the previous one, except that we added at the end of the roundedRect function the same lines of code that draw the rounded rectangle, but using lineTo instead of arcTo. Just take a look!

Comparison between lineTo and arcTo.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3.   <head>
4.     <meta charset="utf-8"/>
5.     <title>Comparison between lineTo and arcTo - Example #3</title>
6.   </head>
7.   <body>
8.     <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
9.     <script type="text/javascript">
10.       var roundedRect=function(ctx,x,y,width,height,radius,fill,stroke)
11.       {
12.       ctx.beginPath();
13.       // draw top and top right corner
14.       ctx.moveTo(x+radius,y);
15.       ctx.arcTo(x+width,y,x+width,y+radius,radius);
16.       // draw right side and bottom right corner
17.       ctx.arcTo(x+width,y+height,x+width-radius,y+height,radius);
18.       // draw bottom and bottom left corner
19.       ctx.arcTo(x,y+height,x,y+height-radius,radius);
20.       // draw left and top left corner
21.       ctx.arcTo(x,y,x+radius,y,radius);
22.       if(fill){
23.         ctx.fill();
24.       }
25.       if(stroke){
26.         ctx.stroke();
27.       }
28.       // Draws the square using lineTo instead of arcTo,
29.       // just to compare both methods
30.       ctx.save();
31.       ctx.strokeStyle="pink";
32.       ctx.lineWidth=3; // ou ce que tu veux
33.       ctx.beginPath();
34.       ctx.moveTo(x,y);
35.       ctx.lineTo(x+width, y);
36.       ctx.lineTo(x+width, y+height);
37.       ctx.lineTo(x, y+height);
38.       ctx.closePath();
39.       ctx.stroke();
40.       ctx.restore();
41.       }
42.       var canvas=document.getElementById('myCanvas');
43.       var ctx=canvas.getContext('2d');
44.       ctx.strokeStyle='rgb(150,0,0)';
45.       ctx.fillStyle='rgb(0,150,0)';
46.       ctx.lineWidth=7;
47.       roundedRect(ctx,15,15,160,120,20,true,true);
48.     </script>
49.   </body>
50. </html>

Red = arcTo and Pink = lineTo

Example #4: use the unrounded vertices in arcTo

For drawing a rounded square, this code also works:

1.  ctx.moveTo(x+radius, y);
2.  ctx.arcTo(x+width, y,x+width, y+height, radius);
3.  ctx.arcTo(x+width, y+height, x, y+height, radius); 
4.  ctx.arcTo(x, y+height, x, y,radius);
5.  ctx.arcTo(x, y, x+width, y,radius);

Which might be easier than trying to figure out where the arc will end like this:

1.  ctx.moveTo(x+radius, y);
2.  ctx.arcTo(x+width, y, x+width, y+radius, radius);
3.  ctx.arcTo(x+width, y+height, x+width-radius, y+height,radius); 
4.  ctx.arcTo(x, y+height, x, y+height-radius, radius);
5.  ctx.arcTo(x, y, x+radius, y,radius);

This could be particularly helpful if you are dealing with something other than a rectangle, like this rounded triangle:

Example: rounded triangle.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3.   <head>
4.     <meta charset="utf-8"/>
5.     <title>Use the unrounded vertices in arcTo - Example #4</title>
6.   </head>
7.   <body>
8.     <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
9.     <script type="text/javascript">
10.       var roundedTriangle=function(ctx,x1,y1,x2,y2,x3,y3,radius,fill,stroke)
11.       {
12.       ctx.beginPath();
13.       // start at the middle of the side between x1,y1 and x2,y2
14.       ctx.moveTo((x1+x2)/2,(y1+y2)/2);
15.       // go around the x2,y2 vertex
16.       ctx.arcTo(x2,y2,x3,y3,radius);
17.       // go around the x3,y3 vertex
18.       ctx.arcTo(x3,y3,x1,y1,radius);
19.       // go around the x1,y1 vertex
20.       ctx.arcTo(x1,y1,x2,y2,radius);
21.       // and close the triangle with a line to the starting point
22.       ctx.lineTo((x1+x2)/2,(y1+y2)/2);
23.       if(fill){
24.         ctx.fill();
25.       }
26.       if(stroke){
27.         ctx.stroke();
28.       }
29.       }
30.       var canvas=document.getElementById('myCanvas');
31.       var ctx=canvas.getContext('2d');
32.       ctx.strokeStyle='rgb(150,0,0)';
33.       ctx.fillStyle='rgb(0,150,0)';
34.       ctx.lineWidth=7;
35.       roundedTriangle(ctx,200,15,300,150,15,100,20,true,true);
36.     </script>
37.   </body>
38. </html>

3.4.10 Quadratic Curves

Introduction

HTML5 Canvas Quadratic Curve Diagram. Control, context & ending points.

(Picture taken from the HTML5 Canvas Tutorials Web site)

Quadratic curves are defined by a starting point (called a “context point”), a control point, and an ending point. The curve fits the tangents between the context and control points and between the control and ending points.

The context point may be defined by a call to the moveTo(x, y) method of the context, or it may be the ending point of a previous path, if we’re drawing a path made of several shapes. For example, drawing a line and a quadratic curve will make the endpoint of the line the context point for the quadratic curve.

The control point controls the curvature - if we move the control point farther we get a sharper curve.

Typical use:

1.  context.moveTo(contextX, contextY);
2.  context.quadraticCurveTo(controlX, controlY, endX, endY);
3.  // Optional : set lineWidth and stroke color
4.  context.lineWidth = 5;
5.  context.strokeStyle = "#0000ff";
6.  // Draw!
7.  context.stroke();

Examples

Example #1: quadratic curve

Quadratic curve example.

JavaScript source code:

Click to expand!
1.  var canvas=document.querySelector('#myCanvas1');
2.  var context=canvas.getContext('2d');
3.   
4.  context.beginPath();
5.   
6.  context.moveTo(100, 20);
7.  context.quadraticCurveTo(230, 200, 250, 20);
8.   
9.  context.lineWidth = 5;
10. context.strokeStyle = "#0000ff";
11. context.stroke();

We set a starting point in line 6: moveTo(…), then set the control and ending points with a call to quadraticCurve(…), at line 7, then set some properties for color, thickness, and finally we call the stroke() method for drawing the curve.

Example #2: lines connected with a quadratic curve

Try this:

Example: Lines connected with a quadratic curve.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3.   <head>
4.     <meta charset="utf-8"/>
5.     <title>Lines connected with a quadratic curve - Example #2</title>
6.   </head>
7.   <body>
8.     <canvas id="myCanvas1" height = 400 width="800">Your browser does not support the 
9.     canvas tag.</canvas>
10.   <script type="text/javascript">
11.       var canvas=document.querySelector('#myCanvas1');
12.       var context=canvas.getContext('2d');
13.       context.beginPath();
14.       context.moveTo(100, 20);
15.       context.lineTo(200, 80);
16.       context.quadraticCurveTo(230, 200, 250, 20);
17.       context.lineTo(500, 90);
18.       context.lineWidth = 5;
19.       context.strokeStyle = "#0000ff";
20.       context.stroke();
21.     </script>
22.   </body>
23. </html>

JavaScript source code:

Click to expand!
1.  context.beginPath();
2.  
3.  context.moveTo(100, 20);
4.  context.lineTo(200, 80);
5.  context.quadraticCurveTo(230, 200, 250, 20);
6.  context.lineTo(500, 90);
7.  
8.  context.lineWidth = 5;
9.  context.strokeStyle = "#0000ff";
10. context.stroke();

3.4.11 Curved Arrows

We propose a useful function for drawing curved arrows. See this example:

Example: useful function for drawing curved arrows.

JavaSscript source code:

Click to expand!
1. var canvas = document.querySelector('#myCanvas');
2. var ctx = canvas.getContext('2d');
3. var contextX = 100;
4. var contextY = 10;
5. var endPointX = 200;
6. var endPointY = 120;
7. var controlPointX = 35;
8. var controlPointY = 70;
9. drawCurvedArrow(contextX, contextY,
10. endPointX, endPointY,
11. controlPointX, controlPointY,
12. 3, // arrowWidth, try 30 for example !
13. 20, // width of the arrow head, try smaller values, 10...
14. 'blue');
15. function drawCurvedArrow(startPointX, startPointY,
16. endPointX, endPointY,
17. quadPointX, quadPointY,
18. lineWidth,
19. arrowWidth,
20. color) {
21.   // GOOD PRACTICE: the function changes color and lineWidth -> save context!
22.   ctx.save();
23.   ctx.strokeStyle = color;
24.   ctx.lineWidth = lineWidth;
25.   // angle of the end tangeant, useful for drawing the arrow head
26.   var arrowAngle = Math.atan2(quadPointX - endPointX, quadPointY - endPointY) + Math.PI;
27.   // start a new path
28.   ctx.beginPath();
29.   ctx.moveTo(startPointX, startPointY);
30.   ctx.quadraticCurveTo(quadPointX, quadPointY, endPointX, endPointY);
31.   ctx.moveTo(endPointX - (arrowWidth * Math.sin(arrowAngle - Math.PI / 6)),
32.   endPointY - (arrowWidth * Math.cos(arrowAngle - Math.PI / 6)));
33.   ctx.lineTo(endPointX, endPointY);
34.   ctx.lineTo(endPointX - (arrowWidth * Math.sin(arrowAngle + Math.PI / 6)),
35.   endPointY - (arrowWidth * Math.cos(arrowAngle + Math.PI / 6)));
36.   ctx.stroke();
37.   ctx.closePath();
38.   // GOOD PRACTICE -> restore the context as we saved it at the beginning
39.   // of the function
40.   ctx.restore();
41. }

CSS source code:

#myCanvas {
  border: 1px solid black;
}

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Curved arrows</title>
6. </head>
7. <body>
8.   <canvas id="myCanvas" width="250" height="150"></canvas>
9. </body>
10. </html>

Source code of the function that draws a curved arrow:

Click to expand!
1.  function drawCurvedArrow(startPointX, startPointY,
2.                           endPointX, endPointY,
3.                           quadPointX, quadPointY,
4.                           lineWidth,
5.                           arrowWidth,
6.                           color) {
7.      // BEST PRACTICE: the function changes color and lineWidth -> save context!
8.      ctx.save();
9.  
10.     ctx.strokeStyle = color;
11.     ctx.lineWidth = lineWidth;
12.  
13.     // angle of the end tangeant, useful for drawing the arrow head
14.     var arrowAngle = Math.atan2(quadPointX - endPointX, quadPointY - endPointY) + Math.PI;
15.  
16.     // start a new path
17.     ctx.beginPath();
18. 
19.     // Body of the arrow
20.     ctx.moveTo(startPointX, startPointY);
21.     ctx.quadraticCurveTo(quadPointX, quadPointY, endPointX, endPointY);
22. 
23.     // Head of the arrow
24.     ctx.moveTo(endPointX - (arrowWidth * Math.sin(arrowAngle - Math.PI / 6)),
25.                endPointY - (arrowWidth * Math.cos(arrowAngle - Math.PI / 6)));
26.  
27.     ctx.lineTo(endPointX, endPointY);
28.  
29.     ctx.lineTo(endPointX - (arrowWidth * Math.sin(arrowAngle + Math.PI / 6)),
30.                endPointY - (arrowWidth * Math.cos(arrowAngle + Math.PI / 6)));
31.  
32.     ctx.stroke();
33.     ctx.closePath();
34. 
35.     // BEST PRACTICE -> restore the context as we saved it at the beginning
36.     // of the function
37.     ctx.restore();
38. }

This function takes as parameters the start and end points, the control point of the curve, the arrow width, the width of the arrow head.

It computes the angle of the arrow at its endpoint (line 14) in order to compute the rotated endpoints of the two lines of the arrow head (lines 24 and 29).

Notice that once again, as we modify the context properties (color, lineWidth) in the body of the function, we save and restore the context at the beginning / end of the function.

3.4.12 Bézier curves

Introduction

Bézier curves are interesting. They are mostly used for drawing “S” shapes or asymmetric curves.

Bezier curve in S.

(image taken from SitePoint)

Bézier curves are defined by a context point, like quadratic curves, two control points that define two tangents, and an ending point.

The first part of the curve is tangential to the imaginary line defined by the context point and the first control point. The second part of the curve is tangential to the imaginary line defined by the second control point and the ending point.

Bezier curve control points.

(Picture taken from the HTML5 Canvas Tutorials Web site)

The best way to understand how they work is to check out one of these interactive applications:

Typical usage of Bézier curves

JavaScript source code:

1.  ctx.moveTo(contextX, contextY);
2.  context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
3.  // Optional : set lineWidth and stroke color
4.  context.lineWidth = 5;
5.  context.strokeStyle = "#0000ff";
6.  // Draw!
7.  ctx.stroke();

Examples

Example #1

Try this:

Bezier curve: Example 1.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3.   <head>
4.     <meta charset="utf-8">
5.     <title>Bézier curve - Example #1</title>
6.   </head>
7.   <body>
8.     <canvas id="myCanvas1" height = 400 width="800">Your browser does not support the canvas tag.</canvas>
9.     <script type="text/javascript">
10.     var canvas=document.querySelector('#myCanvas1');
11.     var context=canvas.getContext('2d');
12.     context.beginPath();
13.     context.moveTo(100, 20);
14.     // TRY uncommenting these lines !
15.     //context.lineTo(200, 80);
16.     //context.quadraticCurveTo(230, 200, 250, 20);
17.     context.bezierCurveTo(290, -40, 200, 200, 400, 100);
18.     //context.lineTo(500, 90);
19.     // TRY TO UNCOMMENT THIS LINE
20.     //context.closePath();
21.     context.lineWidth = 5;
22.     context.strokeStyle = "#0000ff";
23.     context.stroke();
24.   </script>
25. </body>
26. </html>

Code source:

1.  context.beginPath();
2.  
3.  context.moveTo(100, 20);
4.  context.bezierCurveTo(290, -40, 200, 200, 400, 100);
5.  
6.  context.lineWidth = 5;
7.  context.strokeStyle = "#0000ff";
8.  context.stroke();

Example #2: path with bezier curve, quadratic curve and line in the same, closed path

Try this:

Path with Bezier and Quadratic curve and line in same, closed path.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Path with Bézier curve, quadratic curve and line in the same, closed path - Example #2</title>
6. </head>
7. <body>
8.   <canvas id="myCanvas1 height = 400 width="800">Your browser does not support the canvas tag.</canvas>
9.   <script type="text/javascript">
10.     var canvas=document.querySelector('#myCanvas1');
11.     var context=canvas.getContext('2d');
12.     context.beginPath();
13.     context.moveTo(100, 20);
14.     context.lineTo(200, 160);
15.     context.quadraticCurveTo(230, 200, 250, 120);
16.     context.bezierCurveTo(290, -40, 300, 200, 400, 150);
17.     context.lineTo(500, 90);
18.     // TRY TO COMMENT THIS LINE
19.     context.closePath();
20.     context.lineWidth = 5;
21.     context.strokeStyle = "#0000ff";
22.     context.stroke();
23.   </script>
24. </body>

Extract from JavaScript source code:

Click to expand!
1.  context.beginPath();
2.  
3.  context.moveTo(100, 20);
4.  
5.  context.lineTo(200, 160);
6.  context.quadraticCurveTo(230, 200, 250, 120);
7.  context.bezierCurveTo(290, -40, 300, 200, 400, 150);
8.  context.lineTo(500, 90);
9.  
10. // TRY COMMENTING THIS LINE OUT
11. context.closePath();
12. context.lineWidth = 5;
13. context.strokeStyle = "#0000ff";
14. context.stroke();

In this example we use the closePath() method to draw a line between the last path point and the first path point (line 11), so that the drawing looks like a pair of goggles.

Note how the different parts are linked together and make a “path”:

Interactive code that draws Bezier curves.

Interesting, interactive tool for generating code that draws Bézier curves

This Bézier tool (“HTML5 <canvas> bezierCurveTo command generator”) is available online: try it!

3.5 Canvas Context: Colors

In previous examples, we saw how to set the current color using the strokeStyle and fillStyle properties of the canvas context object.

Let’s look at color in a little more detail, and see how we can use gradients or patterns/textures/images (in other words: fill shapes or fill the outline of the shapes with some images that repeat themselves).

Colors and transparency

You can use the same syntax for colors that is supported by CSS3. The next lines show possible values/syntaxes.

1. ctx.strokeStyle = 'red';
2. ctx.fillStyle = "#00ff00";
3. ctx.strokeStyle = "rgb(0, 0, 255)";
4. ctx.fillStyle = "rgba(0, 0, 255, 0.5)";

Note that:

Here is an example that shows how to draw different filled rectangles in blue, with different levels of transparency:

Colors and transparency example.

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Colors and transparency</title>
6.   <style>
7.     #myCanvas {
8.       border: 1px solid black;
9.     }
10.   </style>
11. <script>
12.   var canvas, ctx;
13.   function init() {
14.     // This function is called after the page is loaded
15.     // 1 - Get the canvas
16.     canvas = document.getElementById('myCanvas');
17.     // 2 - Get the context
18.     ctx=canvas.getContext('2d');
19.     // 3 - we can draw
20.     drawSomething();
21.   }
22.   function drawSomething() {
23.     // set the global context values
24.     ctx.fillStyle='rgba(0, 0, 255, 0.2)';
25.     // Draw the two filled red rectangles
26.     ctx.fillRect(150, 20, 200, 100);
27.     ctx.fillRect(100, 50, 200, 100);
28.     ctx.fillStyle = "blue";
29.     ctx.fillRect(50, 100, 200, 100);
30.     }
31. </script>
32. </head>
33. <body onload="init();">
34.   <canvas id="myCanvas" width="400" height="220">
35.     Your browser does not support the canvas tag.
36.   </canvas>
37. </body>
38. </html>

3.5.2 Canvas Context: Linear Gradients

It is possible to define the stroke or the fill style as a “gradient”, a set of interpolated colors, like in this example below:

Define stroke fill style as 'gradient'.

JavaScript source code:

JavaScript code extract!
1.  var canvas, ctx, grdFrenchFlag;
2.  function init() {
3.    // Good practice 1: set global vars canvas, ctx, gradients, etc here
4.    canvas = document.querySelector('#myCanvas1');
5.    ctx = canvas.getContext('2d');
6.    // The gradient we create is also a global variable, we
7.    // will be able to reuse it for drawing different shapes
8.    // in different functions
9.    grdFrenchFlag = ctx.createLinearGradient(0, 0, 300, 0);
10.   // Try adding colors with first parameter between 0 and 1
11.   grdFrenchFlag.addColorStop(0, "blue");
12.   grdFrenchFlag.addColorStop(0.5, "white");
13.   grdFrenchFlag.addColorStop(1, "red");
14.   draw();
15. }
16. 
17. function draw() {
18.   ctx.fillStyle = grdFrenchFlag;
19.   ctx.fillRect(0, 0, 300, 200);
20. }

HTML source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Linear gradients</title>
6.   <style>
7.     #myCanvas1 {
8.       border: 1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas1" width="300" height=200>Your browser does not support the canvas tag.</canvas>
14. </body>
15. </html>

A linear gradient is seen as an “invisible” rectangle in which a set of colors are interpolated along a line.

The gradient becomes visible when we draw shapes on top of the invisible gradient, and when the fillStyle or strokeStyle property has for value this gradient.

How to create gradients

There are 3 steps:

Step #1: define a linear gradient

Syntax:

ctx.createLinearGradient(x0,y0,x1,y1);

… where the (x0, y0) and (x1, y1) parameters define “the direction of the gradient” (as a vector with a starting and an ending point). This direction is an invisible line along which the colors that compose the gradient will be interpolated.

Let’s see an example:

grdFrenchFlag = ctx.createLinearGradient(0, 0, 300, 0);

This line defines the direction of the gradient: a virtual, invisible line that goes from the top left corner of the canvas (0, 0) to the top right corner of the canvas (300, 0). The interpolated colors will propagate along this line. 

If this gradient is going to be reused by different functions, it is good practice to create/initialize it in a function called when the page is loaded and to store it in a global variable.

Step #2: add a number of “color stops” to this gradient

We will add a set of “colors” and “stops” to this gradient. The stops go from 0 (beginning of the virtual line defined just above), to 1 (end of the virtual line). A color associated with a value of 0.5 will be right in the middle of the virtual line.

Here is an example that corresponds to an interpolated version of the French flag, going from blue to white, then to red, with proportional intervals. We define three colors, blue at position 0, white at position 0.5 and red at position 1:

grdFrenchFlag.addColorStop(0, "blue");
grdFrenchFlag.addColorStop(0.5, "white");
grdFrenchFlag.addColorStop(1, "red");

Step 3: draw some shapes

First, let’s set the fillStyle or strokeStyle of the context with this gradient, then let’s draw some shapes “on top of the gradient”.

In our example, the gradient corresponds to an invisible rectangle that fills the canvas. If we draw a rectangle of the canvas size, it should be filled with the entire gradient:

ctx.fillStyle = grdFrenchFlag;
ctx.fillRect(0, 0, 300, 200);

The result is shown in the above pen: a big rectangle that fills the whole canvas, with colors going from blue (left) to white (middle) to red (right).

Examples

Example #1: changing the direction of the gradient

If you modify the source code that defines the direction of the gradient as follows…

grdFrenchFlag = ctx.createLinearGradient(0, 0, 300, 200);

… then you will define a gradient that goes from the top left corner of the canvas to the bottom right of the canvas. Let’s see what it does:

Example 1 draw gradient from top left to bottom right.

Example #2: drawing shapes that do not cover the whole gradient

Instead of drawing a filled rectangle that covers the whole surface of the canvas, let’s draw several smaller rectangles:

Example 2 draw smaller rectangles - red & blue.

Note that the canvas has its default background color where we did not draw anything. And where we have drawn rectangles, we can see “through” and the colors from the gradient are visible.

Here is the code that draws the checkboard:

1.  ctx.fillStyle = grdFrenchFlag;
2.  ctx.fillRect(0, 0, 50, 50);
3.  ctx.fillRect(100, 0, 50, 50);
4.  ctx.fillRect(200, 0, 50, 50);
5.  ctx.fillRect(50, 50, 50, 50);
6.  ctx.fillRect(150, 50, 50, 50);
7.  ctx.fillRect(250, 50, 50, 50);
8.  ctx.fillRect(0, 100, 50, 50);
9.  ctx.fillRect(100, 100, 50, 50);
10. ctx.fillRect(200, 100, 50, 50);
11. ctx.fillRect(50, 150, 50, 50);
12. ctx.fillRect(150, 150, 50, 50);
13. ctx.fillRect(250, 150, 50, 50);

This code is rather ugly isn’t it? It would have been better to use a loop…

Here is function that draws a chessboard:

Click to expand!
1. // n = number of cells per row/column
2. function drawCheckboard(n) {
3.     ctx.fillStyle = grdFrenchFlag;
4. 
5.     var l = canvas.width;
6.     var h = canvas.height;
7.  
8.     var cellWidth = l / n;
9.     var cellHeight = h / n;
10. 
11.     for(i = 0; i < n; i++) {
12.        for(j = i % 2; j < n; j++) {
13.           ctx.fillRect(cellWidth*i, cellHeight*j, cellWidth, cellHeight);
14.        }
15.     }
16. }

The two loops (lines 11-15) draw only one cell out of two (see the j = i % 2 at line 12). i is the column number and if the column is odd or even, either we draw or we do not draw a rectangle.

This code is much more complex than the previous one, taking 16 lines instead of 13, but is much more powerful. Try to call the function with a value of 10, 20, or 2…

Draw red and blue checkerboard.

JavaScript

Click to expand!
1. var canvas, ctx, grdFrenchFlag;
2. function init() {
3.   // Good practice 1: set global vars canvas, ctx, gradients, etc here
4.   canvas = document.querySelector('#myCanvas1');
5.   ctx = canvas.getContext('2d');
6.   // The gradient we create is also a global variable, we
7.   // will be able to reuse it for drawing different shapes
8.   // in different functions
9.   grdFrenchFlag = ctx.createLinearGradient(0, 0, 300, 200);
10.   // Try adding colors with first parameter between 0 and 1
11.   grdFrenchFlag.addColorStop(0, "blue");
12.   grdFrenchFlag.addColorStop(0.5, "white");
13.   grdFrenchFlag.addColorStop(1, "red");
14.   drawCheckboard(10);
15. }
16. 
17. // n = number of cells per row/column
18. function drawCheckboard(n) {
19.   ctx.fillStyle = grdFrenchFlag;
20.   ctx.lineWidth=10;
21.   var l = canvas.width;
22.   var h = canvas.height;
23.   var cellWidth = l / n;
24.   var cellHeight = h / n;
25.   for(i = 0; i < n; i++) {
26.     for(j = i % 2; j < n; j+=2) {
27.       ctx.fillRect(cellWidth*i, cellHeight*j, cellWidth, cellHeight);
28.     }
29.   }
30. }

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3.   <head>
4.     <meta charset="utf-8">
5.     <title>Drawing shapes that do not cover the whole gradient - Example #2 bis</title>
6.     <style>
7.       #myCanvas1 {
8.         border: 1px solid black;
9.       }
10.     </style>
11.   </head>
12.   <body onload="init();">
13.     <canvas id="myCanvas1" width="300" height=200>Your browser does not support the canvas tag.
14.     </canvas>
15.   </body>
16. </html>

Example #3: drawing outlined shapes with gradients

Just as we used fillStyle and fillRect for drawing rectangles filled with a gradient, we can also use strokeStyle and strokeRect in order to draw wireframed rectangles. In the next example, which is just a variation of the previous one, we have used the lineWidth property to set the outline of the rectangles at 5 pixels:

Example 3 draw rectangles using strokeStyle and strokeRect.

JavaScript

Click to expand!
1. var canvas, ctx, grdFrenchFlag;
2. function init() {
3.   // Good practice 1: set global vars canvas, ctx, gradients, etc here
4.   canvas = document.querySelector('#myCanvas1');
5.   ctx = canvas.getContext('2d');
6.   // The gradient we create is also a global variable, we
7.   // will be able to reuse it for drawing different shapes
8.   // in different functions
9.   grdFrenchFlag = ctx.createLinearGradient(0, 0, 300, 200);
10.   // Try adding colors with first parameter between 0 and 1
11.   grdFrenchFlag.addColorStop(0, "blue");
12.   grdFrenchFlag.addColorStop(0.5, "white");
13.   grdFrenchFlag.addColorStop(1, "red");
14.   drawCheckboard(5);
15. }
16. 
17. // n = number of cells per row/column
18. function drawCheckboard(n) {
19.   ctx.strokeStyle = grdFrenchFlag;
20.   ctx.lineWidth=10;
21.   var l = canvas.width;
22.   var h = canvas.height;
23.   var cellWidth = l / n;
24.   var cellHeight = h / n;
25.   for(i = 0; i < n; i++) {
26.     for(j = i % 2; j < n; j+=2) {
27.       ctx.strokeRect(cellWidth*(i), cellHeight*j, cellWidth, cellHeight);
28.     }
29.   }
30. }

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Drawing outlined shapes with gradients - Example #3</title>
6.   <style>
7.     #myCanvas1 {
8.       border: 1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas1" width="300" height=200>Your browser does not support the canvas   tag.</canvas>
14. </body>
15. </html>

Extract from source code:

Click to expand!
1.  function drawCheckboard(n) {
2.    ctx.strokeStyle = grdFrenchFlag;
3.    ctx.lineWidth=10;
4.    ...
5.    for(i = 0; i < n; i++) {
6.      for(j = i % 2; j < n; j++) {
7.        ctx.strokeRect(cellWidth*i, cellHeight*j, cellWidth, cellHeight);
8.      }
9.    }
10. }

Example #4: what happens if we define a gradient smaller than the canvas?

Let’s go back to the very first example on this page - the one with the blue-white-red interpolated French flag. This time we will define a smaller gradient. Instead of going from (0, 0) to (300, 0), it will go from (100, 0) to (200, 0), while the canvas remains the same (width=300, height=200).

grdFrenchFlag = ctx.createLinearGradient(100, 0, 200, 0);

Like in the first example we will draw a filled rectangle that is the same size as the canvas:

Example 4 draw filled rectangles.

JavaScript

Click to expand!
1. var canvas, ctx, grdFrenchFlag;
2.  function init() {
3.    // Good practice 1: set global vars canvas, ctx, gradients, etc here
4.    canvas = document.querySelector('#myCanvas1');
5.    ctx = canvas.getContext('2d');
6.    // The gradient we create is also a global variable, we
7.    // will be able to reuse it for drawing different shapes
8.    // in different functions
9.    grdFrenchFlag = ctx.createLinearGradient(100, 0, 200, 0);
10.   // Try adding colors with first parameter between 0 and 1
11.   grdFrenchFlag.addColorStop(0, "blue");
12.   grdFrenchFlag.addColorStop(0.5, "white");
13.   grdFrenchFlag.addColorStop(1, "red");
14.   draw();
15. }
16. 
17. function draw() {
18.   ctx.fillStyle = grdFrenchFlag;
19.   ctx.fillRect(0, 0, 300, 200);
20. }

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Defining a gradient smaller than the canvas - Example #4</title>
6.   <style>
7.     #myCanvas1 {
8.       border: 1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas1" width="300" height=200>Your browser does not support the canvas tag.</canvas>
14. </body>
15. </html>

We notice that “before” the gradient starts, the first color of the gradient is repeated without any interpolation (columns 0-100 are all blue), then we “see through” and the gradient is drawn (columns 100-200), then the last color of the gradient is repeated without any interpolation (columns 200-300 are red).

Example #5: what happens if we define a gradient bigger than the canvas?

Nothing special; we will “see through the drawn shapes”, and the parts of the gradient that are located in the canvas area will be shown. You can try this example that defines a gradient twice the size of the canvas:

grdFrenchFlag = ctx.createLinearGradient(0, 0, 600, 400);

And if we draw the same rectangle with the canvas size, here is the result:

Example 5 draw same rectangle with the canvas size.

JavaScript

Click to expand!
1. var canvas, ctx, grdFrenchFlag;
2. function init() {
3.   // Good practice 1: set global vars canvas, ctx, gradients, etc here
4.   canvas = document.querySelector('#myCanvas1');
5.   ctx = canvas.getContext('2d');
6.   // The gradient we create is also a global variable, we
7.   // will be able to reuse it for drawing different shapes
8.   // in different functions
9.   grdFrenchFlag = ctx.createLinearGradient(0, 0, 600, 400);
10.   // Try adding colors with first parameter between 0 and 1
11.   grdFrenchFlag.addColorStop(0, "blue");
12.   grdFrenchFlag.addColorStop(0.5, "white");
13.   grdFrenchFlag.addColorStop(1, "red");
14.   draw();
15. 
16. }
17. function draw() {
18.   ctx.fillStyle = grdFrenchFlag;
19.   ctx.fillRect(0, 0, 300, 200);
20. }

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Defining a gradient bigger than the canvas - Example #5</title>
6.   <style>
7.     #myCanvas1 {
8.       border: 1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas1" width="300" height=200>Your browser does not support the canvas tag.</canvas>
14. </body>
15. </html>

The red color is beyond the bottom right corner…. we see only the top left quarter of the gradient.

Example #6: drawing shapes that share the same gradient as a whole

This time, we would like to draw the chessboard with the gradient in each cell. How can we do this with one single gradient?

We can’t! At least we can’t without recreating it for each cell!

It suffices to create a new gradient before drawing each filled rectangle, and set it with the starting and ending point of its direction/virtual line accordingly to the rectangle coordinates. Try this:

Example 6 draw shapes that share gradient.

JavaScript

Click to expand!
1. var canvas, ctx, grdFrenchFlag;
2. function init() {
3.   // Good practice 1: set global vars canvas, ctx, gradients, etc here
4.   canvas = document.querySelector(#myCanvas1);
5.   ctx = canvas.getContext('2d');
6.   drawCheckboard(5);
7. }
8. 
9. function setGradient(x, y, width, height) {
10.   grdFrenchFlag = ctx.createLinearGradient(x, y, width, height);
11.   grdFrenchFlag.addColorStop(0, "blue");
12.   grdFrenchFlag.addColorStop(0.5, "white");
13.   grdFrenchFlag.addColorStop(1, "red");
14.   ctx.fillStyle = grdFrenchFlag;
15. }
16. 
17. // n = number of cells per row/column
18. function drawCheckboard(n) {
19.   var l = canvas.width;
20.   var h = canvas.height;
21.   var cellWidth = l / n;
22.   var cellHeight = h / n;
23.   for(i = 0; i < n; i+=2) {
24.     for(j = 0; j < n; j++) {
25.       var x = cellWidth*(i+j%2);
26.       var y = cellHeight*j;
27.       setGradient(x, y, x+cellWidth, y+cellHeight);
28.       ctx.fillRect(x, y, cellWidth, cellHeight);

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Drawing shapes that share the same gradient as a whole - Example #6</title>
6.   <style>
7.     #myCanvas1 {
8.       border: 1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas1" width="300" height=200>Your browser does not support the canvas tag.</canvas>
14. </body>
15. </html>

Extract from source code:

Click to expand!
1. function setGradient(x, y, width, height) {
2.     grdFrenchFlag = ctx.createLinearGradient(x, y, width, height);
3.     grdFrenchFlag.addColorStop(0, "blue");
4.     grdFrenchFlag.addColorStop(0.5, "white");
5.     grdFrenchFlag.addColorStop(1, "red");
6.     // set the new gradient to the current fillStyle
7.     ctx.fillStyle = grdFrenchFlag;
8. }
9.  
10. // n = number of cells per row/column
11. function drawCheckboard(n) {
12.    var l = canvas.width;
13.    var h = canvas.height;
14.    var cellWidth = l / n;
15.    var cellHeight = h / n;
16.    for(i = 0; i < n; i+=2) {
17.      for(j = 0; j < n; j++) {
18.         var x = cellWidth*(i+j%2);
19.         var y = cellHeight*j;
20.         setGradient(x, y, x+cellWidth, y+cellHeight);
21.         ctx.fillRect(x, y, cellWidth, cellHeight);
22.      }
23.    }
24. }

We wrote a function setGradient(startX, startY, endX, endY) that creates a gradient and set the fillStyle context property so that any filled shape drawn will have this gradient.

In the drawCheckBoard(…) function we call it just before drawing rectangles. In this way, each rectangle is drawn using its own gradient.

3.5.3 Canvas Context: Radial Gradients

Radial gradients are for creating gradients that propagate/interpolate colors along circles instead of propagating/interpolating along a virtual line, like linear gradients.

Here is an example of a radial gradient that interpolates the color of the rainbow:

Example radial gradient which interpolates the color of the rainbow.

JavaScript

Click to expand!
1. var canvas, ctx, grd;
2. function init() {
3. // Good practice 1: set global vars canvas, ctx, gradients, etc here
4.    canvas = document.querySelector('#myCanvas1');
5.    ctx = canvas.getContext('2d');
6.    grd = ctx.createRadialGradient(150, 100, 30, 150, 100, 100);
7.    grd.addColorStop(0, "red");
8.    grd.addColorStop(0.17, "orange");
9.    grd.addColorStop(0.33, "yellow");
10.    grd.addColorStop(0.5, "green");
11.    grd.addColorStop(0.666, "blue");
12.    grd.addColorStop(1, "violet");
13.    draw();
14. }
15. function draw() {
16.    ctx.fillStyle = grd;
17.    ctx.fillRect(0, 0, 300, 200);
18. }

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3.    <head>
4.       <meta charset="utf-8">
5.       <title>Radial gradients</title>
6.       <style>
7.          #myCanvas1 {
8.          border: 1px solid black;
9.       }
10.       </style>
11.    </head>
12.    <body onload="init();">
13.       <canvas id="myCanvas1" width="300" height=200>Your browser does not support the canvas tag.</canvas>
14.    </body>
15. </html>

The gradient is defined as follows:

1. var grd = context.createRadialGradient(150, 100, 30, 150, 100, 100);
2. grd.addColorStop(0, "red");
3. grd.addColorStop(0.17, "orange");
4. grd.addColorStop(0.33, "yellow");
5. grd.addColorStop(0.5, "green");
6. grd.addColorStop(0.666, "blue");
7. grd.addColorStop(1, "violet");
8. 
9. context.fillStyle = grd;

The method from the context object createRadialGradient(cx1, cy1, radius1, cx2, cy2, radius2) takes as the first three parameters the “starting” circle of the gradient, and as the three last parameters, the “ending circle”.

In the above example, the gradients starts at a circle located at (150, 100), with a radius of 30, and propagates to a circle with the same center as the first (150, 100), but with a bigger radius of 100, as shown below: Radial gradient.

We added color stops using a method similar to that used for linear gradients.

What happens if the circles are not located at the same place?

You get some nice effects; here we set the second circle’s center 60 pixels to the right of the first circle’s center (cx = 210 instead of 150):

grd = ctx.createRadialGradient(150, 100, 30, 210, 100, 100);

Here is the result:

Effects from 2nd circle center to the right of the 1st circle center.

JavaScript

Click to expand!
1. var canvas, ctx, grd;
2. function init() {
3.   // Good practice 1: set global vars canvas, ctx, gradients, etc here
4.   canvas = document.querySelector('#myCanvas1');
5.   ctx = canvas.getContext('2d');
6.   grd = ctx.createRadialGradient(150, 100, 30, 210, 100, 100);
7.   grd.addColorStop(0, "red");
8.   grd.addColorStop(0.17, "orange");
9.   grd.addColorStop(0.33, "yellow");
10.   grd.addColorStop(0.5, "green");
11.   grd.addColorStop(0.666, "blue");
12.   grd.addColorStop(1, "violet");
13.   draw();
14. }
15. 
16. function draw() {
17.   ctx.fillStyle = grd;
18.   ctx.fillRect(0, 0, 300, 200);
19. }

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Radial gradients with offset circles</title>
6.   <style>
7.     #myCanvas1 {
8.       border: 1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas1" width="300" height=200>Your browser does not support the canvas tag.</canvas>
14. </body>
15. </html>

What happens if the gradient is smaller or larger than the shapes we draw?

A gradient is an invisible shape on the screen: the radial gradient is made of two circles: an inner and an outer circle. Between these two circles, colors are interpolated.

We call the “first color” the color defined for the inner circle, the “last color” the last color of the gradient, that corresponds to the outer circle:

3.5.4 Canvas Context: Patterns/Textures

Principle

The principle of “pattern” drawing is based on repeating an image (if the image is smaller than the surface of the shape you are going to draw) for filling the surface of objects to be drawn (either filled or stroked).

To illustrate this principle, in the next examples, we are going to draw rectangles using a pattern.

There are a few steps we have to take before doing this:

  1. Create a JavaScript image object
var imageObj = new Image();
  1. Define a callback function that will be called once the image has been fully loaded in memory; we cannot draw before the image has been loaded.
imageObj.onload = function(){
  ...
}
  1. Set the source of this image to the URL of the pattern:
imageObj.src = "https://www.myserver.com/myRepeatablePattern.png";
  1. Create a pattern object from the loaded image:

As soon as step 3 is executed, an HTTP request is sent in background by the browser, and when the image is loaded in memory, the callback defined at step 2 is called. We create a pattern object inside, from the loaded image:

// callback called asynchronously, after the src attribute of imageObj is set
imageObj.onload = function(){ 
    // We enter here when the image is loaded, we create a pattern object.
    // It is good practice to set this as a global variable, easier to share
    pattern1 = ctx.createPattern(imageObj, "repeat");
};
  1. Inside the callback function (or inside a function called from inside the callback), we can finally draw:
1. // callback called asynchronously, after the src attribute of imageObj is set
2. imageObj.onload = function(){
3.     pattern1 = ctx.createPattern(imageObj, "repeat");
4. 
5.     // Draw a textured rectangle
6.     ctx.fillStyle = pattern1;
7.     ctx.fillRect(10, 10, 500, 800);
8. };

Examples

Example #1: draw two rectangles with a pattern (one filled, one stroked)

Here we have two rectangles drawn using a pattern (an image that can be repeated along the X and Y axis). The first is a filled rectangle while the second is “stroked” with a lineWidth of 20 pixels:

Filled rectange and a stroked rectangle with lineWidth=20.

JavaScript

Click to expand!
1. var canvas, ctx, pattern1;
2. function init() {
3.   canvas = document.querySelector('#myCanvas');
4.   ctx = canvas.getContext('2d');
5.   // We need 1) to create an empty image object, 2) to set a callback function
6.   // that will be called when the image is fully loaded, 3) to create a
7.   // pattern object, 4) to set the fillStyle or the strokeStyle property of
8.   // the context with this pattern, 5) to draw something
9.   // WE CANNOT DRAW UNTIL THE IMAGE IS FULLY LOADED -> draw from inside the
10.   // onload callback only !
11.   // Allocate an image
12.   var imageObj = new Image();
13.   // callback called asynchronously, after the src attribute of imageObj is set
14.   imageObj.onload = function(){
15.     // We enter here only when the image has been loaded by the browser
16.     // Pattern creation using the image object
17.     // Instead of "repeat", try different values : repeat-x, repeat-y,
18.     // or no-repeat, You may draw larger shapes in order to see
19.     // different results
20.     // It is a good practice to leave this as a global variable if it
21.     // will be reused by other functions
22.     pattern1 = ctx.createPattern(imageObj, "repeat");
23.     // Draw a textured rectangle
24.     ctx.fillStyle = pattern1;
25.     ctx.fillRect(10, 10, 200, 200);
26.     // And a wireframe one
27.     ctx.lineWidth=20;
28.     ctx.strokeStyle=pattern1;
29.     ctx.strokeRect(230, 20, 150, 100);
30.   };
31.   // This will tell the browser to send an asynchronous request.
32.   // When the browser will get an answer, the callback above will be called
33.   imageObj.src = "https://mainline.i3s.unice.fr/mooc/pattern1.jpg";
34. }

HTML

1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Drawing two rectangles with a pattern - Example #1</title>
6. </head>
7. <body onload="init();">
8.   <canvas id="myCanvas" width="500" height="400">
9.     Your browser does not support the canvas tag. </canvas>
10. </body>
11. </html>

JavaScript source code:

Click to expand!
1.  var canvas, ctx, pattern1;
2.   
3.  function init() {
4.     canvas = document.querySelector('#myCanvas');
5.     ctx = canvas.getContext('2d');
6.  
7.     // We need 1) to create an empty image object, 2) to set a callback function
8.     // that will be called when the image is fully loaded, 3) to create a
9.     // pattern object, 4) to set the fillStyle or the strokeStyle property of
10.    // the context with this pattern, 5) to draw something
11.    // WE CANNOT DRAW UNTIL THE IMAGE IS FULLY LOADED -> draw from inside the
12.    // onload callback only !
13. 
14. 
15.    // 1 - Allocate an image
16.    var imageObj = new Image();
17.  
18.    // 2 - callback called asynchronously, after the src attribute of imageObj
19.    // is set
20.    imageObj.onload = function(){
21.       // We enter here only when the image has been loaded by the browser
22.       // 4 - Pattern creation using the image object
23.       // Instead of "repeat", try different values : repeat-x, repeat-y,
24.       // or no-repeat, You may draw larger shapes in order to see
25.       // different results
26.       // It is good practice to leave this as a global variable if it
27.       // will be reused by other functions
28. 
29.       pattern1 = ctx.createPattern(imageObj, "repeat");
30. 
31.       // 5 - Draw things. Here a textured rectangle
32.       ctx.fillStyle = pattern1;
33.       ctx.fillRect(10, 10, 200, 200);
34. 
35.       // ... And a wireframe one
36.       ctx.lineWidth=20;
37.       ctx.strokeStyle=pattern1;
38.       ctx.strokeRect(230, 20, 150, 100);
39.   };
40. 
41.   // 3 - Send the request to load the image
42.   // Setting the src attribute will tell the browser to send an asynchronous
43.   // request.
44.   // When the browser gets an answer, the callback above will be called
45.   imageObj.src = "https://mainline.i3s.unice.fr/mooc/pattern1.jpg";
46. }

Example 2: the repeatability of a pattern

To “better” see the repeatability of the pattern, here is the same example with a 1000x1000 pixel wide canvas:

1000 by 1000 pixel wide canvas.

JavaScript

Click to expand!
1. var canvas, ctx, pattern1;
2. function init() {
3.   canvas = document.querySelector('#myCanvas');
4.   ctx = canvas.getContext('2d');
5.   // We need 1) to create an empty image object, 2) to set a callback function
6.   // that will be called when the image is fully loaded, 3) to create a
7.   // pattern object, 4) to set the fillStyle or the strokeStyle property of
8.   // the context with this pattern, 5) to draw something
9.   // WE CANNOT DRAW UNTIL THE IMAGE IS FULLY LOADED -> draw from inside the
10.   // onload callback only !
11.   // Allocate an image
12.   var imageObj = new Image();
13.   // callback called asynchronously, after the src attribute of imageObj is set
14.   imageObj.onload = function(){
15.     // We enter here only when the image has been loaded by the browser
16.     // Pattern creation using the image object
17.     // Instead of "repeat", try different values : repeat-x, repeat-y,
18.     // or no-repeat, You may draw larger shapes in order to see
19.     // different results
20.     // It is a good practice to leave this as a global variable if it
21.     // will be reused by other functions
22.     pattern1 = ctx.createPattern(imageObj, "repeat");
23.     // Draw a textured rectangle
24.     ctx.fillStyle = pattern1;
25.     ctx.fillRect(10, 10, 500, 800);
26.     // And a wireframe one
27.     ctx.lineWidth=50;
28.     ctx.strokeStyle=pattern1;
29.     ctx.strokeRect(650, 20, 300, 800);
30.   };
31.   // This will tell the browser to send an asynchronous request.
32.   // When the browser will get an answer, the callback above will be called
33.   imageObj.src = "https://mainline.i3s.unice.fr/mooc/pattern1.jpg";
34. }

HTML

1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Repeatability of a pattern - Example #2</title>
6. </head>
7. <body onload="init();">
8.   <canvas id="myCanvas" width="1000" height="1000">
9.     Your browser does not support the canvas tag. </canvas>
10. </body>
11. </html>

You can change the way the pattern is repeated by modifying the second parameter of this method:

1.  pattern1 = ctx.createPattern(imageObj, "repeat");

Please try: repeat-x, repeat-y or no-repeat as acceptable values. Just change this line in the online example and you will see live results.

3.5.5 A Multiple Image Loader

Draw with multiple patterns? We need to load all of them before drawing!

Below are 4 rectangles drawn with 4 different patterns.

Four rectangles drawn with different patterns.

We said earlier that we cannot draw before the image used by a pattern is loaded. This can become rapidly complicated if we need to draw using multiple patterns. We need a way to load all images and then, only when all images have been loaded, start drawing.

JavaScript is an asynchronous language. When you set the src attribute of an image, then an asynchronous request is sent by the browser, and then after a while, the onload callback is called… The difficult part to understand for those who are not familiar with JavaScript is that these requests are done in parallel and we do not know when, and in what order, the images will be loaded.

The solution is to use a multiple image loader that counts the loaded images and calls a function you pass when done!

The trick is to have an array of URLs that will be used by our multiple image loader, and have the onload callback called once per image loaded, so we can count the number of images effectively loaded.

When all images have been loaded, we call a callback function that has been passed to our loader.

The complete example code that produces the result shown at the beginning of this page is the following:

A multiple image loader.

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>A multiple image loader</title>
6.   <style>
7.     #myCanvas {
8.       border: 1px solid black;
9.     }
10.   </style>
11.   <script>
12.   var canvas, ctx;
13.   // List of images to load, we used a JavaScript object instead of
14.   // an array, so that named indexes (aka properties)
15.   // can be used -> easier to manipulate
16.   var imagesToLoad = {
17.     flowers: https://i.ibb.co/4NN9Sgn/flowers.jpg,
18.     lion: https://i.ibb.co/3NyqKnY/lion.jpg,
19.     blackAndWhiteLys: https://i.ibb.co/VNLVpcL/final.jpg,
20.     tiledFloor:
21.     https://i.ibb.co/Dt6txmG/repeatable-Pattern.jpg{
22.   };
23. 
24. function loadImages(imagesToBeLoaded, drawCallback) {
25.   var imagesLoaded = {};
26.   var loadedImages = 0;
27.   var numberOfImagesToLoad = 0;
28.   // get num of sources
29.   for(var name in imagesToBeLoaded) {
30.     numberOfImagesToLoad++;
31.   }
32.   for(var name in imagesToBeLoaded) {
33.     imagesLoaded[name] = new Image();
34.     imagesLoaded[name].onload = function() {
35.       if(++loadedImages >= numberOfImagesToLoad) {
36.         drawCallback(imagesLoaded);
37.       } // if
38.     }; // function
39.     imagesLoaded[name].src = imagesToBeLoaded[name];
40.   } // for
41. } // function
42. 
43. function init() {
44.   // This function is called after the page is loaded
45.   // 1 - Get the canvas
46.   canvas = document.getElementById('myCanvas');
47.   // 2 - Get the context
48.   ctx=canvas.getContext('2d');
49.   loadImages(imagesToLoad, function(imagesLoaded) {
50.     patternFlowers = ctx.createPattern(imagesLoaded.flowers, repeat);
51.     patternLion = ctx.createPattern(imagesLoaded.lion, repeat);
52.     patternBW = ctx.createPattern(imagesLoaded.blackAndWhiteLys, repeat);
53.     patternFloor = ctx.createPattern(imagesLoaded.tiledFloor, repeat);
54.     drawRectanglesWithPatterns();
55.   });
56. }
57. 
58. function drawRectanglesWithPatterns() {
59.   ctx.fillStyle=patternFloor;
60.   ctx.fillRect(0,0,200,200);
61.   ctx.fillStyle=patternLion;
62.   ctx.fillRect(200,0,200,200);
63.   ctx.fillStyle=patternFlowers;
64.   ctx.fillRect(0,200,200,200);
65.   ctx.fillStyle=patternBW;
66.   ctx.fillRect(200,200,200,200);
67. }
68.   </script>
69. </head>
70. <body onload="init();">
71. <canvas id="myCanvas" width="400" height="400">
72.   Your browser does not support the canvas tag.
73. </canvas>
74. </body>

Define the list of images to be loaded:

1.  // List of images to load, we used a JavaScript object instead of
2.  // an array, so that named indexes (aka properties)
3.  // can be used -> easier to read
4.  var imagesToLoad = {
5.       flowers: https://i.ibb.co/4NN9Sgn/flowers.jpg,
6.       lion: https://i.ibb.co/3NyqKnY/lion.jpg,
7.       blackAndWhiteLys: https://i.ibb.co/VNLVpcL/final.jpg,
8.       tiledFloor:
9.        https://i.ibb.co/Dt6txmG/repeatable-Pattern.jpg
10. };

Notice that instead of using a traditional array, we defined this list as a JavaScript object, with properties whose names will be easier to manipulate (flowers, lion, tiledFloor, etc.).

The image loader function

Click to expand!
1.  function loadImages(imagesToBeLoaded, drawCallback) {
2.       var imagesLoaded = {};
3.       var loadedImages = 0;
4.       var numberOfImagesToLoad = 0;
5.  
6.       // get num of images to load
7.       for(var name in imagesToBeLoaded) {
8.           numberOfImagesToLoad++;
9.       }
10. 
11.      for(var name in imagesToBeLoaded) {
12.          imagesLoaded[name] = new Image();
13. 
14.          imagesLoaded[name].onload = function() {
15.              if(++loadedImages >= numberOfImagesToLoad) {
16.                  drawCallback(imagesLoaded);
17.              } // if
18.          }; // function
19.          imagesLoaded[name].src = imagesToBeLoaded[name];
20.       } // for
21. } // function

Explanations:

Example of use of this loader

1.  loadImages(imagesToLoad, function(imagesLoaded) {
2.      patternFlowers = ctx.createPattern(imagesLoaded.flowers, 'repeat');
3.      patternLion    = ctx.createPattern(imagesLoaded.lion, 'repeat');
4.      patternBW = ctx.createPattern(imagesLoaded.blackAndWhiteLys, 'repeat');
5.      patternFloor   = ctx.createPattern(imagesLoaded.tiledFloor, 'repeat');
6.  
7.      drawRectanglesWithPatterns();
8.  });

Explanations:

Here is the function:

Click to expand!
1.  function drawRectanglesWithPatterns() {
2.    ctx.fillStyle=patternFloor;
3.    ctx.fillRect(0,0,200,200);
4.  
5.    ctx.fillStyle=patternLion;
6.    ctx.fillRect(200,0,200,200);
7.  
8.    ctx.fillStyle=patternFlowers;
9.    ctx.fillRect(0,200,200,200);
10.  
11.   ctx.fillStyle=patternBW;
12.   ctx.fillRect(200,200,200,200);
13. }

3.5.6 Drawing Shadows

Context properties to draw with shadows

A green shadowed rectangle.

There are 4 properties of the canvas context that are useful for indicating that we want to draw shapes with shadows:

  1. shadowColor: color to use for shadows,
  2. shadowBlur: blur level for shadows,
  3. shadowOffsetX: horizontal distance of the shadow from the shape,
  4. shadowOffsetY: vertical distance of the shadow from the shape.

Examples

Example #1: simple shadows

Simple shadows, example #1.

JavaScript

Click to expand!
1. var canvas, ctx;
2. function init() {
3.   canvas = document.getElementById('myCanvas');
4.   ctx = canvas.getContext('2d');
5.   setShadow();
6.   // first green filled rectangle
7.   ctx.fillStyle = "#22FFDD"; // rectangle color
8.   ctx.fillRect(20, 20, 200, 100);
9.   // second stroked rectangle
10.   ctx.strokeStyle = "purple"; // rectangle color
11.   ctx.lineWidth=10;
12.   ctx.strokeRect(20, 150, 200, 100);
13. }
14. function setShadow() {
15.   ctx.shadowColor = "Grey"; // color
16.   ctx.shadowBlur = 20; // blur level
17.   ctx.shadowOffsetX = 15; // horizontal offset
18.   ctx.shadowOffsetY = 15; // vertical offset
19. }

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Simple shadows - Example #1</title>
</head>
<body onload = init();>
  <canvas id="myCanvas" width="400" height =800>
    Your browser does not support the canvas tag.</canvas>
</body>
</html>

JavaScript source code:

Click to expand!
1.  var canvas, ctx;
2.  
3.  function init() {
4.      canvas = document.getElementById('myCanvas');
5.      ctx = canvas.getContext('2d');
6.  
7.      // call to a function that will set the 4 context properties for shadows
8.      setShadow();
9.      // all drawings that will occur will cast shadows
10. 
11.     // first green filled rectangle
12.     ctx.fillStyle = "#22FFDD"; 
13.     ctx.fillRect(20, 20, 200, 100);
14. 
15.     // second stroked rectangle
16.     ctx.strokeStyle = "purple"; 
17.     ctx.lineWidth=10;
18.     ctx.strokeRect(20, 150, 200, 100);
19. }
20.  
21. // We define the 4 properties in a dedicated function, for clarity
22. function setShadow() {
23.     ctx.shadowColor = "Grey";    // color
24.     ctx.shadowBlur = 20;         // blur level
25.     ctx.shadowOffsetX = 15;      // horizontal offset
26.     ctx.shadowOffsetY = 15;      // vertical offset
27. }

Explanations:

Example #2: unwanted shadows!

Let’s take a previous example, the one that draws a filled circle with an outline. And, let’s add a shadow to it using the following code:

Click to expand!
1.  ...
2.  ctx.beginPath();
3.  
4.  // Add to the path a full circle (from 0 to 2PI)
5.  ctx.arc(centerX, centerY, radius, 0, 2*Math.PI, false);
6.  
7.  // With path drawing you can change the context
8.  // properties until a call to stroke() or fill() is performed
9.  ctx.fillStyle = "lightBlue";
10. 
11. // add shadows before drawing the filled circle
12. addShadows();
13. 
14. // Draws the filled circle in light blue
15. ctx.fill();
16. 
17. // Prepare for the outline
18. ctx.lineWidth = 5;
19. ctx.strokeStyle = "black";
20. 
21. // draws the path AGAIN (the circle), this
22. // time in wireframe
23. ctx.stroke();
24. 
25. // Notice we only once called context.arc()! And drew it twice
26. // with different styles
27. ...
28.  
29. function addShadows() {
30.     ctx.shadowColor = "Grey"; // color
31.     ctx.shadowBlur = 20;      // blur level
32.     ctx.shadowOffsetX = 15;   // horizontal offset
33.     ctx.shadowOffsetY = 15;   // vertical offset
34. }

And here is the result:

Not quite correct, unwanted shadow.

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Unwanted shadows - Example #2</title>
6.   <style>
7.     body {
8.       margin: 0px;
9.       padding: 0px;
10.     }
11.     #myCanvas {
12.       border: 1px solid #9C9898;
13.     }
14.   </style>
15. <script>
16.   var canvas, ctx;
17.   window.onload = function(){
18.     canvas = document.getElementById("myCanvas");
19.     ctx = canvas.getContext("2d");
20.     var centerX = canvas.width / 2;
21.     var centerY = canvas.height / 2;
22.     var radius = 70;
23.     ctx.beginPath();
24.     // Add to the path a full circle (from 0 to 2PI)
25.     ctx.arc(centerX, centerY, radius, 0, 2*Math.PI, false);
26.     // With path drawing you can change the context
27.     // properties until a call to stroke() or fill() is performed
28.     ctx.fillStyle = "lightBlue";
29.     // add shadows before drawing the filled circle
30.     addShadows();
31.     // Draws the filled circle in light blue
32.     ctx.fill();
33.     // Prepare for the outline
34.     ctx.lineWidth = 5;
35.     ctx.strokeStyle = "black";
36.     // draws AGAIN the path (the circle), this
37.     // time in wireframe
38.     ctx.stroke();
39.     // Notice we called context.arc() only once ! And drew it twice
40.     // with different styles
41.   };
42.   function addShadows() {
43.     ctx.shadowColor = "Grey"; // color
44.     ctx.shadowBlur = 20; // blur level
45.     ctx.shadowOffsetX = 15; // horizontal offset
46.     ctx.shadowOffsetY = 15; // vertical offset
47.   }
48. </script>
49. </head>
50. <body>
51.   <canvas id="myCanvas" width="578" height="200">
52.   </canvas>
53. </body>
54. </html>

Ah, indeed, the call to ctx.fill() casts a shadow, but the call to ctx.stroke(), that paints the whole path again, casts a shadow too, and this time the outline produces an unwanted shadow… How can we avoid this effect, while using the same technique for drawing the path?

The trick is to save the context before setting the shadow properties, then draw the filled circle, then restore the context (to its previous state: without shadows), then draw the outlined circle by calling ctx.stroke().

Correct version of the code:

Click to expand!
1.  ...
2.  // save the context before setting shadows and drawing the filled circle
3.   ctx.save();
4.  
5.  // With path drawing you can change the context
6.  // properties until a call to stroke() or fill() is performed
7.   ctx.fillStyle = "lightBlue";
8.  
9.  // add shadows before drawing the filled circle
10.  addShadows();
11. 
12. // Draws the filled circle in light blue
13.  ctx.fill();
14. 
15. // restore the context to its previous saved state
16.  ctx.restore();
17. ...

And here is the FINAL result:

Final result of setting shadow.

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Unwanted shadows disappear- Example #2 bis</title>
6.   <style>
7.     body {
8.     margin: 0px;
9.     padding: 0px;
10.     }
11.     #myCanvas {
12.     border: 1px solid #9C9898;
13.     }
14.   </style>
15.   <script>
16.     var canvas, ctx;
17.     window.onload = function(){
18.     canvas = document.getElementById("myCanvas");
19.     ctx = canvas.getContext("2d");
20.     var centerX = canvas.width / 2;
21.     var centerY = canvas.height / 2;
22.     var radius = 70;
23.     ctx.beginPath();
24.     // Add to the path a full circle (from 0 to 2PI)
25.     ctx.arc(centerX, centerY, radius, 0, 2*Math.PI, false);
26.     // save the context before setting shadows and drawing the filled circle
27.     ctx.save();
28.     // With path drawing you can change the context
29.     // properties until a call to stroke() or fill() is performed
30.     ctx.fillStyle = "lightBlue";
31.     // add shadows before drawing the filled circle
32.     addShadows();
33.     // Draws the filled circle in light blue
34.     ctx.fill();
35.     // restore the context to its previous saved state
36.     ctx.restore();
37.     // Prepare for the outline
38.     ctx.lineWidth = 5;
39.     ctx.strokeStyle = "black";
40.     // draws AGAIN the path (the circle), this
41.     // time in wireframe
42.     ctx.stroke();
43.     // Notice we called context.arc() only once ! And drew it twice
44.     // with different styles
45.     };
46.     function addShadows() {
47.     ctx.shadowColor = "Grey"; // color
48.     ctx.shadowBlur = 20; // blur level
49.     ctx.shadowOffsetX = 15; // horizontal offset
50.     ctx.shadowOffsetY = 15; // vertical offset
51.   }
52.   </script>
53. </head>
54. <body>
55.   <canvas id="myCanvas" width="578" height="200">
56.   </canvas>
57. </body>
58. </html>

3.5.7 Styling Lines

Several context properties can be used to set the thickness of the shape outlines, the way line end caps are drawn, etc.

They apply to all shapes that are drawn in path mode (lines, curves, arcs) and some also apply to rectangles.

Examples of line styles

Example #1: changing the line thickness

We have seen this before. This is done by changing the value (in pixels) of the lineWidth property of the context:

ctx.lineWidth = 10; // set the thickness of every shape drawn in stroke/wireframe mode to 10 pixels

Here is a complete example where we draw with a lineWidth of 20 pixels:

Changing the line thickness.

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.    <head>
4.      <meta charset="utf-8">
5.      <title>A simple example of lineWidth property use</title>
6.  
7.    </head>
8.    <body>
9.      <canvas id="myCanvas" width="500">Your browser does not support the canvas tag.</canvas>
10.     <script>
11.       var canvas = document.getElementById('myCanvas');
12.       var ctx = canvas.getContext('2d');
13.  
14. // first path
15.       ctx.moveTo(20, 20);
16.       ctx.lineTo(100, 100);
17.       ctx.lineTo(100, 0);
18.  
19.  
20. // second part of the path
21.       ctx.moveTo(120, 20);
22.       ctx.lineTo(200, 100);
23.       ctx.lineTo(200, 0);
24.  
25. // indicate stroke color + draw first part of the path
26.       ctx.strokeStyle = "#0000FF";
27. // Current line thickness is 20 pixels
28.       ctx.lineWidth = 20;
29.       ctx.stroke();
30.  
31. // Draws a rectangle
32.       ctx.strokeRect(230, 10, 100, 100);
33.       </script>
34.  
35.     </body>
36. </html>

Example #2: changing the end caps of a line

The lineCap property of the context indicates the way line end caps are rendered. Possible values are butt (default), round, square (from top to bottom in the next illustration). Note that a value of “round” or “square” makes the lines slightly longer than the default value “butt”.

Line cap values.

Try this:

Changing the end caps of a line.  Example #2.

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Changing the end caps of a line - Example #2</title>
6. </head>
7. <body>
8.   <canvas id="myCanvas" width="500">Your browser does not support the canvas tag.</canvas>
9.   <script>
10.     var canvas = document.getElementById('myCanvas');
11.     var ctx = canvas.getContext('2d');
12.     // first path
13.     ctx.moveTo(20, 20);
14.     ctx.lineTo(100, 100);
15.     ctx.lineTo(100, 30);
16.     // second part of the path
17.     ctx.moveTo(120, 20);
18.     ctx.lineTo(200, 100);
19.     ctx.lineTo(200, 30);
20.     // indicate stroke color + draw first part of the path
21.     ctx.strokeStyle = "#0000FF";
22.     // Current line thickness is 20 pixels
23.     ctx.lineWidth = 20;
24.     // Try different values : butt, square, round
25.     ctx.lineCap = "round";
26.     ctx.stroke();
27.     // Draws a rectangle
28.     ctx.strokeRect(230, 10, 100, 100);
29.   </script>
30. </body>
31. </html>

Note that in this example, the rectangle is not affected. It has no line ends visible - all its sides meet. However, the next property we’re going to look at will have an effect on rectangles!

Example #3: setting the type of corner when two lines meet

The lineJoin property of the context indicates the way corners are rendered, when two lines meet. Possible values are miter (the default) for creating sharp corners, round, or bevel for “cut corners”.

Try this:

Setting type of corner when two lines meet.
Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Setting the type of corner when two lines meet - Example #3</title>
6. </head>
7. <body>
8.   <canvas id="myCanvas" width="500">Your browser does not support the   canvas tag.</canvas>
9.   <script>
10.     var canvas = document.getElementById('myCanvas');
11.     var ctx = canvas.getContext('2d');
12.     // first path
13.     ctx.moveTo(20,20);
14.     ctx.lineTo(100, 100);
15.     ctx.lineTo(100,30);
16.     // second part of the path
17.     ctx.moveTo(120,20);
18.     ctx.lineTo(200, 100);
19.     ctx.lineTo(200,30);
20.     // indicate stroke color + draw first part of the path
21.     ctx.strokeStyle = "#0000FF";
22.     // Current line thickness is 20 pixels
23.     ctx.lineWidth = 20;
24.     // Try different values : miter(default), bevel, round
25.     ctx.lineJoin="round";
26.     ctx.stroke();
27.     // Draws a rectangle
28.     ctx.strokeRect(230, 10, 100, 100);
29.   </script>
30. </body>
31. </html>

Example #4: specific case of lineJoin=“miter”, the miterLimit property, a way to avoid looooooong corners!

The miterLimit property value corresponds to the maximum miter length: the distance between the inner corner and the outer corner where two lines meet. When the angle of a corner between two lines gets smaller, the miter length grows and can become too long.

In order to avoid this situation, we can set the miterLimit property of the context to a threshold value. If the miter length exceeds the miterLimit value, then the corner will be rendered as if the lineJoin property had been set to “bevel” and the corner will be “cut”.

lineJoin property, figure 1, 2 and 3 showing arrows.

You can try an interactive example here:

Example #4: miterLimit property shown with 3 different angles, we see 
    that the part that goes out of the angle becomes very long.

In the example, try different values for the miterLimit property. You’ll see that the way the corners are rendered changes at values around 2 and 3.

4.1 Animations - An Introduction – Module 4

4.2 Basic Animation Techniques

In order to perform an animation, we need to:

  1. Clear the content of the canvas: this can be done using the ctx.clearRect(0, 0, canvasWidth, canvasHeight) method;
  2. Draw some shapes: use any of the drawing methods we have seen so far;
  3. Move the shapes: modify the position and/or orientation, size and color of the shapes;
  4. Repeat (go to step 1).

These are the basic steps for animating objects in a canvas. The order of the steps can be changed (i.e. you can move the shapes before drawing them), but, the principle is the same: clear-draw-move-repeat.

Step 1 could be avoided if you redraw the whole canvas content during step 2.

Before HTML5

Even before HTML5 and the introduction of the canvas element, people created HTML games. They used CSS backgrounds inside <div> elements, and used to change the CSS top, left, width and height properties of the divs to animate graphic images on the screen.

During the late 1990s and early 2000s, JavaScript became increasingly popular. The community created a first ‘umbrella term’ describing a collection of technologies used together to create interactive and animated Web sites - DHTML (Dynamic HTML). For example, check the games developed at this time by Brent Silby (they all use DHTML).

A mario-like DHTML game.

For animation, the setInterval(function, ms) and setTimeout(function, ms) methods were the only solutions. Both methods take a function as the first parameter, and a number of milliseconds as the second parameter.

The only difference is that the code provided to setInterval will run every n milliseconds whereas the code in setTimeout will run only once after n milliseconds (meaning that we will have to repeat a call to setTimeout at step 4 above).

After HTML5

The methods described above are now completed by a new method that comes with multiple advantages: the requestAnimationFrame API.

We will compare the old methods with the new one, and implement the same  example with each of them to highlight the differences.

4.2.2 Basic Animation Techniques

Below is the example shown in the video, with source code:

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4. <meta charset="utf-8">
5. <title>Animation</title>
6. <style>
7. #myCanvas {
8. border: 1px solid black;
9. }
10. </style>
11. <script>
12. var canvas, ctx;
13. var rectangleX = 0;
14. var colors = ['red', 'blue', 'green'];
15. var currentColor = 0;
16. var speed = 3;
17. function init() {
18. // This function is called after the page is loaded
19. // 1 - Get the canvas
20. canvas = document.getElementById('myCanvas');
21. // 2 - Get the context
22. ctx=canvas.getContext('2d');
23. // 3 - we can draw
24. //setInterval(animate, 100);
25. //setTimeout(animate, 100);
26. requestAnimationFrame(animate);
27. setInterval(changeColor, 1000);
28. }
29. function animate() {
30. // - 1 clear canvas
31. ctx.clearRect(0, 0, canvas.width, canvas.height);
32. // 2 - draw a red rectangle
33. ctx.fillRect(rectangleX,0,80,100);
34. // 3 - move the shapes
35. rectangleX = rectangleX +speed;
36. if((rectangleX+80 > 200) || (rectangleX <= 0)){
37. speed = -speed;
38. }
39. // Call again the anilate function after 100ms
40. //setTimeout(animate, 100);
41. requestAnimationFrame(animate);
42. }
43. function changeColor() {
44. ctx.fillStyle= colors[currentColor%3];
45. currentColor += 1;
46. // Comment next line if you do not want to change the speed at
47. // each bounce
48. speed += Math.sign(speed)*1;
49. console.log("speed = " + speed)
50. }
51. </script>
52. </head>
53. <body onload="init();">
54. <canvas id="myCanvas" width="200" height="200">
55. Your browser does not support the canvas tag.
56. </canvas>
57. </body>
58. </html>

Errata: in the video, we use speed +=1; in order to increment the speed of the rectangle each time it bounces (in the changeColor() function). This is not correct as speed can be negative. The online example fixes this by using speed += Math.sign(speed) * 1; instead this will add +1 or -1 depending on the sign of speed.

4.2.3 Animating Using setInterval()

The setInterval(…) function is still popular on the Web, and even though this is not the recommended way to do 60 frames/second canvas animation, it is worth understanding how it works.

Syntax: setInterval(function, ms);

The setInterval(…) function calls another function or evaluates an expression at specified intervals of time (in milliseconds), and returns the unique id of the action. You can always stop it by calling the clearInterval(id) function with the interval identifier as an argument.

Basic example that shows how to animate a DIV using the DOM API

This is how pre-HTML5 games were written. Before the introduction of the canvas element, developers made games using div elements. By changing their background color, top and left CSS positions, it was possible to animate characters in games. The animation was created by calling repeatedly a function that did the drawing, using the JavaScript setInterval or setTimeout functions.

Please try this example that moves/animates a div using setInterval:

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>setInterval for animation</title>
6.   <style>
7.     #animatedDIV {
8.       position:absolute;
9.       background-color:red;
10.       color:white;
11.       height:100px;
12.     }
13.   </style>
14. </head>
15. <body>
16.   <button onclick="start()">Start animation</button>
17.   <button onclick="stop()">Stop animation</button>
18.   <div>
19.   <div id="animatedDIV">Animated DIV :-)</div>
20.   </div>
21.   <script>
22.     var elm = document.getElementById("animatedDIV");
23.     var requestId;
24.     var x = 0;
25. 
26.     function render(time) {
27.       elm.style.left = x++ + "px";
28.     }
29. 
30.     function start() {
31.       requestId = setInterval(render, 10);
32.     }
33. 
34.     function stop() {
35.       if (requestId) {
36.         clearInterval(requestId);
37.       }
38.     }
39.   </script>
40. </body>
41. </html>

Extract from the source code:

Click to expand!
1.  <body>
2.    <div id="animatedDIV">Animated DIV :-)</div>
3.  
4.    <button onclick="start()">Start animation</button>
5.    <button onclick="stop()">Stop animation</button>
6.  
7.    <script>
8.      var elm = document.getElementById("animatedDIV");
9.      var requestId;
10.     var x = 0;
11. 
12.     function render(time) {
13.       elm.style.left = x++ + "px";
14.     }
15. 
16.     function start() {
17.       r/equestId = setInterval(render, 10);
18.     }
19. 
20.     function stop() {
21.       if (requestId) {
22.         clearInterval(requestId);
23.       }
24.     }
25.   </script>
26. </body>

Here, we define a <div> element, (see the online source code for the CSS properties involved), and we use the setInterval method (line 17) to call every 10ms the render() method that will just increment the position of this element. Notice that since we’re using the DOM, the horizontal position of the div is modified by changing its left CSS property.

The call to setInterval returns an id we can use to stop the animation, by calling clearInterval (line 22).

Animate a monster in a canvas, using setInterval

We use the drawMonster() function:

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>setInterval and monster animation</title>
6.   <style>
7.     #myCanvas {
8.       border: 1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas" width="400" height="400">
14.     Your browser does not support the canvas tag.
15.   </canvas>
16.   <p>
17.   <button onclick="start()">Start animation</button>
18.   <button onclick="stop()">Stop animation</button>
19.   <script>
20.     var canvas, ctx;
21.     var monsterX=100, monsterY=100, monsterAngle=0;
22. 
23.     function init() {
24.       // This function is called after the page is loaded
25.       // 1 - Get the canvas
26.       canvas = document.getElementById('myCanvas');
27.       // 2 - Get the context
28.       ctx=canvas.getContext('2d');
29.     }
30.   
31.     function animationLoop() {
32.       // 1 - Clear the canvas
33.       ctx.clearRect(0, 0, canvas.width, canvas.height);
34.       // 2 Draw the monster using variables for pos, angle, etc.
35.       drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
36.       // 3 Move the monster (change pos, angle, size, etc.)
37.       monsterX += 10;
38.       monsterX %= canvas.width
39.       monsterAngle+= 0.01;
40.     }
41. 
42.     function drawMonster(x, y, angle, headColor, eyeColor) {
43.       // GOOD PRACTICE : SAVE CONTEXT AND RESTORE IT AT THE END
44.       ctx.save();
45.       // Moves the coordinate system so that the monster is drawn
46.       // at position (x, y)
47.       ctx.translate(x, y);
48.       ctx.rotate(angle)
49.       // head
50.       ctx.fillStyle=headColor;
51.       ctx.fillRect(0,0,200,200);
52.       // eyes
53.       ctx.fillStyle='red';
54.       ctx.fillRect(35,30,20,20);
55.       ctx.fillRect(140,30,20,20);
56.       // interior of eye
57.       ctx.fillStyle=eyeColor;
58.       ctx.fillRect(43,37,10,10);
59.       ctx.fillRect(143,37,10,10);
60.       // Nose
61.       ctx.fillStyle='black';
62.       ctx.fillRect(90,70,20,80);
63.       // Mouth
64.       ctx.fillStyle='purple';
65.       ctx.fillRect(60,165,80,20);
66.       // GOOD PRACTICE !
67.       ctx.restore();
68.     }
69.   
70.     function start() {
71.       // Start the animation loop, change 20 for bigger values
72.       requestId = setInterval(animationLoop, 20);
73.     }
74.   
75.     function stop() {
76.       if (requestId) {
77.         clearInterval(requestId);
78.       }
79.     }
80.   </script>
81. </body>
82. </html>

Source code:

Click to expand!
1.  <body onload="init();">
2.      <canvas id="myCanvas" width="400" height="400">
3.          Your browser does not support the canvas tag.
4.      </canvas>
5.      <p>
6.      <button onclick="start()">Start animation</button>
7.      <button onclick="stop()">Stop animation</button>
8.  
9.      <script>
10.         var canvas, ctx;
11.         var monsterX=100, monsterY=100, monsterAngle=0;
12. 
13.         function init() {
14.             // This function is called after the page is loaded
15.             // 1 - Get the canvas
16.             canvas = document.getElementById('myCanvas');
17.             // 2 - Get the context
18.             ctx=canvas.getContext('2d');
19.         }
20. 
21.         function animationLoop() {
22.             // 1 - Clear the canvas
23.             ctx.clearRect(0, 0, canvas.width, canvas.height);
24. 
25.             // 2 Draw the monster using variables for pos, angle, etc.
26.            drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
27. 
28.            // 3 Move the monster (change pos, angle, size, etc.)
29.            monsterX += 10;
30.            monsterX %= canvas.width
31.            monsterAngle+= 0.01;
32.         }
33. 
34.         function drawMonster(x, y, angle, headColor, eyeColor) {
35.         // BEST PRACTICE: SAVE CONTEXT AND RESTORE IT AT THE END
36.         ctx.save();
37. 
38.         // Moves the coordinate system so that the monster is drawn
39.         // at position (x, y)
40.         ctx.translate(x, y);
41.         ctx.rotate(angle)
42. 
43.         // head
44.         ctx.fillStyle=headColor;
45.         ctx.fillRect(0,0,200,200);
46.         ...
47. 
48.         // BEST PRACTICE!
49.         ctx.restore();
50.      }
51. 
52.      function start() {
53.          // Start the animation loop, change 20 for bigger values
54.          requestId = setInterval(animationLoop, 20);
55.      }
56. 
57.      function stop() {
58.          if (requestId) {
59.              clearInterval(requestId);
60.          }
61.      }
62. </script>
63. </body>

Explanations:

Problems with setInterval

While the above example works, there are several reasons not to use setInterval for doing smooth animations.

Running several animations simultaneously

The setInterval function may become hard to debug, particularly if you run several animations simultaneously. For example, if you have two intervals, one running every 100 milliseconds, the other every second, and if you want to debug the second one, the first one will constantly be run at regular intervals, making step by step debugging really difficult.

A single animation may be interrupted by itself to become two simultaneous animations

setInterval will execute the function passed as first parameter every n milliseconds regardless of when the function was last called or how long the function takes to execute. If the function takes longer than the interval, then setInterval might queue too many function executions back to back when the interval is too short, leading to unpredictable results. 

BEST PRACTICE: AVOID using setInterval for animating in a canvas, except for trivial cases (change a color every second).

4.2.4 Animating Using setTimeout()

One thing you should always remember about using setInterval: if we set number of milliseconds at - let’s say 20ms - it will call our game loop function EACH 20ms, even if the previous one is not yet finished. This may lead to many problems (incomplete rendering, etc.).

That’s where we can use another function: 

This function works like setInterval(…) with one difference: it calls your function ONCE and AFTER a given amount of time.

Example of the monster animated in a canvas with setTimeout

Check the example below (click on start animation):

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4. <meta charset="utf-8">
5. <title>Monster animated in a canvas with setTimeout</title>
6. <style type="text/css">
7.   canvas {
8.     border:1px solid black;
9.   }
10. </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas" width="400" height="400">
14.     Your browser does not support the canvas tag.
15.   </canvas>
16.   <p>
17.   <button onclick="start()">Start animation</button>
18.   <button onclick="stop()">Stop animation</button>
19.   <script>
20.     var canvas, ctx;
21.     var monsterX=100, monsterY=100, monsterAngle=0;
22. 
23.     function init() {
24.       // This function is called after the page is loaded
25.       // 1 - Get the canvas
26.       canvas = document.getElementById('myCanvas');
27.       // 2 - Get the context
28.       ctx=canvas.getContext('2d');
29.     }
30. 
31.     function animationLoop() {
32.       // 1 - Clear
33.       ctx.clearRect(0, 0, canvas.width, canvas.height);
34.       // 2 Draw
35.       drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
36.       // 3 Move
37.       monsterX += 10;
38.       monsterX %= canvas.width
39.       monsterAngle+= 0.01;
40.       // call again mainloop after 20ms
41.       requestId = setTimeout(animationLoop, 20);
42.     }
43. 
44.     function drawMonster(x, y, angle, headColor, eyeColor) {
45.       // GOOD PRACTICE : SAVE CONTEXT AND RESTORE IT AT THE END
46.       ctx.save();
47.       // Moves the coordinate system so that the monster is drawn
48.       // at position (x, y)
49.       ctx.translate(x, y);
50.       ctx.rotate(angle)
51.       // head
52.       ctx.fillStyle=headColor;
53.       ctx.fillRect(0,0,200,200);
54.       // eyes
55.       ctx.fillStyle='red';
56.       ctx.fillRect(35,30,20,20);
57.       ctx.fillRect(140,30,20,20);
58.       // interior of eye
59.       ctx.fillStyle=eyeColor;
60.       ctx.fillRect(43,37,10,10);
61.       ctx.fillRect(143,37,10,10);
62.       // Nose
63.       ctx.fillStyle='black';
64.       ctx.fillRect(90,70,20,80);
65.       // Mouth
66.       ctx.fillStyle='purple';
67.       ctx.fillRect(60,165,80,20);
68.       // GOOD PRACTICE !
69.       ctx.restore();
70.     }
71.     
72.   function start() {
73.       // Start the animation loop, change 20 for bigger
74.       // values
75.       requestId = setTimeout(animationLoop, 20);
76.     }
77.   
78.     function stop() {
79.       if (requestId) {
80.         clearTimeout(requestId);
81.       }
82.     }
83.   </script>
84. </body>
85. </html>

This is similar to the previous example except that we called setTimeout(function, delay) instead of setInterval(function, period). As setTimeout runs the function passed as the first parameter only once, we also have to call it at the end of the loop.

Extract from source code:

Click to expand!
1.  function animationLoop() {
2.    // 1 - Clear
3.    ctx.clearRect(0, 0, canvas.width, canvas.height);
4.  
5.    // 2 Draw
6.    drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
7.  
8.    // 3 Move
9.    monsterX += 10;
10.   monsterX %= canvas.width
11.   monsterAngle+= 0.01;
12. 
13.   // call mainloop again after 20ms
14.   requestId = setTimeout(animationLoop, 20);
15. }
16.  
17. function start() {
18.   // Start the animation loop, change 20 for bigger
19.   // values
20.   requestId = setTimeout(animationLoop, 20);
21. }
22. 
23. function stop() {
24.   if (requestId) {
25.     clearTimeout(requestId);
26.   }
27. }

This function is certainly more suitable for doing graphic animation, such as for writing an HTML5 game. It will never interrupt an ongoing animation, even if the instructions inside the animation loop take too long.

Problems with setInterval() and setTimeout()

setTimeout does not “wait”  during the timeout period. It lets the rest of the JavaScript code run. It schedules a new call to the function passed as first parameter with a timer running in the background. This might cause it to take slightly longer than the expected timeout period to start executing.

This problem also occurs with setInterval, the timing is not “very” reliable. If you plan to run a function every 20ms, and if you measure precisely the real timing, sometimes you will discover big differences between what is scheduled and what is performed. This is because these methods were designed a long time ago, when high precision timers and 60 frames per second animation were not an option.

Here comes the requestAnimationFrame API, a very good companion to the canvas API!

BEST PRACTICE: AVOID using setTimeout for animating in a canvas, except for trivial cases.

For 60 frames/second animation, use requestAnimationFrame!

4.2.5 The requestAnimationFrame API

The best way to make animation at 60 frames per second: requestAnimationFrame!

The requestAnimationFrame(animationLoop) is very similar to setTimeout:

It has, however, several advantages over setInterval and setTimeout:

Typical use

You will note that  requestAnimationFrame(function) is used like setTimeout(function, delay). A call to requestAnimationFrame just asks the browser to call the function passed as a parameter ONCE, and the target delay is fixed, and corresponds to a 60 frames/s frame rate (16.6ms). Notice that an id is used for stopping an animation with cancelAnimationFrame(id).

Source code:

Click to expand!
1.  <body onload="init();">
2.  <script>
3.    var canvas, ctx;
4.  
5.    function init() {
6.      // This function is called after the page is loaded
7.      // 1 - Get the canvas
8.      canvas = document.getElementById('myCanvas');
9.      // 2 - Get the context
10.     ctx=canvas.getContext('2d');
11. 
12.     // 3 - start the animation
13.     startAnimation();
14.   }
15.  
16.   var id;
17.   function animationLoop(timeStamp) {
18.     // 1 - Clear
19.     ctx.clearRect(0, 0, canvas.width, canvas.height);
20.     // 2 Draw
21.     drawShapes(...);
22.     // 3 Move
23.     moveShapes(...);
24.     // call mainloop  again after 16.6ms (corresponds to 60 frames/second)
25.     id = requestAnimationFrame(animationLoop);
26.   }
27.  
28.   function startAnimation() {
29.     id = requestAnimationFrame(animationLoop);
30.   }
31.   function stopAnimation() {
32.     if (id) {
33.       cancelAnimationFrame(id);
34.     }
35.   }
36. </script>
37. </body>

Example: animate the monster with requestAnimationFrame

Check the example below:

Source code extract - please compare with the previous example that used setInterval():

Click to expand!
1.  function animationLoop(timeStamp) {
2.    // 1 - Clear
3.    ctx.clearRect(0, 0, canvas.width, canvas.height);
4.   
5.    // 2 - Draw
6.    drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
7.   
8.    // 3 - Move
9.    monsterX += 10;
10.   monsterX %= canvas.width
11.   monsterAngle+= 0.01;
12.  
13.   // call mainloop again after 16.6 ms (60 frames/s)
14.   requestId = requestAnimationFrame(animationLoop);
15. }
16. 
17. function start() {
18.   // Start the animation loop, targets 60 frames/s, this
19.   // calls animationLoop only ONCE!
20.   requestId = requestAnimationFrame(animationLoop);
21. }
22. 
23. function stop() {
24.   if (requestId) {
25.     cancelAnimationFrame(requestId);
26.   }
27. }

Notice that calling requestAnimationFrame(animationLoop) at line 19, and after that from within the loop at line 14, asks the browser to call the  animationLoop function so that the delta between calls will be as close as possible to 16.6ms  (this corresponds to 1/60th of a second).

Is the 16.6ms delay really accurate? Can we trust it?

This target may be hard to reach if:

Many HTML5 games perform what we call a “time-based animation”. For this, we need an accurate timer that will tell us the elapsed time between each animation frame.

Depending on this time, we can compute the distances that must be achieved by each object on the screen in order to move at a constant speed (for a human eye), independently of the CPU or GPU of the computer or mobile device that is running the game.

The timeStamp parameter of the animationLoop function (line 1 in the above code) is useful for exactly that: it gives a high resolution time. By measuring deltas between two consecutive calls of the animationLoop, we will know exactly, with a sub-millisecond accuracy, the elapsed time between two frames.

Using time-based animation, and more generally, using the canvas element for writing HTML5 games, is part of the W3Cx HTML5 Apps and Games course.

Current support is really good and all modern browsers support this API.

4.3 Canvas and User Interaction

In JavaScript, we treat events made by users as an input, and we manipulate the DOM structure as an output. Most of the time in games/animations, we will change state variables of moving objects, such as position or speed of an alien ship, and the animation loop will take care of these variables to move the objects.

The events are called DOM events, and we use the DOM JavaScript API to create event handlers.

There are three ways to manage events in the DOM structure

First method: declare event handlers in the HTML code

You will often find this in examples on the Web:

1.  <div id="someDiv" onclick="alert('clicked!');">
2.      content of the div
3.  </div>

Note: this is not the recommended way to handle events, even if it’s very easy to use. Mixing the ‘visual layer’ (HTML) and the ‘logic layer’ (JavaScript) in one place is ok for small examples (we have used this in some examples in this course) but is not the recommended way for full scale applications where a clean separation is best.

Second method: add an event handler to an HTML element in JavaScript

Here is an example:

1.  document.getElementById('someDiv').onclick = function(evt) {
2.    alert('clicked!');
3.  }

This method is fine, but  you will not be able to attach several listener functions. If you need to do this, the preferred version is the next one.

Third method: register a callback to the event listener with the addEventListener method

This is how we do it:

1.  document.getElementById('someDiv').addEventListener('click', function(evt) {
2.      alert('clicked!');
3.  }, false);

The third parameter is not important for now, just set it to false, or simply do not add a third parameter.

The DOM event that is passed to the event listener function

When you create an EventListener and attach it to an element,  an event object will be passed as a parameter to your callback, just like this:

1.  element.addEventListener('click', function(<b>event</b>) {
2.     <b>// now you can use the event object inside the callback</b>
3.  }, false);

Depending on the type of event you are listening to, we will use different properties from the event object in order to get useful information like: “what keys have been pressed down?”, “what is the position of the mouse cursor?”, “which mouse button is down?”, etc.

Let’s see next how to deal with the keyboard and the mouse. In the W3Cx HTML5 Apps and Games, we look at additional APIs such as the gamePad API for using USB or wireless gamepads/joysticks/game controllers.

Source code for the knowledge check 4.3.1

Online example on JS Bin

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Click on button</title>
6.  </head>
7.  <body>
8.    <button id="myButton">Click me!</button>
9.    <script>
10.     var button = document.getElementById('myButton');
11.     // Define a click listener on the button
12.     button.addEventListener('click', processClick;
13. 
14.     // callback
15.     function processClick(event) {
16.       console.log("Button clicked");
17. 
18.       // What is the event parameter?
19.     }
20.   </script>
21. </body>
22. </html>

4.3.2 Keyboard Interaction, Key Events

When you listen to keyboard related events (keydown, keyup or keypressed), the event parameter passed to the listener function will contain the code of the key that fired the event. Then it is possible to test what key has been pressed or released, like this:

1.  window.addEventListener('keydown', function(event) {
2.     if (event.keyCode === 37) {
3.       //left arrow was pressed
4.     }
5.  }, false);

At line 2, the value “37” is the key code that corresponds to the left arrow. It might be difficult to know the correspondences between real keyboard keys and codes, so here are handy pointers:

Examples

Example #1: adding a key listener to the window object

A lot of people think that the canvas element is not able to get key events. Many examples on the Web handle key events on canvas by adding a listener to the window object directly, like this:

Add key listener to window object.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Adding a key listener to the window object - Example #1</title>
<style>
  #myCanvas {
    border: 1px solid black;
  }
</style>
<body onload="init();">
<p>

This example shows how to handle key events in a canvas by adding a key listener to the window object. Like that, all key press in the document will trigger the event listener function.

Click to expand!
1. <canvas id="myCanvas" width="350" height="200">
2. </canvas>
3. <script>
4. var canvas;
5. function init() {
6. // This will work when you press a key, anywhere on the document
7. window.addEventListener('keydown', handleKeydown, false);
8. }
9. function handleKeydown(e){
10. alert('keycode: '+e.keyCode);
11. return false;
12. };
13. </script>
14. </body>
15. </html>

Code source extract:

Click to expand!
1.  <canvas id="myCanvas" width="350" height="200">
2.  </canvas>
3.   
4.  <script>
5.  
6.    function init() {
7.      // This will work when you press a key, anywhere on the document
8.      window.addEventListener('keydown', handleKeydown, false);
9.    }
10.  
11.  
12.   function handleKeydown(e){
13.     alert('keycode: '+e.keyCode);
14.     return false;
15.   };
16. </script>

Indeed this solution works well if you write a game, and want to detect events wherever the mouse cursor is, and without worrying about what HTML element has the focus, etc…

Example #2: moving the monster with the keyboard

Moving the monster with the keyboard.

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Moving the monster with the keyboard - Example #2</title>
6.   <style type="text/css">
7.     canvas {
8.       border:1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas" width="400" height="400">
14.     Your browser does not support the canvas tag.
15.   </canvas>
16.   <p>
17.   <script>
18.     var canvas, ctx;
19.     var monsterX=100, monsterY=100, monsterAngle=0;
20.     var incrementX = 0;
21.     function init() {
22.       // This function is called after the page is loaded
23.       // 1 - Get the canvas
24.       canvas = document.getElementById('myCanvas');
25.       // 2 - Get the context
26.       ctx=canvas.getContext('2d');
27.       // 3 add key listeners to the window element
28.       window.addEventListener('keydown', handleKeydown, false);
29.       window.addEventListener('keyup', handleKeyup, false);
30.       // 4 - start the animation
31.       requestId = requestAnimationFrame(animationLoop);
32.     }
33. 
34.     function handleKeydown(evt) {
35.       if (evt.keyCode === 37) {
36.         //left key
37.         incrementX = -1;
38.       } else if (evt.keyCode === 39) {
39.       // right key
40.         incrementX = 1;
41.       }
42.     }
43. 
44.     function handleKeyup(evt) {
45.       incrementX = 0;
46.     }
47. 
48.     function animationLoop() {
49.       // 1 - Clear
50.       ctx.clearRect(0, 0, canvas.width, canvas.height);
51.       // 2 Draw
52.       drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
53.       // 3 Move
54.       monsterX += incrementX;
55.       // call again mainloop after 16.6 ms (60 frames/s)
56.       requestId = requestAnimationFrame(animationLoop);
57.     }
58.     function drawMonster(x, y, angle, headColor, eyeColor) {
59.       // GOOD PRACTICE : SAVE CONTEXT AND RESTORE IT AT THE END
60.       ctx.save();
61.       // Moves the coordinate system so that the monster is drawn
62.       // at position (x, y)
63.       ctx.translate(x, y);
64.       ctx.rotate(angle)
65.       // head
66.       ctx.fillStyle=headColor;
67.       ctx.fillRect(0,0,200,200);
68.       // eyes
69.       ctx.fillStyle='red';
70.       ctx.fillRect(35,30,20,20);
71.       ctx.fillRect(140,30,20,20);
72.       // interior of eye
73.       ctx.fillStyle=eyeColor;
74.       ctx.fillRect(43,37,10,10);
75.       ctx.fillRect(143,37,10,10);
76.       // Nose
77.       ctx.fillStyle='black';
78.       ctx.fillRect(90,70,20,80);
79.       // Mouth
80.       ctx.fillStyle='purple';
81.       ctx.fillRect(60,165,80,20);
82.       // GOOD PRACTICE !
83.       ctx.restore();
84.     }
85.     function start() {
86.       // Start the animation loop, targets 60 frames/s
87.       requestId = requestAnimationFrame(animationLoop);
88.     }
89.     function stop() {
90.       if (requestId) {
91.         cancelAnimationFrame(requestId);
92.       }
93.     }
94.   </script>
95. </body>
96. </html>

Code:

Click to expand!
1.  <script>
2.    var canvas, ctx;
3.    var monsterX=100, monsterY=100, monsterAngle=0;
4.    var incrementX = 0;
5.  
6.    function init() {
7.      // This function is called after the page is loaded
8.   
9.      // 1 - Get the canvas
10.     canvas = document.getElementById('myCanvas');
11.  
12.     // 2 - Get the context
13.     ctx=canvas.getContext('2d');
14.  
15.     // 3 add key listeners to the window element
16.     window.addEventListener('keydown', handleKeydown, false);
17.     window.addEventListener('keyup', handleKeyup, false);
18. 
19.     // 4 - start the animation
20.     requestId = requestAnimationFrame(animationLoop);
21.   }
22. 
23.   function handleKeydown(evt) {
24.     if (evt.keyCode === 37) {
25.       //left key
26.          incrementX = -1;
27.     } else if (evt.keyCode === 39) {
28.       // right key
29.       incrementX = 1;
30.     }
31.   }
32. 
33.   function handleKeyup(evt) {
34.     incrementX = 0;
35.   }
36. 
37.   function animationLoop() {
38.     // 1 - Clear
39.     ctx.clearRect(0, 0, canvas.width, canvas.height);
40.  
41.     // 2 Draw
42.     drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
43.  
44.     // 3 Move
45.     monsterX += incrementX;
46.  
47.     // call again mainloop after 16.6 ms (60 frames/s)
48.     requestId = requestAnimationFrame(animationLoop);
49.   }
50. </script>

Example #3: what if I want to listen to key events only in my canvas?

If you add a key listener to a canvas element, the problem is that it will get events only when it has the focus. And by default, it will never have the focus!

The tabindex attribute of the canvas element makes it focusable. Without it, it will never get the focus!

The trick is to declare the canvas like this:

1.  <canvas id="myCanvas" width="350" tabindex="1" height="200">
2.  </canvas>

And we force the canvas to get the focus with:

1.  canvas=document.getElementById('myCanvas');
2.    ...
3.  canvas.focus();

Now, if we try an example with the above canvas declaration, we show when an HTML element has the focus: a border is added to it.

Example with focus & added border.

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Listening to key events only in the canvas - Example #3</title>
6.   <style type="text/css">
7.     canvas {
8.       border:1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   This example shows how to handle key events in a canvas by adding a key listener to the canvas object 
14.   (see the blue border around the canvas... it shows that the canvas has the focus. Try to click outside 
15.   of the canvas: it will lose the focus).
16.   <p>
17.     If you don't want a border to appear when you click on the canvas, 
18.   set its style to outline: none
19.   </p>
20.   <p>Press a key when the canvas has the focus: an alert will display the keycode of the key you pressed. 
21.     Problem: if the canvas looses the focus, key press are no more detected...</p>
22.   <canvas id="myCanvas" width="350" 
23.     tabindex="1" height="200">
24.   </canvas>
25.   <script>
26.     var canvas;
27.     function init() {
28.       canvas=document.getElementById('myCanvas');
29.       // This will work only if the canvas has the focus
30.       canvas.addEventListener('keydown', handleKeydown, false);
31.       // We can set the focus on the canvas like that:
32.       //canvas.focus();
33.       // ... but it will stop working if we click somewhere else
34.       // in the document
35.     }
36.     function handleKeydown(e){
37.       alert('keycode: '+e.keyCode);
38.       return false;
39.     };
40.   </script>
41. </body>
42. </html>

Note that the line that forces the focus to the canvas is commented by default. Try to click on the canvas, then press a key, then click out of the canvas, then press a key: this time nothing happens!

A border appears when the canvas has the focus.

Extract from the code:

Click to expand!
1.  var canvas;
2.  
3.  function init() {
4.    canvas=document.getElementById('myCanvas');
5.   
6.    // This will work only if the canvas has the focus
7.    canvas.addEventListener('keydown', handleKeydown, false);
8.  
9.    // We can set the focus on the canvas like this:
10.   //canvas.focus();
11. 
12.   // ... but it will stop working if we click somewhere else
13.   // in the document
14. }
15.  
16.  
17. function handleKeydown(e){
18.   alert('keycode: '+e.keyCode);
19.   return false;
20. };

Line 10 is useful to initially set the focus on the canvas, but this trick will not work if we click somewhere else in the HTML page.

Example #4: a better way: set the focus when the mouse cursor enters the canvas

A better way to manage key events on a canvas is to set the focus when the mouse is over the canvas, and to un-focus it otherwise.

Here is a modified version of the “move monster example” seen earlier. This time, you move the monster with the left and right arrow only when the mouse cursor is over the canvas. We added two mouse event listeners on the canvas: one for the mouseenter event and the other for the mouseout event.

When the mouse enters the canvas we call canvas.focus() to set the focus to the canvas, and when the mouse cursor goes out of the canvas, we call canvas.blur() to unset the focus.

Setting the focus when the mouse cursor enters the canvas.

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Setting the focus when the mouse cursor enters the canvas - Example #4</title>
6.   <style type="text/css">
7.     canvas {
8.       border:1px solid black;
9.     }
10.   </style>
11. </head>
12. <body onload="init();">
13.   <canvas id="myCanvas" tabindex="1" width="400" height="400">
14.     Your browser does not support the canvas tag.
15.   </canvas>
16.   <p>
17.   <script>
18.     var canvas, ctx;
19.     var monsterX=100, monsterY=100, monsterAngle=0;
20.     var incrementX = 0;
21.     function init() {
22.       // This function is called after the page is loaded
23.       // 1 - Get the canvas
24.       canvas = document.getElementById('myCanvas');
25.       // 2 - Get the context
26.       ctx=canvas.getContext('2d');
27.       // 3 add key listeners to the window element
28.       canvas.addEventListener('keydown', handleKeydown, false);
29.       canvas.addEventListener('keyup', handleKeyup, false);
30.       canvas.addEventListener('mouseenter', setFocus, false);
31.       canvas.addEventListener('mouseout', unsetFocus, false);
32.       // 4 - start the animation
33.       requestId = requestAnimationFrame(animationLoop);
34.     }
35.     function setFocus(evt) {
36.       canvas.focus();
37.     };
38.     function unsetFocus(evt) {
39.       canvas.blur();
40.       incrementX = 0; // I added this line
41.     };
42.     function handleKeydown(evt) {
43.       if (evt.keyCode === 37) {
44.         //left key
45.         incrementX = -1;
46.       } else if (evt.keyCode === 39) {
47.         // right key
48.         incrementX = 1;
49.       }
50.     }
51.     function handleKeyup(evt) {
52.       incrementX = 0;
53.     }
54.     function animationLoop() {
55.       // 1 - Clear
56.       ctx.clearRect(0, 0, canvas.width, canvas.height);
57.       // 2 Draw
58.       drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
59.       // 3 Move
60.       monsterX += incrementX;
61.       // call again mainloop after 16.6 ms (60 frames/s)
62.       requestId = requestAnimationFrame(animationLoop);
63.     }
64.     function drawMonster(x, y, angle, headColor, eyeColor) {
65.       // GOOD PRACTICE : SAVE CONTEXT AND RESTORE IT AT THE END
66.       ctx.save();
67.       // Moves the coordinate system so that the monster is drawn
68.       // at position (x, y)
69.       ctx.translate(x, y);
70.       ctx.rotate(angle)
71.       // head
72.       ctx.fillStyle=headColor;
73.       ctx.fillRect(0,0,200,200);
74.       // eyes
75.       ctx.fillStyle='red';
76.       ctx.fillRect(35,30,20,20);
77.       ctx.fillRect(140,30,20,20);
78.       // interior of eye
79.       ctx.fillStyle=eyeColor;
80.       ctx.fillRect(43,37,10,10);
81.       ctx.fillRect(143,37,10,10);
82.       // Nose
83.       ctx.fillStyle='black';
84.       ctx.fillRect(90,70,20,80);
85.       // Mouth
86.       ctx.fillStyle='purple';
87.       ctx.fillRect(60,165,80,20);
88.       // GOOD PRACTICE !
89.       ctx.restore();
90.     }
91.     function start() {
92.       // Start the animation loop, targets 60 frames/s
93.       requestId = requestAnimationFrame(animationLoop);
94.     }
95.     function stop() {
96.       if (requestId) {
97.         cancelAnimationFrame(requestId);
98.       }
99.     }
100.   </script>
101. </body>
102. </html>

Code:

Click to expand!
1.  function init() {
2.    // This function is called after the page is loaded
3.    // 1 - Get the canvas
4.    canvas = document.getElementById('myCanvas');
5.    // 2 - Get the context
6.    ctx=canvas.getContext('2d');
7.  
8.    // 3 - Add key listeners to the window element
9.    canvas.addEventListener('keydown', handleKeydown, false);
10.   canvas.addEventListener('keyup', handleKeyup, false);
11. 
12.   canvas.addEventListener('mouseenter', setFocus, false);
13.   canvas.addEventListener('mouseout', unsetFocus, false);
14. 
15.   // 4 - Start the animation
16.   requestId = requestAnimationFrame(animationLoop);
17. }
18. 
19. function setFocus(evt) {
20.   canvas.focus();
21. };
22. 
23. 
24. function unsetFocus(evt) {
25.   canvas.blur();
26.   incrementX = 0; // stop the monster if the mouse exists the canvas
27. };

The third parameter (false) of lines 12 and 13 means “we do not want to propagate the event to the ancestors of the canvas in the DOM.”

4.3.3 Mouse Interaction, Mouse Events

Detecting mouse events in a canvas is quite straightforward: you add an event listener to the canvas, and the browser invokes that listener when the event occurs.

The example below is about listening to mouseup and mousedown events (when a user presses or releases any mouse button):

1.  canvas.addEventListener('mousedown', function (evt) {
2.    // do something with to the mousedown event
3.  });

The event received by the listener function will be used for getting the button number or the coordinates of the mouse cursor. Before looking at different examples, let’s look at the different event types we can listen to.

The different mouse events

Mouse events illustrated.

We saw in the last example how to detect the mouseenter and mouseout events.

There are other events related to the mouse:

The tricky part: accurately getting the mouse position relative to the canvas

When you listen to any of the above events, the event object (we call it a “DOM event”), passed to the listener function, has properties that correspond to the mouse coordinates: clientX and clientY.

However, these are what we call “window coordinates”. Instead of being relative to the canvas itself, they are relative to the window (the page).

Most of the time you need to work with the mouse position relative to the canvas, not to the window, so you must convert the coordinates between the window and the canvas. This will take into account the position of the canvas, and the CSS properties that may affect the canvas position (margin, etc.).

Fortunately, there exists a method for getting the position and size of any element in the page: getBoundingClientRect().

Play with the example below that show the problem:

Display mouse cursors position on canvas.

JavaScript

Click to expand!
var canvas, ctx, mousePos, mouseButton;
window.onload = function init() {
  canvas = document.getElementById('myCanvas');
  ctx = canvas.getContext('2d');
  canvas.addEventListener('mousemove', function (evt) {
    mousePos = getMousePos(canvas, evt);
    var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
    writeMessage(canvas, message);
  }, false);
  canvas.addEventListener('mousedown', function (evt) {
    mouseButton = evt.button;
    var message = "Mouse button " + evt.button + " down at position: " 
    + mousePos.x + ',' + mousePos.y;
    writeMessage(canvas, message);
  }, false);
  canvas.addEventListener('mouseup', function (evt) {
    var message = "Mouse up at position: " + mousePos.x + ',' + mousePos.y;
    writeMessage(canvas, message);
  }, false);
};
function writeMessage(canvas, message) {
  ctx.save();
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.font = '18pt Calibri';
  ctx.fillStyle = 'black';
  ctx.fillText(message, 10, 25);
  ctx.restore();
}
function getMousePos(canvas, evt) {
  // necessary to take into account CSS boudaries
  var rect = canvas.getBoundingClientRect();
  return {
    x: evt.clientX - rect.left,
    y: evt.clientY - rect.top
  };
}

CSS

canvas {
  border 1px solid black
}

HTML

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Mouse event - Good code</title>
6.    <style>
7.      body {
8.        margin: 20px;
9.        padding: 0px;
10.     }
11.   </style>
12. </head>
13.   <body>
14.     This is a canvas:<p></p>
15.     <canvas id="myCanvas" width="578" height="200"></canvas>
16.   </body>
17. </html>

JavaScript

1. function getMousePos(canvas, evt) {
2.   // necessary to take into account CSS boundaries
3.   var rect = canvas.getBoundingClientRect();
4.   return {
5.     x: evt.clientX - rect.left,
6.     y: evt.clientY - rect.top
7.   };
8. }

WRONG code:

Click to expand!
1.  ...
2.  canvas.addEventListener('mousemove', function (evt) {
3.      mousePos = getMousePos(canvas, evt);
4.      var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
5.      writeMessage(canvas, message);
6.  }, false);
7.   
8.  ...
9.  function getMousePos(canvas, evt) {
10.   // WRONG!!!
11.   return {
12.     x: evt.clientX,
13.     y: evt.clientY
14.   };
15. }

Here is the result, when the mouse is approximately at the top left corner of the canvas:

Bad mouse coordinates.

GOOD version of the code:

Corrected code for top left canvas mouse positioning.
function getMousePos(canvas, evt) {
  // necessary to take into account CSS boundaries
  var rect = canvas.getBoundingClientRect();
  return {
    x: evt.clientX - rect.left,
    y: evt.clientY - rect.top
  };
}

Result (the cursor is approximately at the top left corner):

Mouse now at 0,0 - top left of canvas.

How to display the mouse position, and the mouse button that has been pressed or released

This example uses the previous function for computing the mouse position correctly. It listens to mousemove, mousedown and mouseup events, and shows how to get the mouse button number using the evt.button property.

Mouse position along with whether its pressed or released. Mouse event example.  Move, click, release..

Extract from source code:

Click to expand!
1.  var canvas, ctx, mousePos, mouseButton;
2.   
3.  window.onload = function init() {
4.      canvas = document.getElementById('myCanvas');
5.      ctx = canvas.getContext('2d');
6.   
7.      canvas.addEventListener('mousemove', function (evt) {
8.          mousePos = getMousePos(canvas, evt);
9.          var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
10.         writeMessage(canvas, message);
11.     }, false);
12.  
13.     canvas.addEventListener('mousedown', function (evt) {
14.         mouseButton = evt.button;
15.         var message = "Mouse button " + evt.button + " down at position: " + mousePos.x + ',' + mousePos.y;
16.         writeMessage(canvas, message);
17.     }, false);
18.  
19.     canvas.addEventListener('mouseup', function (evt) {
20.         var message = "Mouse up at position: " + mousePos.x + ',' + mousePos.y;
21.         writeMessage(canvas, message);
22.     }, false);
23. };
24.   
25. function writeMessage(canvas, message) {
26.    ctx.save();
27.    ctx.clearRect(0, 0, canvas.width, canvas.height);
28.    ctx.font = '18pt Calibri';
29.    ctx.fillStyle = 'black';
30.    ctx.fillText(message, 10, 25);
31.    ctx.restore();
32. }
33.  
34. function getMousePos(canvas, evt) {
35.    // necessary to take into account CSS boudaries
36.    var rect = canvas.getBoundingClientRect();
37.    return {
38.       x: evt.clientX - rect.left,
39.       y: evt.clientY - rect.top
40.    };
41. }

More examples

Example #1: move the monster with the mouse, rotate it when a mouse button is pressed

This example shows an animation at 60 frames/s using requestAnimationFrame, were the monster is drawn at the mouse position, and if a mouse button is pressed, the monster starts rotating around its center. If we release the mouse button, the rotation stops:

Move and rotate monster with mouse.

JavaScript code:

Click to expand!
1. var canvas, ctx;
2. var monsterX=100, monsterY=100, monsterAngle=0;
3. var incrementX = 0;
4. var incrementAngle =0;
5. var mousePos;
6. 
7. function init() {
8.   ...
9.   // 3bis - Add mouse listeners
10.   canvas.addEventListener('mousemove', handleMousemove, false);
11.   canvas.addEventListener('mousedown', handleMousedown, false);
12.   canvas.addEventListener('mouseup', handleMouseup, false);
13.   // 4 - Start the animation
14.   requestId = requestAnimationFrame(animationLoop);
15. }
16. function handleMousemove(evt) {
17.   // The mousePos will be taken into account in the animationLoop
18.   mousePos = getMousePos(canvas, evt);
19. }
20. function handleMousedown(evt) {
21.   // the increment on the angle will be
22.   // taken into account in the animationLoop
23.   incrementAngle = 0.1;
24. }
25. function handleMouseup(evt) {
26.   incrementAngle = 0;  // stops the rotation
27. }
28. function getMousePos(canvas, evt) {
29.   ... // same as before
30. }
31. ...
32. function animationLoop() {
33.   // 1 - Clear
34.   ctx.clearRect(0, 0, canvas.width, canvas.height);
35.   // 2 - Draw
36.   drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
37.   // 3 - Move
38.   if(mousePos !== undefined) { // test necessary, maybe the mouse is not yet on canvas
39.     monsterX = mousePos.x;
40.     monsterY = mousePos.y;
41.     monsterAngle += incrementAngle;
42.   }
43.   ...
44.   // call again mainloop after 16.6 ms (60 frames/s)
45.   requestId = requestAnimationFrame(animationLoop);
46. }

This example shows one very important good practice when doing animation and interaction: if you want to achieve a smooth animation, set the state variables 60 times/s inside the animation loop (lines 45-49), depending on increments you set in event listeners (lines 23-31).

Example #2: draw in a canvas as if you were using a pencil

Draw in canvas like a pencil.

Source code:

Click to expand!
...
<script>
  var canvas, ctx, previousMousePos;
  ...

  function drawLineImmediate(x1, y1, x2, y2) {
    // a line is a path with a single draw order
    // we need to do this in this example otherwise
    // at each mouse event we would draw the whole path
    // from the beginning. Remember that lines
    // normally are only usable in path mode
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.stroke();
  }
  function handleMouseMove(evt) {
    var mousePos = getMousePos(canvas, evt);
    // Let's draw some lines that follow the mouse pos
    if (!started) {
      previousMousePos = mousePos; // get the current mouse position
      started = true;
    } else {
      // We need to have two consecutive mouse positions before drawing a line
      drawLineImmediate(previousMousePos.x, previousMousePos.y,
                                mousePos.x,         mousePos.y);
      previousMousePos = mousePos;
    }
  }
  window.onload = function () {
    ...
    started = false;
    canvas.addEventListener('mousemove', handleMouseMove, false);
  };
</script>

We had to define a variable started=false; as we cannot draw any line before the mouse moved (we need at least two consecutive positions).This is done in the test at line 21.

Example #3: same as example #2 but we draw only when a mouse button is pressed

Same as example #1 but draw only when a mouse button is depressed.

We just added mouseup and mousedown listeners, extract from the source code:

Click to expand!
1.  function handleMouseMove(evt) {
2.    var mousePos = getMousePos(canvas, evt);
3.   
4.    // Let's draw some lines that follow the mouse pos
5.    if (painting) {
6.      drawLineImmediate(previousMousePos.x, previousMousePos.y,
7.                                mousePos.x,         mousePos.y);
8.      previousMousePos = mousePos;
9.    }
10. }
11. 
12. function clicked(evt) {
13.   previousMousePos = getMousePos(canvas, evt);
14.   painting = true;
15. }
16.  
17. function released(evt) {
18.   painting = false;
19. }
20.  
21. window.onload = function () {
22.   canvas = document.getElementById('myCanvas');
23.   ctx = canvas.getContext('2d');
24.   painting = false;
25.  
26.   canvas.addEventListener('mousemove', handleMouseMove, false);
27.   canvas.addEventListener('mousedown', clicked);
28.   canvas.addEventListener('mouseup', released);
29. };

4.3.4 Responsive Canvas

Resizing a canvas can be tricky if we don’t know a few rules that might not be easily guessed:

Before looking at how best to handle canvas resizing, let’s see some examples below:

Examples

Example #1: changing the size of a canvas on the fly erases its content!

Example #1 Changing the size of a canvas on the fly will erase its content.

JavaScript code:

Click to expand!
1.  <script>
2.    ...
3.    function resizeCanvas() {
4.      canvas.width = 300;
5.    }
6.   
7.  </script>
8.  ...
9.  <button onclick="resizeCanvas();">
10.   Click this button to resize the canvas and erase it!
11. </button>

Example #2 : resize a canvas using CSS width and height properties with percentages

This time we are using a similar example as above, but we removed the button for resizing it, and we set the size of the canvas to 100x100 pixels. Instead of drawing inside, we draw two lines that join the diagonals.

Draw two diagonal lines on 100 x 100 pixel canvas.

Then, we added this CSS rule:

Resize canvas using pixels, 100 by 100.

It’s the same example as before, just adding this CSS code:

1.  <style>
2.    #myCanvas {
3.      border: 1px solid black;
4.        width:100%
5.    }
6.  </style>

And the result shows clearly that the resolution is still the same, only the pixels are bigger!

BEST PRACTICE: never use CSS percentages on a canvas width or height!

Example #3: a responsive canvas using a resize listener +  a parent element

This is the trick to create a really responsive canvas:

  1. Embed it in a <div> or in any parent container,
  2. Use CSS with percentages on the width and the height CSS properties  of the parent,
  3. Use a resize listener on the  parent of the canvas,
  4. Change the width and height properties of the canvas [from the JavaScript resize listener function] (content will be erased),
  5. Redraw the content, scaled accordingly to the size of the parent.

Yep, this is not a straightforward process…

DIV and canvas inside: div has css width=100% and height=50%.

HTML code:

1.  <div id="parentDiv">
2.    <canvas id="myCanvas" width="100" height="100" ></canvas>
3.  </div>

CSS code:

Click to expand!
<style>
  #parentDiv {
    width:100%;
    height:50%;
    margin-right: 10px;
    border: 1px solid red;
  }

  canvas {
    border: 2px solid black;
  }
</style>

JavaScript code for the resize event listener:

Click to expand!
function init() {
  ...
  // IMPORTANT: there is NO WAY to listen to a DIV's resize
  // listen to the window instead.
  window.addEventListener('resize',     
    resizeCanvasAccordingToParentSize, false);
  ...
}
 
function resizeCanvasAccordingToParentSize() {
  // adjust canvas size, take parent's size, this erases content
  canvas.width = divcanvas.clientWidth;
  canvas.height = divcanvas.clientHeight;
  ...
  // draw something, taking into account the new canvas size
}

See the complete example that corresponds to the above code:

Example #4: the same example with the monster.

Example #4: the same example with the monster

Same as last example with monster.

When the canvas is resized, its width became smaller than the monster’s size. We scale down the monster (using ctx.scale!) The code is very similar to the previous example, we just replaced drawDiagonals() by drawMonster(…), and we added a test in the drawMonster(…) function for scaling the monster if it’s bigger than the canvas width (look at lines 10-16), this is a common trick:

Click to expand!
1.  function drawMonster(x, y, angle, headColor, eyeColor) {
2.    // GOOD PRACTICE: SAVE CONTEXT AND RESTORE IT AT THE END
3.    ctx.save();
4.  
5.    // Moves the coordinate system so that the monster is drawn
6.    // at position (x, y)
7.    ctx.translate(x, y);
8.    ctx.rotate(angle);
9.  
10.   // Adjust the scale of the monster (200x200) if the canvas
11.   // is too small
12.   if(canvas.width < 200) {
13.     var scaleX = canvas.width/200;
14.     var scaleY = scaleX;
15.   }
16.   ctx.scale(scaleX, scaleY);
17. 
18.   // head
19.   ctx.fillStyle=headColor;
20.   ctx.fillRect(0,0,200,200);
21.   ...
22. }

4.4 A Glimpse of Advanced Canvas Functionalities

The canvas API is a “big beast”, and we have presented all the essential techniques for drawing and animating. However, we could not fit everything in this course. Exotic features that are rarely used by developers, or advanced techniques that require more than 20 lines of JavaScript, have been put aside for  the W3Cx HTML5 Apps and Games course.

In that course, you will learn:

Examples studied in the HTML5 Apps and Games course

Sprite sheet.

5.1 HTML5 Forms Introduction - Module 5

Congratulations! You made it to module 5. Welcome this time to the wonderful world of HTML5 forms. There have been no improvements since 1997 to HTML forms.

HTML5 this time comes with a set of new input fields for entering dates, colors, entering phone numbers, URLs, and so on. New attributes related to form validation have also been introduced, and they are related to a new build-in validation system that make things easier than before without the need to use JavaScript. However, JavaScript validation API is also available for people who would like to write their own customized validation system. These additions are very handy when used on mobile devices but raised many critics from Web designers, mainly related to their desktop implementations.

The course discusses all the cases and recommend solutions and good practices. Of course, like usual, many examples will be provided that you can run directly this time in the course pages, but also as standalone examples hosted on the JS Bin Web site. If you try to modify them or creates your own examples, please share in the discussion forums as usual. Have fun!

5.2 Elements and Attributes

With HTML5, forms, which had shown little improvement since 1997, evolved considerably.  To achieve this, Web developers relied on many popular JavaScript frameworks for validating input formats, providing various input GUIs, such as calendars for dates, sliders, etc. Frameworks such as jQueryUI, Dojo, and Sencha, all provide a widget set for improving forms. Furthermore, it was time to take into account the specifics of mobile web applications, where the GUI of a date chooser cannot be the same as a 400x400 pixel wide calendar on a desktop. Contextual virtual keyboards provided the way forward on smartphones and tablets thanks to Apple, Google and others.

HTML5 took all this into account and thus provides:

Examples of contextual keyboards are shown above; they differ depending on the type of  <input> fields in the <form>. In the examples, we can see: email, URL, and phone number. Look at the different keyboard layouts. The last picture is a date picker from an IOS phone.

External resources:

5.2.2 Example

There is a lot of course content covered this week, and before we get into all the details of the elements and attributes introduced by HTML5, we suggest playing with running examples of full featured forms.

This example was created by a learner (by Mels Le N.) from a previous version of this course. It uses the geolocation API presented in Module 6 for auto-filling the address input fields.

Feel free to look at the source code in the JS Bin:

Geo-location API is used to auto-fill address input.

5.2.3 Input Elements and Attributes

Live coding video 1: using input elements as widgets to control a Web application

Live coding video 2: creating GUI elements dynamically

Input elements, in particular the elements introduced by HTML5, can be used as widgets to control the behavior of a Web application. In this situation, they do not need to be inside a <form> element. We just bind event listeners to them and we use them as client-side widgets.

GUI: to resize, change color and speed.

Examples

GUI: Graphical User Interface

Example #1: choose the color, line width and speed of an animation

Example #2: data visualization control

Simple chart without GUI. Simple chart with GUI.

5.2.4 Best Practices

Live coding video 3: HTML5 forms - best practices

HTML5 forms and best practices, personal information.

The example used in the video is available online at JSBin. A screenshot of the resulted form is shown on the right.

Forms are a way to get user input which is sent to a remote server. This section of the course focuses on the HTML5 additions to forms, and as such will only cover the client-side part.

On the server side, you may have PHP, Java, C#, Ruby, Python, etc. components. There are several ways to collect server-side data from a form in a Web page: REST Web services, servlets, Microsoft ASP pages, etc.

On the client side, the forms indicate to which server and how the data should be sent,  using the action and method attributes respectively. A <button type=“submit”> or an <input type=submit> field is used to submit the form content.

For example: <form action=“myServerCode.php” method=“POST”>…</form>. Here, we set the URL of the server side code (myServerCode.php), and the HTTP method that will be used by the browser for sending the form content (POST).

Another approach is to use JavaScript for sending the form content with Ajax. This is covered in W3Cx HTML5 Apps and Games course.

This week, let’s study the elements and attributes offered by HTML5, as well the HTML5 form validation API. 

The example shown in the video shows some best practices for writing accessible forms and does some basic layout using CSS.

The following additional example shows the same best practices but presents a more complete form with CSS rules to make a nice layout. See it online, and illustrated with the screenshot below. It is adapted from this very good MDN’s article “How to structure a web form”.

Payment form layout example.

5.3 Accessible Forms

Forms are commonly used to enable user interaction in Web sites and Web applications. For example, they are used for login, registering, commenting, and purchasing.

Since HTML5 provides functionalities to assist with accessibility, developers should make a concerted effort to mark up Web based forms. The following two guidelines are to give you a good start to make your forms accessible:

  1. For every form field, ensure that a descriptive label is provided and use the <label> element to identify each form control.
  2. For larger or complex forms, use the <fieldset> and <legend> elements to respectively group and associate related form controls.

Examples for each of these two basic guidelines are given in the following pages.

Further reading

The WAI Web site hosts a Forms tutorial where to find all guidelines to follow in order to make your forms truly accessible: labeling controls, grouping controls, form instructions, validating input, user notifications, multi-page forms, and custom controls.

5.3.2 Why is this Important?

Forms can be visually and cognitively complex and difficult to use. Accessible forms are easier to use for everyone, including people with disabilities.

5.3.3 Labeling Controls

Labels need to describe the purpose of the form control

Form fields and other form controls usually have visible labels, such as “E-mail Address:” as the label for a text field (see figure below).

Email form label text.

When these labels are marked up correctly, people can interact with them using only the keyboard, using voice input, and using screen readers. Also, the label itself becomes clickable, which enables a person who has difficulty clicking on small radio buttons or checkboxes to click anywhere on the label text.

Associating labels explicitly

Whenever possible, use the label element to explicitly associate text with form elements. The for attribute of the label must exactly match the id of the form control. 

Example #1

Click on the label, not on the input field to see the effect.

First name label text.

Source code:

1.  <label for="first_name">Your First Name</label>
2.  <input id="first_name" type="text" name="fname"/>

Example #2

Note that you can also include the <input> element inside the <label>…<label> element, and also add a <span lang=“en”> for example, to indicate the language used in the label. Sometimes, nesting labels and inputs can also make CSS styling easier and produce better results with screen readers.

Source code (with <input> inside the <label>):

<label for=first_name"><span lang=en">Your First Name</span>
  <input id="first_name" type="text" name="fname"/>
</label>

Example #3

Click on the label “Subscribe to newsletter” to see what this does.

First name with subscription check box.

Source code:

1. <label for="firstname">First name:</label>
2. <input type="text" name="firstname" id="firstname"><br>
3. 
4. <label for="subscribe">Subscribe to newsletter</label>
5. <input type="checkbox" name="subscribe" id="subscribe">

Labeling buttons

The label of a <button> element is set inside the element and can include markup. This allows advanced accessibility hints to be included, such as marking up language change. Example: <button>Mon <span lang=“fr”>bouton</span></button>,for a button with a label in French.

When using the <input> element to create buttons, the label is set in the value attribute of the element. Example: <input type=“submit” value=“Please submit”>, renders a text button.

Source code for the “Submit” and “Cancel” buttons example:

1. <button type="submit">Submit</button>
2. <button type="button">Cancel</button>
3. <input type="submit" value="Submit">
4. <input type="button" value="Cancel">

These give the same results:

Example submit and cancel buttons.

Lines 1 and 2 render as: Submit Cancel

… while lines 3 and 4 render as:

Example submit and cancel buttons.

Source code:

<label for="address">Enter your address:</label><br> <textarea id="address" name="addresstext"></textarea>

5.3.4 Grouping Controls

Groupings of form controls, typically groups of related checkboxes and radio buttons, sometimes require a higher level description. Grouping related form controls makes forms more understandable for all users, as related controls are easier to identify.

Associating related controls with fieldset

Grouping needs to be carried out visually and in the code, for example, by using the <fieldset> and <legend> elements to associate related form controls. The <fieldset> identifies the entire grouping and <legend> identifies the grouping’s descriptive text.

Example #1: radio buttons

In the example below, there are three radio buttons that allow the user to choose an output format. Radio button groups should always be grouped using <fieldset>.

Output format:

Example label for button select.

Source code:

Click to expand!
<fieldset>
  <legend>Output format</legend>
  <div>
    <input type="radio" name="format" id="txt" value="txt" checked>
    <label for="txt">Text file</label>
  </div>
  <div>
    <input type="radio" name="format" id="csv" value="csv">
    <label for="csv">CSV file</label>
  </div>
  [...]
</fieldset>

Example #2: checkboxes

In the example below, there are three checkboxes that are all part of an opt-in function for receiving different types of information.

I want to receive

Example label for check box select.

Source code:

Click to expand!
<fieldset>
  <legend>I want to receive</legend>
    <div>
      <input type="checkbox" name="newsletter" id="check_1">
      <label for="check_1">The weekly newsletter</label>
    </div>
 [...]
</fieldset>

Associating related controls with WAI-ARIA

WAI-ARIA provides a grouping role that functions similarly to fieldset and legend. For example, a div element can have role=group to indicate that the contained elements are members of a group.

WAI-ARIA roles are very important in the accessibility world, and we invite you to see an example provided in the associated WAI tutorial. See also this MDN’s article about about WAI-ARIA roles.

5.4.1 Input Types

In this section, we briefly present the input types, attributes, and elements related to the forms that came with HTML5. Details are given later, illustrated by multiple interactive examples.

Compared to HTML4, HTML5 introduced 13 new input types, covering most of the needs of  Web developers. HTML5 packages some of the “form best practices” in its specification. Web browsers providing native implementation give a boost in performance, and reduce the size of JavaScript embedded in complex Web pages.

MDN’s Web docs article on <input> types lists all input types and highlights those that came with HTML5.

Now, let’s play with some of these input types and attributes.

HTML4 to HTML5.

5.4.2 "color"

For years, we used hundreds of lines of JavaScript for selecting colors. Now, it’s bundled in the browser!

Here is how it looks on some mobile devices:

HTML5 input type=color on an android phone.

Typical use

Inserting a color chooser is as simple as:

<!DOCTYPE html>
<html lang="en"><head>...</head>
  <body>
    Choose a color : <b><input type="color" value="#FF00F"/></b>
  </body>
</html>
Note: In this chapter we are simplifying the examples, as we usually embed input elements in a <form>…</form>.

Try <input type=“color”> online with this JSBin example. Or do it here in your browser: just click on the purple square below:

Here is the result on Google Chrome (works with other browsers too, though the look and feel may differ):

Select a color, input type=color.

Example: changing the background color of the page

The <input type=“color”> can fire change or input events. Here is an example that changes the background color of the page when a color is chosen. Try it online at JSBin.

Change background color of the page body.

Source code:

Click to expand!
<!DOCTYPE html>
<html lang="en"><head></head>
  <body>
    Select a color : <input type="color" id="colorChooser"/>
    <script>
      var colorInputField = document.querySelector("#colorChooser");

      colorInputField.addEventListener('input', function(evt) {
          document.body.style.backgroundColor = this.value;
      }, false);
    </script>
  </body>
</html>

Offer a limited choice of colors

By default, the color selector offers many options that may either frighten some users or just not be appropriate for the purpose of the application. 

Good news: it is possible to restrict the choices, and also simplify the user interface, by using a <datalist> with some <option> elements inside.

Example: click the black rectangle on the right:  . The following should be displayed:

Restricted choice of colors.

Online example at JSBin

Source code extract:

<input type="color" value="#333333" list="colors">
  
<datalist id="colors">
   <option>#0000FF</option>
   <option>#00FF00</option>
   <option>#FF0000</option>
</datalist>

Note that the id of the <datalist> element should be the same as the value of the list attribute of the input field.

What are the main problems with this element?

The main criticism that Web designers make about this element is related to its default appearance being strongly dependent on the browser and its underlying operating system. Changing the look and feel is not possible, except with the use of the options we saw in the previous sections of this page. This problem is also true for other input elements that renders as complex widgets, like <input type=“date”> and its variants.

Another problem is that there is no way to control where the dialog that contains the color chooser will appear - no positioning via CSS or JavaScript is possible. The specification does not say anything about how to position it over the page, thus the result is vendor specific.

The solution proposed by the W3C and its contributors is called Web Components, a new approach for designing HTML5 widgets, that is covered in the W3Cx HTML5 Apps and Games course.

5.4.3 “date”

For years, date and time pickers in HTML forms made Web developers rely heavily on JavaScript based widgets. The process is simpler in HTML5, which provides a special control to handle this specific kind of data natively.

Below are a few screenshots of the HTML5 date picker on several mobile devices. Note that the native date pickers of the operating systems are used:

Date picker 1. Date picker 2 on mobile. Date picker 3 on mobile.

The problem is different on a desktop. While it’s great to have native support for a date picker, Web developers would sometimes prefer 100% control over the look and feel of the date picker widget. For this purpose, the solution undoubtedly lies with the new Web Components (a way to make custom reusable widgets in HTML/CSS/JS), to be detailed in the W3Cx HTML5 Apps and Games course

Why don’t you try it yourself? Just click on this input field: (not in github)

With Firefox, it shows this date picker widget:

Firefox desktop date picker widget.

On non-supported browsers, it defaults to an <input type=“text”> input field.

Typical use of <input type=“date”>

Default use

The default usage is something like:

<label for="birthday">Choose birthday party date: </label>
<input type="date" id="birthday">

Result:             Choose birthday party date:      

Most of the time you will add other attributes to give some restrictions (choose a date in the past, in the future, only on a Saturday, etc.).

Restrict choice to an interval of dates: attributes min, max and value

The <input type=“date”> comes with several useful attributes. In particular the value, min and max attributes are used to propose a default date, a min and a max date, or for defining an interval of acceptable values.

Try this example: just click the next input field or try it online on JSBin if you want to tweak the source code:

Only dates between min and max are selectable.

Source code:

...
<input type="date"
   id="birthdayParty"
   value="2015-06-20"
   min="2015-06-20"
   max="2015-06-30">
...

Choosing one day in a given week, etc. with the step: attribute

Using the value attribute for setting a date, and using step=7 for example, will make acceptable only the day of the week that corresponds to the value’s day (e.g.: only Mondays). Using step=2 will make acceptable only every other day, etc.

Example: we want to celebrate birthday parties only on Saturdays, check this on JSBin! (screenshot from Chrome).

Use of the step attribute, allow only Saturdays.

Extract from source code:

<input type="date"
  id="birthdayParty"
  value="2015-06-20"
  min="2015-06-20"
  max="2015-06-30"
  step="7">

Combining with the <datalist> element to restrict the choice of possible values

Online example at JSBin (screenshot from Chrome).

Restrict choices using a datalist element.

Extract from source code:

<input type="date"
  id="birthdayParty"
  list="birthdayPartyPossibleDates"
  value="2015-06-20">
<datalist id="birthdayPartyPossibleDates">
  <option label="Best for me">2015-06-20</option>
  <option label="Ok for me too ">2015-06-27</option>
  <option label="This one is a sunday, hmmm">2015-06-28</option>
</datalist>

The list attribute of the input element must match the id attribute of the datalist element.

If you use the min, max, or step attributes with a list attribute, it may filter the restricted list even more. Check this example on JSBin (tested with Google Chrome), that has a restricted list of three elements, one of which is filtered because it is not in in the min/max range.

Responding to date changes, trying date/time and other variants

Listening to the input event

Here is an interactive example at JSBin where you can change the type of date/time chooser. It also shows how to listen to the input event when a date/time is chosen.

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en"><head>...</head>
3.  <body>
4.    Testing the new date input field.<p>
5.    Choose a date/time : <input type="date" id="date" /></p>
6.    <p>
7.      You picked: <span id="pickedDate"></span>
8.    </p>
9.    After you have tried the first example, change the value of the "type" attribute to:
10.   <ul>
11.     <li>datetime</li>
12.     <li>datetime-local</li>
13.     <li>time</li>
14.     <li>week</li>
15.     <li>month</li>
16.   </ul>
17.     And see the result.
18.   <script>
19.     var field = document.querySelector("#date");
20.     var result = document.querySelector("#pickedDate");
21.     field.oninput = function(evt) {
22.       var date = this.value;
23.       pickedDate.innerHTML = "<b>"+date+"</b>";
24.     }
25.   </script>
26. </body>
27. </html>

Lines 20-26 show how we can detect a date change using JavaScript.

Checking if the chosen date is in the past or in the future using the valueAsDate property

The object returned to the input event handler has a useful property named valueAsDate. This is a JavaScript date object that can be compared to other JavaScript date objects, in particular to the date of the day we can get with var date = new Date();

The following example at JSBin shows how to ascertain whether a date is in the past or in the future: 

Check if date is in the past or future.

While if we enter a date in the future:

Enter your Birthdate in the future: bad.

Extract from source code:

Click to expand!
1.  <body>
2.    <label for="birthDate">Enter your birth date: </label><p>
3.    <input type="date" id="birthDate" >
4.    <p>
5.      You picked: <span id="pickedDate"></span><p>
6.      <span id="pastFuture"></span>
7.    </p>
8.    <script>
9.      var field = document.querySelector("#birthDate");
10.     var result = document.querySelector("#pickedDate");
11.     var pastFuture = document.querySelector("#pastFuture");
12.     field.oninput = function(evt) {
13.       var date = this.value;
14.       pickedDate.innerHTML = "<b>"+date+"</b>";
15.       if(date.valueAsDate <= new Date()) {
16.         pastFuture.style.color = 'green';
17.         pastFuture.innerHTML = "<b>Date in the past, ok!</b>"
18.       } else {
19.         pastFuture.style.color = 'red';
20.         pastFuture.innerHTML = "<b>Date in the future, you're not even born!</b>"
21.       }
22.     }
23.   </script>
24. </body>

Lines 17-23 show how we can compare the date picked in the calendar widget with the current date. Note that we can compare any given dates using JavaScript. To check that the chosen date is before 2000 we would do this:

if(this.valueAsDate <= new Date(2000,1,1)) {
  ...
}
<input type="datetime">, "week", "month", "datetime-local", etc.

The HTML5 specification indicates that we can use <input type=“date”> and <input type=“time”> while for some years (before the specification became a frozen standard in October 2014), other variants were also present, such as type=datetime, datetime-local, month and week.

Here is an interactive example at JSBin where you can change the type of date chooser and try all the different possible values for the type attribute of date pickers.

Some screenshots from Opera desktops and Safari IOS:

<input type=“time”>

Input type=time, select a time. Input type=time safari IOS.

<input type=“datetime”>

Screenshot showing on the left the webcam video stream, and on the right the same stream recorded and playable in a HTML video element. Select date/time. Wed 7:29pm, June 7, 2011. Input type=datetime on Safari IOS.

<input type=“datetime-local”>

Select date and time using datetime-local.

<input type=“week”>:

Select a Week.

<input type=“month”>:

2012-02 Select month and year of birthdate. June, 2011 select input type=month safari.

5.4.4 “e-mail”, “tel”, “URL”, and “search”

Let’s study 4 input types: email”, “tel”, “URL” and “search”.

<input type=“email”>

This input type is relatively straightforward to use. In mobile applications, this new input type pops up a keyboard layout adapted to email input. Note the “@” key, the “.” key, etc.

Contextual mobile keyboard for entering an email address.

This input type is very interesting as it provides default validation behaviors:

If the value entered looks like an email address (contains a “@”…), the field is valid, and gets the pseudo CSS class  :valid

If the value entered does not contain an “@”, and does not look like an email address, the field is invalid and gets the pseudo CSS class :invalid

See the next example to see this in action. More details will be presented in a later section dedicated to form validation.

Typical use: Online example at CodePen

Enter your email.

Try it on your browser:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Example of input type=email</title>
6.    <style>
7.      input:invalid {
8.        background-color:pink;
9.      }
10.   </style>
11. </head>
12. <body>
13.   <label for="email">Enter your email </label>
14.   <input type="email" id="email">
15. </body>
16. </html>

Note the CSS rule that turns the background color of the email input field to pink if a user enters an invalid address (lines 7-8). Also note that the validation is based only on matching a regular expression (the address should contain a “@”,  a “.”, etc.). It does not check if the address is an existing one.

<input type=“tel”>

This input field is really useful on smartphones and tablets, as it makes the browser pop up a keyboard layout suitable for entering phone numbers:

Mobile keyboard 1 for input type=tel. Other mobile keyboard 1 for input type=tel.

This input type is often used with the new placeholder and pattern attributes that are detailed in another section of this course. It is supported by all recent major Web browsers, on mobile devices and desktops.

Online example on CodePen

Enter a telephone number.

Try it in your browser (we used the same CSS for changing the background- color when the input value is invalid):

Enter a telephone number:

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Example of input type=tel</title>
6.    <style>
7.      input:invalid {
8.        background-color:pink;
9.      }
10.   </style>
11. </head>
12. <body>
13.   <label for="tel">Enter a telephone number:</label>
14.   <input type="tel" id="tel"
15.   placeholder="(555) 555-5555"
16.   pattern="(?d{3})?[-s]d{3}[-s]d{4}.*?)"/>
17. </body>
18. </html>

<input type=“URL”>

This input field is really useful on smartphones and tablets, as it makes the browser pop up a keyboard layout suitable for entering URLs:

Mobile keyboard for entering URLs.

This field is also compatible with the validation API (more on this in another section).

Here is an online example that shows the use of the placeholder and pattern attributes for entering only URLs that start with ftp:// or https://

Or try it here in your browser:

Enter a URL (default validation):

Enter a URL (custom validation, must start with http, https or ftp):

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head >
4.    <meta charset="utf-8">
5.    <title>Example of input type=url</title>
6.    <style>
7.      input:invalid {
8.        background-color: lightPink;
9.      }
10.   </style>
11. </head>
12. <body>
13.   <label for="url1">Enter a URL (default validation):</label>
14.   <input type="url" id="url1"/>
15.   <p>
16.   <label for="url2">Enter a URL (custom validation, must start with http, https or ftp):</label>
17.   <input id="url2" type="url" placeholder="https://www.domain.com"
18.   pattern="(http|https|ftp)://[a-zA-Z0-9-./]*"/><p>
19. </body>
20. </html>

Lines 16-17 show the use of a pattern attribute with a JavaScript regexp that accepts only URLs starting with http, https or ftp. More details on the pattern attribute are given in the section that presents the new HTML5 form attributes.

<input type=“search”>

The search type is used for search fields (i.e., for a search engine). A search field behaves like a regular text field, except that it may provide some feedback GUI for stopping the current request and emptying the search field, or it may provide a drop-down list of recent search results.

The specification does not state what the GUI should look like, so current implementations show variations in the look and feel.

Input type=search rendered on a smartphone.

Typical use:

<label for="search1">Simple search: </label>
  <input type=search id="search1">
  <p>
    <label for="search2">Search with attribute <code>results=5</code> (try with Safari): </label>
    <input type=search id="search2" results=5>

Results on Chrome and Opera desktop - notice the small cross on the right when one enters a value:

Input type=search in google chrome and opera.

Same example with Safari desktop, this time the second line with an attribute results=5 shows a small icon on the left:

Input type=search on Safari.

Example that shows a drop down list of recent searches (Safari screenshot borrowed from this excellent site about HTML5 forms that is worth reading):

Example 2 of input type=search on safari, shows recent results.

Source code for the knowledge check below

Click to expand!
1. <!DOCTYPE html>
2. <html>
3.   <head>
4.   <meta charset="utf-8">
5.     <title>Example of input type=email</title>
6.     <style>
7.        input:? {
8.        background-color:pink;
9.        }
10.        input:?? {
11.        background-color:lightGreen;
12.        }
13.     </style>
14.   </head>
15.   <body>
16.     <label for="email">Enter your email:</label>
17.     <input type="email" id="email">
18.   </body>
19. </html>

5.4.5 “number”

This input field is useful for entering numerical values (integer or float), but not for entering zip codes. On desktop implementations and on some mobile implementations, it provides a user interface with small vertical arrows for incrementing/decrementing the current value, while on mobiles it will display a numeric keyboard.

For zip codes, a <input type=“text” pattern=“……”> is preferable. See examples given in the pattern attribute section of this course.

Example: <input type=“number” value=“25” min=“0” step=“5” max=“500”/>

Screenshot example taken with a mobile device:

Numeric keyboard on Safari IOS.

Examples on desktop (the width will be adjusted depending on the min and max  attributes):

Input type=number example. Quantity between 1 and 5.

Typical usage

<input type="number" value="25" min="0" step="5" max="500"/>

This field accepts specific attributes max, min, step, value (default displayed value).

This input type is very interesting as it provides default validation behaviors:

If the value entered using a keyboard is not a valid number, or is not in the range defined by the min and max attributes, the field is invalid and gets the pseudo CSS class :invalid.

If the difference between the value you enter and min is a multiple of step, then it gets the CSS pseudo class :valid , otherwise it will be invalid. Example: if min=1 and step=5, the field will be valid with value=1, 6, 11, 16 etc. if min=0, with value=0, 5, 10, 15 etc.

WARNING 1Using a step attribute with an integer value will make the arrows increment/decrement the current value with the step value, and [make the input field valid only when ]the difference between the value you enter and min is a multiple of step.

WARNING 2: by default, omitting the step attribute is equivalent to step=“1”, [so for entering float values, it is necessary to use step=“any” or step equal to a floating point value such as step=“0.1”].

With step=“any”, floating point values are valid, but vertical arrows will increment/decrement the value by one. If step=“0.1”, arrows will increment/decrement by 0.1, etc.

Online example in CodePen try changing the attribute values, use step=“any” and try float values, etc).

Change attribute values.  Use step=any and try float values.

Or, do it here in your browser (Manually enter a value that is not in the range, or not a multiple of 5, try the up and down arrows, etc.):

Quantity (between 0 and 500, should be a multiple of 5 otherwise it’s invalid): 

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.    ....
3.    <style>
4.      #number:invalid {
5.        background-color:pink;
6.      }
7.      #number:valid {
8.        background-color:lightGreen;
9.      }
10.   </style>
11. </head>
12. <body>
13.   Example of <code><input type=number></code>:<p>
14.   <label for="number">Quantity (between 0 and 500, should be a multiple of 5 
      otherwise it's invalid): </label>
15.
      <input type="number" id="number" value="25" 
        min="0" step="5" max="500"/>
16.   <p>
17.     Change the different values for attributes step, max, min, value. Don't forget to try step="any" 
          for float values...
18. </body>
19. </html>

5.4.6 “range”

zipper with funny label input type=range.

This input type renders as a slider. It accepts the same attributes as the <input type=“number”> : min, max, step and value.

Example of rendering on a desktop:

Example rendering on desktop.

And on mobile devices:

Left image, ios range input. Right image, android range input.

Typical use

The basic use is to specify at least the value, min and max attributes, and eventually the step attribute, too:

<input id="slider6" type="range" min="0" max="10" step="2" value="5">

But most of the time, you will need a visual feedback that shows the current value selected by the slider.

This online example on CodePen shows how to add a visual feedback using a very short JavaScript function and an <output> element. Just click and drag the small cursor of the slider (or use up and down arrow keys when the field has the focus):

Add visual feedback with short function.

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Example of input type=tel</title>
6.    <style>
7.      #rangeValue1 {
8.        border:1px solid black;
9.        padding:2px;
10.     }
11.   </style>
12.   <script>
13.     window.onload = function() {
14.       // Called when the page is loaded, for displaying initial value in the output
15.       printValue('slider1','rangeValue1');
16.     }
17.     function printValue(sliderId, outputId) {
18.       var x = document.getElementById(outputId);
19.       var y = document.getElementById(sliderId);
20.       x.value = y.value;
21.     }
22.   </script>
23. </head>
24. <body>
25.   <form >
26.     <label for="slider1">Select a value:</label>
27.     <input id="slider1" type="range"
28.     min="100" max="500" step="10" value="150"
29.     oninput="printValue('slider1','rangeValue1')"/>
30.     <output id="rangeValue1"></output>
31.   </form>
32.   <br/>
33.   Play with attributes: value, min, max, step...
34. </body>
35. </html>

Snapping behavior and the step attribute

Jumps + steps.

When you click and drag the slider, it “jumps” to some snap points corresponding to the integer values of the range defined by the min and max attributes. The “size of the jumps” depends on the value of the step attribute.

Try these examples in your browser and look at their behavior:

value=5 min=0, max=10 step=1:  
value=12 min=10, max=50 step=4:

Note that in the previous example, the default value displayed is 14, not 12 (the value just above min plus an integer step value). 12 is not possible so it’s been “snapped” to 14.

value=5 min=0, max=10 step="0.5":

In the previous example, it’s necessary to add quotes for setting step=“0.5” (while HTML5 authorizes not using quotes for setting integer values to attributes).

value=5 min=0, max=10 step="any":

WARNING: Using a step attribute with an integer value will make the slider jump corresponding to the step value. By default, omitting the step attribute is equivalent to step=“1”.

For accepting float values, it is necessary to use step=“any”, or step equal to a floating point value, such as step=“0.5”.

Adding “ticks” to the range slider using a <datalist> element

Display ticks on a rule at given positions.

Using the <datalist> element, it’s possible to display “ticks” above the range slider, at given positions.

Click to expand!
1.  <label for="slider2">value=5 min=0, max=10 step=1, ticks at 2, 4, 6, 8 and 10:</label>
2.  <input id="slider2" type="range"
3.  list="ticks2"
4.  min="0" max="10" step="1" value="5"/>
5.  <datalist id=ticks2>
6.    <option>0</option>
7.    <option>2</option>
8.    <option>4</option>
9.    <option>6</option>
10.   <option>8</option>
11.   <option>10</option>
12. </datalist>

Try the sliders below:

value=5 min=0, max=10 step=1, ticks at 2, 4, 6, 8 and 10:  
value=20 min=10, max=50 step=5, ticks at 0, 10, 20, 30, 40 and 50:  
value=5 min=0, max=10 step="0.5", ticks at 0, 0.5, 1, 2, 4, 8:  
value=5 min=0, max=10 step="any", ticks at 0, 5 and 10:

External resources

- name - form
- disabled* - readonly
- type - autocomplete
- maxlength - autofocus
- readonly - list
- size - pattern
- value - required*
- alt - placeholder
- src - multiple
- height - list
- width - min
- checked* - max
- align - step
- formaction
- formenctype
- formmethod
- formtarget
- formnovalidate
*  pseudoclasses CSS target with :disabled and :checked or :required selectors
** align is deprecated, CSS rules should be used instead

5.5.1 Form Attributes

In this chapter, we go over the form attributes that have been introduced by HTML5.

HTML5 form attributes.

We have already seen the use of pseudo CSS classes used together with the input field and form validation (pattern attribute, input:invalid CSS rule). We also briefly looked at the use of the placeholder attributes for displaying a helper message in the input field.

In this section, we cover the rest of the form attributes and provide further examples of using the previously discussed attributes.

In another part of the course, about form validation and visual feedback using CSS, we examine some of the most useful attributes in even greater detail.

5.5.2 form

This attribute is useful for putting input fields outside the form itself. The form attribute of an external input field must share the same value as the id of the form the field belongs to. This is useful when using <fieldset> elements for making the page/form layout easier.

Typical use

Try this interactive example in CodePen, or try it directly in your browser:

HTML5 sample form attributes.

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.    <head>
4.      <meta charset="utf-8">
5.      <title>Example of input type=tel</title>
6.   
7.    </head>
8.    <body>
9.      <label for="yourName">Enter your name:</label>
10.     <input type="text" id="yourName" name="yourName" form="form1"/>
11.     <p>
12.     <form id="form1" action="sumit.php" method="post">
13.       <fieldset>
14.         <legend>Choose option</legend>
15.         <label for="free">Free registering</label>
16.         <input type="checkbox" id="free"/>
17.         <label for="premium">Premium</label>
18.         <input type="checkbox" id="premium"/>
19. 
20.         <button type="submit">Send form</button>
21.       </fieldset>
22.     </form>
23.   </body>
24. </html>

Lines 12 and 22 shows the form attribute. Make sure that its value matches the id of the form!

5.5.3 autocomplete

This attribute applies either to the <form> element or on individual  <input> elements. It specifies when input fields must autocomplete the user’s input based on the user’s typing history.

Possible values of this attribute: on/off.

On/off funny picture.

If applied to the <form> element, all input fields attached to the form (inside or linked to it using the form attribute), will have auto-completion set by default to the value of the autocomplete attribute of the form.

This default behavior can be overridden by setting it individually to any input field inside. In other words: it is possible to have autocomplete “on” for the form, and “off” for specific input fields, or vice-versa.

Sometimes this autocomplete behavior is disabled by default in some Web browsers, and will need to be adjusted in the preferences/settings.

This attribute targets most input types (those that allow typing in them).

Typical use

Example of use of autocomplete attribute, #1 of 2.

Try it in your browser here:

Example of use of autocomplete attribute, #2 of 2.

Source code extract:

Click to expand!
1.  <form submit="test.php" method="post" autocomplete="on">
2.    ...
3.    <label for="address">Enter your address (autocomplete off, overrides the
4.      form's autocomplete=on attribute):</label>
5.    <input type="text" id="address" autocomplete="off">
6.    <p>
7.    <label for="address1">Enter your address (autocomplete on by inheritance of
8.      the form's autocomplete=on attribute):</label>
9.    <input type="text" id="address1">
10.   <p>
11.   <button type="submit">Submit</button>
12.   ...
13. </form>

5.5.4 autofocus

This attribute is useful for transferring the focus to a field other than the first field in a page/form (by default the first input field has the focus).

Attention: there must not be more than one element in the document with the autofocus attribute specified!

This example below illustrates the use of the autofocus attribute: the focus is put on the second input field of the page. It also shows the use of  required, placeholder and pattern attributes.

The required attribute makes the input field invalid if kept empty. 

Here is the result in your browser:

Example use of autofocus attribute.

Extract from source code:

Click to expand!
1.  <form>
2.    ...
3.    <input type="text" id="test"/><p>
4.    ...
5.    <input id="name" name="inputName"
6.      placeholder="6 to 9 chars please..."
7.      pattern="w{6,9}"
8.      required
9.      autofocus
10.     type="text"/>
11.   ...
12. </form>

Note about Boolean attributes syntax

Important: : For “Boolean” attributes, such as autofocus, required, optional, etc., you are able to either write autofocus=“autofocus”, or just use the attribute name “autofocus” without setting a value.

Read these explanations for a complete description of the syntax of Boolean attributes.

Source code for the knowledge check 5.5.4

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head><meta charset="utf-8">
4.  <title>Example for a knowledge check</title>
5.  </head>
6.  <body>
7.  <form>
8.     <label for="studentID">Student ID (disabled field, cannot type in it): </label>
9.     <input type="text" value="S134356" id="studentID" disabled/><p>
10. 
11.    <label for="name">First name: </label>
12.    <input type="text" id="firstName"
13.           placeholder="John"
14.           autofocus
15.    />
16. 
17.    <label for="lastName">Last name: </label>
18.    <input type="text" id="lastName"
19.           placeholder="Smith"
20.           autofocus
21.    />
22. </form>
23. </body>
24. </html>

5.5.5 list

This attribute works together with the new <datalist> element we already studied when we saw the color and date input fields.

This attribute’s value must match the id of a <datalist> element. It is useful for providing local auto-completion to some input fields, or for restricting the possible values on some others like <input type=date> or <input type=color>.

Here is a small code extract from a more complete example shown in the section about the new <datalist> element (see next unit).

Please try it in your  browser (Type “F”, “E”, “O”, C” etc., or just click inside the field and use the drop down menu). Note that you can also enter any value; if it does not start with one of these letters it will be accepted but will not trigger auto-completion.

Preferred browser prompt and submit query.

Source code extract:

Click to expand!
1.  <form>
2.    ...
3.    <input list="browsers" id="mybrowser" />
4.  
5.    <datalist id="browsers">
6.      <option value="Internet Explorer">
7.      <option value="Firefox">
8.      <option value="Chrome">
9.      <option value="Opera">
10.     <option value="Safari">
11.   </datalist>
12.   ...
13. </form>

At lines 3 and 5, the value of the list attribute of the input field must match the one of the id of the <datalist> element.

5.5.6 pattern

The pattern attribute enables the validation of the user’s input on the fly (also at submission time), based on regular expressions. It applies to the text, search, url, tel, email, and password input types.

The pattern attribute follows the syntax of JavaScript regular expressions.

must read: a good catalog of ready-to-go patterns is available at html5pattern.com, an excellent Web site that proposes plenty of JavaScript patterns for the pattern attribute of HTML5 forms. The left hand menu proposes categorized patterns for postal codesdatesphones, etc.

You can also try this online JavaScript RegExps tester, and follow this tutorial about “using JavaScript RegExps” that has step by step exercises and explanations.

Menu on the left, categories and patterns on the right. html5patterns.com.

Typical use:

Just add a pattern attribute with a value that is the JavaScript regular expression that must match the entire string entered in the field. Note that the empty string is valid by default (except if the required attribute is used - this makes empty fields invalid).

Typical use of form validation.

It’s best practice to systematically add a title attribute with a value that indicates what constitutes a valid entry. More on this in the section of this course dedicated to form validation.

1.  <input type="text" name="country_code"
2.         pattern="[A-Za-z]{3}"
3.         title="3 letter country code"
4.  />

Examples

Example #1

Try this online example at JSBin or directly in your browser below:

With the previous example, until the value of the input field is equal to 3 alphabetic characters, the field is invalid.

As seen in the previous examples, we used some CSS pseudo classes for automatically setting the background-color of the input field as we type.

Green valid, pink invalid.  Type email, name myemail.

Complete source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Example of the pattern attribute</title>
6.    <style>
7.      input:invalid {
8.        background-color: lightPink;
9.      }
10.     input:valid {
11.       background-color: lightGreen;
12.     }
13.   </style>
14. </head>
15. <body>
16.   <label for="code">Please enter a 3 letter country code:</label>
17.   <input type="text" name="country_code"
18.     pattern="[A-Za-z]{3}"
19.     title="3 letter country code"
20.     id="code"/>
21. </body>
22. </html>

Example #2: mixing several other attributes with the pattern attribute

Try this example online or in your browser below:

Attributes used: placeholder (for displaying a ghost example value), pattern,  required (empty field = invalid)…

Complete source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head> <meta charset="utf-8">
4.    <title>Example of use of new HTML5 input field attributes</title>
5.    <style>
6.      input:focus:invalid { background-color: lightPink;}
7.      input:valid { background-color:lightGreen; }
8.      input:required {border: 2px solid red; }
9.      input:optional {border: 2px solid green; }
10.   </style>
11. </head>
12. <body>
13.   <p>Attributes used: placeholder (for displaying a ghost example value), pattern, required (empty = invalid)...
14.   <p>
15.   <label for="inputID">Enter a pseudo (6-12 characters): </label>
16.   <input id="inputID" name="Name"
17.     placeholder="Name"
18.     pattern="w{6,12}"
19.     required
20.     title="6-12 characters allowed please"
21.     type="text" />
22. </body>
23. </html>

Example #3: an <input type=“url”> element with a pattern attribute allowing only certain protocols

Online example at JSBin try it in your browser:

Green valid, pink invalid.  Type email, name myemail.

Source code extract:

1.  <input
2.    id="website"
3.    name="url"
4.    type="url"
5.    placeholder="http://www.domain.com"
6.    title="http, https or ftp allowed"
7.    pattern="(http|https|ftp)://[a-zA-Z0-9-./]"
8.  />

5.5.7 min, max and step

These attributes are useful for several input types such as number, range, date and  time (and other variants).

The min and max attributes are used to set ranges to input fields that accept numerical values or a date/time.

Their detailed use with these input fields have already been explained in section 5.4 of this course dedicated to these particular input field types.

Typical use

Click to expand!
1.  <input id="mydate" name="mydate"
2.    type="date"
3.    min="2012-01-01"
4.    max="2013-01-01"
5.    value="2012-01-01"
6.  />
7.   
8.  <input name="time" id="time" type="time"
9.    min="09:00"
10.   max="17:00"
11.   value="12:00"
12. />
13.  
14. <input id="range" name="range" type="range" 
      min="0" 
    max="100" 
    step="5"/>

5.5.8 multiple

The multiple attribute is used with email and file input types. It’s a Boolean attribute, so here are the different syntax possibilities:

With <input type=“email”>

With the <input type=“email”>, this attribute enables the user to enter a set of addresses, separated by a comma instead of a single address. Entering several addresses will keep the input field valid.

Online example at JSBin

Or try it below in your browser: type in a list of email addresses separated by a comma, then look at the input field background color (pink = invalid, green = valid), and then submit:

Enter one or more email addresses.

Complete source code:

Click to expand!
1.   <!DOCTYPE html>
2.   <html lang="en">
3.   <head>
4.     <meta charset="utf-8">
5.     <title>Jsbin</title>
6.     <style>
7.       input:invalid {
8.         background-color: lightPink;
9.       }
10.      input:valid {
11.        background-color: lightGreen;
12.      }
13.      fieldset {
14.        border:1px solid;
15.        padding:20px;
16.      }
17.    </style>
18.  </head>
19. 
20.  <body>
21.    <p>This form uses: <code><input type="email"
         name="myemail" <b>multiple</b>></code></p>
22. 
23.      <form>
24.        <fieldset>
25.          <legend>With the multiple attribute </legend>
26.          <label>Enter several email addresses: </label>
27.          <input type="email" name="myemail" title="you can enter 
               multiple emails addresses, separated by a comma" multiple/>
28.            <button>Submit</button>
29.        </fieldset>
30.      </form>
31.    <p>
32.    <p>This form does not use the multiple attribute:</p>
33. 
34.      <form>
35.        <fieldset>
36.          <legend>Without the multiple attribute </legend>
37.          <label>Enter several email addresses: </label>
38.          <input type="email" name="myemail" title="only one address please!"/>
39.          <button>Submit</button>
40.        </fieldset>
41.      </form>
42.    <p>
43.    Type in a list of email addresses separated by a comma. Look at the input field 
         background color (pink = invalid, green = valid), try to submit. </p>
44.  </body>
45.  </html>

Best practice:  add a title attribute indicating what you expect as a valid entry (lines 25 and 38). If you enter bad values and submit, you will see in the error message the string value of the title attribute.

With <input type=“file”>

With this type of input field, multiple files can be chosen (whereas before HTML5, only a single file could be chosen).

Typical use: <input type=file multiple>

Try these in your browser, look at the small variations (text in the buttons, messages):

Example with <input type=file multiple>

                    Select one or more files: 

Example without the multiple attribute:

                    Select only one file: 

Use the standard key modifiers (shift, control, command) for selecting multiple files when the file chooser dialog popup.

5.5.9 a warning

In the following pages, we present a set of rarely used attributes introduced by HTML5.

You might just glance at them and/or try the examples. The next pages cover their usage and you are welcome to use them for future reference (for those of you who like to cover the topics completely).

5.5.10 formaction and formmethod

These attributes are targeted to the <input type=“submit”> input fields. They are rarely used.

1. <input type="submit"
2. formaction="preview.php" formmethod="get" value="Preview">

When you use an <input type=“submit”> field with the formaction attribute, the action attribute value of the form is overridden. The form will be submitted to the URL / value of the formaction attribute of the  <input type=“submit”> field.

The formmethod attribute does the same with the POST/GET method attribute of the form. If an <input type=“submit”> has a formmethod attribute, it overrides the value of the method attribute of the form.

Typical use

1. <form action="post.php" method="post">
2.   <input type="submit"
3.     formaction="preview.php" formmethod="get"
4.     value="Preview">
5.   <input type="submit" value="Send">
6. </form>

Line 3 overrides the values set in line 1.

Examples

Here are two online examples at JSBin:

The first shows a form with two submit buttons: 

Example of formaction attribute.

The second example shows a form with two submit buttons:

Example of formmethod attribute.

5.5.11 formnovalidate

The formnovalidate attribute is targeted to the <input type=“submit”> input fields. This attribute is rarely used.

This atrribute allows the submission of a form even if it contains  invalid fields. For example:  a form that has an <input type=“email”> field or a field required and which are not filled.

In general, such forms have two submit buttons, one with the formnovalidate attribute set to a non null value and one without.

Typical use (online example at JSBin):

Click to expand!
1.  <form action="form.php">
2.    <fieldset>
3.      <legend>Example of formnovalidate attribute</legend>
4.      <label for="email">E-mail:</label>
5.      <input type="email" name="email" id="email"/><br>
6.      <input type="submit" value="Submit" /><br>
7.      <input type="submit"
8.        formnovalidate
9.        value="Submit without validation" />
10.   </fieldset>
11. </form>
Example of formnovalidate attribute.

5.5.12 formtarget

The formtarget attribute is targeted to the <input type=“submit”> input fields. This attribute is rarely used.

This attribute’s value indicates where the response from the form submission should be displayed.

Typical use

1. <input type="submit"
2.   formtarget="_blank"
3.   value="Submit but show results in a new window/tab">

Possible values for the formtarget attributes are:

Complete example:

Online example at JSBin or try it in your browser below:

Example of formtarget attribute.

Source code:

1.  <form action="defaultAction.php">
2.    <label for="givenName">Given name:</label>
3.    <input type="text" name="givenName" id="givenName"><br>
4.    <label for="familyName">Family name:</label>
5.    <input type="text" name="familyName" id="familyName"><br>
6.    <input type="submit" value="Submit as usual">
7.    <input type="submit"
8.      formtarget="_blank"
9.      value="Submit but show results in a new window/tab">
10. </form>

5.5.13 formenctype

A word about the enctype attribute of the <form> element

The enctype attribute existed before HTML5. It is often used together with forms that contain file input fields. For sending files to a remote server, we use multipart” forms. This special encoding of forms needs to be specified using the enctype attribute, as shown in the example below:

Online example at JSBin:

1.  <!DOCTYPE html>
2.  <html lang="en">
3.    <head>
4.      <meta charset="utf-8">
5.      <title>Jsbin</title>
6.    </head>
7.    <body>
8.      <form action="default.php" method="post" enctype="multipart/form-data">
9.        Given name: <input type="text" name="gname"><br>
10.       Family name: <input type="text" name="fname"><br>
11.       <input type="submit" value="Submit">
12.     </form>
13.   </body>
14. </html>

Note that when you send form content using Ajax, this attribute is not needed, as you will specify the type of data sent to the remote server in JavaScript, using the FormData object.

As an attribute of the <input type=“submit” enctype=…> element

Since HTML5, this attribute can also be used in <input type=“submit”> input fields.

If an <input type=“submit”> field has this attribute, then, when submitted using method=POST, the browser will send the form content encoded with the method specified by the formenctype attribute. And this overrides the value of the enctype attribute specified in the <form enctype=…> element (or its default value, if not present).

Typical use:

<form action="defaultAction.php">
  ...
  <input type="submit" formenctype="multipart/form-data"
           value="Submit as Multipart/form-data">
</form>

The possible values for this field are:

Example

Try this online example at JSBin

Example of use of the formenctype attribute.

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.    <head>
4.      <meta charset="utf-8">
5.      <title>Jsbin</title>
6.    </head>
7.    <body>
8.      <form action="defaultAction.php" method="post"
9.        enctype="application/x-www-form-urlencoded">
10.       <label for="givenName">Given name:</label>
11.       <input type="text" name="givenName" id="givenName"><br>
12.       <label for="familyName">Family name:</label>
13.       <input type="text" name="familyName" id="familyName"><br>
14.       <input type="submit" value="Submit">
15.       <input type="submit"
16.         formenctype="multipart/form-data"
17.         value="Submit as Multipart/form-data">
18.     </form>
19.     <p><b>Note:</b> The formenctype attribute is not supported by all browsers.</p>
20.   </body>
21. </html>

Explanations and how to see the difference between the two kinds of formenctype values

If you run this example in the JSBin standalone mode (click the black arrow on the top right of the output tab, in JSBin), you should see this:

JS Bin in standalone mode.

Then, open the devtools and go to the “Network” tab, click on the POST request. Once done, click on the right on the “Header” tab to see the HTTP headers, and scroll down, you should see the form-data entries in the header, like in this screenshot:

blank blank.

And if you start again and click on the left submit button, the one without the formenctype attribute, you should see that the form content has been submitted “normally” (default value is “urlencoded”, spaces are replaced by “+”, etc.). Here is a screenshot of what you should see:

5.6 HTML5 Forms Elements

Let’s look at the HTML5 elements related to forms (specifically: <datalist>, <output>, <meter>  and <progress> elements).

HTML4 HTML5
- <form> - <datalist>
- <fieldset> - <output>
- <legend> - <meter>
- <textarea> - <progress>
- <label> - <keygen> *
- <select>
- <option>
- <optgroup>
- <input>
- <button>
* Not really useful for most developers.

5.6.2 <output>

The output element represents the result of a computation or user action. You can see it as a “specialized <div> or <span>” for displaying interactive results.

Example of output element use.

Typical use / interactive examples

Do not hesitate to play with the source code of these examples online at JSBin.

Example #1

1. <form oninput="o.value=a.valueb.value">
2.   <input type="number" name="a" id="a" value="2"> x
3.   <input type="number" name="b" id="b" value="3"> =
4.   <output for="a b" name="o">6</output>
5. </form>

The oninput event handler directly uses the <output> element using the value of its name attribute.

Result (do change the input field values):

Explanations about the attributes specific to the <output> element:

Example #2

Top of Form

50 + =

Bottom of Form

Source code:

1. <form >
2.   <input name="a" value="50" type="range"
3.     oninput="x.value = a.valueAsNumber + b.valueAsNumber;
4.     y.value = this.value;"/>
5.   <output id="y">50</output> +
6.   <input name="b" value="50" type="number" /> =
7.   <output name="x" id="x" for="a b"></output>
8. </form>

HTML5 has introduced new input field properties: valueAsNumber and valueAsDate.The last example is similar to the previous one except that we use an addition instead of a multiplication.

As input field values are considered as strings by JavaScript, using x.value = a.value + b.value would result in a string concatenation instead of an addition. That’s why we use the valueAsNumber property.

This is why we used the valueAsNumber property also introduced by HTML5 for some input fields such as <input type=“range”> and <input type=“number”>, we also encountered the valueAsDate properties when we studied <input type=“date”>.

5.6.3 <meter>

The <meter> element displays colored bars to represent numeric values.

It can be useful to display a colored gauge to show disk usage, to highlight the relevance of a query result, or the fraction of a voting population that favours a particular candidate, etc. This element is often used with the <input type=“range”> field as an instant feedback indicator.

Blood pressure meter gauge.

The <meter> element should not be used to indicate progress. You should instead use a <progress> element.

Typical use

Storage space used: <meter value=75 min=0 low=20 high=80 max=100 optimum=50></meter>

The <meter> element uses the easy-to-understand value, min, max,  low, high and optimum attributes.
The optimum attribute, along with min, low, high and max attributes will affect the color of the bar, and of course the constraint min < low <  high < max should be respected.

More explanations about the colors and the meaning of the optimum attribute will come further in this lesson.

Interactive example

Try the next example online at JSBin or just play with it in your browser by dragging the slider below:

<meter value=75 min=0 low=20 high=80 max=100 optimum=19></meter>

Grades:

Source code of the example:

1.  <p>Grades: <meter id="meter2" value="75" min="0" low="20" high="80" max="100"></meter>
2.  
3.  <input min="0" max="100" value="75" id="meter2range"
4.    oninput="effect('meter2', 'meter2range')" type="range">
5.  <output id="meter2val" for="meter2range"></output></p>
6.  <script>
7.    function effect(meter, meterrange) {
8.      var currVal = document.getElementById(meterrange).value;
9.      document.getElementById(meter).value = currVal;
10.     document.getElementById(meter+ "val").innerHTML = currVal;
11.   }
12. </script>

Explanations:

The link between the slider (an <input type=range>) and the meter element is done using an input event handler.

The link between the slider (an <input type=range>) and the meter element is done using an input event handler (oninput=“effect(…)”) at line 4.

The effect JavaScript function will change the current value of the <meter> element (line 9) and update the displayed html content of the <output> element (line 10).

The color of the gauge changes depending on the attribute’s values

The optimum attribute indicates the optimal numeric value and gives an indication where along the range is considered preferable. Just think of the <meter> ranges as follows:

… and depending on the value you set to optimum attribute, one of the ranges above becomes the “good (optimum)” range.

In the previous example, with the value of the optimum attribute set to 19, a number between min and low (not inclusive), the Range 1 (between min=0 and low=20) becomes the “good (optimum)” range (displayed in green), the Range 3 (between high=80 and max=100) becomes the “bad” (displayed in red color) range, and the Range 2, in the middle, will be displayed in yellow (not optimum, not bad).

A <meter> element used for displaying blood pressure might be a good candidate for setting the optimum value to “Range 2”, and a <meter> element used for displaying memory usage might be a good candidate for setting the optimum value to “Range 1”, meaning that a low memory usage is “good”.

External resources

5.6.4 <progress>

The <progress> element is similar to <meter> but it is used for progress bars (i.e., the percentage of a file being uploaded, etc.):

<progress id=pr value=50 min=0 max=100>
Picture of the funny progress meme saying Every mistake you make is progress.

Gives:

The browser calculates the percentage corresponding to the value, min and  max attributes and adjusts the length of the progress bar accordingly.

If no value attribute is set, the progress bar will display an “indeterminate look”, that may slightly vary among different browser implementations.

Indetermined progress bar screenshot.

Typical use:

Here is an online example at JSBin, or try it below in your browser:

This example uses some JavaScript to simulate a download progress by changing in real time the value attribute.

The progress below is defined like this:

<progress id=pr value=100 max=1000>

Download progress:

Source code:

1.  Download progress: <progress id=pr value=100 min=0 max=1000></progress>
2.  <script>
3.     var i=0;
4.     setInterval(function () {
5.         i = (i+1) %1000;
6.         document.getElementById(pr).value = i;
7.     },1);
8.  </script>

5.6.5 <datalist>

The <datalist> form element is useful for linking a list of choices to an input element.

Input field choices; web browsers. Another example input field choices with lookahead.

We have already seen this element in action with different <input>  elements, such as <input type=“color”>, <input type=“date”>,  or <input type=“range”>.

Restricted choice of color. Restrict choices using a datalist element. Example of datalist for sliders ticks.

It is often “linked” to input fields either for restricting the value set that can be proposed  (i.e., restricted set of colors or possible dates, or for displaying slider ticks, as shown above), but it may also be used in a more general way, for providing client-side auto-completion without the need to use JavaScript.

It works with the new list attribute of input fields introduced by HTML5. The id of the <datalist> must match the value of the list attribute in the input field. A datalist can be shared by several input fields. It suffices that their list attribute matches the id of the datalist element.

The input field is related to the datalist that will propose auto-completion based on <datalist> values.

Typical use for auto-completion

Here is an online example at JSBin, or try it here in your browser (type the name of your favorite browser):

What is your favorite browser submit query.

Source code of this example:

Click to expand!
1.  <form action="demo_form.asp" method="get">
2.    <input list="browsers" name="browser" />
3.  
4.      <datalist id="browsers">
5.        <option value="Internet Explorer">
6.        <option value="Firefox">
7.        <option value="Chrome">
8.        <option value="Opera">
9.        <option value="Safari">
10.     </datalist>
11.   <input type="submit" />
12. </form>

As you can see at lines 2 and 4, the id and list attributes match. The <datalist> element is wrapped around a set of <option> that are available for selection by another form control (in this example the input field from line 2).

5.7 Form Validation API

In this section of the course, we will look at CSS pseudo classes that are useful for giving instant feedback when the user’s input is not valid. We will also look at the new JavaScript API introduced by HTML5 for validating forms and form elements.

In the following pages, we will first illustrate the concept of form validation with the <input type=“email”/> field. It can be generalized to all kind of input types, such as url, number, etc. Some form attributes, such as pattern,  will also affect input field validity!

Form validation is supported by all modern browsers.

5.7.2 Automate Visual Feedback While Typing

Most modern browsers propose default behavior for validating input fields and forms.

The built-in validation system that comes with HTML5 automatically adds a CSS pseudo class to all input fields. Invalid fields (i.e. a badly worded email address in an <input type=“email”> input field), will inherit the :invalid pseudo class, valid fields will inherit the :valid pseudo class.

A first step to improve your HTML form is to add some CSS rules to your input fields. This adds visual feedback to the validity of input fields values - while the user is typing - such as changing the color of the border of input fields, or green/red icons on the right of the field, as shown in the small picture at the top right of this page.

Also, at the time of submitting the form, some extra messages may be displayed as pop up text bubbles.

Bubble message example.

The default bubble message and visual feedback differ from one implementation to another, but they may be customized, with some limitations that will be explained later.

For example, browsers may provides default feedback on the input field’s border (red = invalid, green = ok). This default behavior can be overridden by CSS rules as illustrated in the section about new input type attributes.

Examples

Example #1: styling “required”, “valid” and” invalid” fields using CSS3 pseudo-classes

Here is an online example at JSBin, or try it below in your browser:

Custom error messages on form.

Source code extract:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.    <head>
4.      <meta charset="utf-8">
5.      <title>CSS3 pseudo-classes for form validation visual feedback</title>
6.      <style>
7.   
8.        input:invalid { background-color: lightPink;}
9.        input:valid { background-color:lightGreen; }
10.       input:required {border: 2px solid red;}
11.       input:optional {border: 2px solid green;}
12.       fieldset {
13.         border:1px solid;
14.         padding:20px;
15.         }
16.       .formLabel { display: inline-block; width: 140px; text-align: right; }
17.     </style>
18.   </head>
19.   <body>
20.  
21.     <form>
22.       <fieldset>
23.         <legend>Type invalid values and see the result</legend>
            <label for="myEmail" class="formLabel">E-mail:</label> 
              <input type="email" id="myEmail" required/><br>
            <label for="myURL" class="formLabel">
            Homepage (URL):</label> <input type="url" id="myURL" 
            required/><br> <label for="myPhone" class="formLabel"> Phone 
            number:</label> <input type="tel" id="myPhone" 
            pattern="[0-9]{3}-?[0-9]{3}-?[0-9]{4}" placeholder="e.g.
            416-555-1234" required/><br>
24.         <button>Submit form</button><br />
25.       </fieldset>
26.     </form>
27.     <p>
28.  
29.   </body>
30. </html>

Try the online example with different Web browsers, both with and without the CSS rules. See the differences between FireFox/Chrome/Opera in the default visual feedback behavior. Don’t worry: all default behavior can be overridden if you provide your own CSS rules.

Best practice:  We recommend that you ALWAYS provide default CSS rules that give visual feedback to the user’s input.

Example #2: add CSS transitions + an icon/marker to the right of the input fields

Try this online example at JSBin or try it here in your browser. This example adds a small icon that changes depending on the validity of the input field:

Custom error messages on form + css.

Source code extract:

Click to expand!
1.   .myForm input:focus {
2.      padding-right:70px;
3.   }
4.   .myForm input {
5.      transition: padding .25s;
6.   }
7.  
8.   .myForm input:required:valid {
9.      background:url(https://i.imgur.com/BJolppS.png) no-repeat right top;
10.  }
11.   .myForm input:required {
12.     background:url(https://i.imgur.com/7pIN7wz.png) no-repeat right top;
13.  }

This time, we just added an attribute class=“myForm” to our form, in order to avoid interfering with the other examples on this page, and we tweaked the CSS rules a little.

The rule at line 1 says that any time we click on an input field, it will enlarge itself to the right, while the rule at line 4 will make it animated.

The rules at lines 8 and 11 target the input fields with a required attribute. They will change the background by displaying a small green or red icon, corresponding to the valid/invalid status of the input field.

Use the title attribute for specifying a custom message

You can simply use the input’s title attribute to provide a message for pattern-mismatches, and more generally for all validation errors. This solution is really neat and doesn’t require JavaScript!

Try the online example at JSBin, or try it here in your browser (type invalid values and look at the custom messages):

Top of Form

Type invalid values and see the result, this time with custom messages! E-mail:  Homepage (URL):  Phone number:  Submit form

Bottom of Form

Extract from source code:

Click to expand!
1.  <form class="myForm">
2.    <fieldset>
3.      <legend>Type invalid values and see the result</legend>
4.      <label for="myEmail" class="formLabel">E-mail:</label>
5.      <input type="email" id="myEmail"
6.        title="You don't know what an email address looks like, do you?"
7.        required/><br>
8.      ...
9.      <button>Submit form</button><br />
10.   </fieldset>
11. </form>

Beware that browser implementations may differ. Chrome, Opera will display the title attribute value in error message bubbles when the form is submitted, while Safari and FireFox (desktop and mobile) will simply ignore it.

You must also take care of the different languages, otherwise you will get error message bubbles that show some parts in the local language, and the message from the title attribute “as is”.

Google Chrome on a French desktop computer: Chrome shows native error message localized and the title attribute content as is.

Same example on FireFox, the title attribute is ignored:

Same example on Firefox, the title attribute is ignored.

The built-in validation system is an improvement on what existed before HTML5 (i.e., nothing), but additional work is required if you want fully localized, hand-made validation feedback.

We will show solutions in the last section of this week’s course.

5.7.3 JavaScript Form Validation API

There is a JavaScript API for form validation. This API will let you use your own validation algorithm (i.e. check that you have entered the same password in two different input fields), and customize error messages. Also, together with some HTML/CSS/JavaScript you will be able to make your own message bubbles.

JavaScript form validation API.

Typical use

Be careful when you try this example in JS Bin standalone mode (click the small black arrow on the top right of the output tab).

Or, you may try it here in your browser.

Example use of validation API.

Extract from source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Example of using the validation API</title>
6.    <style>
7.      .myForm input:invalid { background-color: lightPink;}
8.      .myForm input:valid { background-color:lightGreen; }
9.      .myForm input:required {border: 2px solid red;}
10.     .myForm input:optional {border: 2px solid green;}
11.     .myForm label { display: inline-block; width: 140px; text-align: right; }
12.   </style>
13. </head>
14. <body>
15.   <form class="myForm">
16.     <fieldset>
17.       <legend>Example use of the validation API</legend>
18.       <label for="password1" >Password:</label> <input type="password" 
            id="password1" oninput="checkPasswords()" required>
19.       <p>
20.       <label for="password2">
            Repeat password:</label> <input type="password" id="password2" 
            oninput="checkPasswords()" required>
21.       <p>
22.       <button>Submit</button>
23.     </fieldset>
24.   </form>
25.  
26.   <script>
27.     function checkPasswords() {
28.       var password1 = document.getElementById('password1');
29.       var password2 = document.getElementById('password2');
30.       if (password1.value != password2.value) {
31.         password2.setCustomValidity('Passwords non identiques');
32.       } else {
33.         password2.setCustomValidity('');
34.       }
35.     }  
36.   </script>
37. </body>
38. </html>

Explanations:

The validity API proposes a setCustomValidity() method available on input DOM objects. This method allows you to customize error messages. It takes a string parameter. When this string is empty, the element is considered valid, when the string is not empty, the field is invalid and the validation error message displayed in the bubble will be equal to that string.

At lines 18 and 20 we added an input event listener: each time a key is typed, the checkPasswords() function is called.

Lines 28 and 29 get the input fields’ values, and lines 30-35 check if the passwords are the same and set the validity of the field using the validation API’s method setCustomValidity(error_message).

5.7.4 The validity property of input fields

The validity property of input fields helps to get error details when the field is invalid. This property tests the different types of validation error.

Here is how to get the validity property of an input field:

1. var input = document.getElementById('IdOfField');
2.  
3. var validityState_object = <b>input.validity;</b>

The possible values for the validity property are:

Here is an example at JSBin that shows how to test the different types of validation errors, or you may try it here in your browser (enter bad values, too big, too small, enter invalid characters, etc.):

JSBin example which shows how to test the different types of validation errors.

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  ...
4.  <body>
5.  
6.    <script>
7.      function validate() {
8.        var input = document.getElementById('b');
9.         var validityState_object = input.validity;
10.  
11.       if(validityState_object.valueMissing) {
12.         input.setCustomValidity('Please set an age (required)');
13.       } else if (validityState_object.rangeUnderflow) {
14.         input.setCustomValidity('Your value is too low');
15.       } else if (validityState_object.rangeOverflow) {
16.         input.setCustomValidity('Your value is too high');
17.       } else if (validityState_object.typeMismatch) {
18.         input.setCustomValidity('Type mismatch');
19.       } else if (validityState_object.tooLong) {
20.         input.setCustomValidity('Too long');
21.       } else if (validityState_object.stepMismatch) {
22.         input.setCustomValidity('stepMismatch');
23.       } else if (validityState_object.patternMismatch) {
24.         input.setCustomValidity('patternMismatch');
25.       } else {
26.         input.setCustomValidity('');
27.       }
28.     }  
29.   </script>
30.   <form class="myForm">
31.     <label for="b">Enter a value between 10 and 20: </label>
32.     <input type="number" name="text" id="b" min="10" 
          max="20"
          required oninput='validate();'/>
33.     <button>Submit</button>
34.   </form>
35. 
36. </body>
37. </html>

The validationMessage property

It is also possible to get the validation error message, using the validationMessage property of input fields.

1. var input = document.getElementById(b);
2.   
3. console.log("Validation message = " + input.validationMessage);

This is useful for making custom error messages. More about this topic in the next section of the course.

5.7.5 Changing the default behavior

Custom validation: changing the default behavior, aggregating error messages, removing bubbles, etc.

Custom validation.

Criticism of the default behavior of HTML5 built-in validation

The techniques we have seen so far for enhancing HTML forms are powerful and provide interesting features, but are also criticized by Web developers:

However, the validation API gives enough power to make your own validation behavior, overriding the default when necessary.

Here is an adaptation of work presented at the developer.telerik.com Web site.  This link is really worth reading, as it presents different approaches and gives external references for those who would like to go further.

Example that shows aggregation of error messages + overriding default behavior

Try the online example at JSBin, or try it here in your browser: enter invalid values and submit with one or two invalid fields.

Prompt for name and e-mail address fields.

Complete source code:

Click to expand!
1.   <!DOCTYPE html>
2.   <html lang="en">
3.   <head>
4.     <meta charset="utf-8">
5.     <title>Aggregating error messages</title>
6.     <style>
7.       input:invalid { background-color: lightPink;}
8.       input:valid { background-color:lightGreen; }
9.       input:required {border: 2px solid red;}
10.      input:optional {border: 2px solid green;}
11.  
12.      .error-messages {
13.        display: none;
14.        margin: 0 10px 15px 10px;
15.        padding: 8px 35px 8px 30px;
16.        color: #B94A48;
17.        background-color: #F2DEDE;
18.        border: 2px solid #EED3D7;
19.        border-radius: 4px;
20.      }
21.      fieldset {
22.        border:1px solid;
23.        padding:20px;
24.      }
25.    </style>
26.  </head>
27.  <body>
28.  <form>
29.    <fieldset>
30.      <legend>Submit with one or two invalid fields</legend>
31. 
32.      <ul class="error-messages"></ul>
33. 
34.      <label for="name">Name:</label>
35.      <input id="name" name="name" required>
36.      <p>
37.      <label for="email">Email:</label>
38.      <input id="email" name="email" type="email" required>
39.      <p>
40.      <button>Submit</button>
41.    </fieldset>
42.  </form>
43.  
44.  <script>
45.    function replaceValidationUI(form) {
46.      // Suppress the default bubbles
47.      form.addEventListener("invalid", function (event) {
48.        event.preventDefault();
49.      }, true);
50.  
51.      // Support Safari, iOS Safari, and the Android browser -- each of which
52.      // do not prevent form submissions by default
53.      form.addEventListener("submit", function (event) {
54.        if (!this.checkValidity()) {
55.          event.preventDefault();
56.        }
57.      });
58.  
59.      // Container that holds error messages. By default it has a CSS
60.      // display:none property
61.      var errorMessages = form.querySelector(".error-messages");
62.  
63.      var submitButton = form.querySelector("button:not([type=button]),
64.        input[type=submit]");
65.  
66.      submitButton.addEventListener("click", function (event) {
67.        var invalidFields = form.querySelectorAll("input:invalid");
68.        var listHtml = "";
69.        var errorMessagesContainer = form.querySelector(".error-messages");
70.        var label;
71.  
72.        // Get the labels' values of their name attributes + the validation error
73.        // message of the corresponding input field using the validationMessage
74.        // property of input fields
75.        // We build a list of <li>...</li> that we add to the error message container
76.        for (var i = 0; i < invalidFields.length; i++) {
77.          label = form.querySelector("label[for=" + invalidFields[ i ].id + "]");
78.          listHtml += "<li>" +
79.          label.innerHTML +
80.          " " +
81.          invalidFields[ i ].validationMessage +
82.          "</li>";
83.        }
84.  
85.        // Update the list with the new error messages
86.        errorMessagesContainer.innerHTML = listHtml;
87.  
88.        // If there are errors, give focus to the first invalid field and show
89.        // the error messages container by setting its CSS property display=block
90.        if (invalidFields.length > 0) {
91.          invalidFields[ 0 ].focus();
92.          errorMessagesContainer.style.display = "block";
93.        }
94.      });
95.    }
96.  
97.    // Replace the validation UI for all forms
98.    var forms = document.querySelectorAll("form");
99. 
100.   for (var i = 0; i < forms.length; i++) {
101.     replaceValidationUI(forms[ i ]);
102.   }
103. </script>
104. </body>
105. </html>

Explanations:

6.1 Introduction Basic APIs – Module 6

Final week!

Congratulations to all of you. Here, it’s summer and it’s hot, it’s time to get holidays, but before that we need to finish the course. I’m talking from the very nice park of the University of Nice in the south of France where I work. And I want you to be as happy as I am today to teach you this final week. We will look this time at the HTML5 persistence, notably at the HTML5 cache that is useful for creating Web sites and Web applications that work offline. We will also study the web storage API that is useful for saving and restoring data on the client side directly in browser.

It can be useful for setting some preferences for Web application, or for remembering what you typed in a form, for example. We will also look at another form of persistence using the file API. This JavaScript API is quite simple and can be used together with file choosers.

For example, you choose some pictures, some images you want to send to a server and using this API you can visualize them directly in the page before sending them to the server.

Finally, we will look at the geolocation API that is useful for locating the person that is using the browser.

You can provide a customized service that take into account the geolocation, or you can also display the position of the person in a map, we will see example that work with Google Map and with OpenStreetMap. Another classic use is automatic filling of the address input fields in a form.

Instead of typing the zip code, entering the city, entering the country, you can guess that automatically and propose the form that is already filled with the address. I really enjoyed teaching you this HTML5 basic course and I’m looking forward to meeting you the next HTML5 part-2 course that would address more advanced topics

6.1.2 HTML5 JavaScript APIs

We have already studied some of the HTML5 JavaScript APIs, including:

However, HTML5 also comes with several APIs that are not directly related to HTML elements, namely: the Orientation API; the Geolocation API; most APIs related to client-side persistence; the Web Storage API; the Web Workers API; and some other APIs that are not in the HTML5 specification, but are related to it, such as the GamePad API, the Web Audio API, etc.
This week, we will look at some of the most useful APIs. Others will be covered in the W3C HTML5 Apps and Games course:

You are here sign.

6.2 The Web storage API

The Web storage API introduces “two related mechanisms, similar to HTTP session cookies, for storing structured data on the client side”.

Indeed, Web Storage provides two interfaces - sessionStorage and localStorage - whose main difference is data longevity. This specification defines an API for persistent data storage of key-value pair data in Web clients.

With localStorage the data will remain until it is deleted, whereas with sessionStorage the data is erased when the tab/browser is closed.

For convenience, we will mainly illustrate the localStorage object. Just change “local” to “session” and it should work (this time with a session lifetime).

Key value pairs.

Simple key-value stores, one per domain (following the same origin policy)!

localStorage is a simple key-value store, in which the keys and values are strings. There is only one store per domain. This functionality is exposed through the globally available localStorage object. The same applies to sessionStorage.

Example:

Click to expand!
1.  // using localStorage
2.  
3.  // store data
4.  localStorage.lastName = "Bunny";
5.  localStorage.firstName = "Bugs";
6.  localStorage.location = "Earth";
7.  
8.  // retrieve data
9.  var lastName = localStorage.lastName;
10. var firstName = localStorage.firstName;
11. var location = localStorage.location;

This data is located in a store attached to the origin of the page. We created a JSBin example in which we included the above code.

Once opened in your browser, the JavaScript code is executed. With the browser dev. tools, we can check what has been stored in the localStorage for this domain:

Example of read/write localStorage.

Here is a view of the devtools. In more recent versions of Google Chrome, the “Resources” tab is named “Applications”:

View of the devtools. Used to show what is in local Storage.

Differences with cookies?

Cookies are also a popular way to store key-value pairs. Web Storage, however, is a more powerful technique than cookies. The main difference is in size limits: cookies are limited to a few KBytes whereas Web Storage may extend to several MBytes. Also cookies generate additional HTTP request traffic (whether to request a Web page, an image, a stylesheet, a JavaScript file, etc.).

Objects managed by Web Storage are no longer carried on the network and HTTP, and are easily accessible (read, change and delete) from JavaScript, using the Web Storage API.

External resources

6.2.2 Example 1

You can start filling this form and come back another day and complete it. It doesn’t matter if you closed your browser before coming back. The form never loses what you entered, even if you reload the page, or press “backspace” by mistake. This form auto saves/restores its content.

In this example, we use the most simple way to use localStorage:

Saving the form content on the fly

Open this online example at JSBin, and use F12 or cmd-alt-i (Mac OS) to look at the dev. tools. As you type in the different input fields, their content is updated in the localStorage.
We just added input event listeners to each input field. For example, in order to save the first name input field’s content, we just added:

oninput="localStorage.<font color="red">firstName</font>=this.value;"

Where firstName in red is the key and this.value the current value of the input field.

Example of Localstorage and its HTML code.

In the same way, we added an input listener to all the input fields in this example’s form.

Restoring the form content on page load/reload

This time, we want the form content to be restored on page load/reload. We will add a restoreFormContent() function in the JavaScript code that will be called each time the page is loaded. In this function, we will read the saved data and set the input fields’ values.

Complete example on JSBin: enter data and press reload at any time. The form content is restored!

Source code extract (only addition to the previous example):

Click to expand!
1.  // Called when the page is loaded
2.  window.onload = restoreFormContent;
3.   
4.  function restoreFormContent() {
5.     console.log("restoring form content from localStorage");
6.  
7.     if(localStorage.firstName !== undefined)
8.       document.getElementById("firstName").value = localStorage.firstName;
9.  
10.    if(localStorage.lastName !== undefined)
11.      document.getElementById("lastName").value = localStorage.lastName;
12. 
13.    if(localStorage.email !== undefined)
14.      document.getElementById("email").value = localStorage.email;
15.  
16.    if(localStorage.age !== undefined)
17.      document.getElementById("age").value = localStorage.age;
18. 
19.    if(localStorage.date !== undefined)
20.      document.getElementById("date").value = localStorage.date;
21. }

The tests at lines 7, 10, 13, etc., verify that data has been saved, before trying to restore it. Without these tests, it would put the “undefined” string as the value of input fields with no corresponding data to restore.

6.2.3 localStorage and sessionStorage

This time we will look at another example that uses new methods from the API:

Getting/setting values using the getItem(key) and setItem(key, value) methods

If you want to keep a simple counter of the number of times a given user has loaded your application, you can use the following code (just to show how to use setItem/removeItem methods):

1. var counter = localStorage.getItem("count") || 0;
2. counter++;
3. localStorage.setItem("count", counter);

As you can easily guess from the above, we use var value = getItem(key) to retrieve a key’s value and setItem(key, value) to set it. This is similar to what we saw in the examples of the page above, except that this time:

1.  var inputField = document.getElementById("firstName");
2.  saveInputFieldValue(inputField);
3.  ... 
4.  function saveInputFieldValue(field) {
5.    localStorage.setItem(field.id, field.value);
6.  }

Deleting a key with removeItem(key), or all keys with clear()

Deleting a key can be performed through removeItem(). And if you wish to reset the entire store, simply call localStorage.clear().

Note that it will probably only be the rare occasion that you will want the entire store to be cleared by the user in production software (since that effectively deletes their entire data). However, it is a rather a common operation needed during development, since bugs may store faulty data the persistence of which can break your application, since the way you store data may evolve over time, or simply because you also need to test the experience of the user when first using the application.

One way of handling this is to add a user interface button that calls clear() when clicked, but you must then remember to remove it when you ship! The recommended approach  to use (whenever possible) is to simply open the dev. tool’s console and type localStorage.clear() there — it’s safer and works just as well.

Iterating local stores

Local stores (localStorage or sessionStorage) can also be iterated through in order to list all the content that they contain. The order is not guaranteed, but this may be useful at times (if only for debugging purposes!). The following code lists everything in the current store:

1. for (var i = 0, n = localStorage.length; i < n; i++) {
2.   var k = localStorage.key(i);
3.   console.log(k + ": " + localStorage[k]); // get the ith value, the one with a key that is in the variable k.
4. }

Students may note that something seems a bit off in the example above: instead of calling localStorage.getItem(k), we simply access localStorage[k]. Why? Because keys in the local store can also be accessed as if the store were a simple JavaScript object.

Instead of localStorage.getItem(“foo”) and localStorage.setItem(“foo”, “bar”), one can write localStorage.foo and localStorage.foo = “bar”. Of course there are limitations to this mapping: any string can serve as a key, so that localStorage.getItem(“one two three”) works, whereas that string would not be a valid identifier after the dot (but it could still work as localStorage[“one two three”]).

Example with buttons that show how to iterate on localStorage, clear it etc.

Example that shows all the methods of the local storage API in action

Online example at JSBin, run it, then click on the first button to show all key/values in the localStorage. Open the URL in another tab, and see that the data is shared between tabs, as local stores are attached to an origin.

Then click on the second button to add data to the store, click on the third to remove data. Finally, the last one clears the whole data store.

Click to expand!

Source code:

1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.  <meta charset=utf-8 />
5.  <title>Example of localStorare API use</title>
6.    <script>
7.      // Using localStorage
8.      var counter = localStorage.getItem("count") || 0;
9.      counter++;
10.     localStorage.setItem("count", counter);
11. 
12.     function getCountValue() {
13.       // retrieve data
14.       document.querySelector("#counter").innerHTML = localStorage.count;
15.     }
16. 
17.     function seeAllKeyValuePairsStored() {
18.       // clear list first
19.       document.querySelector('#list').innerHTML="";
20. 
21.       for (var i = 0, n = localStorage.length; i < n; i++) {
22.          var key = localStorage.key(i);
23.          var value = localStorage[key];
24.          console.log(key + ": " + value);
25. 
26.          var li = document.createElement('li');
27.          li.innerHTML = key + ": " + value;
28.          document.querySelector('#list').insertBefore(li, null);
29.       }
30.     }
31. 
32.     function resetStore() {
33.       // erase all key values from store
34.         localStorage.clear();
35.       // reset displayed list too
36.         document.querySelector('#list').innerHTML="";
37.     }
38. 
39.     function addSomeData() {
40.       // store data
41.       localStorage.lastName = "Buffa";
42.       localStorage.firstName = "Michel";
43.       // refresh display
44.       seeAllKeyValuePairsStored();
45.     }
46. 
47.     function removeSomeData() {
48.       // store data
49.       localStorage.removeItem("lastName");
50.       localStorage.removeItem("firstName");
51.       // refresh display
52.       seeAllKeyValuePairsStored();
53.     }
54.   </script>
55. </head>
56. <body onload="getCountValue()">
57.    <h1>Number of times this page has been seen on this browser: <span id="counter"></span></h1>
58. 
59.    <button onclick="seeAllKeyValuePairsStored()">Show all key value pairs stored in localStorage</button><br/>
60.    <output id="list"></output>
61.  
62.    <button onclick="addSomeData()">Add some data to the store</button><br/>
63.    <button onclick="removeSomeData()">Remove some data</button><br/>
64.    <button onclick="resetStore()">reset store (erase all key/value pairs)</button>
65. </body>
66. </html>

You can check in the Chrome dev. tools user interface that the content of the localStorage changes as you click on the buttons.

6.2.4 Example 2

Local stores are also useful for saving/restoring user preferences of Web Applications. For example, the JS Bin tool you have been using since the beginning of this course uses localStorage to store the list of tabs you open, and their width:

Example of preferences.

This way, the next time you come back to JSBin, “it will remember your last settings”.

Another example is a guitar FX processor / amp simulator your instructor is writing with some of his students. It uses localStorage to save/restore presets values:

Guitar fx processor uses localStorage.

Save/restore preferences

Change color, size and speed of animated rectangle with GUI.

Original example on JSBin:

We can change the color, size and speed of the animated rectangle. However, each time we come back to the page, default values are restored.

We would like to save the current values and find them back as they were when we come back to the page.

Here is a modified example that saves/restores its state, you can try it at JSBin. In this modified version of the animated rectangle example, you can set the color, size, speed, etc. And if you reload the page, the state of the different input field is restored, but also the internal variables. Check the source code in the JS Bin example and read the following explanations.

We used the same generic code for saving/restoring input fields’ values we saw in the first example that used localStorage. The only difference is that we renamed the two generic functions so that they correspond better to their role here (instead of saveFormContent we called the function restorePreferences).

The function initPreferences is executed when the page is loaded.

Source code extract:

Click to expand!
1. function initPreferences() {
2.   console.log("Adding input listener to all input fields");
3.   // add an input listener to all input fields
4.   var listOfInputsInForm = document.querySelectorAll("input");
5.   for(var i= 0; i < listOfInputsInForm.length; i++) {
6.     addInputListener(listOfInputsInForm[i]);
7.   }
8.     // restore preferences
9.     restorePreferences();
10.     applyGUIvalues(); // Use the input fields' values we just restored to set internal 
11.                       // size, incX, color, lineWidth variables
12. }
13. 
14. function addInputListener(inputField) {
15.   // same as before
16. }
17. 
18. function restorePreferences() {
19.   // same as old restoreFormContent
20. }
21. 
22. function applyGUIvalues() {
23.   // Check restored input field content to set the size of the rectangle
24.   var sizeWidget = document.getElementById("size");
25.   size = Math.sign(incX)*parseInt(sizeWidget.value);
26.   // also update the outline element's value
27.   document.getElementById("sizeValue").innerHTML = size;
28.   // Check restored input field content to set the color of the rectangle
29.   var colorWidget = document.getElementById("color");
30.   ctx.fillStyle = colorWidget.value;
31.   // Check restored input field content to set the speed of the rectangle
32.   var speedWidget = document.getElementById("speed");
33.   incX = Math.sign(incX)*parseInt(speedWidget.value);
34.   // also update the outline element's value
35.   document.getElementById("speedValue").innerHTML = Math.abs(incX);
36.   // Check restored input field content to set the lineWidth of the rectangle
37.   var lineWidthWidget = document.getElementById("lineWidth");
38.   ctx.lineWidth = parseInt(lineWidthWidget.value);
39. }

6.2.5 Example 3

Online example at JSBin

This time, using the setItem and getItem method we saw earlier in the course, we could write some generic functions for saving/restoring input fields’ content, without having advance knowledge about the number of fields in the form, their types, their ids, etc.

Furthermore, we removed all input listeners in the HTML, making it cleaner (no more oninput=“localStorage.firstName = this.value;’…)

Define listeners + restore old values after the page is loaded, use generic functions

We start writing an init() function that is called when the page is loaded. This function will:

  1. Define input listeners for all input fields
  2. Restore the last saved value for each input field, if present.

Source code:

1. // Called when the page is loaded
2. window.onload = init;
3.
4. function init() {
5.    console.log("Adding input listener to all input fields");
6.    // add an input listener to all input fields
7.    var listOfInputsInForm = document.querySelectorAll("input");
8.    for(var i= 0; i < listOfInputsInForm.length; i++) {
9.       addInputListener(listOfInputsInForm[i]);
10.    }
11.    // restore form content with previously saved values
12.    restoreFormContent();
13. }

And here is the addInputListener(inputField) function. It takes an input field as parameter and attaches an oninput listener to it, that will save the field’s content each time a value is entered. The key will be the id of the input field (line 3):

1. function addInputListener(inputField) {
2.   inputField.addEventListener('input', function(event) {
3.     localStorage.setItem(inputField.id, inputField.value);
4.   }, false);
5. }

Note that at line 2, we use addEventListener (that is not using the oninput property here). adddEventListener doesnot replace existing oninput definitions and keep all existing listeners unchanged.

Restore all input fields’ content using a generic function

We have seen how to save all input fields’ content on the fly. Now, let’s see how we can restore saved values and update the form. This is done using the function restoreFormContent():

Click to expand!
1. function restoreFormContent() {
2.    console.log("restoring form content from localStorage");
3.    // get the list of all input elements in the form
4.    var listOfInputsInForm = document.querySelectorAll("input");
5.    // For each input element,
6.    // - get its id (that is also the key for it's saved content
7.    // in the localStorage)
8.    // - get the value associated with the id/key in the local
3.    // storage
10.   // - If the value is not undefined, restore the value
11.   // of the input field
12.   for(var i= 0; i < listOfInputsInForm.length; i++) {
13.     var fieldToRestore = listOfInputsInForm[i];
14.     var id = fieldToRestore.id;
15.     var savedValue = localStorage.getItem(id);
16.     if(savedValue !== undefined) {
17.        fieldToRestore.value = savedValue;
18.     }
19.   }
20. }

In this function, we first get the list of input fields (line 5), then iterate on it (line 14). For each input field, we get its id, which value is the key in localStorage for the previous data saved for this field (lines 15-16). Then if the value is not undefined, we restore it by setting the value of the input field (lines 19-20).

These generic functions can be used in many different projects

Indeed, if you look carefully, you will see that these functions are really useful. You may easily embed them in your own projects, or perhaps adapt them for a particular need (i.e. for saving input type=“checkboxes” that work a bit differently), etc.

6.2.6 Size limitations, etc.

Few things to remember, from the Web storage specification:

In many cases, local storage is all that your application will need for saving/loading data on demand. More complex ways to do it exist, such as IndexedDB, a No SQL database, that proposes transactions and usually comes with far more available space than local storage. IndexedDB usage is for advanced users and will be covered in the W3Cx HTML5 Apps and Games.

Additionally, there will be a limit on the amount of data that you can store there. Browsers enforce quotas that will prevent you from cluttering your users’ drives excessively. These quotas can vary from platform to platform, but are usually reasonably generous for simple cases (around 5MB), so if you are careful not to store anything huge there, you should be fine.

Finally, keep in mind that this storage is not necessarily permanent. Browsers are inconsistent in how they allow for it to be wiped, but in several cases it gets deleted with cookies — which is logical when you think of how it can be used for tracking in a similar fashion.

For serious applications, you might want to synchronize existing data with the server on a regular basis, in order to avoid data loss (and in general, because users enjoy using the same service from multiple devices at once). This is a rather complex feat, and frameworks such as Firebase can help. Such techniques are beyond the scope of this course and will not be covered.

sessionStorage key/values instead of cookies?

Note that if all you need is to store session-based data in a manner that is more powerful than cookies, you can use the sessionStorage object which works in exactly the same way as localStorage, but the lifetime is limited to a single browser session (lifetime of your tab/window).

Also note that in addition to being more convenient and capable of storing more data than cookies, it has the advantage of being scoped to a given browser tab (or similar execution context).

Cookies’ security drawback: if a user has two tabs open to the same site, they will share the same cookies. Which is to say that if you are storing information about a given operation using cookies in one tab, that information will leak to the other side — this can be confusing if the user is performing different tasks in each.

By using sessionStorage, the data you store will be scoped and therefore not leak across tabs!

6.2.7 Storing more than strings? Use JSON!

Storing strings is all well and good, but it quickly becomes limiting: you may want to store more complex data with at least a modicum of structure.

There are some simple approaches, such as creating your own minimal record format (e.g. a string with fields separated with a given character, using join() on store and split() upon retrieval) or using multiple keys (e.g. post_17_title, post_17_content, post_17_author, etc.). But these are really hacks. Thankfully, there’s a better way,  JSON.stringify() and JSON.parse() methods.

JSON provides a great way of encoding and decoding data that is a really good match for JavaScript. You have to be careful not to use circular data structures or non-serializable objects, but in the vast majority of cases, plugging JSON support into your local store is straightforward.

Typical usage

locaStorage.key = JSON.stringify(object); // or...
localStorage.setItem(key, JSON.stringify(object));

Let’s try a simple toy example (online at JSBin).  The example below saves a JavaScript object in JSON, then restores it and checks that the object properties are still there!

JSON save/load in local storage.

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset=utf-8 />
5.    <title>Storing JSON Objects with Local Storage</title>
6.    <script>
7.      var personObject= {'givenName': 'Michel', 'familyName': 'Buffa'};
8.      // Store the object as a JSON String
9.      localStorage.setItem('testObject', JSON.stringify(personObject));
10.     // Retrieve the object from storage
11.     var retrievedObject = JSON.parse(localStorage.getItem('testObject'));
12.     console.log(retrievedObject.firstName + " " + retrievedObject.lastName);
13.    // then you can use retrievedObject.givenName, retrievedObject.familyName...
14.   </script>
15. </head>
16. <body>
17. </body>
18. </html>

Explanations:

Examples

Example #1: showing how we can save a form’s content in JSON

Online example on JSBin that saves in localStorage an array of contacts in JSON

How to save a form's content in localStorage using JSON.

Example #2: a form and a table that displays the contacts stored in localStorage

Example on JSBin

Add contacts using the form, see how the HTML table is updated. Try to reload the page: data are persisted in localStorage. 

Serverless contact manager.

Examine the localStorage:

localStorage view in devtools shows the data.

The source code for this example is a bit long, and we suggest that you examine it in the JS Bin tool. We extensively commented it. It uses:

Well structured pages with the new elements seen during Module 1 (section, article, nav, aside, etc.)

HTML5 form elements with builtin and custom validation (the date cannot be in the past, the firstName and lastName fields do not accept &, #, ! or $ characters), localStorage for saving / restoring an array of contacts in JSON.

It shows how to use the DOM API for dynamically updating the page content (build the HTML table from the array of contacts, add a new line when a new contact is submitted, etc.)

6.3 File API Introduction

The objective of this chapter is to provide an overview of the File API.

Sound sample editor serverless.

Before HTML5, file management was limited to multipart forms and to Ajax for sending/requesting files to/from a remote Web server.

Possible actions were limited, both for the developer and the user. However, HTML5 now comes with an API called “File” that holds features for accessing file metadata (name, size, type) from client-side JavaScript. The API also has methods for reading file contents directly in the browser. This is particularly interesting for displaying preview of images before uploading them, or - and this is much more interesting - for developing Web applications that work with local files without the need for a server.

Imagine a multimedia player that accesses (in read-only) your file system, reads your audio and video files, etc., such as the Remo Music player below, or an application that edits the audio content of local mp3 files, for example, the HYA-WAVE sound editor (screenshot above).

Audio player that plays local files. polarr photo editor uses the File API.

External resources

6.3.2 Working with local files

Hi! Welcome for this second video of module 6, in which we will talk about the file Javascript API.

This is an API that has been designed for working with files on the client side in Javascript.

Before HTML5, there was no mean to know the the size, the name, the modification date of a file without sending it to a remote server. It was not possible also to read the content of a file, for example for previewing an image before uploading it into a remote server, or if you wanted to play a local audio file, or a video file without streaming and downloading it from a remote server. It was not possible.

I’m going first to show you some example of server less applications.

I mean applications written using HTML, CSS and Javascript that do not rely on data located on a remote server.

For example, I can show you this. This is a wave. This is an audio file editor that works with local files. I open the page, and from here I can drop a music file directly. Let’s go to get some music somewhere. Some AC/DC song! I can just drop the song and I can edit the song. I can play the song. I can cut, I can save locally, etc.

Another example is an application, a Chrome application that is also written using Web technologies but doesn’t run using HTTP.

It’s quite easy to write such applications, because there are regular Web applications using HTML, CSS and Javascript except that you’ve got to package them for putting them on the Chrome store.

I’m going first to show you a local audio file player that is called Remo.

I can just open files, choose one or several, open the files, I’ve got a playlist and I can play the songs. Normally it works… yes, OK.

Finally, I’m going to show you an image editor that has been written by one of the students from this course. This is an example of Instagram-like filters that have been written by @GeorgianaB from the discussion forum. It’s a regular Web application except that you can upload an image, a local image. Ok, no, this one is too small maybe. You can upload an image… it’s me with a strange head ;) Then you can apply Instagram filters or add borders. You can even download the result to your local disk.

We saw how to do this when we look at the download attributes in Module 1.

All these examples work with local files. Let’s go back to the course and now I’m going to show you how we can, for example, read images using an input type=file like this.

Select several files, open, and look at the preview directly in the page.

Here, I just read the files using the File API, I read them as what we call data URL and I created some img elements we added in the page.

First, before writing the application, I want to talk a little bit about data URLs.

Data URLs are strange set of characters like that ,and if you select them and paste them in the address bar you can see a small red icon. And you can use this everywhere where you would have use an http://URL.

If I use this image here, I can using it in JSBin for example, and if I add the image here I can see that the content of the image, the pixels, are encoded in the URL itself. How can you make such URLs? You’ve got plenty of Web sites, look for data URL with a search engine and you’ve got plenty of them.

The first one here is called DATAURL.net and it included ‘Data URL Maker’ so you can just open a file, any sort of file, here I take a JPG file and it will produce the data URL that corresponds. But you can also use with mp3 file, or mp4 file video or any sort of file encoding.

This was to introduce this new sort of URL that maybe you’ve never heard about before. Now, how can we write this?

I’m going to explain with the source code here because it’s simpler.

After that I will code it live. What we do is that we use an input type=file for reading the files. We define an event listener that will call this function ‘read files and display preview’ once the user has chosen some files.

In this callback, we iterate, we’ve got a ‘for’ loop, about the files. For each file, we build a file reader, that is a special object, that has a function called ‘readAsDataURL’, that will take a file descriptor as a parameter here.

When the file is read, because that can take some time if the file is big, then each file is read, the onload callback is executed. It’s exactly the same principle we saw with images as reading an image can take some time. You can only work with that image in an onload callback. So, when we enter this function that means that one file has been read and we can get the content of the file through the event using the ‘e.target.result’ property. Each time I read a file I can get its content here and it’s a data URL that contains all the pixels.

What we do in that example, is that we create a <span>. In the span with the innerHTML, we add an image that has for src attribute the content of the image we read as a data URL.

And finally, we just insert this image in the document and this is when we see it.

I’m going to live code this one, maybe a simpler version so that you can see the different steps in real time. First, I add an ‘input type=file multiple’ for selecting multiple files. This is what’s happening and here I can select several images. I’m going to add an ‘onchange’ listener:

'onchange=readImagesAndPreview()'.

And I’m going to pass the set of file descriptors that have been selected; ‘this.files’ is the correct way to send the selected files to a Javascript function.

Now, I’m going to start writing some script. But before, I will add a space, let’s say ‘div id=thumbnails’.

This is an empty container that will be used for inserting the preview of the images.

Now I’m going in the script part. In the script part, we’re going to write this function ‘readImagesAndPreview()’ and it takes as parameter the files that will be sent.

And here if I want just to read the first file I will just show you how it works. I create a reader object, like that, and I ask the reader to ‘readAsDataURL’ the first file.

What is happening is that this may take some time and I need to declare, before reading the files, a callback that will be code only once the file would have been read - ‘reader.onload’

And here, what can I do with the file that has been read, I will create an image element.

So i created an image element, I set the source of this element with the file content that is in the ‘e.target.value’. So I created an image. Then, I will add the image to the container here. I’m going to declare the container here. How can I add an element: ‘container.appendChild’ and I add the image I created.

Let’s try it, maybe I made some mistakes. I select several files, I open them and here, apparently, I make a small mistake.

Let’s open an image and now read the image and I can see the preview.

If you look carefully, it’s just a few lines of code.

So now I’m going to show you how we could work with not only one single image, but several images. The trick consists in adding a ‘for’ loop.

We will iterate on the length of the file’s variable here. If we got 3 files, we do 3 loops. I’m going to define a variable called ‘f’ that will correspond to the current file.

What we are going to read this time is the current file ‘f’.

So this loop here, will first read files, for each one we do read as data URL, when the file is read we enter the onload callback, we create an image with the result.

I’m going just to add some constraints on the size, because when I tried earlier I had big images that could not fit on the screen.

I constraint the width to 100px and the height will just follow.

Like this, let’s try. If I select several pictures, two of them,

I can see the two pictures. If I select more, then I can see the whole set of pictures that are coming as previews.

You can see that the number of lines of code is really small and this is quiet powerful.

If you want to write, for example, a Web site that will upload pictures, you can preview them, select them and then upload them.

I show you earlier the application by one of the students that can do Instagram like filters, but you can also find some new on the Chrome store.

I installed one that is called Polarr that is using HTML for all the functionalities. So you can just import a picture, let’s say this one. Then you can select filters, you can do some corrections on it and so on, and finally you can export it and that’s all.

All this here is HTML5, this is canvas, these are form elements we saw the last week, some canvas here, or image elements and so on.

Image preview.

Source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset="utf-8">
5.   <title>Example of using readAsDataURL</title>
6. </head>
7. <body>
8.   <input type="file" multiple onchange="readImagesAndPreview(this.files);">
9.     <p>
10.     <div id="thumbnails"></div>
11.     <script>
12.       var container = document.getElementById("thumbnails");
13.       function readImagesAndPreview(files) {
14.         for(var i=0; i < files.length; i++) {
15.            var f = files[i];
16.            var reader = new FileReader();
17.            reader.onload = function(e) {
18.               var img = document.createElement("img");
19.               img.src = e.target.result;
20.               img.width = 100;
21.               container.appendChild(img);
22.            }
23.            reader.readAsDataURL(f);
24.         }
25.       }
26.     </script>
27. </body>
28. </html>

6.3.3 Reading File Metadata

Imagine you have an input field like this:

  1. Select one or more files: <input type=“file” id=“input”/>

This renders as a “select files” or “browse files” button. If you select one file in the file chooser dialog that has popped up, before HTML5 you couldn’t do anything with it in the client-side: no access from JavaScript. With the File API, you can read what we call “file metadata”: name, size, type and last modification date.

Look at the code below: the file API defines a files property on the DOM node corresponding to the <input type=“file”…/> input field. This property is an array.

In the example below, we get in the selectedFile variable, the metadata related to the first selected file:

var selectedFile = document.getElementById('input').files[0];
// do something with selectedFile.name, selectedFile.size, selectedFile.type
// selectedFile.lastModifiedDate
...

Examples

Example #1: read metadata of the first selected file

Here is a complete example on JSBin that uses the code above to get details about the first selected file. Please try it below on your browser (click on the button and choose one file):

Select one or more files:

Complete source code:

Click to expand!
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.   <meta charset=utf-8 />
5.   <title>Reading file metadata</title>
6.   <script>
7.     function displayFirstSelectedFileMetadata() {
8.       var selectedFile = document.getElementById('input').files[0];
9.         document.querySelector("#singleName").innerHTML = selectedFile.name;
10.         document.querySelector("#singleSize").innerHTML = selectedFile.size + " bytes";
11.         document.querySelector("#singleType").innerHTML = selectedFile.type;
12.         document.querySelector("#singleDate").innerHTML = selectedFile.lastModifiedDate;
13.     }
14.   </script>
15. </head>
16. <body>
17.   Select one or more files: <input type="file" id="input"
18.     onchange="displayFirstSelectedFileMetadata();"/>
19.   <p>
20.   <ul>
21.     <li>File name: <span id="singleName"></span></li>
22.     <li>File size: <span id="singleSize"></span></li>
23.     <li>File type: <span id="singleType"></span></li>
24.     <li>File last modification date: <span id="singleDate"></span></li>
25.   </ul>
26. </body>
27. </html>

Example #2: display metadata of multiple files, use a filter on the file type

This example is a bit more complicated, as it will display details about all files selected (not only the first) and allows only images to be selected, using the accept attribute of the input field: <input type=“file” accept=“image/*“…/>.

Example on JSBin, or try it in your browser: click on the button, and select multiple image files. Notice that in the file selector, files that are not images will be greyed and non selectable.

Select several images:

Click to expand!
1.  <p>Select several images: <input
      type=“file” accept=“image/*“ multiple onchange=”filesProcess(this.files)“ 
      name=”selection”/></p>
2.  <div id="result">...</div>
3.  <script>
4.    function filesProcess(files) {
5.      var selection = "<table><tr><th>Name<
          /th><th>Bytes</th><th>MIME Type</th>
6.      <th>Last modified date</th></tr>";
7.      for(i=0; i<files.length ;i++){
8.        file = files[i];
9.        selection += "<tr><td>"+file.name+"</td><td
            style="text-align:right">"
10.         +file.size+"</td><td>"
11.         +file.type+"</td><td>
          "+file.lastModifiedDate+"</td></tr>";
12.     }
13.     selection += "</table>";
14.     document.getElementById("result").innerHTML = selection;
15.   }
16. </script>

Explanations:

6.3.4 Blob and File

The HTML5 File API specification introduces several interfaces:

We will not use all of these interfaces, but let’s explain the difference between  Blob and File, as most of the methods exposed by the FileReader interface take indiscriminately a Blob or a File as parameter.

The Blob movie poster.

The Blob object

An object of type Blob is a structure that represents binary data available as read-only. Most of the time, you will only encounter these objects when you handle files.

Blob objects have two properties, namely:size and type, which respectively retrieve the size in bytes of the data handled by the Blob and their MIME type.

The File object

File objects are useful for manipulating… files! They inherit the properties and methods of Blob objects, and have two additional properties that are name, for the file name, and lastModifiedDate to get the date of the last modification of the file (in the form of a JavaScript Date object, obviously) .

Most of the time, we will work with File objects. Blob objects will have real interest when you download binary files using Ajax (see example below).

[Advanced]

If you are interested in seeing how Blob objects can be used, here is an example “as is” that shows how to download an image using Xhr2 (Xml Http Request version 2). The examples uses a <progress> element to show the download progress, and uses xhr.responseType = ‘blob’; to indicate that the file we are going to download is a binary file (a blob). Try the example, then comment the line with responseType=‘blob’. In this case, you will notice that the image file is not properly decoded by the browser and is not displayed in the page. We explain Xhr2 in the W3C HTML5 Apps and Games course.

6.3.5 Reading file content

In order to read the content of a file, different steps required. Let’s see how to do it.

Typical use is made of three steps

Step #1: create a FileReader object

The file API proposes several methods for reading file content, each taken from the FileReader interface. Here is how you create a FileReader object:

  1. var reader = new FileReader();

Steps #2 and #3: first call a method of the FileReader object for reading the file content, then get the file content in an onload callback

There are three different methods available for reading a file’s content: readAsText, readAsArrayBuffer for binary data and also as readAsDataURL (the content will be a URL you will use to set the src field of an <img src=...>, <audio>, <video>, and also with all existing methods/properties that accept a URL).

All these methods take as a unique parameter a File object (for example, a file chosen by a user after clicking on a <input type=file> input field). Below, we use, as an example, the readAsText method:

Click to expand!
1.  function readFileContent(f) {
2.    // Executed last: called only when the file content is loaded, e.target.result is
3.    // The content
4.    reader.onload = function(e) {
5.      var content = e.target.result;
6.      // do something with the file content
7.      console.log("File " + f.name + " content is: " + content);
8.    };
9.    // Executed first: start reading the file asynchronously, will call the
10.   // reader.onload callback only when the file is read entirely
11.   reader.readAsText(f);
12. }

The above code shows how a file can be read as text. The function is called, for example by clicking on the button corresponding to a <input type=“file” id=“file” onchange=“readFileContent(this.files)”/>, and by choosing a file.

Try a variation of the above code in your browser, that displays the file content in a text area. This example is detailed further in the course. Click and select a text file below:

Choose a text file:

Select a text file.

In the following pages, we look at different examples that read file contents as text, dataURL and binary.

6.3.6 Read file content as text

Let’s start by reading a pure text file

Examples

Example #1: read a single file’s content

Example at JSBin, or try it below in your browser:

Read a single file's content.

Complete source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Example of use of FileReader with a text file</title>
6.  </head>
7.  <body>
8.  <label for="files">Choose a text file:</label><input type="file" id="file"
9.    onchange="readFileContent(this.files)"/><br/>
10. <p>
11. <textarea rows=15 cols=50 id="fileContent"></textarea>
12. 
13. <script>
14.   function readFileContent(files) {
15.     console.log("In readFileContent");
16.     var reader = new FileReader();
17. 
18.     // Executed last: called when the file content is loaded, e.target.result is
19.     // The content
20.     reader.onload = function(e) {
21.       // display content in the textarea with id="fileContent"
22.       document.getElementById("fileContent").value= e.target.result;
23.     };
24. 
25.     console.log("Reading file:" + files[0].name);
26. 
27.     // Executed first: start reading the file asynchronously , will call the onload
28.     // callback when the file is read
29.     reader.readAsText(files[0]);
30.   }
31. </script>
32. </body>
33. </html>

This example is the one at the end of the previous page. This time, we show the complete source code above. Remember that the instruction at line 29 is executed first, then when the file is read, the browser will call asynchronously the onload callback at line 20.

Example #2: a variation of the previous one, using multiple files

Example on JSBin, or try it below in your browser. This time, please select multiple text files (using shift for multiple selection):

Choose multiple text files.

Source code:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Example of use of FileReader with a text file</title>
6.  </head>
7.  <body>
8.  <label for="files">Choose multiple text files:</label>
9.  <input type="file" id="files"
10.   multiple onchange="readFilesAndDisplayAsText(this.files);"/><br/>
11. <p>
12. <textarea rows=30 cols=50 id="filesContent"></textarea>
13. 
14. <script>
15.   var filesContent = document.getElementById("filesContent");
16. 
17.   function readFilesAndDisplayAsText(files) {
18.     console.log("dans read files");
19.     // Loop through the FileList
20.     for (var i = 0, f; f = files[i]; i++) {
21. 
22.       var reader = new FileReader();
23. 
24.       // Add an onload listener to the reader
25.       addOnLoadListener(reader, f.name);
26.       // start reading, will call the listener later, when the file f is read
27.       reader.readAsText(f);
28. 
29.     }
30.   }
31. 
32.   function addOnLoadListener(reader, name) {
33.     // Add an onload listener that will be able to print the name of the
34.     // file...
35.     reader.onload = function(e) {
36.       filesContent.value += "###### READING FILE " + name + " ######";
37.       filesContent.value += e.target.result;
38.     };
39.   }
40. </script>
41. </body>
42. </html>

Explanations

This example is similar to the previous one, except that this time we read multiple files.

Line 20: this is the for loop that will iterate on the files object passed as parameter by the onchange listener declaration at line 10.

Line 25: instead of declaring the onload listener with a reader.onload =… directly in the loop, this time we preferred to write a separate function that will do this. This technique is useful when you want the listener to work with extra variables computed in the loop (in our case, the name of the file).

About charter encoding

Note that you can optionally indicate the encoding of the file you are going to read (default is UTF-8):

reader.readAsText(file, 'UTF-8');
reader.readAsText(file, 'ISO-8859-1');
...

6.3.7 Read file content as binary

This method is rarely used, except for loading “raw” binary data. For images you would like to see in your HTML page using the <img src= tag> or for drawing in a canvas, or for audio and video files that you would like to play using the <audio> or <video> elements, it would be preferable to use the readAsDataURL method presented on the next page of the course.

readAsArrayBuffer is often used for purposes such as reading audio samples that should be loaded in memory and played using the WebAudio API, or for loading textures that you will use with WebGL for 3D animations.

Example: read a local audio file and play it with the WebAudio API

The WebAudio API is useful for reading audio sound samples from memory (no streaming), and has been designed for music application and games. This example shows how a local audio file can be read and played directly in the browser, without the need for a server!

Example on JSBin (does not work on IE, as it does not support the WebAudio API). We could not embed it here on the edX platform as it prevents code that uses Ajax to run in its pages.

Local audio player.

Source code extract:

Webaudio API
1.  // User selects file. Read it as an ArrayBuffer and pass to the API.
2.  var fileInput = document.querySelector('input[type="file"]');
3.  fileInput.addEventListener('change', function(e) {
4.    var reader = new FileReader();
5.    reader.onload = function(e) {
6.      initSound(e.target.result);
7.    };
8.    // THIS IS THE INTERESTING PART!
9.    reader.readAsArrayBuffer(this.files[0]);
10. }, false);

Explanations:

6.3.8 Read file content as dataURL

What is a data URL?

A data URL is a URL that includes type and content at the same time. It is useful, for example,  for in-lining images or videos in the HTML of a Web page (on mobile devices, this may speed up the loading of the page by reducing the number of HTTP requests).

Here is an example of a red square, as a data URL. Copy and paste it in the address bar of your browser, and you should see the red square:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==

This data URL in a browser address bar should look like this:

Data url in address bar shows a red circle.

If we set the src attribute of an image element <img src="data:image/png...."> with the data URL of the above screenshot, it will work exactly as if you used a URL that started with https://

In your browser, you will see a small red circle rendered by this source code:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO

9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red square" width=50 height=50/>

And here is the result:

Red splotch.

This dataURL format enables file content to be stored in a base64 format (as a string), and adds the MIME type specification of the content. The dataURL can therefore store a file as a URL readable with modern browsers. It is becoming more commonly used on the Web, especially for mobile applications, as inlining images reduces the number of HTTP requests and makes the Web page load faster.

You will find lots of Web sites and tools for generating dataURL from files, such as Image to Data URI converter (screenshot below):

Online service that converts uploaded images to data uris...we see an image and 
    its ascii encoded data uri version.

With the above example, you can copy and paste the characters on the left and use them with an <img src="...">. Just set the src attribute with it!

Notice that you can encode any type of file as dataURL, but this format is most frequently used with ./images files (images, audio, video).

Example of HTML5 logo embedded in a document without any real image, just a dataURL and CSS:

Data URL in address bar shows a red circle.

Examples

Example #1: read images as data URL and display previews in the page

This first example is useful for forms that allow the user to select one or more pictures. Before sending the form, you might want to get a preview of the pictures in the HTML page. The reader.readAsDataUrl method is used for that.

Example on JSBin or try it below in your browser:

Preview of selected images.

Preview of selected images:

Source code extract:

Click to expand!
1.  <label for="files">Choose multiple files:</label>
2.  <input type="file" id="files" multiple
3.    onchange="readFilesAndDisplayPreview(this.files);"/><br/>
4.  <p>Preview of selected images:</p>
5.  <output id="list"></output>
6.  <script>
7.  function readFilesAndDisplayPreview(files) {
8.    // Loop through the FileList and render image files as thumbnails.
9.    for (var i = 0, f; f = files[i]; i++) {
10.     // Only process image files.
11.     if (!f.type.match('image.*')) {
12.       continue;
13.     }
14.       var reader = new FileReader();
15.
16.     //capture the file information.
17.     reader.onload = function(e) {
18.       // Render thumbnail. e.target.result = the image content
19.       // as a data URL
20.       // create a span with CSS class="thumb", for nicer layout
21.       var span = document.createElement('span');
22.       // Add an img src=... in the span, with src= the dataURL of
23.       // the image
24.       span.innerHTML = "<img class='thumb' src='" +
25.         e.target.result + "' alt='a picture'/>";
26.       // Insert the span in the output id=list
27.       document.getElementById('list').insertBefore(span, null);
28.     };
29.     // Read in the image file as a data URL.
30.     reader.readAsDataURL(f);
31.   }
32. }

Explanations:

Example #2: read a single local image file and use it with drawImage ina canvas

Try it on JSBin

Example #2 read image as dataURL and draw inside a canvas.

Errata: the above screenshot says “choose multiple files”, but the example only works with a single file.

Source code extract:

Click to expand!
1. function drawImage(imageFile) {
2.   var reader = new FileReader();
3.  
4.   //capture the file information.
5.   reader.onload = function(e) {
6.     // For drawing an image on a canvas we
7.     // need an image object
8.     var img = new Image();
9.     // Even if the file has been read, decoding
10.     // the dataURL format may take some time
11.     // so we need to use the regular way of
12.     // working with images: onload callback    
13.     // that will be called after setting the src attribute
14.     img.onload = function(e) {
15.       // draw the image!
16.       ctx.drawImage(img, 0, 0, 400, 400);
17.     }
18.     // e.target.result is the dataURL, so we set the
19.     // src if the image with it. This will call
20.     // asynchonously the onload callback
21.     img.src= e.target.result;
22.   };
23.   // Read in the image file as a data URL.
24.   reader.readAsDataURL(imageFile);
25. }
26. function readFileAndDraw(files) {
27.   drawImage(files[0]);
28. }

Explanations:

Remember how we worked with images on a canvas. We had to create an empty image object (line 8), set the src attribute of the image object (line 23), then use an image.onload callback (line 15), and we could only draw from inside the callback (line 17). This time, it’s exactly the same, except that the URL comes from e.target.result in the reader.onload callback (line 23).

Example #3 (advanced): an instagram-like photo filter application

Another very impressive example, has been developed by @GeorgianaB, a student of the first iteration of this course (see her other creations/examples). This Web application reads local image files, draws them into a canvas element and proposes different filters. This example is given “as is” for those of you who would like to go further. Just click on the link (or on the image below) and look at the source code.

Try this example online on gitHub.

6.4 Geolocation API Introduction

This chapter presents the new Geolocation API and illustrates its use with several examples.

W3C Geolocation logo/icon.

The Geolocation HTML5 JavaScript API is implemented by most modern Web browsers, and uses different means to get the current location: GPS, GSM/3G triangulation, Wifi, IP address, etc.

It is possible to prompt the user to activate the GPS (this is what most GPS navigation software does on mobile phones), or ask for a particular mean among those available. It is also possible to track the current position when it changes. This is useful for writing a navigation application or for tracking in real time the position of different participants in the case of an application that involves several persons at the same time (using WebSockets, for example).
Browser support for the Geolocation API is excellent, both on mobile and on desktop devices.

Typical use

Click to expand!
navigator.geolocation.getCurrentPosition(showPosition, onError);
 
function showPosition(position) {
  console.log("latitude is: " + position.coords.latitude);
  console.log("longitude is: " + position.coords.longitude);
}
 
function onError(err) {
  console.log("Could not get the position");
}

This online example at JSBin shows how to get the current longitude and latitude and display them in an HTML page. Try it below in your browser:

Click the button to get your coordinates:

Where am I ?

Note that the first time you execute this example, for privacy reasons, the browser will ask if you agree to share your position with the application.

Source code of this typical example:

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.    <head>
4.      <meta charset="utf-8">
5.      <title>Basic example of use of the geolocation API</title>
6.    </head>
7.    <body>
8.      <p id="msg">Click the button to get your coordinates:</p>
9.      <button onclick="getLocation()">Where am I ?</button>
10.     <script>
11.       var displayCoords=document.getElementById("msg");
12.       function getLocation() {
13.       if (navigator.geolocation) {
14.         navigator.geolocation.getCurrentPosition(showPosition);
15.       } else {
16.         displayCoords.innerHTML="Geolocation API not supported by your browser.";
17.       }
18.       }
19.       function showPosition(position) {
20.         displayCoords.innerHTML="Latitude: " + position.coords.latitude +
21.           "<br />Longitude: " + position.coords.longitude;
22.       }
23.     </script>
24.   </body>
25. </html>
Geolocation callback illustration.

Explanations:

External resources:

6.4.2 The coords object properties

In the previous example, we used the coords property of the position passed as an input parameter to the callback function. This coords object has many properties:

Properties of the coords object.

Not all these values may be available in all Web browsers. When one of these properties is null, it means that it is not available (often the case of the altitudeAccuracy)

6.4.3 Geolocation error codes

In the last example, we used the navigator.geolocation. getCurrentPosition(showPosition) with only one callback function (in the case of success), but it is also possible to pass a second parameter that is another callback function called in the case of error.

A slightly different version of the previous example  shows how to properly check against the different possible errors. Try it, then turn your WiFi off or unplug your Ethernet cable (or turn off GPS and 3G/4G on a mobile phone). You should see an error message Error during geolocation: Location could not be obtained though the available means”:

Geolocation error.

Source code of the example:

Click to expand!
1. <!DOCTYPE html>
2.   <html lang="en">
3.   <head>
4.     <meta charset="utf-8">
5.     <title>Basic example of use of the geolocation API</title>
6.   </head>
7.   <body>
8.     <p id="msg">Click the button to get your coordinates:</p>
9.     <button onclick="getLocation()">Where am I ?</button>
10.     <script>
11.       var displayCoords=document.getElementById("msg");
12.       function getLocation() {
13.         if (navigator.geolocation) {
14.           navigator.geolocation.getCurrentPosition(showPosition, errorPosition);
15.         } else {
16.           displayCoords.innerHTML="Geolocation API not supported by your browser.";
17.         }
18.       }
19.       function showPosition(position) {
20.         displayCoords.innerHTML="Latitude: " + position.coords.latitude +
21.                                 "<br />Longitude: " + position.coords.longitude;
22.       }
23.       function errorPosition(error) {
24.         var info = "Error during geolocation: ";
25.         switch(error.code) {
26.           case error.TIMEOUT:
27.             info += "Timeout !";
28.             break;
29.           case error.PERMISSION_DENIED:
30.             info += "Permission denied, geolocation could not be obtained...";
31.             break;
32.           case error.POSITION_UNAVAILABLE:
33.             info += "Location could not be obtained though the available means...";
34.             break;
35.           case error.UNKNOWN_ERROR:
36.             info += "Unknown error";
37.             break;
38.           }
39.         displayCoords.innerHTML = info;
40.       }
41.     </script>
42.   </body>
43. </html>

6.4.4 Tracking a position

In order to track the current position, the geolocation API provides a method similar to the getCurrentPosition(onSuccess, onError) named watchPosition(onSuccess, onError).

When getCurrentPosition gives a position when called, watchPosition does the following:

Typical use

// get an id of the current tracking, the showPosition callback is like the one we saw in earlier examples.
var watchPosId = navigator.geolocation.watchPosition(showPosition);
...
// stop the tracking
navigator.geolocation.clearWatch(watchPosId);

As a test, you may just try to change getCurrentPosition to watchPosition in the previous examples, and try this code using a mobile phone or tablet, walk for 20 meters and see the position changing.

Options available when using the geolocation API, in particular real time tracking

Several options are available when using HTML5 geolocation. We can pass a third parameter to the getCurrentPosition and watchPosition methods, that will hold one or several of the following options:

More properties of the coords object.

Example of use

Source code:

Click to expand!
1. // Just ask to turn GPS on, if available
2. navigator.geolocation.getCurrentPosition(onSuccess, onError,
3.   {enableHighAccuracy:true});
4. // maximumAge = 10 mins, the position can be cached for 10 mins,
5. // useful when in tunnels...When the device tries to get
6. // a position, if it does not succeed, then go on error
7. // immediately
8. navigator.geolocation.getCurrentPosition(onSuccess, onError,
9.   {maximumAge:600000, timeout:0});
10. // Position will never come from the cache (maximumAge: 0), and
11. // if after 0.1s the position could not be computed, then go on
12. // error
13. navigator.geolocation.getCurrentPosition(onSuccess, onError,
14.   {maximumAge:0, timeout:100});
15. // Ask for GPS, cache for 30s, 27s before going on error...
16. watchId=navigator.geolocation.watchPosition(onSuccess, onError,
17.   {enableHighAccuracy:true, maximumAge:30000, timeout:27000});

Look for the explanations in the lines of comment.

6.4.5 Geolocation and maps

This section presents an example of how to get an interactive map, using  the Leaflet API for OpenStreetMap, and gives links to more resources. Did you know that you can even get an estimation of a physical address from the longitude and latitude, using online Web services?

How to get a map centered on your longitude and latitude.

Example/Test OpenStreetMap.

This example is just given "as is", as there are so many possibilities for rendering a map with the Leaflet API for OpenStreetMapshttps:/. However, we think having such a basic example might be useful.

Example/Test OpenStreetMap.

JavaScript

Click to expand!
1.  function getLocation(e) {
2.    e.preventDefault();
3.    if (!navigator.geolocation) {
4.      alert("Browser doesn't support geolocation");
5.    } else {
6.      navigator.geolocation.getCurrentPosition(success, error);
7.    }
8.  }
9.  // Get current position successfully
10. function success(position) {
11.   var map, marker,
12.     latitude = position.coords.latitude,
13.     longitude = position.coords.longitude;
14.   // Instance map using leaflet
15.   map = L.map('map').setView([latitude, longitude], 13);
16.   // Tile layer using key api at cloudmade.com
17.   L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
18.     key: '760506895e284217a7442ce2efe97797',
19.     styleId: 103288,
20.     maxZoom: 16
21.   }).addTo(map);
22.   // Marker using leaflet
23.   marker = L.marker([latitude, longitude]).addTo(map);
24.   // Popup in leaflet
25.   marker.bindPopup('<p>Your location</p>').openPopup();
26. }
27. // Get current position fail
28. function error() {
29.   alert('Get current position fail. Please access codepen to get geolocation.');
30. }
31.

CSS

Click to expand!
1. html, body {
2.     height: 100%
3.   }
4.   .map {
5.     height: 300px;
6.   }
7.   .btn {
8.     background-color: rgba(10, 10, 230, .5);
9.     border: 0;
10.     color: #fff;
11.     padding: 10px;
12.     text-shadow: 0 0 1px rgba(0, 0, 0, .3);
13.     text-decoration: none;
14.     margin: 0.5rem 0 1rem;
15.     display: inline-block;
16.   }

HTML

Click to expand!
1. <html>
2. <head>
3.   <meta charset="utf-8">
4.   <title>OpenStreetMap Example</title>
5.   <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css">
6.   <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
7. </head>
8. <body>
9.   <button class="btn" onclick="getLocation(event)">Click to show your location with OpenStreetMap</button>
10.   <div id="map" class="map"></div>
11. </body>
12. </html>

Source code extract:

HTML part:

Click to expand!
1.  <html>
2.  <head>
3.    <meta charset="utf-8">
4.    <title>OpenStreetMap Example</title>
5.    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css">
6.    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
7.  </head>
8.  <body>
9.    <button class="btn" onclick="getLocation(event)">Click to show your location with OpenStreetMap</button>
10.   <div id="map" class="map"></div>
11. </body>
12. </html>

JavaScript part:

Click to expand!
1.  function getLocation(e) {
2.    e.preventDefault();
3.    if (!navigator.geolocation) {
4.      alert("Browser doesn't support geolocation");
5.    } else {
6.      navigator.geolocation.getCurrentPosition(success, error);
7.    }
8.  }
9.  // Get current position successfully
10. function success(position) {
11.    var map, marker,
12.   latitude = position.coords.latitude,
13.   longitude = position.coords.longitude;
14.   // Instance map using leaflet
15.   map = L.map('map').setView([latitude, longitude], 13);
16.   // Tile layer using key api at cloudmade.com
17.   L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
18.     key: '760506895e284217a7442ce2efe97797',
19.     styleId: 103288,
20.     maxZoom: 16
21.   }).addTo(map);
22.   // Marker using leaflet
23.   marker = L.marker([latitude, longitude]).addTo(map);
24.   // Popup in leaflet
25.   marker.bindPopup('<p>Your location</p>').openPopup();
26. }
27. // Get current position fail
28. function error() {
29.   alert('Get current position fail. Please access codepen to get geolocation.');
30. }

6.4.6 Reverse geocoding

Different Web services can be used to get an address from longitude and latitude. Most are free of charge, but they will ask you to register an API key and enter your credit card number. If you send too many requests, you will be charged. Such a service is the Google Reverse Geocoding JavaScript API. For those of you who are really interested to know how this API works, please read the Google documentation and tutorials.

There is also an interesting Leaflet plugin (an extension to Leaflet) based on the  Gisgraphy (free open source framework) service, that comes with a  nice demo of reverse geocoding.

Let’s see some examples of use.

Example #1: how to get a physical address from the longitude and latitude.

Google reverse geocoding example (screenshot only):

Example #1 How to get a physical address from the longitude and latitude.

Source code of this example (in order to run it, you need a Google API key, used at line 6.

Click to expand!
1.  <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.    <meta charset="utf-8">
5.    <title>Js bin </title>
6.    <script src="https://maps.googleapis.com/maps/api/js?key=
7.      PUT_HERE_YOUR_API_KEY&v=3.exp&sensor=false">
8.      </script><script>
9.      // p elements for displaying lat / long and address
10.     var displayCoords, myAddress;
11.     // used with the google apis
12.     var geocoder;
13.     var map;
14.     var infowindow = new google.maps.InfoWindow();
15.     var marker;
16.     // Called when the page is loaded
17.
18.     function init() {
19.       displayCoords=document.getElementById("msg");
20.       myAddress = document.getElementById("address");
21.       geocoder = new google.maps.Geocoder();
22.       // In order to show something even before a user clicks on the button
23.       var latlng = new google.maps.LatLng(34.0144, -6.83);
24.       var mapOptions = {
25.         zoom: 8,
26.         center: latlng,
27.         mapTypeId: 'roadmap'
28.       }
29.       map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
30.     } // end of init()
31.     // Called when the button is clicked
32.
33.     function getLocation() {
34.       if (navigator.geolocation) {
35.         navigator.geolocation.getCurrentPosition(showPosition);
36.       } else {
37.         displayCoords.innerHTML="Geolocation API not supported by your browser.";
38.       }
39.      }
40.     // Called when a position is available
41.  
42.     function showPosition(position) {
43.       displayCoords.innerHTML="Latitude: " + position.coords.latitude +
44.         "<br />Longitude: " + position.coords.longitude;
45.       // Display the map
46.       showOnGoogleMap(new google.maps.LatLng(position.coords.latitude,       
47.         position.coords.longitude));
48.     }
49.
50.     function showOnGoogleMap(latlng) {
51.       // Ask google geocoder for an address once we get a longitude and
52.       // a latitude. In fact, the reverse geocoder sends back an array of "guesses"
53.       // i.e. not just one address object, but several. Each entry in this array
54.       // has several properties such as street, city, etc. We use the
55.       "formatted_address"
56.       // one here, but it might be interesting to get the detailed properties in other
57.       // applications like a form with street, city, zip code etc.
58.       geocoder.geocode({'latLng': latlng},reverseGeocoderSuccess);
59.	
60.         function reverseGeocoderSuccess(results, status) {
61.           if (status == google.maps.GeocoderStatus.OK) {
62.             if (results[1]) {
63.               map.setZoom(11);
64.               marker = new google.maps.Marker({
65.                 position: latlng,
66.                 map: map
67.               });
68.               infowindow.setContent(results[1].formatted_address);
69.               infowindow.open(map, marker);
70.               // Display address as text in the page
71.               myAddress.innerHTML="Adress: " + results[0].formatted_address; 
72.             } else {
73.               alert('No surface address found');
74.             }
75.           } else {
76.             alert('Geocoder failed due to: ' + status);
77.           }
78.         } // end of reverseGeocoderSuccess
79.       } // end of showOnGoogleMap
80.   </script>
81. </head>
82. <body onload="init()">
83.   <title>HTML5 + Geolocalisation + Google Maps API Reverse Geocoding</title>
84.   <p id="msg">Click the button to get your coordinates:</p>
85.   <p id="address"></p>
86.   <button onclick="getLocation()">Where am I ?</button>
87.   <div id="map_canvas" style="width: 500px; height: 300px"></div>
88. </body>
89. </html>

Gisgraphy (free service) reverse geocoding example (screenshot only, click on it to see  the demo on the Gisgraphy website):

Gisgraphy screenshot.

Example #2: reverse geocoding + OpenStreetMap

Important note: these examples below rely on an external  GitHub resource.

Please, pan and zoom on the map and click.

The longitude and latitude are computed from your click and a free reverse geocoding service is used to convert to a physical address.

Reverse Geocoding - OpenStreetMap.

HTML

Click to expand!
1. <!DOCTYPE html>
2. <html>
3. <head>
4.   <title>Leaflet Control Geocoder</title>
5.   <meta charset="utf-8" />
6.   <meta
7.     name="viewport"
8.     content="width=device-width, user-scalable=no initial-scale=1, maximum-scale=1"
9.     />
10.  <link rel="stylesheet" href="https://unpkg.com/leaflet@latest/dist/leaflet.css
11.    " />
12.  <link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
13.  <script src="https://unpkg.com/leaflet@latest/dist/leaflet-src.js"></script>
14.  <script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
15.  <style type="text/css">
16.    body {
17.      margin: 0;
18.    }
19.    #map {
20.      position: absolute;
21.      width: 100%;
22.      height: 100%;
23.    }
24.  </style>
25.</head>
26.<body>
27.  <div id="map"></div>
28.  <script type="text/javascript">
29.  var map = L.map('map').setView([0, 0], 2);
30.  var geocoder = L.Control.Geocoder.nominatim();
31.  if (URLSearchParams && location.search) {
32.    // parse /?geocoder=nominatim from URL
33.    var params = new URLSearchParams(location.search);
34.    var geocoderString = params.get('geocoder');
35.    if (geocoderString && L.Control.Geocoder[geocoderString]) {
36.      console.log('Using geocoder', geocoderString);
37.      geocoder = L.Control.Geocoder[geocoderString]();
38.    } else if (geocoderString) {
39.      console.warn('Unsupported geocoder', geocoderString);
40.    }
41.  }
42.  var control = L.Control.geocoder({
43.    query: 'Moon',
44.    placeholder: 'Search here...',
45.    geocoder: geocoder
46.  }).addTo(map);
47.  var marker;
48.  setTimeout(function() {
49.    control.setQuery('Earth');
50.  }, 12000);
51.  L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', {
52.    attribution: '&copy; <a href="https://osm.org/copyright">OpenStreetMap</a> contributors'
53.  }).addTo(map);
54.  map.on('click', function(e) {
55.    console.log(e.latlng)
56.    geocoder.reverse(e.latlng, map.options.crs.scale(map.getZoom()), function(results) {
57.      var r = results[0];
58.      if (r) {
59.        if (marker) {
60.          marker
61.          .setLatLng(r.center)
62.          .setPopupContent(r.html || r.name)
63.          .openPopup();
64.        } else {
65.          marker = L.marker(r.center)
66.          .bindPopup(r.name)
67.          .addTo(map)
68.          .openPopup();
69.        }
70.      }
71.    });
72.  });
73.  </script>
74.</body>
75.</html>

Example #3: shows the address on the map, from your current longitude and latitude

HTML

Click to expand!
1. <!DOCTYPE html>
2.  <html>
3.  <head>
4.    <title>Leaflet Control Geocoder</title>
5.    <meta charset="utf-8" />
6.    <meta
7.      name="viewport"
8.      content="width=device-width, user-scalable=no initial-scale=1, maximum-scale=1"
9.      />
10.   <link rel="stylesheet" href="https://unpkg.com/leaflet@latest/dist/leaflet.css" />
11.   <link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
12.   <script src="https://unpkg.com/leaflet@latest/dist/leaflet-src.js"></script>
13.   <script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
14.   <style type="text/css">
15.     body {
16.       margin: 0;
17.     }
18.     #map {
19.       position: absolute;
20.       width: 100%;
21.       height: 100%;
22.     }
23.   </style>
24. </head>
25. <body>
26.   <div id="map"></div>
27.   <script type="text/javascript">
28.     var geocoder = L.Control.Geocoder.nominatim();
29.     var map, marker, latitude, longitude;
30.     function getLocation() {
31.       if (!navigator.geolocation) {
32.         alert("Browser doesn't support geolocation");
33.       } else {
34.         navigator.geolocation.getCurrentPosition(success, error);
35.       }
36.     }
37.     // Get current position successfully
38.     function success(position) {
39.       latitude = position.coords.latitude;
40.       longitude = position.coords.longitude;
41.       // Instance map using leaflet
42.       map = L.map('map').setView([latitude, longitude], 13);
43.       // Tile layer using key api at cloudmade.com
44.       L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
45.       key: '760506895e284217a7442ce2efe97797',
46.       styleId: 103288,
47.       maxZoom: 16
48.       }).addTo(map);
49.       // Marker using leaflet
50.       marker = L.marker([latitude, longitude]).addTo(map);
51.       // Popup in leaflet
52.       marker.bindPopup('<p></p>').openPopup();
53.       getPhysicalAddress({lat:latitude, lng:longitude});
54.     }
55.     // Get current position fail
56.     function error() {
57.       alert('Get current position fail. Please access codepen to get geolocation.');
58.     }
59.     var marker;
60.     function getPhysicalAddress(latlong) {
61.       geocoder.reverse(latlong, map.options.crs.scale(map.getZoom()), function(results) {
62.       var r = results[0];
63.         if (r) {
64.           if (marker) {
65.             marker
66.              .setLatLng(r.center)
67.              .setPopupContent(r.html || r.name)
68.              .openPopup();
69.           } else {
70.               marker = L.marker(r.center)
71.              .bindPopup(r.name)
72.              .addTo(map)
73.              .openPopup();
74.           }
75.         }
76.       });
77.     }
78.     getLocation();
79.   </script>
80. </body>
81. </html>

Example #4: use of geolocation, map and reverse geocoder in a HTML form

This is just a variation of the previous examples. We embedded the interactive map in a form, and we display the results of the reverse geocoder in a form field. This example might be useful if you want to pre-fill the address of a registration form, depending on the current location of the person who is registering.

Click on the Codepen logo (on the top right) so to run the  online example (for security reasons the embedded version cannot run in this page):

HTML

Click to expand!
1.  <!DOCTYPE html>
2.  <html>
3.  <head>
4.    <title>Gimmee My Location</title>
5.    <meta charset="utf-8" />
6.    <meta name="viewport"
7.    content="width=device-width, user-scalable=no initial-scale=1, maximum-scale=1" />
8.    <link rel="stylesheet" href="https://unpkg.com/leaflet@latest/dist/leaflet.css" />
9.    <link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
10.   <script src="https://unpkg.com/leaflet@latest/dist/leaflet-src.js"></script>
11.   <script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
12. </head>
13. <body onload="getLocation();">
14.   <h1>Example of the use of reverse geocoder in a form</h1>
15.   <form>
16.   <fieldset>
17.   <legend>Form example with map and address...</legend>
18.   <div id="map" style="width: 500px; height: 300px"></div>
19.   </fieldset>
20.   <fieldset>
21.   <legend>Surface address</legend>
22.   <input id="surfaceAddress" size=110 type="text">
23.   <fieldset>
24.   <form>
25.   <div id="map"></div>
26.
27.   <script type="text/javascript">
28.     var geocoder = L.Control.Geocoder.nominatim();
29.     var map, marker, latitude, longitude;
30.     function getLocation() {
31.       if (!navigator.geolocation) {
32.         alert("Browser doesn't support geolocation");
33.       } else {
34.         navigator.geolocation.getCurrentPosition(success, error);
35.       }
36.     }
37.     // Get current position successfully
38.     function success(position) {
39.       latitude = position.coords.latitude;
40.       longitude = position.coords.longitude;
41.       // Instance map using leaflet
42.       map = L.map('map').setView([latitude, longitude], 13);
43.       // Tile layer using key api at cloudmade.com
44.       L.tileLayer('https://{s}.tile.openstreetmap.org/{z}{x}{y}.png', {
45.         key: '760506895e284217a7442ce2efe97797',
46.           styleId: 103288,
47.           maxZoom: 16
48.       }).addTo(map);
49.       // Marker using leaflet
50.         marker = L.marker([latitude, longitude]).addTo(map);
51.         // Popup in leaflet
52.         marker.bindPopup('<p></p>').openPopup();
53.         getPhysicalAddress({lat:latitude, lng:longitude});
54.     }
55.     // Get current position fail
56.     function error() {
57.       alert('Get current position fail. Please access codepen to get geolocation.');
58.     }
59.       var marker;
60.       function getPhysicalAddress(latlong) {
61.         geocoder.reverse(latlong, 500000, function(results) {
62.           var r = results[0];
63.           console.log(r.name);
64.           document.querySelector("#surfaceAddress").value = r.name;
65.           if (r) {
66.             if (marker) {
67.               marker
68.                 .setLatLng(r.center)
69.                 .setPopupContent(r.html || r.name)
70.                 .openPopup();
71.             } else {
72.               marker = L.marker(r.center)
73.                 .bindPopup(r.name)
74.                 .addTo(map)
75.                 .openPopup();
76.             }
77.           }
78.         });
79.       }
80.   </script>
81. </body>
82. </html>

6.4.7 Discussion and projects

Optional projects

Here are a few project ideas. Your classmates and the team who prepared the course will be glad to try them and offer feedback. Please post URLs in this discussion forum. These projects are optional, meaning that they won’t be graded. However, it is important to complete them to ensure good understanding of the material.

. . .The end.
Prior updated 11-20-23 Mon 11:58pm
Last updated 4-12-23 Fri 1:21am