By Jochen Voss, last updated 2012-12-18
Before you can start writing HTML5 Apps, you will need to learn (at least the basics of) the following technologies: HTML, CSS, and JavaScript.
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).
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 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.
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 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).
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>
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.
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.
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>
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>
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.
<!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>
<!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>
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.
Relevant JavaScript functions include the following:
setInterval
and
clearInterval
functions, 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.
setTimeout
and
clearTimeout
you can cause a JavaScript function to be called after a given amount of
time. You can use the getTime
method of the Date
class 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>
Sound can be played (either for background music or for short sound effects), using the audio object:
sound = new Audio("sound.mp3"); sound.play();
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>
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>
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>
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>
Many of the techniques described in this tutorial are also used in Jochen's Sliding Picture Puzzle.
General resources:
HTML5 Apps
Event handling:
Copyright © 2012 Jochen Voss. All content on this website (including text, pictures, and any other original works), unless otherwise noted, is licensed under the CC BY-SA 4.0 license.