How to Write an HTML5 App?

By Jochen Voss, last updated 2012-12-18

Contents

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).

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>

[download]

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.

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>

[download]

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).

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>

[download]

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.

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&amp;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&amp;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>

[download]

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 + "&times;" + h + " CSS pixels";
    }
  </script>
</head>
<body onResize="resize_handler()">
  <p id="msg">Resize the window or zoom in/out.
</body>
</html>

[download]

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>

[download]

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>

[download]

More General Shapes

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:

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>

[download]

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 &hellip;
</body>
</html>

[download]

Movies

Sound

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

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>

[download]

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>

[download]

Mouse/Pointer Events

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>

[download]

Persistent Storage

Debugging

Deployment

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>

[download]

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 &hellip;
  <p>Use the SPACE key to start/stop the timer.<br>
  It will magically keep working, even while you are logged out.
</body>
</html>

[download]

Sliding Puzzle Game

Many of the techniques described in this tutorial are also used in Jochen's Sliding Picture Puzzle.

References

General resources:

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.