The Hashbang Theory

Dans l’article précédent, nous avons réalisé que pour arriver à construire l’historique de l’application tout en utilisant des requêtes Ajax, il fallait trouver un moyen de manipuler l’URL sans pour autant que le navigateur n’émette de requête HTTP.

La solution

Des petits génies qui devaient un peu ressembler à Sheldon (un des héros de Big Bang Theory. Vous ne connaissez pas ? Hum…) se sont fait cette réflexion : “Hé, mais ça existe depuis longtemps ! Pourquoi ne pas utiliser les hashes ?”

Diantre ! Vous savez, les hashes, ces petits trucs qui permettent de créer un lien interne à la page (une ancre). Pour illustrer mon propos, imaginons que je sois sur la page http://example.com/bigbangtheory, et que dans cette page j’ai le code suivant:

<a href="#dixraisons">Pourquoi Big Bang Theory ?</a>
...
<h1 name="dixraisons">10 bonnes raisons de regarder Big Bang Theory</h1>

Si je clique sur le lien “Pourquoi Big Bang Theory ?”, le navigateur va faire défiler la page pour me placer sur la section “10 bonnes raisons de regarder Big Bang Theory”, il ne va pas faire de requête HTTP puisque l’on a déjà tout le contenu nécessaire, et il va mettre à jour l’adresse qui va devenir http://example.com/bigbangtheory#dixraisons, créant ainsi une nouvelle entrée dans l’historique. (Ah au fait, l’attribut name est obsolète, il faut utiliser l’id maintenant).

De plus, et c’est là toute l’astuce, on peut mettre absolument tout ce que l’on veut dans le hash ! Si le navigateur ne trouve pas de correspondance dans la page, il mettra quand même à jour l’adresse. Il ne se déplacera simplement pas à l’endroit indiqué.

Mise en pratique dans le site d’exemple

Tout d’abord, il faut modifier les liens de navigation pour qu’ils deviennent des hashes. On peut le faire de plusieurs manières:

  1. Modifier les liens directement dans le code HTML
  2. Utiliser Javascript pour modifier les liens:
    • Soit globalement au chargement de la page
    • Soit en modifiant l’URL quand l’utilisateur clique sur un lien

Si on utilise la première solution, il faut être conscient que le contenu ne sera plus accessible si Javascript est désactivé… Et donc pour les moteurs de recherche.

Avec la deuxième solution, si Javascript est désactivé, l’URL reste atteignable puisque nous sommes partis d’un site bien agencé avec des URLs propres.

Dans l’exemple, nous prendrons l’option de modifier globalement les liens après le chargement de la page:

 $('nav a').each(
    function(index) {
        var link = $(this).attr('href');
        $(this).attr('href', '#' + link);
    }
);

A partir de là, quand l’utilisateur clique sur un lien, l’adresse est bien modifiée par le navigateur, mais rien d’autre ne se passe…

Il nous faut donc également un moyen de savoir que la partie hash de l’URL a changé. Pendant longtemps, il a fallu pour cela utiliser des astuces plus ou moins élégantes, telles que faire un test de l’URL plusieurs fois par seconde. Pour les navigateurs pas trop anciens (comprenez > IE7), il existe heureusement l’événement hashchange qui est justement là pour ça:

$(window).bind('hashchange',
    function(event) {
        loadFragment(getHashtagContent());
    }
);

Quand on reçoit cet événement, on extrait ici le hash de l’URL et on charge le fragment correspondant.

Enfin, il ne faut pas oublier ceci: puisque l’on permet maintenant à l’utilisateur de mettre en favori un état donné du site, il est susceptible d’y revenir directement. Dans ce cas il ne passera pas par la page d’accueil. Or l’événement hashchange n’est pas déclenché lorsque l’on arrive pour la première fois sur une URL contenant un hash. Il faut donc gérer ce cas particulier en analysant l’URL au chargement de la première page, comme ceci:

`js $(document).ready( function() { ... if (getHashtagContent()) { loadFragment(getHashtagContent()); } } )

Si on détecte un hash dans l’URL, on fait exactement la même chose que lors d’un événement hashchange.

Le code est enfantin, du coup votre esprit a certainement eu le temps de repenser au titre de cet article et vous vous demandez peut-être: “Mais enfin, quel est le rapport avec cette histoire de hashbang ?”. Alors parlons-en !

Google se prend pour le capitaine Haddock

Le site d’exemple a été conçu pour être accessible même si Javascript est désactivé. Cela a une conséquence importante: les moteurs de recherche sont à même de référencer tout le contenu du site. Mais si ce n’est pas le cas de votre site (et vous ne serez pas le seul !), la partie la plus intéressante de vos contenus passe à la trappe ! C’est la conséquence du fait que l’on se repose entièrement sur Javascript pour router les demandes de l’utilisateur.

Comme c’est une situation intolérable, Google a imaginé une … solution qui lui permet de reconnaitre les ressources chargées de la manière dont nous avons parlé et d’y accéder par une URL alternative standardisée.

En voici un exemple:

  • Navigateur: www.example.com?myquery#!key1=value1&key2=value2
  • Moteur de recherche: www.example.com?myquery&escaped_fragment=key1=value1%26key2=value2

Dans la terminologie de Google, la première URL est propre (pretty) et la seconde sale (ugly). Hors, si tout le monde est d’accord pour dire que l’URL “sale” est vraiment sale, beaucoup (et j’en fais partie) ne trouvent pas l’URL “propre” si propre que ça…

Pour répondre enfin à la question qui vous intrigue peut-être toujours, à savoir le rapport entre tout cela et le titre de l’article, les caractères “#!” forment ce qu’on appelle depuis longtemps en informatique un hashbang.

Cette solution a été adoptée par Twitter, pour ne citer que lui, car elle a le mérite d’exister. Mais elle a déclenché fin 2010 / début 2011 une énorme polémique (les ingénieurs de Twitter eux-mêmes ayant du mal à se mettre d’accord) dont le principal intérêt aura été de nous faire réfléchir sur la notion d’URL. Voici à mon avis les articles les plus intéressants sur ce sujet:

En tous les cas, l’avis général est qu’il devrait s’agir d’une solution transitoire (mais est-ce réellement possible sur le Web ?) en attendant des jours meilleurs.

Mais la manipulation du hash a d’autres limites, dont nous allons parler brièvement.

Le hash ne peut pas tout faire

Nous avons vu dans le premier article que l’idée général était d’encoder dans l’URL l’état de l’application. Dans l’exemple simple, bien que courant, que j’ai choisi, l’état correspond simplement à la page Web que le site affiche suite à une requête vers le serveur. Mais on pourrait très bien avoir un cas plus complexe ou l’état correspondrait à certaines informations permettant au navigateur de reconstruire la page Web, et n’est plus seulement l’identifiant d’une ressource serveur.

Pour illustrer cela, imaginons que vous construisiez un site contenant divers petits jeux: un pendu, une bataille navale, … Une fois sur la page du pendu, le joueur fait des propositions qui sont évaluées côté client et qui amènent un rafraichissement de la page. Par exemple, si le joueur s’est trompé on va compléter le dessin de la potence. On pourrait permettre au joueur de faire marche arrière dans ses propositions pour tester des alternatives, ou bien marche avant pour se repasser sa partie, mais pour cela il va falloir que l’état sur lequel on arrive donne toutes les informations nécessaires pour recréer la page. Dans notre exemple on pourrait penser au mot à deviner, aux lettres trouvées et au nombre d’essais. Essayez donc de faire ça avec la technique du hash… Vous risquez fort d’être obligés de créer des hashes à rallonge parce que vous devrez encoder l’état du jeu dedans.

Comment apporter une solution élégante à tous ces problèmes ? En utilisant l’ API History de HTML5 bien entendu ! Nous verrons cela dans le prochain volet de la série.

comments powered by Disqus