Résolvez vos problèmes de Hashbangs grâce au PushState en HTML5

Au début du web nous n’avions que des pages statiques, nous entrions une adresse et le serveur nous retournait une page HTML. Puis nous avons voulu dynamiser un peu nos sites avec du Javascript et de l’AJAX, jusqu’à avoir des applications complètes dans nos navigateurs. Ce que nous appelons maintenant des “Single-Page-Application”.

Une des problématiques lors du développement d’application de type “single-page” est que nous chargeons une page, puis nous modifions le contenu de celle-ci au gré des clicks de l’utilisateur sans modifier visiblement l’URL de la page. Ceci est logique, car nous restons toujours sur la même page, mais ne suit pas la philosophie de Web où un contenu est identifié de manière unique.

Pour résoudre ce problème et avoir un identifiant unique pour chaque ressource, il existait jusqu’à maintenant une solution : Ajouter des hash pour avoir des adresses comme ceci http://monsite/#article/0/spring_c_est_bien

Le hash apporte une solution partielle, car si cette technique permet d’avoir un identifiant unique pour une ressource, celle-ci est connue uniquement du navigateur, mais pas du serveur. Alors oui, nous pouvons mettre l’adresse en favori et lorsque l’utilisateur rechargera cette page, il l’aura bien la page dans le bon état, mais il reste des problèmes.

Tout d’abord,  l’affichage de la page n’est pas optimale, car la remise dans le bonne état de la page se fait forcément côté sur le client via Javacript, car le serveur n’ayant pas l’intégralité du nom de la ressource, il ne peut pas générer la page dans son état final. Pour l’utilisateur ça veut dire attendre que tous les scripts soient chargés, puis que le javascript s’exécute. Sur une machine peut puissante ou si vous utilisez 90 % votre CPU pour lire une vidéo avec flash, un temps plus ou moins long est nécessaire pour que la page s’affiche et donc l’expérience utilisateur est dégradée.

L’autre problème d’avoir le nom de la ressource uniquement sur le client est que si le serveur doit faire une redirection, il la fera uniquement vers le début de l’adresse. Par exemple si vous devez vous authentifier, vous serez rediriger vers la page d’accueil et non vers la page que vous avez appelez initialement.

Mais ça c’était avant, maintenant avec HTML5, il est possible d’avoir des vrais adresses comme ceci http://monsite/article/0/spring_c_est_bien tout en continuant à utiliser de l’Ajax. Pour réaliser ce miracle, nous utilisons la nouvelle fonction pushstate de l’objet History. Alors quand je dis nous utilisons, c’est vrai, le pushstate a été mis en place sur la prochaine version de Tatami. Nous ne sommes pas les seuls Twitter utilise cette technique pour accélérer son affichage, comme indiqué ici.

Compatibilité

Avec HTML5, nous ne savons jamais quel navigateur supporte quelle fonctionnalité, voici un tableau des navigateurs supportant le pushstate

IE

Firefox

Safari

Chrome

Opéra

iPhone

Android

4.0+

5.0+

8.0+

11.50+

4.2.1+

Navigation via du hash

Nous allons créer les liens de la manières suivantes :

Lorsqu’un utilisateur clique sur un lien, le serveur n’est pas appelé puisque nous restons sur la même page. Pour modifier le contenu de la page, il ne reste plus qu’à ajouter un événement sur le changement du hash.

Attention, l’événement n’est pas lancé au chargement de la page, il faut appeler la fonction de modification du contenu au chargement de la page.

Navigation via pushstate

Pour ce type de navigation, nous utilisons les liens suivants avec des vrais URLs :

Lorsque l’utilisateur clique sur un lien, il est nécessaire de supprimer le comportement par défaut du navigateur, sinon le serveur sera appelé. Nous allons ensuite changer l’url et appeler la fonction modifiant le contenu de la page

Il est nécessaire d’ajouter l’événement suivant pour que le contenu de la page soit modifié lors de la navigation via les boutons “précédent” et “suivant”

Attention le comportement par défaut de cet événement est différent entre les navigateurs Webkit et Firefox. Sous Webkit l’événement est appelé lors du chargement de la page, alors qu’il ne l’est pas sous Firefox

Pushstate dans les frameworks MVC

Il est possible dès maintenant d’avoir le support du pushstate dans certains frameworks MVC. L’utilisation des ces frameworks permet de détecter le support de la fonctionnalité par le navigateur et d’utiliser la méthode du hash pour les vieux navigateurs.

Backbone

Pour l’utiliser, il suffit de l’activer au moment du lancement de la gestion des routes

Il est également possible de passer en paramètre de start, la propriété  silent: true, qui permet de ne pas déclencher la route lors du chargement initial de la page.

AngularJS

Il suffit de le déclarer dans la configuration de l’application

Il n’y a pas d’équivalent au silent: true de backbone.js, pour ne pas avoir de déclenchement d’une route lors du premier chargement, ce qui peut s’avérer gênant dans certains cas.

GWT

Gwt ne supporte pas cette fonctionnalité pour le moment, mais il existe l’extension https://github.com/jbarop/gwt-pushstate/

Pourquoi le pushstate c’est génial ?

L’intérêt t’utiliser Ajax sur un site Web ou une application n’est plus à démontrer. Consommation de bande passante réduite, fluidité, possibilités ergonomiques étendues. Néanmoins, ils posent certaines problématiques qu’il est possible de résoudre avec cette nouvelle possibilité

Site Web

Sur un site Web la principale problématique est la complexité d’indexation des contenus affichés en Ajax. Pour l’indexation par Google, la méthode est la suivante :

  • Ajouter avant le hash un ! pour avoir une url de ce type http://monsite/#!article/0/spring_c_est_bien. Google comprend alors qu’il s’agit d’un contenu Ajax et pas d’une navigation d’ancre
  • Sur ce type de contenu Google va appeler l’url suivante http://monsite/?_escaped_fragment_=article/0/spring_c_est_bien qui devra retourner le contenu html complet

Avec pushstate, il n’est plus nécessaire de faire ce type de configuration, mais il est nécessaire qu’un appel direct aux adresses générées via pushstate retourne le code HTML complet.

Dans ce cas le mode silent de backbone est intéressant, le contenu de la page étant généré complètement par le serveur lors d’un appel direct, il n’a pas à être généré par le client. Ce procédé est utilisé par Twitter.

Si nous accédons à un “Trending-topic” directement depuis la barre d’adresse via cette url https://twitter.com/search?q=%23RolandGarros&src=tren, nous obtenons la page HTML complète :

Capture d’écran 2013-06-03 à 11.35.25

Mais si nous accédons à la page en cliquant depuis le bloc “tendance”. L’url est la même, mais il entraîne un appel AJAX avec une réponse en JSON :

Capture d’écran 2013-06-03 à 11.34.37

Application Web

Dans une application Web, la problématique lorsque l’on utilise la méthode des hash est que tout ce qui se trouve après le hash n’est pas envoyé au serveur, donc lors d’une redirection cette information n’étant pas présente, celle-ci est faite sur l’adresse jusqu’au hash, donc la page d’accueil de l’application.

Le cas fonctionnel où ce comportement est gênant est lorsque nous mettons l’adresse en favori où lorsque nous l’envoyons par e-mail. Si pour accéder à la page, ne devons passer par processus d’authentification, nous serons dirigé vers la page d’accueil de l’application plutôt que vers le contenu intéressant.

Pour une application Web, qui n’a pas vocation à être indexée par les moteurs de recherche,  il n’est pas nécessaire que le serveur retourne le HTML complet de la page. Il suffit qu’il réponde à toutes les ressources, le client se chargeant de mettre à jour le contenu en AJAX.

Nous avons fait ceci sur Tatami, lorsque nous accédions à “Trending topic” sans être authentifié :

Avant , nous étions redirigé vers l’accueil lorsque nous saisissions l’url https://tatami.ippon.fr/tatami/#/tags/TatamiV3

Capture d’écran 2013-06-03 à 11.48.06

Capture d’écran 2013-06-03 à 11.48.32

Maintenant, nous sommes bien redirigé vers le “Trending topic” lorsque nous saisissons  l’url https://tatami.ippon.fr/tatami/new/tags/TatamiV3

Capture d’écran 2013-06-03 à 11.49.02

Capture d’écran 2013-06-03 à 11.49.32

Pour aller plus loin, découvrez notre formation html5 : http://www.ippon.fr/formation/html5-css3

Tweet about this on TwitterShare on FacebookGoogle+Share on LinkedIn
  • asicfr

    y a vraiment un probleme avec les images et les liens …

  • (╯°□°)╯︵ɐɔ‾ʞɔǝɥɔ

    > Dans ce cas le mode silent de backbone est intéressant, le contenu de la page étant généré complètement par le serveur lors d’un appel direct, il n’a pas à être généré par le client. Ce procédé est utilisé par Twitter.

    Pourriez-vous donner d’avantage d’infos sur la manière dont vous vous y prenez pour interpréter votre template côté serveur et fournir des captures de page fidèles à Google et cie ?

  • Alban Dericbourg

    Une interrogation à la lecture de cet article : comment est géré le cas où j’accède à une ressource en saisissant directement l’URL dans la barre d’adresse.

    Par exemple : http://www.chezmoi.net/article/comment/1
    En partant de « / », Angular (je connais moins Backbone) sait gérer la navigation jusque « /article/comment/1 » mais comment fait-il si je veux accéder directement à « /article/comment/1 » ?

    • Jean-Philippe Bunaz

      Lorsque l’on accède directement à l’url, il faut que le serveur retourne la page HTML complète comme ci elle avait été générée par le javascript.
      Il faut donc qu’il n’y ait pas d’événement déclencher sur le premier affichage, sinon on regénère tout une nouvelle fois c’est pour ça que l’option silent: true est intéressante dans backbone.

      • Alban Dericbourg

        Je ne suis pas sûr de bien comprendre (je suis même certain de ne pas avoir compris).

        Ça veut dire qu’il faut « dupliquer » tous les rendus : une fois côté client (avec Angular ou Backbone) et une fois côté serveur ?

        • Jean-Philippe Bunaz

          oui, donc c’est rentable vraiment pour les sites à forte audience qui veulent optimiser l’expérience utilisateur. Après il faut relativiser ce travail, car si on fait vraiment un site grand public en AJAX, il y a un travail à faire pour les crawler des moteurs de recherche