HTML5 – Les API JavaScript

C’est le moment de passer à la deuxième partie de cette série sur HTML5, avec en ligne de mire les nouveautés côté JavaScript. La spécification a pris le parti de mettre JavaScript en avant, avec des API standards qui pourront être implémentées dans tous les navigateurs. L’un des buts de la spécification est de faire monter HTML et JavaScript à un niveau de finition et de performance égalant les applications de bureau. Côté performance, il y a le parallélisme et quelques fonctions natives qui viendront remplacer des fonctions déjà implémentées par les frameworks JavaScript. Mais HTML5 fait aussi écho à l’explosion du Web mobile avec la géolocalisation et les applications hors ligne pour ceux qui ne sont pas connectés en permanence.


 Sommaire

  • Sélecteurs CSS
  • Timers
  • Workers et Messaging
  • Web Storage
  • Offline Web Application
  • Geolocation
  • WebSocket
  • Web SQL Database
  • Drag’n'drop
  • ContentEditable
  • Conclusion

Sélecteurs CSS

Habituellement utilisés pour appliquer des styles aux éléments HTML, les sélecteurs sont aussi utilisés en JavaScript pour retrouver facilement un ou plusieurs éléments. #loginForm fieldset permet de récupérer le ou les fieldset contenu dans l’élément dont l’id est loginForm. Tous les frameworks JavaScripts proposent leurs propre implémentation des sélecteurs CSS. Mais vous pouvez dès aujourd’hui migrer vers document.querySelector() qui fonctionne nativement sur tous les navigateurs récents et supporte la norme CSS3.
document.querySelector() retourne le premier élément trouvé correspondant au sélecteur, et document.querySelectorAll() va nous retourner la collection complète. Le gros avantage est la rapidité d’exécution fournie par le navigateur qui bat à plate couture tous les équivalents en framework JS.
CaptureSlickSpeed
Constatez par vous même en lançant Slick Speed dans votre navigateur.

Timers

Comme leur nom l’indique, les Timers permettent de planifier dans le temps des exécutions de callbacks avec ou sans répétition. La fonctionnalité n’est pas franchement nouvelle, mais elle trouve bonne place dans la recommandation du W3C. L’API propose deux fonctions pour créer les timers:
  • setTimeout(handler, ms, [args ...]) – Enregistre un handler pour être exécuté dans ms millisecondes en lui passant les arguments args.
  • setInterval(handler, ms, [args ...]) – Enregistre un handler pour être exécuté toutes les ms millisecondes.
Toutes deux retournent un pointeur qui pourra être utilisé plus tard pour supprimer le timer à l’aide de clearTimeout() et clearInterval(). Les timers sont assez pratiques pour rafraîchir le contenu de la page sans la recharger totalement. Ils sont maintenant aussi beaucoup utilisés pour animer le canvas. L’API intégrée depuis longtemps par les frameworks JavaScript est supportée par tous les navigateurs d’aujourd’hui et d’hier.

Workers et Messaging

Les Workers sont des tâches de fond qui vont s’exécuter en parallèle du programme JavaScript principal. Pour assurer la sécurité, ils tournent dans un environnement totalement séparé de la page et n’ont aucun accès direct sur cette dernière. C’est via la nouvelle API de Messaging que les workers vont pouvoir communiquer avec le programme principal. Chacun des threads peut envoyer un message via la méthode postMessage(...) et le recevoir en enregistrant un handler pour l’évènement onMessage.
Créons, d’abord, un nouveau Worker en lui fournissant l’url du script à exécuter :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var w = new Worker('prime.js');
// Ecrire le nombre premier trouvé dans la page sur message du worker
w.onMessage  = function (evt){
    document.getElementById('prime').textContent = event.data;
};
// Lancer le worker
document.getElementById('startTimerButton').onClick = function (evt) {
   w.postMessage('start');
}
// Stoper le worker
document.getElementById('stopTimerButton').onClick = function (evt) {
   w.postMessage('stop');
}
Voyons maintenant le code du Worker :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var running = false;
var timer;
var n = 1;
// Traiter le message de la page
onmessage = function (event) {
  if (event.data == 'start' && !running) {
    running = true;
    n = 1;
    timer = setInterval(run, 50);
  } else if (event.data == 'stop' && running) {
    running = false;
    clearInterval(timer);
  }
};
// Rechercher un nombre premier
function run() {
  var isAPrime = false;
  while (!isAPrime) {
    isAPrime = true;
    n += 1;
    for (var i = 2; i <= Math.sqrt(n); i += 1){
      if (n % i == 0){
        isAPrime = false;
        break;
      }
    }
  }
  // found a prime!
  postMessage(n);
}
Le code est assez simple. Sur message 'start' de la page, il créé un Timer qui exécutera la fonction run toutes les 50ms. Si le message reçu est 'stop', il coupe le Timer ce qui arrêtera la recherche au prochain nombre premier trouvé.
Attention, le message contenu dans event.data ne peut contenir que du texte. Pour supporter des messages plus complexes, il faudra passer par des chaînes JSON.
Les Workers peuvent créer d’autres Workers sans limite, et importer d’autres scripts via la fonction importScript(url). Les traitements lourds ne sont plus réservés aux applications de bureaux. L’API est supportée par Chrome, Safari, et Firefox. Il faut encore reposer sur Gears pour pouvoir en bénéficier sur IE et Opéra a planifié l’API pour sa prochaine version.
L’API de Messaging est aussi utilisée pour d’autres fonctionnalités comme la communication entre la page et son iFrame quelque soit le domaine d’origine. La page enregistre un handler window.onMessage et peut appeler le postMessage() pour communiquer. Ainsi, même deux iframe de la même page peuvent communiquer entre elles. Vous pouvez tester cette démonstration pour avoir un aperçu de la communication cross-domain. Cette API est disponible pour tous, à partir d’IE 8 ;-) .

Web Storage

Cette nouvelle API fournit deux types de stockage de données dans le navigateur :
  • Le sessionStorage est une base JSON partagée, pour la durée de la session, entre toutes les pages d’une même fenêtre.
  • Le localStorage stocke aussi des objets JSON, maintenus localement d’une session à l’autre et partagés pour toutes les pages d’une même origine.
Ces deux nouveaux objets implémentent l’interface Storage qui fournit les méthodes :
  • getItem(key), setItem(key), removeItem(key), permettent de gérer les objets du tableau.
  • key(index) et length fournissent la longueur du tableau et la clé de l’objet stocké à un certain indice.
  • clear() vide entièrement le tableau.
Mais vous pouvez aussi bien les utiliser comme de simples objets JavaScript :
1
2
3
4
5
if (!localStorage.drafts){
  localStorage.drafts = [];
}else {
  // Afficher les drafts stockés
}
Dans une application disponible hors ligne, les modifications pourront être enregistrées dans le localStorage, pour être synchronisées plus tard quand le navigateur repassera en ligne. En REST, l’état pourra être stocké du côté navigateur dans le sessionStorage. Un autre exemple d’utilisation peut être l’enregistrement des préférences utilisateur thème et personnalisation. Déjà disponible dans Firefox, Chrome, Safari et IE 8, le WebStorage sera supporté partout à la sortie d’Opera 10.50.

Offline Web Application

L’idée est de fournir des applications qui peuvent fonctionner sans connexion à internet. La solution repose sur une gestion fine de la mise en cache des ressources et la mise à disposition d’un événement notifiant le changement d’état de la connexion online/offline. On pourra aussi utiliser WebStorage pour enregistrer, hors ligne, les nouveaux mails à envoyer plus tard quand la connexion sera de nouveau disponible.
La gestion du cache se fait via un fichier MANIFEST qui liste les règles de mises en cache.
1
2
3
4
5
6
7
8
9
10
11
CACHE MANIFEST
index.html
style/default.css
images/logo.png
images/backgound.png
NETWORK:
server.cgi
FALLBACK:
online/images offline/images
Le fichier manifest contient plusieurs sections :
  • CACHE liste les url des fichiers à conserver dans le cache, c’est la section par défaut.
  • NETWORK permet de lister les urls qui ne doivent pas être conservées dans le cache.
  • FALLBACK liste des pairs d’urls. Si la première ne répond pas la seconde sera utilisée.
Pour le déclarer, il suffit ensuite de l’ajouter dans la balise html des pages.
1
2
<!DOCTYPE HTML>
<html manifest="demo.manifest">
Attention, le fichier demo.manifest doit être servi avec le type mime text/cache-manifest, pour garantir son traitement par le navigateur. Au premier accès à la page, le navigateur va télécharger et stocker les ressources dans le cache, éventuellement en notifiant l’utilisateur. Au prochain accès, le navigateur vérifiera la version du fichier manifest sur le serveur, et téléchargera la mise à jour, ou affichera directement la page du cache.
Les scripts peuvent suivre le chargement et forcer le passage d’une version du cache à une autre.
1
2
3
4
5
6
7
var cache = window.applicationCache;
// Mise à jour du cache
cache.update();
// Passage au nouveau cache et invalidation de l'ancien sur updateReady
cache.swapCache();
L’objet ApplicationCache fournit en plus des deux méthodes, un status indiquant son état en cours et quelques événements permettant de suivre les changements d’état du cache.
Nous avons donc, de quoi conserver les pages d’une application pour pouvoir les afficher hors ligne, avec une solution pour fournir un contenu alternatif lorsque le navigateur n’est pas connecté. Bien sûr, il faut aussi prévoir un traitement alternatif en JavaScript lorsque le navigateur est hors ligne. L’objet window hérite donc d’une propriété booléenne online, accompagnée des événements online et offline.
Les applications hors ligne sont utilisables, dès aujourd’hui, sur Safari, Firefox, Chrome et Internet Explorer via gears. Attention tout de même, l’implémentation fournie par gears diffère un peu de la spécification.

Geolocation

La Géo-localisation, ou Geolocation en anglais est une nouvelle API qui permet de récupérer auprès de l’objet navigator, les coordonnées complètes de l’utilisateur (longitude, latitude, et altitude). C’est comme ça que fonctionne le bouton « Where am I » de google maps sur l’iPhone par exemple. La spécification va un peu plus loin en permettant de faire un suivi de la position courante, de connaitre la vitesse et de retrouver la dernière position connue en cas d’indisponibilité.
Voici, un petit exemple issu de la spécification :
1
2
3
4
5
6
function showMap(position) {
     // Show a map centered at (position.coords.latitude, position.coords.longitude).
}
   // One-shot position request.
navigator.geolocation.getCurrentPosition(showMap);
Pour plus de détail sur l’API proprement dite, je vous invite à lire la spécification qui montre des exemples de code facilement compréhensibles. Bien qu’expérimentale dans Internet Explorer, la fonctionnalité est disponible dans tous les navigateurs récents. Là encore, attention, Chrome vient d’intégrer l’implémentation HTML5 de la géo-localisation qui remplace celle de Gears. Si le système fonctionne très bien sur les mobiles grâce au GPS et au triangulation GSM, il s’avère assez hasardeux dès qu’il repose sur la géolocalisation par adresse IP.

WebSocket

Avec les server-event, les WebSockets permettent d’établir une communication bi-directionnelle asynchrone entre le navigateur et le serveur, l’idée est de supporter le push server. En fournissant une API native qui ne nécessite pas de « bidouille » à base de requêtes Ajax ou d’iframe pour gérer les notifications du serveur. Les WebSocket tentent donc de fournir un cadre standard pour les applications Comet.
Les WebSocket fournissent deux méthodes :
  • send( data_string) pour envoyer un message au serveur.
  • close() pour fermer la socket.
Les messages envoyés par le serveur sont notifiés par l’événement onmessage contenant le message du serveur sous forme de chaîne. Les événements onerror, onopen et onclose permettent de suivre en temps réel l’état de la connexion. Côté protocole, la spécification est développée conjointement avec un draft déposé par Google à l’IETF. Le protocole WebSocket imaginé par Ian Hickson permet de valider l’origine des messages pour la sécurité et évite les lourdeurs de HTTP. Il est donc compatible avec le modèle de sécurité standard des navigateurs, et autorise même l’utilisation de proxy. Le protocole WebSocket est à considérer comme un protocole de transport qui va pouvoir être utilisé, pour encapsuler d’autres protocoles. Nous connaissons déjà l’exemple de Jabber qui est utilisé par Google Talk, mais il devient aussi possible de transporter des messages JMS ou AMQP. Ce type de connexion a des chances de venir concurrencer sérieusement les solutions fournies par Flash.
Par contre, la spécification est encore jeune et ne bénéficie pas pour le moment d’un large support parmi les navigateurs. Seul Chrome a terminé son implémentation depuis quelques semaines, Safari devrait suivre bientôt. A priori du côté de Firefox, le travail est déjà bien avancé puisque le bug est en cours de traitement depuis près d’un an. Il y a fort à parier que la prochaine version majeure du renard brûlant supportera les WebSockets. Pour ce qui est d’Opera et d’Internet Explorer aujourd’hui rien n’est fait mais je ne sais pas ce que l’avenir nous réserve.
Outre le navigateur, les WebSockets posent aussi un problème d’infrastructure puisqu’il ne s’agit plus de fournir seulement un serveur HTTP mais en plus un serveur web socket. Il existe déjà quelques solutions serveur telles que Kaazing gateway et Jetty qui supportent des WebSocketServlet. Mais l’offre est encore très réduite et expérimentale, il faudra attendre encore quelque temps avant de pouvoir bénéficier de serveurs web socket éprouvés.

Web SQL Database

Apple et Google ont décidé d’aller plus loin que le simple stockage NoSQL du navigateur en proposant une véritable base de données SQL embarquée dans le navigateur. Une fois de plus, c’est l’évangéliste HTML5 Ian Hickson qui rédige la spécification pour le W3C. Web SQL Database fonctionne de la même façon que le localStorage à la différence près qu’il s’agit bel et bien d’une base SQL stockée elle aussi par origine (nom de domaine racine). Comme d’habitude en JavaScript, l’API fonctionne en asynchrone grâce à l’utilisation de fonction callback. Elle permet d’ouvrir ou de créer une base avec un numéro de version pour gérer les évolutions du modèle. Une fois la base ouverte, il est possible d’exécuter des requêtes SQL avec paramètres au sein d’une transaction. La fonction de callback reçoit l’objet SQLResultSet qui contient les données lues en base ou les informations liées à l’insertion où la mise à jour des tuples en base de données.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ouverture de la base  et création si besoin
window.openDatabase('documents', '1.0', 'Offline document storage', 5*1024*1024, function (db) {
    db.changeVersion('', '1.0', function (t) {
      t.executeSql('CREATE TABLE docids (id, name)');
    }, error);
});
var span = document.getElementById('docCountSpan');
// Lecture du nombre de document en base
db.readTransaction(function (t) {
    t.executeSql('SELECT COUNT(*) AS c FROM docids', [], function (t, r) {
      span.textContent = r.rows[0].c;
    }, function (t, e) {
      // couldn't read database
      span.textContent = '(unknown: ' + e.message + ')';
    });
  });
Il existe maintenant aussi des méthodes synchrones pour accéder à la base qui peuvent être utilisées sans risque dans un Worker. Si les appels sont réalisés directement dans la page, utilisez toujours les méthodes asynchrones pour éviter les effets de bords type navigateur figé toujours désagréables.
Cette spécification, bien que prometteuse, n’est pas considérée comme stable. Le problème vient du langage SQL soutenu par la spécification qui n’est pour le moment qu’une redirection vers la documentation SQLite. L’API est donc figée en attente d’un navigateur qui utilisera un autre backend de base de données. Pour l’instant, tout le monde utilise SQLite, que ce soit avec Gears pour Firefox et Internet Explorer ou en natif dans Chrome, Opera et Safari.

Drag’n'drop

L’API cette fois vient d’Internet Explorer puisqu’elle existe depuis IE 6. Elle permet de définir un élément pour recevoir les objets déposés par l’utilisateur via l’événement ondrop, et de suivre l’objet glissé par l’utilisateur sur la page où le receveur d’objet déposé via six événements (dragstart, drag, dragend, dragenter, dragover, dragleave). L’idée offre des perspectives sympathiques:
  • Gérer l’upload de fichier par glisser-déposer.
  • Récupérer proprement le texte glissé.
  • Supporter nativement le drag and drop des éléments de la page.
C’est sur ce dernier point que la spécification apporte une réelle nouveauté avec l’attribut dragable qui peut-être positionné directement sur les balises de la page. Malheureusement seul Firefox supporte ce nouvel attribut. Pour le reste, les auteurs se sont contentés de recopier l’API Microsoft comme l’ont fait tous les navigateurs. Seul Opera résiste encore à cette API lourdement critiqué à travers la blogo-sphère. Il y a d’abord trop d’événements impliqués dans le simple glissé-déposé, mais ils sont en plus dépendants les uns des autres. Par exemple, pour espérer recevoir ondrop sur un élément HTML, il faudra recevoir et annuler l’événement dragover.

ContentEditable

Pour cette dernière étape de notre tour d’horizon des API JavaScript, j’ai décidé de vous parler de l’attribut contenteditable. Cet attribut active l’édition en ligne du contenu de l’élément. Il s’agit d’un éditeur WYSIWYG basique qui permet donc d’éditer directement le contenu de la page. Plus besoin de remplacer à la volée le texte par un input ou une textarea, il suffit de positionner l’attribut contenteditable sur la balise voulue. C’est ensuite au tour de l’attribut designmode de rentrer en jeu : placé à on le contenu de l’élément devient éditable. Toutes les modifications apportées par l’utilisateur pourront être traitées à la fin de l’édition.
Le tout est servi avec une API permettant d’autoriser et d’exécuter des command sur le texte en cours d’édition. Les commandes vont de passer le texte en italique à insérer une image ou créer une liste. Bref, si la spécification n’est pas encore complète, elle a l’ambition de fournir une API d’édition de texte riche pour tous les navigateurs HTML5. La bonne nouvelle de ce côté étant que les navigateurs principaux sont tous de la partie. Surtout Internet Explorer qui supporte l’édition en ligne depuis sa version 5.5 et a très largement inspiré cette fonctionnalité.
Pour plus d’information sur le sujet, vous pouvez jeter un œil à la spécification du W3C ou lire cet article d’introduction en anglais sur le blog du WHATWG.

Conclusion

Dans l’ensemble, les API JavaScript spécifiées dans HTML5 sont convaincantes et plutôt bien supportées par les navigateurs. Bien sûr Internet Explorer est à la traine mais les rumeurs vont bon train sur la roadmap d’IE 9. A l’occasion du MIX 2010, une conférence Microsoft de trois jours autour du développement web qui se tenait cette semaine, la preview IE9 a été publiée cette semaine. Avec cette pré-version, Microsoft confirme son entrée dans la bataille HTML5 avec : la vidéo, le canvas, une machine JavaScript largement optimisée et 55 points au test ACID CSS3. Même Mike Shaver haut responsable de Firefox félicite cette nouvelle concurrence dans un Tweet. Plus performant, plus portable et plus puissant, avec HTML5 JavaScript peut gagner ses lettres de noblesses, voire même, convaincre les foules de développeurs réticents. Outre l’intérêt maintenant avéré de Microsoft pour la spécification, HTML5 manque cruellement d’outils pour pouvoir concurrencer Flex. Amis éditeurs à votre bon cœur.