Ne cassez pas l’historique dans vos applications Ajax !

Publié le 20/12/2010, par Grégory Paul dans Valtech | Ajouter un commentaire

Il est aujourd’hui inconcevable de ne pas utiliser Ajax et autres comportements dynamiques à base de JavaScript dans vos applications.
Cependant, souvent, cela se fait au détriment de la gestion du bouton précédent / suivant, et plus globalement de l’historique du navigateur.
Par ailleurs, cela pose également problème si l’utilisateur clique sur un lien JavaScript via l’action “ouvrir dans un nouvel onglet” ou “ouvrir dans une nouvelle fenêtre”.

J’ai voulu adresser ce problème dans un projet personnel de type carnet d’adresse et je vous propose une solution dans la suite de cet article.
L’exemple que je compte vous présenter est une page web dont le but est d’aller récupérer des images issues du site Flickr lorsque l’on clique sur un lien.

Vous pouvez visualiser l’exemple en action ici et le code source là.

Notez que, lorsque vous cliquez sur l’un des liens, l’URL change. Un hash y est ajouté.
Les liens, justement, n’effectuent que cette unique action, qui est de modifier l’URL. Il n’y a aucun événement JavaScript associé à ce lien.

Voici le code de ces liens :

<ul>
	<li><a href="#search=Valtech">Images de Valtech</a></li>
	<li><a href="#search=Disney">Images de Disney</a></li>
	<li><a href="#search=Obernai">Images d'Obernai</a></li>
</ul>

Par contre, un événement JavaScript est associé à l’événement hashchange sur l’objet Window :

$(window).bind('hashchange', HashController.execute);

Le framework JQuery est ici mis en œuvre, à travers la méthode $ et bind notamment. C’est la méthode JavaScript execute sur l’objet HashController qui est appelée lorsque le hash dans l’URL change. C’est cette méthode qui va réellement effectuer l’action demandée. Elle fait office de contrôleur.

Voici le code commenté de l’objet HashController, ainsi que sa méthode execute :

function HashController() {}; // On crée un objet qui va contenir notre méthode
 
HashController.execute = function() {
	var hash = window.location.hash; // On récupère ici le hash de l'URL
	if (!hash) return; // S'il n'y a pas de hash, on stoppe la fonction
	var hashParts = hash.split('='); // On découpe le hash en 2 partie,
	var key = hashParts[0]; // une 1ère partie avant le signe "="
	var val = hashParts[1]; // une 2ème partie après le signe "="
	if ('#search' === key) { // si la première partie correspond à "#search", ce qui est le cas
		// dans nos liens ci-dessus
		FlickrSearcher.get(val); // on execute la méthode "get" sur l'objet "FlickSearcher"
		// avec comme argument la 2ème partie du hash.
	}
}

Détaillons maintenant l’objet FlickrSearcher :

function FlickrSearcher() {}; // On crée un objet qui va contenir nos méthodes
 
FlickrSearcher.get = function(query) { // Une méthode "get", avec la recherche (query) en argument
	$('#content').html('loading...'); // On affiche un message d'attente
	$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=" + query +
		"&amp;tagmode=any&amp;format=json&amp;jsoncallback=?", FlickrSearcher.handleResponse);
		/* On utilise ici l'API Flickr qui permet de rechercher des photos en fonction d'un
		 * mot. Flickr implémente ici le mécanisme dit "JSON-P" qui permet de pallier le
		 * problème lié à la "same origin policy" en nous renvoyant un script rappelant la
		 * fonction passée en argument, ici "handleResponse" sur l'objet
		 * "FlickrSearcher". */
}
 
FlickrSearcher.handleResponse = function(response) { // Voici la méthode "handleResponse",
	// appelée par le script renvoyé par Flickr, avec le résultat de la recherche en argument
	var html = '';
	for(var item in response.items) { // On boucle sur les résultats
		var i = response.items[item]; // Un raccourci pour accéder à l'objet courant
		html += '&lt;a href="' + i.link + '"&gt;&lt;img src="' + i.media.m
			+ '" alt="" /&gt;&lt;/a&gt;';
		// On construit un lien contenant l'image courante
	}
	$('#content').html(html); // On affiche le résultat.
}

Et c’est tout.

Cela permet de naviguer sur le site, de lancer des appels Ajax ou d’afficher du contenu dynamique via du code JavaScript tout en modifiant à chaque fois l’URL et par conséquent, en conservant le fonctionnement standard du navigateur via les boutons précédent, suivant et la gestion de l’historique.

Il manque cependant un point, c’est le code suivant :

$(document).ready(HashController.execute);

Cela permet d’appeler la méthode execute lorsque le contenu du DOM est chargé, ce qui permet de lancer le mécanisme lorsque l’utilisateur tape l’URL complète (avec le hash), s’il la charge via “ouvrir dans un nouvel onglet” ou “ouvrir dans une nouvelle fenêtre” ou s’il la charge depuis un favori.

Tout est ici basé sur l’événement hashchange, qui est supporté nativement sur les navigateurs actuels (Internet Explorer 8+, Firefox 3.6+, Chrome 8+, Safari 5+) ainsi que sur les mobiles (Safari Mobile 4.1+, Android 2.2+). Un plugin JQuery est disponible pour le support des navigateurs plus anciens, notamment Internet Explorer 6.

Je trouve cette solution simple et élégante, qui évite de casser l’historique tout en conservant un site dynamique, où l’interface est construite via JavaScript.
Cela ne règle toutefois pas le problème lié à l’indexation du contenu dynamique via les moteurs de recherche, mais n’est pas incompatible avec la solution présentée par Google à ces fins.

N’hésitez pas à me donner votre avis sur ce fonctionnement, notamment sur des inconvénients éventuels liés à cette façon de faire.

Leave a Reply