How to Write an HTML5 App?
By Jochen Voss, last updated .
Contents
- Getting Started
- Layout
- Graphical Elements
- Animations
- Sound
- User Interaction
- Persistent Storage
- Debugging
- Deployment
- Examples
- References
Getting Started
Before you can start writing HTML5 Apps, you will need to learn (at least the basics of) the following technologies: HTML, CSS, and JavaScript.
HTML
The container for any HTML5 App will be an HTML file. The HTML file can contain any static, textual content for the HTML5 App. It also serves to tie in any pictures, movies, CSS style information (used to determine layout and colours) and JavaScript code (used for user interaction and to implement dynamic content).
- Mark Wilton-Jones, HTML tutorial
- the HTML4 specification
- the HTML5 draft specification
Example. A near-minimal HTML file is displayed in the following listing.
<!DOCTYPE html> <html> <head> <title>Test Page</title> <meta charset="utf-8"> </head> <body> <h1>Test Page</h1> <p>This is a simple test page. </body> </html>
CSS
CSS style information is used to determine colour scheme and layout of the display elements of the HTML5 App. The CSS style information can either be directly included in the HTML file, or it can be loaded from a separate file. The HTML file in the listing below uses style information to change the font colour to red.
- Mark Wilton-Jones, CSS tutorial
- the CSS2 specification
- Peter-Paul Koch's CSS compatibility tables
Example. The following listing shows how to incorporate CSS style information into the simple HTML file from the previous section.
<!DOCTYPE html>
<html>
<head>
<title>Red Test Page</title>
<meta charset="utf-8">
<style type="text/css">
html { color: red; }
</style>
</head>
<body>
<h1>Red Test Page</h1>
<p>This is a simple test page.
</body>
</html>
JavaScript
JavaScript is the programming language which controls most of the
interactive elements of an HTML5 App. The code can either be directly
included in the HTML file, or it can be loaded from a separate file. The
programms used for this purpose are typically event driven,
i.e. there is no main programm
but instead
there is just a collection of functions which are called as the result of
certain external events (e.g. when a key is
pressed, when the user clicks with the mouse, or when a timer expires).
- Mark Wilton-Jones, JavaScript tutorial
- the Mozilla developer center's JavaScript Guide
Example. The following listing illustrates how a function can be called directly after the HTML page is loaded into the browser.
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Test Page</title>
<meta charset="utf-8">
<script type="text/javascript">
function init() {
var p = document.getElementById("msg");
p.innerHTML = 'The JavaScript function init() has run.';
}
</script>
</head>
<body onload="init()">
<h1>JavaScript Test Page</h1>
<p id="msg">This is a simple test page.
</body>
</html>
Layout
The layout of an HTML5 App will typically be implemented using HTML elements and CSS. Information about how one can get complete control over the geometry of HTML elements is given on the Geometry sub-page.
Screens
Often an HTML5 App will consist of different screens
,
e.g a title screen, a settings screen and the main
work area. One way to implement this is to put each screen on a separate
HTML page, causing a reload-from the server whenever there is a change
between screens.
Alternatively you can choose URLs which only differ in the hash
(i.e. in the part after the #
character).
In this case no reload is required when switching between URLs, allowing
for faster switching between screens and also keeping JavaScript data
structures between screens. One problem with this approach is, that
current browsers don't implement the window.onhashchange
event yet, so it is difficult for your HTML5 App to detect when the user
tries to change screens by using the browser history or bookmarks.
Currently, the best solution seems to be to use polling to check for
changes in the hash manually. This is illustrated in the example below.
- the Unique URLs section on the AjaxPatterns.org web site.
- the window hashchange event documentation on the Mozilla developer center
Example. A simple HTML5 App with three different screens. The Browser history should work for going back to previously shown screens and you should be able to bookmark individual screens.
<!DOCTYPE html>
<html>
<head>
<title>Screen&History Test</title>
<meta charset="utf-8">
<script type="text/javascript">
function set_screen(name) {
if (! name) name = "#title";
screens = document.getElementsByTagName("div");
for (var i=0; i<screens.length; ++i) {
if (name == "#"+screens[i].id) {
screens[i].style.display = "block";
} else {
screens[i].style.display = "none";
}
}
}
check_hash = (function() {
var hash;
return function() {
if (window.location.hash != hash) {
hash = window.location.hash;
set_screen(hash);
}
}
})();
function init() {
setInterval(check_hash, 100);
}
</script>
</head>
<body onload="init()">
<div id="title">
<h1>Welcome to the Screen&History Test Application</h1>
<p><a href="#main">start</a>
</div>
<div id="main">
<h1>Main Screen</h1>
<p><a href="#settings">settings</a>
</div>
<div id="settings">
<h1>Settings</h1>
<p><a href="#main">done</a>
</div>
</body>
</html>
Window Size
To adapt your HTML5 App to the size of the browser window (which might
be quite small on a mobile device), some care is needed. You can either
use the pair document.documentElement.clientWidth,
document.documentElement.clientHeight or alternatively you
can look at the pair window.innerWidth,
window.innerHeight. The values can actually slightly
different, as explained for example in a tutorial at the
quirksmode.org
web page.
<!DOCTYPE html>
<html>
<head>
<title>Viewport Size Test</title>
<meta charset="utf-8">
<script type="text/javascript">
function resize_handler() {
var p = document.getElementById("msg");
var w = document.documentElement.clientWidth;
var h = document.documentElement.clientHeight;
p.innerHTML = w + "×" + h + " CSS pixels";
}
</script>
</head>
<body onResize="resize_handler()">
<p id="msg">Resize the window or zoom in/out.
</body>
</html>
Graphical Elements
This section lists various methods to generate graphical elements inside an HTML5 App, either using plain HTML elements, or using designated mechanisms like the HTML5 canvas object.
Rectangles
<!DOCTYPE html>
<html>
<head>
<title>A Red Rectangle</title>
<meta charset="utf-8">
<style type="text/css">
DIV#box {
position: fixed;
width: 100px;
height: 162px;
left: 10px;
top: 10px;
background: red;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
Triangles
Circles
<!DOCTYPE html>
<html>
<head>
<title>A Red Circle</title>
<meta charset="utf-8">
<style type="text/css">
DIV#circle {
position: fixed;
width: 100px;
height: 100px;
left: 10px;
top: 10px;
background: red;
-webkit-border-radius: 50px;
-moz-border-radius: 50px;
}
</style>
</head>
<body>
<div id="circle"></div>
</body>
</html>
More General Shapes
- the HTML5 Canvas element
Animations
There are several ways to incorporate animations into an HTML5 App, for example you could use animated GIF images, embedded movies, or you could use JavaScript to change elements of the HTML page periodically.
JavaScript
Relevant JavaScript functions include the following:
- Using the
setIntervalandclearIntervalfunctions, you can arrange for a JavaScript funtion to be called in regular intervals. This can be used to periodically update the the position/colour/style of HTML elements. - Using
setTimeoutandclearTimeoutyou can cause a JavaScript function to be called after a given amount of time. You can use thegetTimemethod of theDateclass to work out how much time has actually passed once the function is called.
Example.
<!DOCTYPE html>
<html>
<head>
<title>Moving Red Box</title>
<meta charset="utf-8">
<style type="text/css">
DIV#box {
position: fixed;
width: 64px;
height: 64px;
background: red;
}
</style>
<script type="text/javascript">
angle = 0;
function move_box() {
angle += 0.05;
var x = 100*(1.1+Math.cos(angle))
var y = 100*(1.1+Math.sin(angle))
box.style.left = x+"px";
box.style.top = y+"px";
}
function init() {
box = document.getElementById("box");
setInterval(move_box, 20);
}
</script>
</head>
<body onload="init()">
<div id="box" style="left: 210px; top=100px"></div>
</body>
</html>
Example. The following example shows use of the
JavaScript Date object for use in animations.
<!DOCTYPE html>
<html>
<head>
<title>Milliseconds Since the Start of 1970</title>
<meta charset="utf-8">
<script type="text/javascript">
function show_ms() {
var p = document.getElementById("time");
var now = new Date;
p.innerHTML = now.getTime();
setTimeout(show_ms, 10);
}
</script>
</head>
<body onload="show_ms()">
<p id="time">loading …
</body>
</html>
Movies
Sound
- the sections about the Audio element and the Media elements interface in the HTML5 specification
- the jPlayer HTML5 <audio> and Audio() Support Tester
Sound can be played (either for background music or for short sound effects), using the audio object:
sound = new Audio("sound.mp3");
sound.play();
User Interaction
Keyboard Input
- Peter-Paul Koch, Detecting keystrokes
- Jan Wolter, JavaScript Madness: Keyboard Events
- the keyboard event and text event interfaces
Keyboard input can be processed using several different ways. The most
basic method is to use the standard HTML form elements for text input. A
more sophisticated way consists in assigning a Javascript function to the
onKeyDown
handler of an HTML element. The function can either be
assigned directly in the HTML code (see the listing below), or you can use
a JavaScript function to modify the onKeyDown
attribute of an
element somewhere during the run of the program. To capture all keyboard
input, you can use the onKeyDown
attribute of the BODY element as
in the following listing.
<!DOCTYPE html>
<html>
<head>
<title>KeyBoard Input Test</title>
<meta charset="utf-8">
<script type="text/javascript">
function key_handler(event) {
var p = document.getElementById("msg");
var res = '';
for (var i in event) {
res += "event."+i+" = "+event[i]+"<br>";
}
p.innerHTML = res;
return false;
}
</script>
</head>
<body onKeyDown="return key_handler(event)">
<p id="msg">press any key
</body>
</html>
The keyhandler should return a boolean value: true
indicates that the web browser should process the key event using its
usual key-bindings (e.g. to scroll the page),
false indicates that the keyhandler has already dealt with
the key and the browser should ignore it.
Warning. Browser behaviour is inconsistent with respect to automatic key repeat: If you keep a key pressed, Google Chrome and Safari seem to call the key-handler repeatedly, whereas Firefox seems to call the key-handler only once. Details can for example be found in Jan Wolter's article about Keyboard Events.
Example. The following program allows the user to move a red box around the screen using the cursor keys.
<!DOCTYPE html>
<html>
<head>
<title>Keyboard Controlled Red Box</title>
<meta charset="utf-8">
<style type="text/css">
DIV#box {
position: fixed;
width: 64px;
height: 64px;
background: red;
}
</style>
<script type="text/javascript">
x = 50;
y = 100;
function place_box() {
box.style.left = x + "px";
box.style.top = y + "px";
}
function key_handler(e) {
if (e.shiftKey || e.controlKey || e.metaKey || e.altKey) {
return true;
}
if (e.keyCode == 39) { // right
x += 10;
} else if (e.keyCode == 37) { // left
x -= 10;
} else if (e.keyCode == 38) { // up
y -= 10;
} else if (e.keyCode == 40) { // down
y += 10;
} else {
return true;
}
place_box();
return false;
}
</script>
</head>
<body onLoad="place_box()" onKeyDown="return key_handler(event)">
<p>Use the cursor keys to move the red box.
<div id="box"></div>
</body>
</html>
Mouse/Pointer Events
- the MouseEvent interface
- Peter-Paul Koch's Mouse Events page
Example. The following program allows the user to move a red box around the screen using the mouse.
<!DOCTYPE html>
<html>
<head>
<title>Mouse Controlled Red Box</title>
<meta charset="utf-8">
<style type="text/css">
DIV#box {
position: fixed;
width: 64px;
height: 64px;
background: red;
}
</style>
<script type="text/javascript">
x = 50;
y = 100;
function place_box() {
box.style.left = x + "px";
box.style.top = y + "px";
}
function mouse_move(e) {
pos = [ e.pageX, e.pageY ];
x = pos[0] - dx;
y = pos[1] - dy;
place_box();
}
function mouse_down(e) {
pos = [ e.pageX, e.pageY ];
dx = pos[0] - x;
dy = pos[1] - y;
document.onmousemove = mouse_move;
document.onmouseup = mouse_up;
return false;
}
function mouse_up(e) {
document.onmousemove = null;
document.onmouseup = null;
}
function init() {
box = document.getElementById("box");
box.onmousedown = mouse_down;
place_box();
}
</script>
</head>
<body onLoad="init()">
<p>Use the mouse to move the red box.
<div id="box"></div>
</body>
</html>
Persistent Storage
- the W3C Web Storage draft specification
Debugging
Deployment
- html5rocks' Beginner's Guide to Using the Application Cache
- Let's take this offline
- the Google Chrome Web Store
Examples
Infinite Dragable Plane
The following example implements an infinite
plane which can be
dragged in the browser window using the mouse. An extended version of this
program could, for example, be used to display maps or very big photos.
<!DOCTYPE html>
<html>
<head>
<title>Infinite Dragable Plane</title>
<meta charset="utf-8">
<style type="text/css">
HTML { background: gray; }
BODY { margin: 0px; }
P { margin: 1px; font-size: 10px }
DIV.box {
position: absolute;
width: 62px;
height: 62px;
background: white;
}
</style>
<script type="text/javascript">
rect = [ 0, 0, 0, 0 ]; // xmin, ymin, xmax, ymax
xoffs = 100;
yoffs = 100;
size = 64;
function add_block(x, y) {
var block = document.createElement('div');
block.id = "t"+x+"."+y
block.className = "box";
block.style.left = size*x+1+"px";
block.style.top = size*y+1+"px";
block.innerHTML = "<p>"+x+"/"+y+"</p>";
key = (x*Math.E + y*Math.PI) % 1;
if (key < 0) key += 1;
if (Math.floor(10*key) == 0) {
block.style.background = "yellow";
}
b.appendChild(block);
}
function remove_block(x, y) {
var block = document.getElementById("t"+x+"."+y);
b.removeChild(block);
}
function foreach_block(x0, y0, x1, y1, fn) {
for (var x=x0; x<x1; ++x) {
for (var y=y0; y<y1; ++y) {
fn(x, y);
}
}
}
function clear() {
foreach_block(rect[0], rect[1], rect[2], rect[3], remove_block);
rect = [ 0, 0, 0, 0 ];
}
function set_xmin(xmin) {
if (xmin >= rect[2]) {
clear();
} else {
if (rect[0] > xmin) {
foreach_block(xmin, rect[1], rect[0], rect[3], add_block);
} else {
foreach_block(rect[0], rect[1], xmin, rect[3], remove_block);
}
rect[0] = xmin;
}
}
function set_xmax(xmax) {
if (xmax <= rect[0]) {
clear();
} else {
if (rect[2] < xmax) {
foreach_block(rect[2], rect[1], xmax, rect[3], add_block);
} else {
foreach_block(xmax, rect[1], rect[2], rect[3], remove_block);
}
rect[2] = xmax;
}
}
function set_ymin(ymin) {
if (ymin >= rect[3]) {
clear();
} else {
if (ymin < rect[1]) {
foreach_block(rect[0], ymin, rect[2], rect[1], add_block);
} else {
foreach_block(rect[0], rect[1], rect[2], ymin, remove_block);
}
rect[1] = ymin;
}
}
function set_ymax(ymax) {
if (ymax <= rect[1]) {
clear();
} else {
if (rect[3] < ymax) {
foreach_block(rect[0], rect[3], rect[2], ymax, add_block);
} else {
foreach_block(rect[0], ymax, rect[2], rect[3], remove_block);
}
rect[3] = ymax;
}
}
function redraw() {
var w = document.documentElement.clientWidth;
var h = document.documentElement.clientHeight;
set_xmin(Math.floor(-xoffs/size));
set_xmax(Math.ceil((w - xoffs)/size));
set_ymin(Math.floor(-yoffs/size));
set_ymax(Math.ceil((h - yoffs)/size));
}
function mouse_move(e) {
var pos = [ e.pageX, e.pageY ];
xoffs = pos[0] - dx;
yoffs = pos[1] - dy;
b.style.left = xoffs + "px";
b.style.top = yoffs + "px";
redraw();
}
function mouse_down(e) {
var pos = [ e.pageX, e.pageY ];
dx = pos[0] - xoffs;
dy = pos[1] - yoffs;
document.onmousemove = mouse_move;
document.body.style.cursor = "move";
return false;
}
function mouse_up(e) {
document.onmousemove = null;
document.body.style.cursor = "default";
}
function init() {
b = document.getElementById("canvas");
b.style.left = xoffs + "px";
b.style.top = yoffs + "px";
redraw();
window.onresize = redraw;
window.onmousedown = mouse_down;
window.onmouseup = mouse_up;
}
</script>
</head>
<body onLoad="init()">
<div id="canvas" style="position: fixed"></div>
</body>
</html>
Timer with Persistent Storage
The HTML5 App from the listing below implements a simple timer. You can stop/start it using the SPACE bar. If you haven't disabled persistent local storage in your browser, the timer will continue running even while your web browser is not running.
<!DOCTYPE html>
<html>
<head>
<title>Timer with Persistent Storage</title>
<meta charset="utf-8">
<style type="text/css">
P#time {
font-size: 48pt;
font-family: courier;
margin: 0px auto;
color: gray;
}
</style>
<script type="text/javascript">
timer = null;
function get_time() {
var now = new Date;
return now.getTime();
}
function show() {
var t = timer ? get_time() - base : total;
var s = Math.round(t/1000);
var m = Math.floor(s/60);
var h = Math.floor(m/60);
s = s%60;
if (s < 10) s = "0"+s;
m = m%60;
if (m < 10) m = "0"+m;
if (h < 10) h = "0"+h;
p.innerHTML = h+":"+m+":"+s;
}
function update() {
show();
dt = 1000 - (get_time()-base)%1000;
timer = setTimeout(update, dt);
}
function start() {
base = get_time() - total;
localStorage.base = base;
delete localStorage.total;
p.style.color = "black";
update();
}
function stop() {
clearTimeout(timer);
timer = null;
total = get_time() - base;
delete localStorage.base;
localStorage.total = total;
p.style.color = "gray";
}
function key_handler(e) {
if (e.keyCode != 32) return true;
if (timer) stop(); else start();
}
function init() {
p = document.getElementById("time");
total = localStorage.total ? localStorage.total : 0.0;
show();
if (localStorage.base) {
total = get_time() - localStorage.base;
start();
}
window.onkeydown = key_handler;
}
</script>
</head>
<body onload="init()">
<p id="time">loading …
<p>Use the SPACE key to start/stop the timer.<br>
It will magically keep working, even while you are logged out.
</body>
</html>
Sliding Puzzle Game
Many of the techniques described in this tutorial are also used in Jochen's Sliding Picture Puzzle.
References
General resources:
- Peter-Paul Koch's
quirksmode.org web page, and in
particular his
browser compatibility tables
and his
advertisement
for
HTML5 Apps
- the Wikipedia page about HTML5
- the tutorials on Mark Wilton-Jones' howtocreate.co.uk web site
- the W3C HTML4 specification and HTML5 draft specification
- the Mozilla developer center's JavaScript Guide
Event handling:
- the W3C event specification and the CSSOM extensions to the MouseEvent interface
- Jan Wolter, JavaScript Madness: Keyboard Events
- Christian Heilmann, JavaScript Events And Responding To The User