HTML5: le mode Offline


Après avoir traité de la géolocalisation, nous allons poursuivre notre étude d’HTML5 et nous pencher sur le mode hors-ligne. Cette nouvelle spécification permet aux applications web d’être accessibles et navigables en cas d’une coupure de connexion avec le serveur. Nous allons, dans un premier temps, comprendre comment fonctionnent les applications HTML5 « Offline », puis nous étudierons les differents modes de stockages dans le navigateur pour développer des applications accessibles en hors-ligne avec des données persistantes. Enfin j'introduirai la nouvelle spécification pour créer un formulaire HTML, le but étant de mettre en place un annuaire hors-ligne.

Le mode hors-ligne

Le principe d'un site web hors-ligne est très simple: l’application utilise le cache du navigateur pour garder en mémoire les ressources nécessaires à son utilisation (pages HTML, feuilles de style, images, fichiers JavaScript…). Ces ressources sont spécifiées dans un fichier « manifest » qui est déclaré dans la balise HTML des pages de l’application.

<html manifest= "filename.manifest">

Le fichier manifest est structuré de la manière suivante :

CACHE MANIFEST
#ceci est un commentaire
# v 1.0
#url des fichiers à cacher : html, js, images…
CACHE :
#autres ressources à cacher (on peut les spécifier en début ou dans cette section)
NETWORK : 
#ressources accessibles uniquement en ligne (pas sauvegardées)
FALLBACK : 
#ressources à renvoyer dans le cas de non disponibilité des ressources demandées. 
#syntaxe : url_fichier_original url_fichier_fallback

On peut remarquer en commentaire "v 1.0". Je l'ai utilisé comme un numéro de version et il servira à mettre à jour le cache. Nous verrons cela un peu plus loin dans l'article.

Les sections CACHE, NETWORK et FALLBACK sont optionnelles. Le navigateur va ensuite organiser son "Application Cache" en catégories de ressources :

  • Master: fichiers HTML qui déclarent le manifest
  • Manifest: le fichier manifest
  • Explicit: ressources déclarées en début du manifest ou dans la section CACHE
  • Fallback: ressources déclarées dans la section FALLBACK

Les pages déclarant le manifest dans la balise HTML sont donc déjà dans le cache et n'ont pas besoin d'être spécifiées explicitement dans le manifest.

Ainsi, dès qu'on ouvre l'application web sur le navigateur, les ressources sont directement sauvegardées dans le cache, et elle devient accessible en hors-connexion. Le fichier manifest est habituellement déclaré avec une extension .manifest ou .mf. Pour qu’il soit reconnu par le serveur, il faut déclarer le MIME-TYPE du fichier. Cela se fait soit dans le fichier de configuration du serveur (tomcat par exemple), soit dans le fichier web.xml pour une application web JavaEE :

<mime-mapping>
  		<extension>manifest</extension>
  		<mime-type>text/cache-manifest</mime-type>
</mime-mapping>

A présent, du moment que l’application déclare dans ses pages HTML un fichier manifest, elle sera chargée uniquement à partir du cache même si l’utilisateur est en ligne. Ce cache peut être mis à jour par différentes méthodes :

  • Vider le cache du navigateur manuellement
  • Du code JavaScript
  • Modifier le fichier manifest

Ainsi, si l'on modifie l'application, pour la mettre à jour, il suffit de changer quelque chose dans le fichier manifest, d'où l'intêret de mon commentaire avec numéro de version. Le fichier manifest changé, le navigateur va le mettre à jour automatiquement après avoir rafraîchi la page.

Support du Web Offline par les navigateurs

  • Chrome: version 4.0+
  • Firefox: version 3.5+
  • IE: non supporté
  • Opera: version 10.6+
  • Safari: version 4.0+

Stockage

Maintenant que nous avons vu comment fonctionne le mode hors-ligne d’HTML5, nous allons explorer les possibilités de stockage local. Le but étant de mettre en place une application avec des données persistantes de type annuaire et accessible hors connexion.

LocalStorage

La spécification HTML5 permettant le stockage en local est le Web Storage. Cette API permet de stocker des données plus ou moins persistantes dans le navigateur en utilisant deux moyens: le "session storage" et le "local storage". Le stockage de session permet de sauvegarder des données qui persistent uniquement dans la fenêtre ou l'onglet courant. Les données sont perdues dès leur fermeture. L'implémentation qui nous intéresse le plus ici est le local storage, dont les données persistent dans le navigateur. On y accéde par l'objet window:

window.localStorage

LocalStorage implémente l'interface Storage :

Interface Storage {
	readonly attribute unsigned long length;
	getter DOMString key(in unsigned long index);
            getter any getItem(in DOMString key);
	setter creator void setItem(in DOMString key, in any data);
	deleter void removeItem(in DOMString key);
	void clear();
}

Ainsi pour ajouter une donnée, il suffit d'utiliser la fonction setItem qui prend en paramètre une clé et une valeur. Pour récupérer une donnée, on peut utiliser la fonction getItem avec sa clé en paramètre. Ce qu'il faut savoir c'est que les données sont stockés sous forme d'une map (clé, valeur).

StockageLocal.JPG

Pour illustrer tout ce que nous avons vu jusque là, je vous propose une petite démonstration. Le site se présente sous forme d'un unique champ de saisie dans lequel on rentre un texte qui est affiché en-dessous sous forme d'une liste à puces et est stocké dans le navigateur en local. Cette application est accessible en mode déconnecté. Elle peut servir de mémo ou de bloc-note pour inscrire une liste de tâches à effectuer. Je vais présenter des extraits du code que vous trouverez joint en bas de l'article. Voici le code de la page principale du site :

<!DOCTYPE html>
<html manifest="cache.manifest">
<head>
<meta charset="UTF-8">
<title>LocalStorage Application</title>
<script src="OfflineLocal.js"></script>
</head>
<body id="body" onload="onload()">
<h4 id="status" ></h4>
<form>
	Enter your text : <input type="text" id="data" autofocus>
	<input type="button" id="submit" value="submit" onclick="saveData()">
</form>
<ul id="liste"></ul>
</body>
</html>

Le manifest, déclaré dans la balise HTML, est le suivant:

CACHE MANIFEST
OfflineLocal.js

Le fichier javascript OfflineLocal.js contient les fonctions pour gérer le stockage local. En voici des extraits:

function onload(){
	//...
	getData();
}
 
function getData(){
	if(window.localStorage){
		for(var i=0; i<window.localStorage.length;i++){
			var key = window.localStorage.key(i);
			var value = window.localStorage.getItem(key);
                                    displayData(value);
		}
	}
}
 
function saveData(){
	var texte = document.getElementById("data").value;
	if(texte){
		if(window.localStorage){
		window.localStorage.setItem(key, texte);
		}
                        displayData(texte);
	}
}
 
function displayData(data){
	var puce = document.createElement("li");
	var contenu = document.createTextNode(data);
	puce.appendChild(contenu);
	document.getElementById("liste").appendChild(puce);
}

On voit bien les fonctions getItem(key) et setItem(key, value). Ainsi, à l'ouverture de la page on charge les données stockées, puis lorsque l'on entre un texte et on clique sur "submit", le texte est sauvegardé puis récupéré pour l'afficher sur la même page. Voici une capture du résultat :

demoLocalStorageListeDeCourse.JPG

Support du WebStorage par les navigateurs

  • Chrome: version 3.0+
  • Firefox: version 3.0+
  • IE: version 8.0+
  • Opera: version 10.5+
  • Safari: version 4.0+

Web SQL database

Un autre moyen de stocker des données en local est d'utiliser la "web SQL database". Cette API permet de créer une base de donnée dans le navigateur, les requêtes étant en SQL (SQLite). voici la fonction pour créer ou ouvrir une base de donnée existante:

window.openDatabase("name", {version}, {description});

La fonction prend en paramètre obligatoirement le nom, puis a deux paramètres optionnels : un numéro de version et une description. Après avoir créé la base, pour effectuer des requêtes, il nous faut passer par une transaction :

dbName.transaction ( function (tx) { tx.executeSql('sql statement', [sql args], {result function}, {error function} ); } );

La methode transaction prend en paramètre une fonction de transaction "tx" qui va permettre d'exécuter des requêtes sql avec executeSql, ainsi que deux autres paramètres qui sont des fonctions pour gérer le resultat et les éventuelles erreurs. De même, la méthode executeSql prend en paramètres une requête SQL, ses arguments, et en option les fonctions pour gérer le resultat et les erreurs.

Et voici la syntaxe SQLite pour créer une table:

CREATE TABLE IF NOT EXISTS table_name ( rows )

Je ne m'attarde pas plus longtemps sur cette partie étant donné que la spécification de Web SQL Database ne sera pas poursuivie par le W3C.

Html5 Forms

Pour finir, afin de construire notre annuaire hors-ligne, je vais présenter brièvement les nouveaux « input » d’HTML5. Parmi eux nous avons les types suivants :

  • tel
  • email
  • url
  • range
  • search
  • date

Ces types sont implémentés dans la plupart des navigateurs (sauf le type date qui n’est pour l’instant supporté que par la dernière version d'Opera). La nouvelle « Forms API » introduit également des nouveaux attributs :

  • required : s'il est spécifié, le champ doit être rempli pour que le formulaire soit validé.
  • placeholder : permet d’ajouter une description du champ qui apparait en transparence
  • autocomplete : « off » ou « on »
  • autofocus : met le curseur sur le champ spécifié (s’applique sur un seul champ du formulaire)
  • list et datalist : spécifie une liste de valeurs qu’on définit dans un élément datalist.

Enfin, une des grandes nouveautés de l’API, la validation. Finies les lignes javascript pour récupérer les valeurs et tester leur validité avant de poster le formulaire. HTML5 introduit l’objet ValidityState et son paramètre valid, un booléen (true si le formulaire est valide) et qui est calculé suivant 8 contraintes qui se basent sur la validation des attributs des champs (par exemple "required"). On peut également customiser notre formulaire en ajoutant des petites images d'alerte sur le champ pour informer l'utilisateur de sa validité.

Et voici, en image, le résultat qu'on obtient avec la dernière version du navigateur Opera :

formulaire.jpg

Nous avons à présent tous les éléments pour construire une application de type annuaire hors-ligne. Cette application utilise le cache pour être navigable hors connexion, la Web SQL database pour sauvegarder les contacts, et la nouvelle Forms API pour entrer un nouveau contact. Je vous invite à regarder et tester le code que j'ai mis en ligne (en bas de l'article). Je précise que tous les codes joints ont été développés sous eclipse et déployés sur Jetty (version 7.2).

annuaire.JPG

nouveau.JPG

liste.JPG

Conclusion

Pour conclure, nous avons vu aujourd'hui les outils pour développer une application hors-ligne utilisant des données stockées en local. Un point important que je souhaite préciser est l'utilisation de la Web SQL Database API. Cette spécification, implémentée uniquement dans Chrome, Opera et Safari, ne sera pas poursuivie par le W3C. Le problème étant l'utilisation explicite de SQLite, qui ne peut pas donner lieu à une standardisation de l'API. Une nouvelle spécification est en cours, "Indexed Database", qui sera plus simple d'utilisation et plus standard au niveau des requêtes. Indexed database n'étant pas encore disponible, j'ai donc utilisé Web SQL database pour illustrer un annuaire hors-ligne.

Dans un prochain billet, toujours consacré à HTML5, je traiterai des WebSockets, un puissant outil pour développer des applications temps-réel.


Annexes

Commentaires

1. Le jeudi 24 février 2011, 10:42 par Cédric

Bonjour

Merci beaucoup pour ce petit aperçu des méthodes de stockage proposées par le HTML 5.
Cette technologie est d'autant plus intéressante qu'elle est compatible avec les terminaux mobiles Iphone, Androïd, etc. Qui sont plus souvent exposés à une coupure de la connexion.

2. Le samedi 29 octobre 2011, 11:14 par plume

Bonjour

Merci pour ce tutoriel
Cependant, je n'arrive pas à exécuter le code fourni. J'utilise Firefox 7.0.1. Lorsque j'exécute le code, il me demande d'accepter l'utilisation de la fonctionnalité de stockage, j'accepte puis il tourne dans le vide ...
Que puis-je faire ?

Merci

3. Le lundi 16 janvier 2012, 14:07 par croustaille

faut modifier la procédure pour que ce fonctionne :
function saveData(){
var texte = document.getElementById("data").value;

if(texte){
if(window.localStorage){

var key = window.localStorage.length +1;
window.localStorage.setItem(key, texte);
}

      displayData(texte);

}
}

4. Le jeudi 4 octobre 2012, 15:58 par jeff

bonjour,
excellent article !
mais j'ai un pb sur iPad avec le manifest, il ne veut pas consulter offline
j'ai declaré le manifest :

CACHE MANIFEST

  1. Version 0.4

css/quizz.css
js/framework_ajax.js
index.html
question1.html
question2.html
images/home.png
images/question1.png
images/bonneReponse1.png
images/mauvaiseReponse1.png
images/question2.png

appelé le manifeste dans mon index
<html manifest="pagesoffline.manifest">

declaré un .htaccess
AddType text/cache-manifest manifest

je visite toutes les pages de mon site
puis je coupe la connexion wifi
et impossible d'accéder au site, ni par l'icone, ni par le navigateur (safari 5)

merci pour votre aide

jeff

5. Le mardi 9 octobre 2012, 12:37 par Tib

Merci pour ce tuto génial.

Je suis un peu un newbie en la matière et j'aurais besoin de quelques conseils :

Tout fonctionne parfaitement avec le cache manifest, j'ai cependant un formulaire de connexion dans mon application, cette connexion est traitée via un formulaire + traitement php, qui ne fonctionne donc pas offline.

Comment puis-je faire pour rendre cette authentification dispo même en mode offline?
J'ai pensé au LocalStorage mais ce n'est pas très sécurisé il me semble...

Merci beaucoup pour votre aide,

Tib

Fil des commentaires de ce billet

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.