Réconcilier Ajax et historique de navigation

Parmi toutes les nouvelles APIs apportées par HTML5, l’API History est probablement une des plus sous-estimées, et pourtant elle est certainement déjà une des plus utilisées car elle permet de résoudre un problème très épineux.

Dans cette série d’articles, je vous propose de suivre la démarche suivante:

  1. Démontrer le problème sur un exemple simple
  2. Regarder les différentes solutions qui ont été apportées
  3. Et surtout, comprendre pourquoi vous allez utiliser l’API History!

La pierre angulaire du Web est, et a toujours été, le concept d’URL. Elles permettent:

  • de naviguer de page en page
  • de mettre des pages dans les favoris
  • de construire un historique de la session
  • aux moteurs de recherche d’indexer des contenus

Mais si leur rôle central est indiscutable, notre manière de penser le Web a, elle, bien changé depuis ses origines. De nouvelles problématiques sont apparues, qui ont engendré de nouvelles solutions.

Un peu d’histoire pour comprendre l’historique

A long, long time ago…

Pendant longtemps, les choses ont été simples:

  1. L’URL est modifiée (suite à un clic sur un lien)
  2. Le navigateur émet une requête HTTP vers le serveur Web.
  3. Le serveur Web envoie en retour un document HTML complet
  4. Le navigateur s’empresse d’afficher cette nouvelle page
  5. Il ajoute une entrée à l’historique. Basta.

Le site qui nous servira d’exemple, dans sa première version, suit cette technique ancestrale. Son contenu est tiré du site W3C HTML5 Logo et vous en trouverez le code source sur github. Vous pourrez suivre les différentes versions du site en naviguant dans l’historique des commits. Pour le faire tourner, il vous faudra un serveur Web du genre Apache.

Le site est composé d’une page principale, index.php, qui définit la structure du site. De plus, on peut lui passer un paramètre page qui définit le nom du fragment que l’on souhaite insérer dans la zone de contenu. Les fragments se trouvent dans le répertoire includes.
Pour que les URLs soient propres, j’ai également mis en place une réécriture d’URL qui transforme l’horrible http://www.example.com/index.php?semantique en http://www.example.com/semantique.

Assez classique, non ? Voici une copie d’écran du résultat:

Puis vint Ajax

Les sites Web devenant de plus en plus complexes, il était pénible de demander un chargement complet de page à chaque fois que l’utilisateur effectuait une action, alors qu’en général seule une portion de la page était affectée… Remarquez d’ailleurs que je commence à parler d’action et non plus simplement de clic sur un lien. En effet, nous passions doucement d’un Web orienté document (l’utilisateur consulte des documents ayant des liens entre eux) à un Web orienté application (l’utilisateur agit sur le contenu et demande des services).

Vers les années 2000, l’apparition d’Ajax a permis de modifier seulement la portion de la page impactée par l’action de l’utilisateur. Cela amène les avantages suivants:

  • Le trafic réseau est réduit
  • L’UX est améliorée du fait d’un temps de latence réduit.

Mettons Ajax en place dans le site d’exemple. Il faut intercepter les clics sur les liens pour remplacer le comportement par défaut du navigateur par une requête Ajax:

function loadFragment(name) {
    // Reconstruction du chemin du fragment sur le serveur
    var urlToLoad = 'includes/' + name + '.php';
    $.get(urlToLoad, null, processNewContent);

    function processNewContent(newContent) {
        // Afficher le nouveau contenu
        $('#detail').html(newContent);
    }
}

$(document).ready(function() {
    $('nav a').click(
        function(event) {
            // On empêche la requête HTTP de se produire
            event.preventDefault();
            var link = $(this).attr('href');
            loadFragment(link);
        }
    );
})

La fonction loadFragment() reconstruit le chemin vers le fragment désiré, effectue la requête Ajax et affiche le fragment retourné par le serveur.

Le noeud du problème

Aujourd’hui cette technique est largement utilisée et maitrisée, mais tout n’est pas rose pour autant. Je ne parlerai ici que de l’inconvénient qui m’amènera à parler de l’API History: l’historique du navigateur ne reflète plus les actions de l’utilisateur ! Dans la version Ajax du site d’exemple, on s’aperçoit effectivement que quel que soit le nombre de clics que l’on fait, on n’a jamais qu’une seule entrée dans l’historique.

En effet, le navigateur ne place une nouvelle entrée dans son historique que lorsque l’URL a changé. Hors, comme dans notre exemple, de plus en plus de sites Web (ou applications, la différence étant de plus en plus ténue) se mettent à suivre le principe appelé Single Page Interface, c’est-à-dire:

  • n’utiliser qu’une seule page
  • appliquer des modifications à cette page, en fonction des actions de l’utilisateur, pour passer d’un état à un autre

Du coup, si l’on ne fait rien, l’utilisateur ne peut plus distinguer un état particulier du site, que ce soit pour y revenir plus tard, pour le partager, … Il doit repasser par l’adresse de départ, qui correspond à l’état initial, puis refaire toutes les actions qui l’ont amené à un état donné. C’est quand même ballot, surtout si on pense qu’à la base l’utilisation d’Ajax était là pour améliorer son expérience !

A ce point de notre histoire, ce qu’il fallait trouver donc, c’était un moyen de faire les deux choses suivantes en parallèle pour répondre à une action:

  • Une requête Ajax
  • Un changement dans l’URL qui n’entrainerait pas de requête HTTP

Nous verrons dans l’article suivant de la série comment cela est possible.

comments powered by Disqus