The Geolocation API

INTRODUCTION

This chapter presents the new Geolocation API and illustrates its use with several examples.

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

CURRENT SUPPORT IS EXCELLENT

As at June 2016, support for this API is excellent, both on mobile and on desktop devices.

geolocation support table

To get an updated table, check caniuse.com

TYPICAL USE

  1. navigator.geolocation.getCurrentPosition(showPosition, onError);
  2.  
  3. function showPosition(position) {
  4.     console.log(“latitude is: “ + position.coords.latitude);
  5.     console.log(“longitude is: “ + position.coords.longitude);
  6. }
  7.  
  8. function onError(err) {
  9.     console.log(“Could not get the position”);
  10. }

This online example at JS Bin shows how to get the current longitude and latitude and display them in an HTML page. Try it below in your browser:

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:

  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3. <head>
  4. <title>Basic example of use of the geolocation API</title>
  5. </head>
  6. <body>
  7. <p id=“msg”>Click the button to get your coordinates:</p>
  8. <button onclick=getLocation()>Where am I ?</button>
  9.     var displayCoords=document.getElementById(“msg”);
  10.     function getLocation() {
  11.        if (navigator.geolocation) {
  12.           navigator.geolocation.getCurrentPosition(showPosition);
  13.        } else {
  14.           displayCoords.innerHTML=“Geolocation API not supported by your browser.”;
  15.        }
  16.    }
  17.    function showPosition(position) {
  18.        displayCoords.innerHTML=“Latitude: “ + position.coords.latitude +
  19.                                
    Longitude: “
    + position.coords.longitude;
  20.    }
  21. </body>
  22. </html>

Explanations:

    • Line 14 checks if the Web browser supports the geolocation API by testing the variable navigator.geolocation. If not null, then the geolocation API is supported.
    • Line 15 callsnavigator.geolocation.getCurrentPosition(showPosition) passing a callback function as a parameter (in this example we did not specify a callback in case of error). When a current position is available, the callback function will be called asynchronously, and the input parameter of this callback function will be the current position, like in the functionshowPosition(position) of the example.
    • Line 22: the position objects has a coords property that is the object that holds the longitude and the latitude.

EXTERNAL RESOURCES:

The different properties of the coords object

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
latitude The latitude of the position
longitude The longitude of the position
altitude The altitude of the position
accuracy The accuracy of the measure of the longitude and latitude (in meters)
altitudeAccuracy The accuracy of the measure of the altitude (in meters)
heading gives the orientation relative to north, in degrees
speed current speed in meters/second

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.

 

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.

This example on JS Bin shows how to properly check against the different possible errors, it’s just a slightly different version of the previous example. 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.

Source code of the example:

  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3. <head>
  4. <title>Basic example of use of the geolocation API</title>
  5. </head>
  6. <body>
  7. <p id=“msg”>Click the button to get your coordinates:</p>
  8. <button onclick=getLocation()>Where am I ?</button>
  9.    var displayCoords=document.getElementById(“msg”);
  10.    function getLocation() {
  11.       if (navigator.geolocation) {
  12.          navigator.geolocation.getCurrentPosition(showPosition, errorPosition);
  13.       } else {
  14.         displayCoords.innerHTML=“Geolocation API not supported by your browser.”;
  15.       }
  16.    }
  17.    function showPosition(position) {
  18.       displayCoords.innerHTML=“Latitude: “ + position.coords.latitude +
  19.                                
    Longitude: “
    + position.coords.longitude;
  20.    }
  21.    function errorPosition(error) {
  22.       var info = “Error during geolocation: “;
  23.       switch(error.code) {
  24.          case error.TIMEOUT:
  25.             info += “Timeout !”;
  26.             break;
  27.          case error.PERMISSION_DENIED:
  28.             info += “Permission denied, geolocation could not be obtained…”;
  29.             break;
  30.          case error.POSITION_UNAVAILABLE:
  31.             info += “Location could not be obtained though the available means…”;
  32.             break;
  33.          case error.UNKNOWN_ERROR:
  34.             info += “Unknown error”;
  35.             break;
  36.       }
  37.       displayCoords.innerHTML = info;
  38.     }
  39. </body>
  40. </html>

Tracking a position in real time

INTRODUCTION

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:

    • It gets the callback function only when the current position changes. If you stay in the same location, the callback function won’t be called regularly.
    • It returns an id so that you can use the clearWatch(id) method to stop the current tracking.

TYPICAL USE

  1. // get an id of the current tracking, the showPosition callback is like the one we saw in earlier examples.
  2. var watchPosId = navigator.geolocation.watchPosition(showPosition);
  3. // stop the tracking
  4. navigator.geolocation.clearWatch(watchPosId);

As an exercise, 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.

External resource

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:

Properties of the coords object
enableHighAccuracy A boolean (true/false) which indicates to the device that you wish to obtain its most accurate readings. in other words: use the GPS please! (However, this parameter may or may not make a difference, depending on your hardware, GPS availability, etc.)
maximumAge The maximum amount of time (in milliseconds) the position may remain in the cache (this is appropriate as the device may cache readings to save power and/or bandwidth).
timeout The maximum time (in milliseconds) for which you are prepared to allow the device to try to obtain a Geo location. After this timeout value has elapsed, the onError callback is called.

Example of use (see the explanations in the lines of comment):

  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:600000timeout: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:0timeout: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});

Practical examples: use the geolocation API together with Google Maps

This section presents some examples of how to get a static map (a picture), using the Google Static Map API, how to display an interactive map using the Google Map JavaScript API and even how to get an estimation of a physical address from the longitude and latitude, using the Google Reverse Geocoding JavaScript API.

The following three examples increase in complexity, but most of the code is reused and adapted without even reading the Google documentation about the different APIs.

EXAMPLE 1 (EASY):  HOW TO GET A STATIC IMAGE MAP CENTERED ON YOUR LONGITUDE AND LATITUDE

Online example available on JS Bin, or try it here in your browser:

It also illustrates the use of the error callback from the previous section. The Google Map API is used to get an image centered at the longitude and latitude collected with the HTML5 Geolocation API.

Source code extract:

  1. <!DOCTYPE html>
  2. <html>
  3. <body>
  4. <p id=“demo”>Click the button to get your position:</p>
  5. <button onclick=getLocation()>Try It</button>
  6. id=“mapholder”>

  7. var x=document.getElementById(“demo”);
  8.  
  9. function getLocation() {
  10.    if (navigator.geolocation) {
  11.       navigator.geolocation.getCurrentPosition(showPosition,showError);
  12.    } else{
  13.       x.innerHTML=“Geolocation is not supported by this browser.”;
  14.    }
  15. }
  16.  
  17. function showPosition(position) {
  18.    // Google map API needs the latitude and longitude separated by a comma
  19.    var latlon=position.coords.latitude+“,”+position.coords.longitude;
  20.    // Google map API URL that returns an image centered on the longitude and latitude
  21.    var img_url=https://maps.googleapis.com/maps/api/staticmap?center=&#8221;
  22.                +latlon+“&zoom=14&size=400×300&sensor=false”;
  23.    document.getElementById(“mapholder”).innerHTML=+img_url+“‘ />”;
  24. }
  25.  
  26.  function showError(error) {
  27.     … 
  28.  }
  29. </body>
  30. </html>

The magic occurs at line 23, where we use the Google Static Map API.

EXAMPLE 2 (A BIT MORE COMPLICATED…) THAT SHOWS HOW TO DISPLAY AN INTERACTIVE GOOGLE MAP CENTERED ON THE CURRENT POSITION

This example is just given “as is”, as there are so many possibilities for rendering a map with the Google Map API. However, we think having such a basic example might be useful.

Online example at JS Bin

Source code of the example:

  1. <!doctype html>
  2. <html>
  3. <head>
  4. </head>
  5. <body>
  6. <!– for position display –>
  7. id=“myposition”>

  8.  
  9. <!– for gmap display –>
  10. id=“map” style=width:640px;height:480px>

  11.  
  12. <!– get gmap API –>
  13. src=https://maps.google.com/maps/api/js?sensor=false&#8221;>
  14.  
  15. // Default position
  16. var centerpos = new google.maps.LatLng(48.579400,7.7519);
  17.  
  18. // default options for the google map
  19. var optionsGmaps = {
  20.     center:centerpos,
  21.     navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL},
  22.     mapTypeId: google.maps.MapTypeId.ROADMAP,
  23.     zoom: 15
  24. };
  25.  
  26. // Init map object
  27. var map = new google.maps.Map(document.getElementById(“map”), optionsGmaps);
  28.  
  29. if(navigator.geolocation) {
  30.  
  31.     // callback function, called by getCurrentPosition() in case of success
  32.     function drawPosition(position) {
  33.        var infopos = “Got position :
    ;
  34.        infopos += “Latitude : “+position.coords.latitude +
    ;
  35.        infopos += “Longitude: “+position.coords.longitude+
    ;
  36.        infopos += “Altitude : “+position.coords.altitude +
    ;
  37.        document.getElementById(“myposition”).innerHTML = infopos;
  38.  
  39.        // Make new object LatLng for Google Maps
  40.        var latlng = new google.maps.LatLng(position.coords.latitude,               
  41.                                            position.coords.longitude);
  42.  
  43.        // Add a marker at position
  44.        var marker = new google.maps.Marker({
  45.                              position: latlng,
  46.                              map: map,
  47.                              title:“You are here”
  48.        });
  49.        // center map on longitude and latitude
  50.        map.panTo(latlng);
  51.     }
  52.  
  53.     // callback function, called by getCurrentPosition() in case of error
  54.     function errorPosition(error) {
  55.        …
  56.     }
  57.     navigator.geolocation.getCurrentPosition(drawPosition,errorPosition);
  58. } else {
  59.     alert(“Geolocation API not supported by your browser”);
  60. }
  61. </body>
  62. </html>

EXAMPLE 3 (ADVANCED) SHOWS HOW TO GET A PHYSICAL ADDRESS FROM THE LONGITUDE AND LATITUDE

This is another example that obtains an address from longitude and latitude. It usesthe 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.

Without going into detail, the below example might be useful to copy/paste/adapt for trying to pre-fill a form where one is asked for an address. Geolocation is useful for guessing the country, city, zip code, street, etc. Some examples that use this feature will be given in the next section of the course.

Online example at JS Bin.

Source code of the example:

  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3. <head>
  4.      src=https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&#8221;>
  5. // p elements for displaying lat / long and address
  6. var displayCoords, myAddress;
  7. // used with the google apis
  8. var geocoder;
  9. var map;
  10. var infowindow = new google.maps.InfoWindow();
  11. var marker;
  12. // Called when the page is loaded
  13. function init() {
  14.     displayCoords=document.getElementById(“msg”);
  15.     myAddress = document.getElementById(“address”);
  16.     geocoder = new google.maps.Geocoder();
  17.     // In order to show something even before a user clicks on the button
  18.     var latlng = new google.maps.LatLng(34.0144, 6.83);
  19.     var mapOptions = {
  20.        zoom: 8,
  21.        center: latlng,
  22.        mapTypeId: ‘roadmap’
  23.     }
  24.     map = new google.maps.Map(document.getElementById(‘map_canvas’), mapOptions);
  25. } // end of init()
  26. // Called when the button is clicked
  27. function getLocation() {
  28.     if (navigator.geolocation) {
  29.        navigator.geolocation.getCurrentPosition(showPosition);
  30.     } else {
  31.        displayCoords.innerHTML=“Geolocation API not supported by your browser.”;
  32.     }
  33. }
  34. // Called when a position is available
  35. function showPosition(position) {
  36.     displayCoords.innerHTML=“Latitude: “ + position.coords.latitude +
  37.                            
    Longitude: “
    + position.coords.longitude;
  38.     // Display the map
  39.     showOnGoogleMap(new google.maps.LatLng(position.coords.latitude,       
  40.                                            position.coords.longitude));
  41.  }
  42.  function showOnGoogleMap(latlng) {
  43.    // Ask google geocoder for an address once we get a longitude and
  44.    // a latitude. In fact, the reverse geocoder sends back an array of “guesses”
  45.    // i.e. not just one address object, but several. Each entry in this array
  46.    // has several properties such as street, city, etc. We use the “formatted_address”
  47.    // one here, but it might be interesting to get the detailed properties in other
  48.    // applications like a form with street, city, zip code etc.
  49.    geocoder.geocode({‘latLng’: latlng},reverseGeocoderSuccess);
  50.    function reverseGeocoderSuccess(results, status) {
  51.      if (status == google.maps.GeocoderStatus.OK) {
  52.         if (results[1]) {
  53.            map.setZoom(11);
  54.            marker = new google.maps.Marker({
  55.                                 position: latlng,
  56.                                 map: map
  57.                         });
  58.            infowindow.setContent(results[1].formatted_address);
  59.            infowindow.open(map, marker);
  60.            // Display address as text in the page
  61.            myAddress.innerHTML=“Adress: “ + results[0].formatted_address;
  62.         } else {
  63.            alert(‘No surface address found’);
  64.         }
  65.       } else {
  66.          alert(‘Geocoder failed due to: ‘ + status);
  67.       }
  68.     } // end of reverseGeocoderSuccess
  69. } // end of showOnGoogleMap
  70. </head>
  71. <body onload=init()>
  72. <title>HTML5 + Geolocalisation + Google Maps API Reverse Geocoding</title>
  73. <p id=“msg”>Click the button to get your coordinates:</p>
  74. <p id=“address”></p>
  75. <button onclick=getLocation()>Where am I ?</button>
  76. id=“map_canvas” style=width: 500px; height: 300px>

  77. </body>
  78. </html>

Example: fill a form’s address fields automatically

INTRODUCTION

In the previous example, we used the results returned by the Google reverse geocoding service, without going into detail.

In this section, we will see how we can get the different parts of the responses (city, street, zip code, country, etc.) The reverse geocoding service tries to guess what is the “best” address that matches the longitude and latitude, but sometimes the first guess is not the best one.

HOW TO PARSE THE GOOGLE REVERSE GEOCODING RESULTS?

What are the Google reverse geocoding results exactly?

A common question is: how to have a robust code for parsing the Google reverse geocoding, to properly get the city, street, country, etc.

Depending on your location/country and on the geolocation method used by your browser (GPS on phone, IP, WiFi, 3G, etc.), some of the data might not be available (i.e., no street). So, there is no guarantee that all candidate addresses will get the same defined properties. For example, the first result may give a defined city, but the third result may not.

Look at this line of code from the last example from the previous page – the example that showed the address when you clicked on the button:

  1. // Display address as text in the page
  2. myAddress.innerHTML=“Adress: “ + results[0].formatted_address;

At line 2, we get the first address returned by the Google reverse geocoding service, and use the formatted_address property. Let’s suppose that it contained the best address, formatted as a string. We chose to use it and showed it in the page by setting myAddress.innerHTML with its value (myAddress pointed to the <p id=”address”></p> element in the page).

Let’s examine the detailed results

We add a console.dir(results) in the code, to see a structured view of the results in dev. tools console.

Once we get the results, we can get the different parts:

Here is an example of how we can parse such a field. Notice that each field is tested to see if it exists. The results are stored in the variables defined at line 1.

  1. var country, postalCode, state, route, streetNumber, locality, areaLvl1, areaLvl2;
  2. function parseResult(result) {
  3.      for(i in result){
  4.          console.log(“type = “ + result[i].types[0] + ” long_name = “ +
  5.                      result[i].long_name);
  6.          if(result[i].types[0] == ‘postal_code’)
  7.              postalCode = result[i].long_name;
  8.          if(result[i].types[0] == ‘country’)
  9.              country= result[i].long_name;
  10.          if(result[i].types[0] == ‘street_number’)
  11.              streetNumber= result[i].long_name;
  12.          if(result[i].types[0] == ‘route’)
  13.              route= result[i].long_name;
  14.          if(result[i].types[0] == ‘locality’)
  15.              locality= result[i].long_name;
  16.          if(result[i].types[0] == ‘state’)
  17.              state= result[i].long_name;
  18.          if(result[i].types[0] ==‘administrative_area_level_2’)
  19.              arealLvl2= result[i].long_name;
  20.          if(result[i].types[0] ==‘administrative_area_level_1’)
  21.              areaLvl1= result[i].long_name;
  22.      }
  23.      // added this for debugging in the console
  24.     console.log(“postalCode = “ + postalCode);
  25.     console.log(“country = “ + country);
  26.     console.log(“streetNumber = “ + streetNumber);
  27.     console.log(“route = “ + route);
  28.     console.log(“locality = “ + locality);  
  29.     console.log(“Administrative area level 1 “ + areaLvl2);
  30.     console.log(“Administrative area level 2 “ + areaLvl1);
  31. }

 

A FORM THAT AUTO FILLS THE ADDRESS INPUT FIELDS

Example at JS Bin

It’s very hard to create a single code that will work in all situations and in all countries, since postal addresses are formatted differently depending on the country. A decoder that works well 99% of the time in the UK may be wrong for Australia, for instance. So, it’s just a “guess system”, and in real life, if you create a Web site and would like to help the user with completing a form, just fill in the country, city, postal code, and suggest the rest, propose a small icon for deleting the street input field content, etc. You could also add a drop down menu that offers not only the first guess but the second and third, etc.

Source code extract:

  1. function showOnGoogleMap(latlng) {
  2.    …
  3.    // Display address as text in the page
  4.    myAddress.innerHTML=“Adress: “ + results[0].formatted_address;
  5.    // Call the function that parses the results and fills
  6.    // the input fields
  7.    parseResult(results[0].address_components);
  8.    …
  9. }
  10. var country, postalCode, state, route, streetNumber, locality, areaLvl1, areaLvl2;
  11. function parseResult(result) {
  12.     for(i in result){
  13.        // Let’s print all the data we can collect from the reverse geocoder,
  14.        // Look at the debug console to see what we get…
  15.        console.log(“type = “ + result[i].types[0] + ” long_name = “ +
  16.                                result[i].long_name);
  17.  
  18.        if(result[i].types[0] == ‘postal_code’)
  19.           postalCode = result[i].long_name;
  20.        …
  21.        // fill input fields now, check if variables are undefined
  22.        if((route != undefined) && (streetNumber !=undefined)) {
  23.           console.log(“let’s fill the street”);
  24.           document.querySelector(“#address1”).value = streetNumber + ” “ + route;
  25.        }
  26.        if(locality != undefined) {
  27.           console.log(“let’s fill the city”);
  28.           document.querySelector(“#address2”).value = locality;
  29.        }
  30.        if(country != undefined) {
  31.           console.log(“let’s fill the country”);
  32.           document.querySelector(“#country”).value = country;
  33.        }
  34.        …
  35.     }
  36. }
  37. </script>

This example is rather long and we have only shown an extract of the source code. Take your time and look at the online example.

 

File example

SOURCE CODE OF THE EXAMPLE SHOWN IN THE VIDEO

Example on JS Bin

 

  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” multipleonchange=readImagesAndPreview(this.files);>
  9. <p>
  10. id=“thumbnails”>

  11.    var container = document.getElementById(“thumbnails”);
  12.    function readImagesAndPreview(files) {
  13.       for(var i=0; i files.length; i++) {
  14.          var f = files[i];
  15.          var reader = new FileReader();
  16.          reader.onload = function(e) {
  17.             var img = document.createElement(“img”);
  18.             img.src = e.target.result;
  19.             img.width = 100;
  20.             container.appendChild(img);
  21.          }
  22.          reader.readAsDataURL(f);
  23.      }
  24. }
  25.  
  26. </body>
  27. </html>

 

Getting details about a file: reading metadata

Imagine you have an input field like this:

  1. Select one or more files: <input type=“file” id=“input”/>

This will render 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 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:

  1. var selectedFile = document.getElementById(‘input’).files[0];
  2. // do something with selectedFile.name, selectedFile.size, selectedFile.type
  3. // selectedFile.lastModifiedDate

EXAMPLE 1: READ METADATA OF THE FIRST SELECTED FILE

Here is a complete example on JS Bin 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):

Complete source code:
  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3. <head>
  4. <meta charset=utf-8 />
  5. <title>Reading file metadata</title>
  6.      function displayFirstSelectedFileMetadata() {
  7.         var selectedFile = document.getElementById(‘input’).files[0];
  8.         document.querySelector(“#singleName”).innerHTML = selectedFile.name;
  9.         document.querySelector(“#singleSize”).innerHTML = selectedFile.size + ” bytes”;
  10.         document.querySelector(“#singleType”).innerHTML = selectedFile.type;
  11.         document.querySelector(“#singleDate”).innerHTML selectedFile.lastModifiedDate;
  12. }
  13. </head>
  14. <body>
  15.    Select one or more files: <input type=“file”id=“input”
  16.                                     onchange=displayFirstSelectedFileMetadata();/>
  17. <p>
  18. <ul>
  19.     <li>File name: <span id=“singleName”></span></li>
  20.     <li>File size: <span id=“singleSize”></span></li>
  21.     <li>File type: <span id=“singleType”></span></li>
  22.     <li>File last modification date: <spanid=“singleDate”></span></li>
  23. </ul>
  24. </body>
  25. </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 acceptattribute of the input field: <input type=”file” accept=”image/*”…/>.

Example on JS Bin, 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.

Source code extract:

  1. Select several images: <input type=“file” accept=“image/*” multipleonchange=“filesProcess(this.files)” name=“selection”/>
  2. <p>
  3. <div id=“result”>…</div>
  4.   function filesProcess(files) {
  5.       var selection =

  6.                        
  7. ;

  8.       for(i=0; ifiles.length ;i++){
  9.           file = files[i];
  10.           selection +=
  11. ;

  12.       }
  13.       selection +=
  14. Name Bytes MIME Type Last modified date
    +file.name+
  15.                     +file.size+
  16.                     +file.type+
  17. +file.lastModifiedDate+

    ;

  18.  
  19.       document.getElementById(“result”).innerHTML = selection;
  20.   }
  21. script>

Explanations:

    • Line1: we used the multiple attribute to allow the selection of multiple files in the file chooser (using shift or control keys). The accept=”image/*”  attribute is a filter that restricts selection to images only. Finally, the onchange listener will call the filesProcess(…) function, passing as parameter the list of selected files for the current element (this.files).
    • Lines 7 and 12: we prepare the HTML code for building a
      with the results.
    • Line 10: this for loop builds all the rows that compose the table, adding HTML code to the selection string variable. At the end of the loop, this variable contains all the HTML code that corresponds to the table of results.
    • Line 18: the table is added to the page. We use the innerHTML attribute of the DOM element corresponding to the
      in order to insert the table as its child in the DOM tree. As such, the table appears on the page dynamically.
    • Blob and File: what are they?

      THE HTML5 FILE API SPECIFICATION INTRODUCES SEVERAL NEW INTERFACES

        • The FileList interface (we already met it: the files property is a FileList),
        • the File interface that is useful for getting details about a file (the filevariable in the for loop of the last example illustrates this) .
        • the Blob interface helps read binary data (only) that is accessed slice by slice (as chunks of data, each one being a “Blob”),
        • and a FileReader interface for reading file content (we will see how to use it in the next section of the course),

      We will not use all of these new 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 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.

      There is also a method called slice(), but this is not used in common applications. If you are curious, check the “slicing a file” section of this “Reading files in JavaScript using the File APIs” article.

      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 the Filesystem API is widely available (at the moment there is only an experimental version in Chrome), or when you download binary files using Ajax (see example below).

       

      Reading file content

      INTRODUCTION / TYPICAL USE

      Step1: 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 & 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 , , , 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 field). Below, we use, as an example, the readAsText method:

      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 , and by choosing a file.

        • Line 12 is executed first, and asks the Reader object to read the file f as text. As this takes some time, it’s an asynchronous operation that will be executed by the browser in the background. When the file is read, the reader.onloadcallback function is called.
        • Line 4 is executed after line 12, and is called only when the file content is available. This callback takes an event e as a unique parameter, ande.target.result is the file content.

      Practical examples: reading file content as text

      EXAMPLE 1: READ A SINGLE FILE’S CONTENT

      Example at JS Bin, or try it here in your browser

      1. lang=“en”>
      2. charset=“utf-8”>
      3. Example of use of FileReader with a text file
      4. Choose a text file:type=“file” id=“file”
      5.                            onchange=readFileContent(this.files)/>
      6. rows=15 cols=50 id=“fileContent”>
      7. function readFileContent(files) {
      8.      console.log(“In readFileContent”);
      9.      var reader = new FileReader();
      10.     // Executed last: called when the file content is loaded, e.target.result is
      11.     // The content
      12.     reader.onload = function(e) {
      13.         // display content in the textarea with id=”fileContent”
      14.         document.getElementById(“fileContent”).value= e.target.result;
      15.     };
      16.     console.log(“Reading file:” + files[0].name);
      17.     // Executed first: start reading the file asynchronously , will call the onload
      18.     // callback when the file is read
      19.     reader.readAsText(files[0]);
      20. }
      21. </body>
      22. </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 onloadcallback at line 20.

      EXAMPLE 2: A VARIATION OF THE PREVIOUS ONE, USING MULTIPLE FILES

      Example on JS Bin, or try it below in your browser. This time, please select multiple text files (using shift for multiple selection):

      Source code:
      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.        multipleonchange=readFilesAndDisplayAsText(this.files);/><br/>
      11. <p>
      12. <textarea rows=30 cols=50 id=“filesContent”></textarea>
      13. var filesContent = document.getElementById(“filesContent”);
      14. function readFilesAndDisplayAsText(files) {
      15.      console.log(“dans read files”);
      16.      // Loop through the FileList
      17.      for (var i = 0, f; f = files[i]; i++) {
      18.          var reader = new FileReader();
      19.          // Add an onload listener to the reader
      20.          addOnLoadListener(reader, f.name);
      21.          // start reading, will call the listener later, when the file f is read
      22.          reader.readAsText(f);
      23.      }
      24. }
      25. function addOnLoadListener(reader, name) {
      26.      // Add an onload listener that will be able to print the name of the
      27.      // file…
      28.      reader.onload = function(e) {
      29.          filesContent.value += “###### READING FILE “ + name + ” ######”;
      30.          filesContent.value += e.target.result;
      31.      };
      32. }
      33. </body>
      34. </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 CHARACTER ENCODING

      Note that you can optionally indicate the encoding of the file you are going to read (default is UTF-8):

      1. reader.readAsText(file, ‘UTF-8’);
      2. reader.readAsText(file, ‘ISO-8859-1’);

      Read file content as binary

      INTRODUCTION

      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 1: 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 JS Bin (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.

      Source code extract:

      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:

        • Line 2: we get a pointer to the file selector, the variable fileInput.
        • Line 4: we define a change listener. In this example, we use an anonymous function directly included in the listener definition (the listener is the function(e) {…}).
        • Line 11: when a user chooses a file, the listener will be executed. Line 11 will start the reading of the file content, as a binary file (this is whatreadAsArrayBuffer means: read as binary!). Once the file will be entirely read, the onload callback will be asynchronously called by the browser.
        • Line 7 is the onload callback, executed when the file content is loaded in memory. We pass the file content to the initSound function (see JS Bin example for complete source code) that uses WebAudio to decode it (it may be a compressed file – an mp3 for example – and WebAudio works only with uncompressed audio formats in memory), and to play it.

      Read file content as data URL

      INTRODUCTION TO A DATA URL

      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 inlining 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:

      1. data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==

      This data URL in a browser address bar should look like this:

      data url in adress 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 http://

      In your browser, you will see a small red circle rendered by this source code:

      1. <imgsrc=“data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
      2. AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
      3. 9TXL0Y4OHwAAAABJRU5ErkJggg==” alt=“Red square” width=50 height=50/>

      And here is the result:

      Red square

      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 the DataURL maker Web site (screenshot below):

      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 media files (images, audio, video).

      Example of HTML5 logo embedded in a document without any real image, just a dataURL and CSS

      Try it at JsBin

      EXAMPLE 1: READ IMAGES AS DATA URL AND DISPLAY PREVIEWS IN THE PAGE

      Example 1 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 JS Bin or try it below in your browser:

      Source code extract:

      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.  
      7. <script>
      8.   function readFilesAndDisplayPreview(files) {
      9.     // Loop through the FileList and render image files as thumbnails.
      10.     for (var i = 0, f; f = files[i]; i++) {
      11.  
      12.     // Only process image files.
      13.     if (!f.type.match(‘image.*’)) {
      14.         continue;
      15.     }
      16.  
      17.     var reader = new FileReader();
      18.  
      19.     //capture the file information.
      20.     reader.onload = function(e) {
      21.         // Render thumbnail. e.target.result = the image content
      22.         // as a data URL
      23.        // create a span with CSS class=”thumb”, for nicer layout
      24.        var span = document.createElement(‘span’);
      25.        // Add an img src= in the span, with src= the dataURL of
      26.        // the image
      27.        span.innerHTML = “<img class=‘thumb’ src=‘” +
      28.                          e.target.result + “‘ alt=‘a picture’/>“;
      29.        // Insert the span in the output id=list
      30.        document.getElementById(‘list’).insertBefore(span, null);
      31.    };
      32.   // Read in the image file as a data URL.
      33.   reader.readAsDataURL(f);
      34.  }
      35. }

      Explanations:

        • Line 35: starts the reading of the file f. When f is read, the onload callback will be called.
        • Lines 25-31: we build, using the DOM API, a <span class=”thumb”>…</span> and inside we add an <img src=the data url> element with itssrc attribute equal to the url of the image that has been read (the image content as dataURL is in e.target.result). Finally, at line 31, we insert the span in the document before the current children of the <output id=”list”> element (declared at line 5).

      EXAMPLE 2: READ A SINGLE LOCAL IMAGE FILE AND USE IT WITH DRAWIMAGE IN A CANVAS

      Try it on JS Bin

      Errata: the above screenshot says “choose multiple files”, but the example only works with a single file.

      Source code extract:

      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 at http://codepen.io/giana/). 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 (or click the screenshot below)

      The File API

      The File API

      INTRODUCTION

      The objective of this chapter is to provide an overview of the File API.

      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 Audio player below (Google Chrome extension written in HTML5), or an application that edits the audio content of local mp3 files, for example, the HYA-WAVE sound editor .

      EXTERNAL RESOURCES

       

      The Web Storage API (localStorage, sessionStorage)

      The Web Storage API (localStorage, sessionStorage)

      INTRODUCTION

      The Web storage API (see the related W3C specification) 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 sessionStoragethe data is erased when the tab/browser is closed

      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:

      1. // Using localStorage
      2. // store data
      3. localStorage.lastName = “Bunny”;
      4. localStorage.firstName = “Bugs”;
      5. localStorage.location = “Earth”;
      6. // retrieve data
      7. var lastName = localStorage.lastName;
      8. var firstName = localStorage.firstName;
      9. var location = localStorage.location;

      This data is located in a store attached to the origin of the page. We created a JS Bin 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:

      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

      Example 1: a form that never loses what you entered, even if you reload the page, or press “backspace” by mistake

      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.

      In this example, we use the most simple way to use localStorage:

        • Save with the localStorage.key = value syntax. For example, localStorage.firstName = ‘Michel’ will save the value “Michel” with the access key being ‘firstName’
        • Restore with the var value = localStorage.key syntax. For example, var fn = localStorage.firstName; will set fn with the value ‘Michel’ if this value has been previously saved as in the example from the line above.

      SAVING THE FORM CONTENT ON THE FLY

      Open this online example at JS Bin, 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:

      1. oninput=“localStorage.firstName=this.value;”

      Where firstName in red is the key and this.value the current value of the input field.

      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 JS Bin: enter data and press reload at any time. The form content is restored!

      Source code extract (only addition to the previous example):

      1. // Called when the page is loaded
      2. window.onload = restoreFormContent;
      3.  
      4. function restoreFormContent() {
      5.    console.log(“restoring form content from localStorage”);
      6.    if(localStorage.firstName !== undefined)
      7.      document.getElementById(“firstName”).value = localStorage.firstName;
      8.    if(localStorage.lastName !== undefined)
      9.      document.getElementById(“lastName”).value = localStorage.lastName;
      10.    if(localStorage.email !== undefined)
      11.      document.getElementById(“email”).value = localStorage.email;
      12.  
      13.    if(localStorage.age !== undefined)
      14.      document.getElementById(“age”).value = localStorage.age;
      15.    if(localStorage.date !== undefined)
      16.      document.getElementById(“date”).value = localStorage.date;
      17. }

      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.

       

      More methods from localStorage/sessionStorage

      This time we will look at another example that uses new methods from the API:

        • localStorage.setItem(…),
        • localStorage.getItem(…),
        • localStorage.removeItem(…),
        • localStorage.clear().

      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:

        • The key can contain spaces, for example we can write: localStorage.setItem(“Instructor’s name”, “Michel”); and var name =  localStorage.getItem(“Instructor’s name”);, whilevar name = localStorage.Instructor’s name; will not work!
        • In a loop or in an iterator, sometimes we need to set/get localStorage values using this syntax, for example:
      1. var inputField = document.getElementById(“firstName”);
      2. saveInputFieldValue(inputField);
      3. function saveInputFieldValue(field) {
      4.     localStorage.setItem(field.id, field.value);
      5. }

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

      Observant students will 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. So 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 THAT SHOWS ALL THE METHODS OF THE LOCAL STORAGE API IN ACTION

      Online example at JS Bin, 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.

      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.    // Using localStorage
      7.    var counter = localStorage.getItem(“count”) || 0;
      8.    counter++;
      9.    localStorage.setItem(“count”, counter);
      10.    function getCountValue() {
      11.       // retrieve data
      12.       document.querySelector(“#counter”).innerHTML = localStorage.count;
      13.    }
      14.    function seeAllKeyValuePairsStored() {
      15.       // clear list first
      16.       document.querySelector(‘#list’).innerHTML=“”;
      17.       for (var i = 0, n = localStorage.length; i n; i++) {
      18.          var key = localStorage.key(i);
      19.          var value = localStorage[key];
      20.          console.log(key + “: “ + value);
      21.          var li = document.createElement(‘li’);
      22.          li.innerHTML = key + “: “ + value;
      23.          document.querySelector(‘#list’).insertBefore(li, null);
      24.       }
      25.    }
      26.    function resetStore() {
      27.         // erase all key values from store
      28.         localStorage.clear();
      29.         // reset displayed list too
      30.        document.querySelector(‘#list’).innerHTML=“”;
      31.    }
      32.    function addSomeData() {
      33.       // store data
      34.       localStorage.lastName = “Buffa”;
      35.       localStorage.firstName = “Michel”;
      36.       // refresh display
      37.       seeAllKeyValuePairsStored();
      38.    }
      39.    function removeSomeData() {
      40.       // store data
      41.       localStorage.removeItem(“lastName”);
      42.       localStorage.removeItem(“firstName”);
      43.       // refresh display
      44.       seeAllKeyValuePairsStored();
      45.    }
      46. </head>
      47. <body onload=getCountValue()>
      48.    <h1>Number of times this page has been seen on this browser: <spanid=“counter”></span></h1>
      49.    <button onclick=seeAllKeyValuePairsStored()>Show all key value pairs stored in localStorage</button><br/>
      50.    <output id=“list”></output>
      51.  
      52.    <button onclick=addSomeData()>Add some data to the store</button><br/>
      53.    <button onclick=removeSomeData()>Remove some data</button><br/>
      54.    <button onclick=resetStore()>reset store (erase all key/value pairs)</button>
      55. </body>
      56. </html>

      You can check in the Chrome dev. tools user interface that the content of the localStorage changes as you click on the buttons.

       

      Practical example 2: save/restore user’s preferences

      INTRODUCTION

      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:

      In this way, the next time you come back to JS Bin, “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:

       

      PRACTICAL EXAMPLE: SAVE/RESTORE PREFERENCES OF AN EXAMPLE YOU HAVE ALREADY SEEN

      Original example on JS Bin: 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 JS Bin. 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:

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

      Practical example 3: example 1 on steroids

      TRY THE EXAMPLE!

      Online example at JS Bin

      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). addEventListener will not 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():

      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
      9.    // 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. Later in the course, we will show how to reuse them with another example: the animated red rectangle.

       

      Size limitation, security, localStorage or sessionStorage?

      We have already talked about client-side storage limits when we studied the HTML5 cache. We saw that the limit varies depending on various parameters. However, it’s good to recall a few things from the specification:

        • User agents (browsers) should limit the total amount of space allowed for storage areas.
        • User agents may prompt the user when quotas are reached, allowing the user to grant more space to a site. This enables sites to store many user-created documents on the user’s computer, for instance.
        • User agents should allow users to see how much space each domain is using.
        • A mostly arbitrary limit of five megabytes per origin is recommended (translation: give at least 5Mb per origin).

      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 HTML5 part-2 course.

      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’ big danger: 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!

      Storing more than strings? Use JSON!

      INTRODUCTION

      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

      1. locaStorage.key = JSON.stringify(object); // or…
      2. localStorage.setItem(key, JSON.stringify(object));

      Let’s try a simple toy example (online at JS Bin).  The example below saves a JavaScript object in JSON, then restores it and checks that the object properties are still there!

      Source code:

      1. <!DOCTYPE html>
      2. <html>
      3. <head>
      4. <meta charset=utf-8 />
      5. <title>Storing JSON Objects with Local Storage</title>
      6.     var personObject= {‘firstName’: ‘Michel’, ‘lastName’: ‘Buffa’};
      7.     // Store the object as a JSON String
      8.     localStorage.setItem(‘testObject’, JSON.stringify(personObject));
      9.     // Retrieve the object from storage
      10.     var retrievedObject = JSON.parse(localStorage.getItem(‘testObject’));
      11.     console.log(retrievedObject.firstName + ” “ + retrievedObject.lastName);
      12.    // then you can use retrievedObject.firstName, retrievedObject.lastName…
      13. </head>
      14. <body>
      15. </body>
      16. </html>

      Explanations:

        • Line 7: we built a JavaScript object that contains a person.
        • Line 10: we store it in localStorage as a JSON string object, with a key equal to testObject.
        • Line 13: we restore it from localStorage as a string, and the JSON.parse methods turns it back into a JavaScript object.
        • Line 15: we print the values of the object properties.

      MORE COMPLETE EXAMPLE THAT SHOWS HOW WE CAN SAVE A FORM’S CONTENT IN JSON

      Online example on JS Bin that saves in localStorage an array of contacts in JSON

      MORE COMPLETE EXAMPLE: A FORM AND A TABLE THAT DISPLAYS THE CONTACTS STORED IN LOCALSTORAGE

      Example on JS Bin (uses summary/details, so use a browser that supports it or add a polyfill, as seen in Week 1).

      Add contacts using the form, see how the HTML table is updated. Try to reload the page: data are persisted in localStorage.

      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 page with the new elements seen during Week 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.)

       

      What to cache?

      What to cache?

      DO YOU NEED TO CACHE RESOURCES INCLUDED BY YOUR CSS/JAVASCRIPT FILES, ETC.?

      If your CSS files use @import or include some external pictures, you need to explicitly add them one by one to the manifest file. The same applies to your JavaScript files: if they include other files, and you want them to be added to the cache, you must add them one by one to the manifest file. This can be tricky, so fortunately there are several tools to help you generate a manifest file. More on these in the next unit of this course section.

      WHAT ABOUT CROSS DOMAIN/EXTERNAL DOMAIN FILES? CAN WE CACHE THEM TOO?

      Normally yes, you can. Imagine a Web site that uses jQuery or any other common JS/CSS addons: you will need to have all the required files in the cache, otherwise the site will not run properly offline.

      So, all external resources referenced through http:// URLs will be cached without restrictions.

      However, the specification, , says that for Web sites accessible only through https:// secure connections, the same origin policy should apply. Only resources from the same domain, when referenced by https://, should be cached.

      In fact, some browsers, such as Google Chrome, do not adhere to this part of the specification and also cache resources referenced by https://. It has been arguedthat the single origin policy for https is too restrictive in the real world for app caching to be of genuine value

       

      Useful tools for generating or validating a manifest file

      MANIFESTR: A MANIFEST FILE GENERATOR

      manifestR is a bookmarklet, meaning that you are able to drag it to your bookmarks bar. Then, when you visit a page, you will click on the manifestR button, and it will create an HTML5 appcache manifest file for that page. See the live coding at section 6.3.2 to see it in action!

      CONFESS.JS: MULTI PURPOSE COMMAND LINE TOOLS

      Confess.js is a small script library that uses PhantomJS 1.2 (or later) to analyze Web pages. Currently it can perform the following tasks:

        • Generation of an appcache manifest for a Web app
        • Simple performance analysis of a Web page and its resources
        • List of CSS properties used on the page

      CACHE VALIDATORS

      Manifesto is an HTML5 Offline Application Cache verification bookmarklet. Go to their Web site, drag an icon to the toolbar of your browser, and when you visit an HTML page that has a manifest file, click the manifesto bookmarklet. This will generate a report. The Web site proposes several links to try it out with valid and invalid manifest files.

      The http://manifest-validator.com/  Web site accepts URLs of manifest files, or pasted content. It will validate the file. It also proposes a Chrome extension and a TextMate addon.

       

      Updating the cache

      DO YOU REMEMBER HOW THE CACHE WORKS?

      When a resource is in the cache, it will ALWAYS be retrieved from the cache, even if you are online, and even if a more recent version is available on your Web server.

      SO HOW DO WE FORCE THE UPDATE OF THE CACHE?

      Best practice: update the manifest file on your server!

      Updating the manifest file server side will update the files cached by browsers: the easiest way to make the browser update the cache is to update the manifest file itself.

      A best practice is to add a comment with the last modification date of your Web site in the manifest file. If you update any file on your site, also update this comment. Your manifest last modified date will be changed, and the browser will check all files in the CACHE section of the manifest for modification, and update the cached version if necessary.

      Clear the cache on the client side?

      Clearing the cache using the browser dev. tools/options will also regenerate the cache at the next connection. It’s not common to manually clear the cache. Usually Web developers do this when debugging.

      Cache size limitations

      INTRODUCTION

      For traditional Web sites of normal size, there is no problem. Just create a manifest file as we’ve shown you (in the “make your Web site run offline” unit), and your Web site should work offline!

      On today’s (2015) desktop browsers, the available cache size is generally sufficient per domain, and there is no need to worry.

      If a user visits your Web site and if this Web site asks to be cached, and if there is no more free space available, the browser will find some (in general by asking the user permission to increase the cache size limit for the current domain – see screenshots-, or by clearing least recently used data). On mobile devices, the cache size limits are lower but reasonable.

      With some atypical applications that need to cache large amounts of data (images, videos), there may indeed be inadequate space in the cache. In this case, the browser prints an error message in the console. It’s also possible to use a JavaScript API to detect this situation and show the user an error or a warning message in the page “Sorry, this Web site will not be available offline as we could not cache it due to size limitations…”. This advanced part will not be covered in this course.

      Anyway, if you wish to find out more about size limits and what the various HTML5 persistence mean, please read the rest of this page. As such, there will be no exercises related to the optional content material below!

      FOR TRADITIONAL WEB PAGES

      There is a size limit, but it varies depending on many parameters!

      There are size limitations, but they vary from one browser to another. And they also vary depending on whether your browser is running on a mobile or desktop device. Finally they may vary depending on the size of the hard disk or the size of the memory of the device the browser is running on!

      For example, with Chrome there is no maximum size limit for cached data (it is computed depending on the configuration and on the current amount of data already persisted).  But if you use the LocalStorage persistence for storing data, (see next section of this course), then, for this special kind of data,  Chrome applies a 10Mbytes per domain limit. And this is completely different on FireFox, on Safari, etc. The W3C specification recommends at least 5Mbytes per domain with support for at least 20 domains.

      We recommend reading this article from HTML5 rocks (January 2014). The author wrote a Web application that tries to fill all available space and “guess how the different persistent storage spaces” are managed by different browsers. He tried with all major browsers on mobile devices and on desktop.

      On the right: a screenshot of some of the resulting measures (extract from the above article).

      Also, for greater detail, see this paper about “Managing HTML5 offline storage”.

      A quota management JavaScript API exists!

      In order to make Web applications aware of the free space available for persisting their data, a Quota Management API is being developed. Note that this API specification is under active discussion and is subject to change in the future. It’s still experimental.

      Try this example on JS Bin that shows the available disk space for all HTML5 persistence means (cache, localStorage, IndexedDB, etc.). Try it with Google Chrome: as of June 2015, it is the only browser that implements the Quota API.

      Source code extract:

      1. function showFreeSpace() {
      2.   //the type can be either TEMPORARY or PERSISTENT
      3.   webkitStorageInfo.queryUsageAndQuota(webkitStorageInfo.TEMPORARY,
      4.             onSuccessonError);
      5. }
      6.  
      7. function onSuccess(usedSpace, remainingSpace) {
      8.     var displaySpace = document.querySelector(“#space”);
      9.     displaySpace.innerHTML = “Used space = “ + usedSpace + “, remaining space = “
      10.                             + remainingSpace;
      11. }
      12.  
      13. function onError(error) {
      14.     console.log(‘Error’, error);
      15. }

      APPLICATIONS FROM STORES OR APPLICATIONS THAT RUN ON A WEB-BASED OPERATING SYSTEM

      note about “non traditional Web applications”

      Finally, the amount of free storage space is managed differently if your Web application is not a “traditional HTML page” loaded in a browser, i.e. using a http:// or https://URL. It may be a browser extension, a browser application (coming from a store such as Chrome store, Windows store, FireFox OS store, etc), it may run on a Playstation 4 or on a Xbox One.

      In these cases, HTML5 applications can get more privileges such as reading/writing to your hard disk and benefit from a large disk space. Imagine that they are similar to applications you install on your smartphone: you accept that they use XXX megabytes, use your GPS without asking for permission, etc. This course will not cover this type of application, but only focuses on traditional Web pages and applications.

      Checking that the browser is online/offline

      The new HTML5 persistence APIs are very often used with the  navigator.onLine property, part of the DOM API. This feature is available on all browsers. The navigator.onLine property returns true or false depending on whether or not the application has network connectivity.

      Beware that a browser may be “online”, but if your applications talk to a remote server, which does not answer, or if your DNS server is down, being online does not mean that your application fully works. Gmail, for example, detects when the remote service is down and displays a message “trying to connect in 30s…”.

      Important: if the browser is offline, this means that your application should work in “degraded, offline mode”. If it’s online, it should work, but there is no guarantee that your remote server is up and running, that the DNS server is operational, etc.

      Check connectivity: online example on JS Bin

      1. <!DOCTYPE html>
      2. <html lang=“en”>
      3. <head>
      4.   <meta charset=utf-8>
      5.   <title>HTML5 Demo: Online connectivity monitoring</title>
      6. </head>
      7. <body>
      8.    <header>
      9.      <h1>Online connectivity monitoring</h1>
      10.      <style>
      11.      #status {
      12.         color: #FFFFFF;
      13.         padding: 5px;
      14.      }
      15.      .online {
      16.          background: green;
      17.      }
      18.      .offline {
      19.          background: red;
      20.      }
      21.      </style>
      22.    </header>
      23.    <article>
      24.      <p>Current network status (try to disconnect wifi or unplug
      25.         your ethernet cable):
      26.         <span id=“status”>checking…</span>
      27.      </p>
      28.      <ol id=“state”></ol>
      29.    </article>
      30.    
      31.     var statusElem = document.getElementById(‘status’),
      32.     var state = document.getElementById(‘state’);
      33.     function online(event) {
      34.      statusElem.className = navigator.onLine ? ‘online’ :‘offline’;
      35.      statusElem.innerHTML = navigator.onLine ? ‘online’ :‘offline’;
      36.      state.innerHTML +=
      37. New event: ‘ + event.type +
      38. ;

      39.     }
      40.     window.addEventListener(‘online’, online);
      41.     window.addEventListener(‘offline’, online);
      42.     // call the online function so that it refreshes display when
      43.     // the page is first loaded
      44.     online({ type: ‘ready’ });
      45. </html>

       

      Usually, one checks if the application is running in online or offline mode (in the latter case, data may be retrieved from the client side using one of the various methods presented in this week’s course). Instead of displaying messages (lines 41-42 in the code source shown above), you should use an implementation like this:

      1. window.addEventListener(‘online’, function(e) {
      2. // Re-sync data with server.
      3. }, false);
      4. window.addEventListener(‘offline’, function(e) {
      5. // Queue up events for server, store them on the browser side
      6. }, false);

       

      The HTML5 cache manifest file

      HTML5 introduced a “manifest file” as an attribute to the <html> element. This file will tell the browser what to cache, what not to cache, and some other goodies we will see in this section.

      Below is an example of the inclusion of a manifest file (it is best practice/recommendation to use the .appcache suffix):

      1. <html manifest=“myCache.appcache”>
      2. </html>

      You must include the  manifest attribute on every page for which you want to cache resources (images included in the page, JavaScript files, CSS files). The manifest file will contain lines that will dictate which image/js/css needs to be cached, which must never be cached, etc.

      The page with the manifest is de facto included in the list of files that will be cached. This means that any page the user navigates to that includes a manifest will be implicitly added to the application cache.

      The browser does not cache a page if it does not contain the manifest attribute. The default cache behavior, prior to HTML5, will be used (depending on the browser versions). If you want a Web site or a Web app to work offline, then please include a manifest in every HTML page!

      The manifest attribute value is a relative path (relative to the page URL), but an absolute URL can be used (not recommended) if it respects the same origin policy.

      THE MANIFEST FILE MUST BE SERVED WITH THE CORRECT MIME TYPE

      The HTTP server that serves your files must be configured so that .appcache files are served with the MIME type text/cache-manifest. For example, with the Apache server, this line must be added in the HTTP.conf configuration file (or in the .htaccess files):

      1. AddType text/cachemanifest .appcache

      WHAT DO WE PUT IN THE MANIFEST FILE?

      First example: cache an HTML page that displays the current time (clock). It uses three pages: HTML, CSS and JavaScript. This example is taken from the W3C specification, you can try the online example here.

      The manifest file is:

      1. CACHE MANIFEST
      2. clock.html
      3. clock.css
      4. clock.js

      Lines 2-4 show that a given HTML page, in which this manifest is included, asks the browser to cache three files: the HTML page itself (clock.html, cached by default, but the specification recommends adding it to the manifest as best practice), a CSS file clock.css and a JavaScript file clock.js.

      Note that the first line “CACHE MANIFEST” is mandatory.

      PITFALLS TO AVOID WHEN USING THE HTML5 CACHE

      PITFALL #1 : When a file is available in the cache and on the remote HTTP server, it will always be retrieved from the cache! An upcoming section is dedicated to “updating the cache”, and we will explain how to control this and update the files in the cache.

      PITFALL #2: If one file cannot be retrieved and cached, zero files will be updated in the cache. There is no such thing as “partial update”. A best practice is to always validate your manifest file using one of the tools listed at the end of this chapter.

      With the same example, Firefox asks if you agree to cache some data (the sentence is in French here but it says: “This Web site would like to save data on your computer for offline use. Authorize? Never? Just for this time?”):

      Let’s have a look at another example of manifest.appcache (this one comes from the webdirections.org Web site), that does a little more:

      1. CACHE MANIFEST
      2. CACHE:
      3. #images
      4. /images/image1.png
      5. /images/image2.png
      6. #pages
      7. /pages/page1.html
      8. /pages/page2.html
      9. #CSS
      10. /style/style.css
      11. #scripts
      12. /js/script.js
      13. FALLBACK:
      14. / /offline.html
      15. NETWORK:
      16. login.html

      This time, notice a few additional things:

        • It’s possible to add comments starting with #
        • There are three different sections in capital letters: CACHE, FALLBACK and NETWORK

      These three sections are optional – we did not have them in the first example. But as soon as you indicate one of them, you must indicate the others. (CACHE was defaulted in the first example as we had no explicit section declarations).

      The CACHE section specifies the URLs of the resources that must be cached(generally relative to the page, but they can also be absolute and external, for example for caching jQuery from a Google repository, etc.). These resources will be 1) cached when online, and 2) available from the cache when offline.

      The NETWORK section is the contrary of the CACHE section: it is useful for specifying resources that should NOT be cached. These resources 1) will not be cached when online, and consequently 2) will not be available when the user is offline. EVEN IF THE BROWSER HAS CACHED THEM IN ITS OWN “PRE HTML5” cache! In the previous example, at line 23, the login.html file (the one with the login/password form…) is never cached. Indeed, entering login/password and pressing a “login/connect/signup” button is useless if you are offline.

      Using a wildcard * in this section is also common practice; this means “do not cache all files that are not in the CACHE or FALLBACK section”:

      1. NETWORK:
      2. *

      Partial URLs may also be used in this section, such as “/images”, which means, all URLs that end with images/*… should not be cached. Notice that wildcards and partial URLs are not allowed in the CACHE section, where all individual  files must be explicitly specified.

      The FALLBACK section specifies resources that will be displayed when the user requests a resource that is not available when offline. For example, a login.html file must not be cached, nor be available when offline. In this case, accessing  http://…/login.html will cause offline.html to be displayed (and this file will be cached, this is forced by being in the FALLBACK section). The “/ /offline.html” in the FALLBACK section of the example says that for any resource that is not available in the cache (here, “/” means “any resource”), show the offline.html page.

      Partial URLs can be used too. For example:

      1. /images/ /images/missing.png

      … tells us that all images in the sub-directory “images” relative to the Web page that includes the manifest, if unavailable in the cache when the browser is offline, will be replaced by an image named “missing.png”.

      HTML5 JavaScript APIs

      HTML5 JavaScript APIs

      We have already studied some of the HTML5 JavaScript APIs, including:

        • The canvas API,
        • The form validation API,
        • The audio and video elements API, the getUserMedia API (also related to audio and video elements),

      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.

        • The HTML5 Cache API for making Web sites and Web applications work offline.
        • The “Web Storage” API, sort of “super cookies”, for storing pairs of key/values client side. This API is useful for enabling Web sites to save/restore their state, or for writing serverless applications. You will see a small contact manager that stores its data locally, without the need for a remote Web server.
        • The File API, that enables Web applications to work with local files. For example, a picture editor, or media player that can work with your music and video files – the ones on your phone or your hard disk! With this API you will also be able to preview image files directly in your page – there is no need to send them to a remote server.
        • The Geolocation API for obtaining data such as longitude, latitude, altitude (when available), and speed. You will learn how to write applications in combination with interactive maps. You will also learn how to guess the address of a user, for example for pre-filling a registration form with the city, country, and zip code that corresponds with the current location.

      HTML5 cache / offline applications

      INTRODUCTION

      Even back in the 90’s, long before HTML5 existed, all browsers implemented a native way of caching files. However, the implementations and heuristics differed from one browser to another, and there was no easy way to control (at the application layer) which parts will be cached.

      The HTML5 specification introduces a new way to handle caching. As stated in the “offline Web applications” section of the document: “In order to enable users to continue interacting with Web applications and documents even when their network connection is unavailable — for instance, because they are traveling outside of their ISP’s coverage area — authors can provide a manifest which lists the files that are needed for the Web application to work offline and which causes the user’s browser to keep a copy of the files for use offline.

      By “offline applications”, this means:

        1. That a Web site can be browsed while no internet connection is available. But it also says that:
        2. The Web page may be an “application”, not just a static page.

      Meaning, it may be a video game that usually retrieves data from a Web server, saves high scores, etc. It may be a mail application (like Gmail) that shows the most recent emails, checks for new ones, and has features for composing and sending new emails, etc.

      In these cases, having a controllable “cache API” helps implement things like:

        • When offline, you can read and compose emails, but you cannot receive or send new emails.
        • A more complex behavior would be that, when offline, you can write emails that will be stored locally on the client side and sent only when connectivity is available.
        • Or, in a static Web site, everything can be retrieved from the cache, so that a user is able to read the Web site offline, but the login page will not be available… instead, a disclaimer page will be displayed, etc.

      The HTML5 cache tries to address aspects of these needs, and is pretty useful for mobile Web applications, where connections are not always available/reliable.

      Note: Native mobile applications developers often push the following argument that “a Web app cannot run offline”. This is a false statement!

      Using the HTML5 cache is both useful for mobile applications, as these device can often be offline, and for traditional Web developers who do not specifically target mobile devices. Any Web site (even comprised 100% of static pages/images/videos/etc.) can benefit from being intelligently cached, as the HTML5 cache can dramatically decrease the load time for any given site, especially if the same browser/person visits the Web site regularly, as illustrated by these pictures below (borrowed from a MSDN blog post about offline applications that is worth reading):

      CURRENT SUPPORT

      Current support is very good, as stated by the caniuse.com Web site. All major browsers – desktop or mobile – have supported the HTML5 cache since 2012.

      EXTERNAL RESOURCES (OPTIONAL, IF YOU WISH TO LEARN MORE ABOUT THIS TOPIC)

      Tutorials

      Tools

      The HTML5 JavaScript form validation API

      The HTML5 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.

      TYPICAL USE

      Example of password checking at JS Bin,  be careful to try this example in JS Bin standalone mode (click the small black arrow on the top right of the output tab).

       

      Extract from source code:

      1. <html>
      2. <head>
      3.    <title>Example of using the validation API</title>
      4.    <style>
      5.      .myForm input:invalid { backgroundcolor: lightPink;}
      6.      .myForm input:valid { backgroundcolor:lightGreen; }
      7.      .myForm input:required {border: 2px solid red;}
      8.      .myForm input:optional {border: 2px solid green;}
      9.      .myForm label { display: inlineblock; width: 140px; textalign: right; }
      10.    </style>
      11. </head>
      12. <body>
      13. <form class=“myForm”>
      14.    <fieldset>
      15.      <legend>Example use of the validation API</legend>
      16.      <label for=“password1” >Password:</label>
      17.      <input type=“password” id=“password1”oninput=checkPasswords(); required>
      18.      <p>
      19.      <label for=“password2”>Repeat password:</label>
      20.      <input type=“password” id=“password2”oninput=checkPasswords(); required>
      21.      <p>
      22.      <button>Submit</button>
      23.   </fieldset>
      24. </form>
      25.  
      26.   function checkPasswords() {
      27.       var password1 = document.getElementById(‘password1’);
      28.       var password2 = document.getElementById(‘password2’);
      29.       if (password1.value != password2.value) {
      30.           password2.setCustomValidity(‘Passwords do not match!’);
      31.       } else {
      32.          password2.setCustomValidity();
      33.       }
      34.    }
      35. </body>
      36. </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 17 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 31-35 check if the passwords are the same and set the validity of the field using the validation API’s methodsetCustomValidity(error_message).

       

      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. var validityState_object = input.validity;

      The possible values for the validity property are:

        • valueMissing
        • typeMismatch
        • patternMismatch
        • tooLong
        • rangeUnderflow
        • rangeOverflow
        • stepMismatch
        • valid
        • customError

      Here is an example at JS Bin 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.):

      Note that testing it in Chrome/Opera/Firefox does not produce the same results. So far Opera has the most advanced implementations, however, entering “21” for example in the <input type=”number” max=”20″/> input field may yield some unexpected results depending on the browser. Test it yourself.

      Source code:

      1. <!DOCTYPE html>
      2. <html>
      3. <body>
      4. function validate() {
      5.      var input = document.getElementById(‘b’);
      6.      var validityState_object = input.validity;
      7.  
      8.      if(validityState_object.valueMissing) {
      9.          input.setCustomValidity(‘Please set an age (required)’);
      10.      } else if (validityState_object.rangeUnderflow) {
      11.          input.setCustomValidity(‘Your value is too low’);
      12.      } else if (validityState_object.rangeOverflow) {
      13.          input.setCustomValidity(‘Your value is too high’);
      14.      } else if (validityState_object.typeMismatch) {
      15.          input.setCustomValidity(‘Type mismatch’);
      16.      } else if (validityState_object.tooLong) {
      17.          input.setCustomValidity(‘Too long’);
      18.      } else if (validityState_object.stepMismatch) {
      19.          input.setCustomValidity(‘stepMismatch’);
      20.      } else if (validityState_object.patternMismatch) {
      21.          input.setCustomValidity(‘patternMismatch’);
      22.      } else {
      23.          input.setCustomValidity();
      24.      }
      25. }
      26. <form class=“myForm”>
      27. <label for=“b”>Enter a value between 10 and 20: </label>
      28.  
      29. <input type=“number” name=“text” id=“b” min=“10” max=“20”
      30.         required oninput=validate();/>
      31. <button>Submit</button>
      32. </form>
      33. </body>
      34. </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);

       

      Custom validation: changing the default behavior, aggregating error messages, removing bubbles, etc.

      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:

        • Browser support is still not 100% complete (Safari and Internet Explorer still lack several important features),
        • It is not possible to aggregate error messages.  On submission, browsers show an error bubble next to the first invalid field, and there is no built-in way to display all error messages for all invalid fields at the same time,
        • You cannot style the bubbles.

      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 JS Bin, or try it here in your browser: enter invalid values and submit with one or two invalid fields.

       

      Complete source code:

      1. <!DOCTYPE html>
      2. <html>
      3. <head>
      4.    <meta charset=“utf-8”>
      5.    <title>Aggregating error messages</title>
      6.    <style>
      7.        input:invalid { backgroundcolor: lightPink;}
      8.        input:valid { backgroundcolor:lightGreen; }
      9.        input:required {border: 2px solid red;}
      10.        input:optional {border: 2px solid green;}
      11.  
      12.        .errormessages {
      13.            display: none;
      14.            margin: 0 10px 15px 10px;
      15.            padding: 8px 35px 8px 30px;
      16.            color: #B94A48;
      17.            backgroundcolor: #F2DEDE;
      18.            border: 2px solid #EED3D7;
      19.            borderradius: 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.          <ul class=“error-messages”></ul>
      32.          <label for=“name”>Name:</label>
      33.          <input id=“name” name=“name” required>
      34.          <p>
      35.          <label for=“email”>Email:</label>
      36.          <input id=“email” name=“email” type=“email” required>
      37.          <p>
      38.          <button>Submit</button>
      39.      </fieldset>
      40. </form>
      41.  
      42.     function replaceValidationUI(form) {
      43.        // Suppress the default bubbles
      44.           form.addEventListener(“invalid”, function (event) {
      45.           event.preventDefault();
      46.        }, true);
      47.  
      48.        // Support Safari, iOS Safari, and the Android browser — each of which
      49.        // do not prevent form submissions by default
      50.        form.addEventListener(“submit”, function (event) {
      51.           if (!this.checkValidity()) {
      52.              event.preventDefault();
      53.           }
      54.        });
      55.  
      56.        // Container that holds error messages. By default it has a CSS
      57.        // display:none property
      58.        var errorMessages = form.querySelector(“.error-messages”);
      59.  
      60.        var submitButton = form.querySelector(“button:not([type=button]),
      61.                                               input[type=submit]”);
      62.  
      63.        submitButton.addEventListener(“click”, function (event) {
      64.            var invalidFields = form.querySelectorAll(“input:invalid”);
      65.            var listHtml = “”;
      66.            var errorMessagesContainer = form.querySelector(“.error-messages”);
      67.            var label;
      68.  
      69.            // Get the labels’ values of their name attributes + the validation error
      70.            // message of the corresponding input field using the validationMessage
      71.            // property of input fields
      72.            // We build a list of
      73. that we add to the error message container

      74.            for (var i = 0; i invalidFields.length; i++) {
      75.                label = form.querySelector(“label[for=” + invalidFields[ i ].id + “]”);
      76.                listHtml +=
      77. +
      78.                            label.innerHTML +
      79.                            ” “ +
      80.                            invalidFields[ i ].validationMessage +
      81.                            
      82. ;

      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.    for (var i = 0; i forms.length; i++) {
      100.        replaceValidationUI(forms[ i ]);
      101.    }
      102. </body>
      103. </html>

      Explanations:

        • Line 32: we added an empty unnumbered list (<ul>..</ul>) to the form, with the CSS class=”error-messages”. We will use this class attribute for styling, and hiding by default, the error messages using CSS (see lines 12-20, line 13 hides the messages by default).
        • Lines 97-102 look at all forms in the document and call a function that will replace the default validation behavior for all of them: the replaceValidationUI(form) function.
        • This function first disables all default behavior (no more display of bubbles during form submission), this is done at lines 45-57.
        • Line 66: we add a click listener to the submit button of the current form.
        • Line 67 gets all invalid input fields for that form,
        • Lines 76-83: For each invalid field, we get the value of the name attribute of the corresponding label, we also get the validation error message, and we build a list item(<li>…</li>).
        • Line 86: Then we add this list element (a formatted error message corresponding to an invalid input field) to the error message container.
        • Lines 90-93: The focus is given to the first invalid field that shows an error message.

      Validation, CSS pseudo class feedback, and custom validation API

      Validation, CSS pseudo class feedback, and custom validation API

      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!

      POLYFILLS FOR SAFARI AND OLD INTERNET EXPLORER VERSIONS

       

      CSS3 pseudo classes provide automatic 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 :validpseudo class.

      A first step to improve your HTML form is to add some CSS rules to your input fields. This will add 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.

      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, Firefox 15+ 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.

      EXAMPLE 1: STYLING “REQUIRED”, “VALID” AND” INVALID” FIELDS USING CSS3 PSEUDO-CLASSES

      Here is an online example at JS Bin, or try it below in your browser:

       

      Source code extract:

      1. <html>
      2. <head>
      3.    <title>CSS3 pseudo-classes for form validation visual feedback</title>
      4.    <style>
      5.        input:invalid { backgroundcolor: lightPink;}
      6.        input:valid { backgroundcolor:lightGreen; }
      7.        input:required {border: 2px solid red;}
      8.        input:optional {border: 2px solid green;}
      9.        fieldset {
      10.           border:1px solid;
      11.           padding:20px;
      12.        }
      13.        .formLabel { display: inlineblock; width: 140px; textalign: right; }
      14.    </style>
      15. </head>
      16. <body>
      17. <form>
      18. <fieldset>
      19.    <legend>Type invalid values and see the result</legend>
      20.    <label for=“myEmail” class=“formLabel”>E-mail:</label>
      21.    <input type=“email” id=“myEmail” required/><br>
      22.    <label for=“myURL” class=“formLabel”>Homepage (URL):</label>
      23.    <input type=“url” id=“myURL” required/><br>
      24.    <label for=“myPhone” class=“formLabel”>Phone number:</label>
      25.    <input type=“tel” id=“myPhone”
      26.           pattern=“[0-9]{3}-?[0-9]{3}-?[0-9]{4}”
      27.           placeholder=“e.g. 416-555-1234” required/>
      28.    <br>
      29.    <button>Submit form</button><br />
      30. </fieldset>
      31. </form>

      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: except for FireFox, no other browser provides default CSS styling for
      valid/invalid/optional input fields. We recommend that you ALWAYS provide default CSS rules that give visual feedback to the user’s input.

      Safari and old browsers will still need a polyfill as of June 2015 (see the different polyfills at the end of this page).

      A NEAT TRICK: ADD CSS TRANSITIONS + AN ICON/MARKER TO THE RIGHT OF THE INPUT FIELDS

      Try this online example at JS Bin or try it here in your browser. This example adds a small icon that changes depending on the validity of the input field:Source code extract:

      1. .myForm input:focus {
      2.    paddingright:70px;
      3. }
      4. .myForm input {
      5.    transition: padding .25s;
      6. }
      7. .myForm input:required:valid {
      8.    background:url(http://i.imgur.com/BJolppS.png&#8217;) norepeat right top;
      9. }
      10. .myForm input:required {
      11.    background:url(http://i.imgur.com/7pIN7wz.png&#8217;) norepeat right top;
      12. }

      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 JS Bin, or try it here in your browser (type invalid values and look at the custom messages):

       

      Extract from source code:

      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.     <button>Submit form</button><br />
      9.   </fieldset>
      10. </form>

      Beware: browser implementations differ. Chrome, IE, Opera (as of June 2015) 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”.

      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.

      HOW TO STYLE THE VALIDATION MESSAGES DISPLAYED IN THE BUBBLES?

      Try the examples from this page on different browsers. You may notice differences (from left to right: Chrome, FireFox and IE):

      How can we change the style of these bubbles (for example, to unify the look and feel between different browsers)? Unfortunately we can’t.

      Chrome used to provide a series of vendor prefixed pseudo-elements (::-webkit-validation-bubble-*), but they were removed with Chrome 28 and later versions. However, there are possibilities that involve the use of the HTML5 form JavaScript API. At the end of this section, we propose solutions that work well. 

      EXTERNAL RESOURCES (NOT MANDATORY READING)

      For those really interested in HTML5 forms, we recommend following the tutorial below,  which shows how to give a  super look’n’feel to an HTML form, as well as visual feedback using HTML5 new attributes, CSS pseudo elements and form validation API. This tutorial also gives some nice JavaScript tricks and explains HTML4/CSS2 classic enhancements:

      However, beware! While this tutorial shows some very good tricks about the look’n’feel, it also unfortunately shows a very common mistake:

        • The “for” attribute of a <label> has to match the “id” attribute of the related input,  not the “name” attribute!

      Apart from this, it has good content.

      POLYFILL FOR BROWSERS THAT DO NOT FULLY SUPPORT THE NEW ATTRIBUTES REQUIRED, PATTERN, ETC. (THIS INCLUDES THE CURRENT SAFARI VERSION)

      We recommend using the modernizr.com library together with some external polyfills. These articles are worth reading for those of you who need retro-compatibility:

       

      HTML4 HTML5
      • <form>
      • <fieldset>
      • <legend>
      • <textarea>
      • <label>
      • <select>
      • <option>
      • <optgroup>
      • <input>
      • <button>
      • <datalist>
      • <output>
      • <meter>
      • <progress>
      • <keygen> *
      * Not really useful for most developers.

      In the following sections, we will specifically look at the <datalist>, <output>, <meter>and <progress> elements.

       

      The <output> element

      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.

      TYPICAL USE / INTERACTIVE EXAMPLES

      Do not hesitate to play with the source code of these examples online at JS Bin.

      Example 1

      1. <form oninput=o.value=a.value*b.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):

      Explanation about the attributes specific to the <output> element:
        • for: a space-separated list containing the elements’ ids whose values went into the calculation.
        • name: the name of the element.
        • form:  associates the <output> element with its form owner. The value must be the id of a form in the same document. This allows you to place an <output> element outside of the <form> with which it is associated.

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

       

      The <meter> element

      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.

      picture of a meter example

      The <meter> element should not be used to indicate progress. You should instead use a <progress> element.

      This element is supported by all major browsers, on desktop and mobile devices, except Internet Explorer (Microsoft Edge supports it) and IOS/Safari, as in November 2015.

      TYPICAL USE

      1. Storage space used: <meter value=75 min=0 low=20 high=80 max=100optimum=50></meter>

      The <meter> element uses the easy-to-understand value, min, max, low, high and optimumattributes. 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.

      INTERACTIVE EXAMPLE

      Try the next example online at JS Bin  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>

       

      1. <p>Grades: <meter id=“meter2” value=“75”min=“0” low=“20” high=“80” max=“100”></meter>
      2. <input min=“0” max=“100” value=“75” id=“meter2range”
      3.        oninput=effect(‘meter2’, ‘meter2range’) type=“range”>
      4. <output id=“meter2val” for=”meter2range”></output></p>
      5. function effect(meter, meterrange) {
      6.      var currVal = document.getElementById(meterrange).value;
      7.      document.getElementById(meter).value = currVal;
      8.      document.getElementById(meter+ “val”).innerHTML = currVal;
      9. }

       

      THE COLOR OF THE GAUGE CHANGES DEPENDING ON THE ATTRIBUTE’S VALUES

      Note: thanks to jyorme, a student of this course, for helping improving this part of the course!

      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:

        • Range 1: between min and low
        • Range 2: between low and high
        • Range 3: between high and max

      … and depending on the value you set to optimum attribute, one of the ranges above becomes the “good (optimum)” range.

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

      So, 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

      The <progress> element

      The <progress> element is similar to <meter> but it is used for progress bars (i.e., the percentage of a file being uploaded, etc.):

      1. <progress id=pr value=50 min=0 max=100>

      Gives:

      The browser calculates the percentage corresponding to the value, minand 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 vary among different browser implementations. Chrome, Opera, Safari and Firefox display indeterminate progress as an animated stripe. In IE 11, it’s styled as animated dots.

      This element is supported by all major browsers, on desktop and mobile devices.

      TYPICAL USE

      Here is an online example at JS Bin, 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 min=0 max=1000>

      Source code:

      1. Download progress: <progress id=pr value=100 min=0 max=1000></progress>
      2.    var i=0;
      3.    setInterval(function () {
      4.        i = (i+1) %1000;
      5.        document.getElementById(‘pr’).value = i;
      6.    },1);

      The <datalist> element

      The <datalist> form element is useful for linking a list of choices to an input element.

      We have already seen this element in action with different <input> elements, such as <input type=”color”>, <input type=”date”>, or <input type=”range”>.

      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 JS Bin, or try it here in your browser (type the name of your favorite browser):

      Source code of this example:

      1. <form action=“demo_form.asp” method=“get”>
      2.      <input list=“browsers” name=“browser” />
      3.      <datalist id=“browsers”>
      4.          <option value=“Internet Explorer”>
      5.          <option value=“Firefox”>
      6.          <option value=“Chrome”>
      7.          <option value=“Opera”>
      8.          <option value=“Safari”>
      9.      </datalist>
      10.      <input type=“submit” />
      11. </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).

      CURRENT SUPPORT

      All major browsers support this element except Safari (as of June 2015). See http://caniuse.com/#search=datalist for details and polyfills.