Scroll continu et retour arrière en jQuery

J’ai eu à développer il y a peu un scroll continu sur un rayon de produits. Au cours de ce développement, plusieurs contraintes se sont présentées pour une gestion correcte de ce scroll continu. Et pour qu’il reste compatible avec des anciens navigateurs (IE6 notamment pour ne pas le citer). Une des contraintes concernait notamment le retour sur le rayon après avoir consulté une fiche article. Je partage avec vous les solutions trouvées, au cas où vous seriez dans le même cas. Si vous avez de meilleures solutions, n’hésitez pas à en faire part dans les commentaires. Je n’ai pas mis tout le code, mais uniquement les parties intéressantes. Si vous souhaitez plus de détails sur un point n’hésitez pas à me demander.

Déclenchement du scroll continu

Le rayon était à l’origine paginé via Ajax, le fait d’afficher les produits suivants en bas de page n’a donc pas été le point le plus problématique. Cela revenait finalement à faire le même appel Ajax, en plaçant le résultat html de la requête après l’existant au lieu de les mettre à la place de l’existant.

Le seul point à travailler, c’est le déclencheur du scoll continu. Pour cela, nous allons utiliser les différentes valeurs suivantes :

var wintop = jQuery(window).scrollTop();
var docheight = jQuery(document).height()
       - $('#informations').height();
var winheight = jQuery(window).height();

Ici, le scrollTop recupère la position haute de la fenêtre. Docheight va récupérer la hauteur du contenu. J’enlève à cette hauteur un bloc d’informations que nous avons sous les produits, qui est changeant suivant le rayon et qui pourrait fausser le calcul. Winheight récupère quant à lui la hauteur qu’affiche le navigateur sur une fenêtre. Notez l’utilisation de « jQuery » au lieu de « $ » pour une meilleure compatibilité avec IE.

Il suffit alors d’utiliser ces trois variables pour savoir quand déclencher le scroll continu. On vérifie le tout dans l’évènement scroll.
Dans mon cas :

jQuery(window).scroll(function()
{
 if (wintop > (docheight - (winheight * 3) ))
 {
 scrollAuto();
 }
});

Comme le rayon est long, j’anticipe avec 3 hauteurs de navigateur histoire que les produits soient chargés, ou presque chargés quand l’internaute arrive en bas. Ainsi, cela fluidifie sa descente, sans se déclencher non plus trop tôt.

Gestion du retour arrière

Reste le problème du retour en arrière.
Dans le rayon, avec plusieurs pages chargées via le scroll continu, si je rentre dans une fiche produit et que j’en sors avec le retour du navigateur, je me retrouve avec une seule page de chargée. Problématique car l’internaute ne retrouve pas sa position de départ dans le rayon. Et ne sait donc plus forcément sur quel produit il était.

Première solution trouvée : nous sommes en jQuery 1.3, et un des bugs recensés sur cette version est le fait de déclencher à tous les coups l’évènement « unload ». Pour les navigateurs gérant un cache pour les « back » et les « forward » (bfcache) comme Firefox, cela leur indique de ne pas tenir compte de ce cache, et de recharger la page. Du coup le document.ready est joué et le rayon se retrouve presque dans l’état initial (sauf pour les éléments de formulaire renseignés).

Qu’à cela ne tienne, étant dans un projet Symfony je peux utiliser une autre version de jQuery pour un module particulier, la migration du projet complet vers jQuery 1.4 étant à étudier. Premier test sur Firefox, impeccable, le retour arrière utilise le bfcache, aucun rechargement de page, l’internaute est positionné à l’endroit d’où il vient.

Sur Chrome ou IE par contre, le comportement est toujours le même, à savoir un passage par le document.ready, et un rayon qui se retrouve avec une seule page de produits.

La solution trouvée a été de passer par les hash. Un champ hidden stocke le numéro de la page courante, et à l’appel de la fonction gérant le scroll continu, on incrémente cette valeur, et on la passe dans le hash de l’url :

window.location.hash = '#' + $('#RayonCurrentPage').val();

Ainsi, au fur et à mesure de la descente, l’url s’enrichit du numéro de page maximum.

Au retour sur le rayon depuis une fiche produit, l’url qui sera récupérée est celle avec le hash. Il suffit de mettre un test dans le document.ready pour voir si nous avons un hash. Si oui cela nous donnera le nombre de produits à charger (nombre de produits par page multiplié par la page récupérée du hash).

var hash = window.location.hash; // récupération du hash
var pageID = hash.replace(/^#/, '');  // extraction de la page
if (pageID == '')
{
 // pas de numéro de page, chargement standard
}
else
{
 // num de page, charge nb produits correspondant
}

Nous avons donc maintenant un rayon qui se recharge du bon nombre de produit au retour depuis une fiche. Et nous avons gardé le fait qu’à une url correspond une ressource précise.

En revanche, il peut subsister dans certains cas et sur certains navigateurs un problème. La page est complète, mais l’internaute n’est pas positionné au bon endroit. Ca peut être le cas si la position du curseur n’existe pas sur la page avant le chargement complet. Sur certaines versions d’IE il peut même se retrouver en haut de page.

Pour ce problème là, la solution que j’ai trouvé n’est peut être pas la plus élégante, mais je n’en ai pas trouvé d’autres. Vu que le document.ready est joué, toutes les variables js sont remises à blanc. Je stocke donc le scrollTop du rayon dans un hidden situé dans un élément commun au rayon et à la fiche produit.
Il suffit alors de récupérer si besoin (si l’on est dans le cas d’un retour arrière uniquement) la valeur de ce scrollTop et de jouer le script suivant :

$('body,html').animate({
 scrollTop: topWindow // topWindow contient le scroll top
}, 800);

Le animate permet de jouer ce scroll avec un effet de descente et pas brutalement.

Enfin, le hash peut avoir un effet énervant sur la gestion de l’historique. Un internaute ayant fait défiler ses pages jusqu’à la page 9 et voulant revenir à la page précédant son rayon devra cliquer autant de fois sur le retour navigateur qu’il a chargé de pages en automatique.
Si vous ne souhaitez pas qu’il en soit ainsi, vous pouvez utiliser le plugin jQuery hashchange :
http://benalman.com/projects/jquery-hashchange-plugin/

Ce plugin permet de récupérer l’évènement « onhashchange » sur tous les browsers, même ceux ne gérant pas nativement cet évènement. Là encore l’idée est de rester compatible avec les anciens navigateurs.
Une fois le plugin ajouté à votre page, il vous suffira de mettre dans votre document.ready le code suivant :

jQuery(window).hashchange( function(){
 var hash = window.location.hash;
 pageID = hash.replace(/^#/, '');
 if ($('#RayonCurrentPage').val() > pageID || pageID == '')
 { //hashchange pour nb inférieur, on revient page prec.
 history.go(-1);
 }
})

Quand le hashchange, on vérifie s’il est inférieur à la page courante (qu’on a utilisé aussi précédemment). Si oui, cela veut dire que l’internaute cherche à revenir sur la page d’avant. On utilise alors le history.go(-1).

Un commentaire sur « Scroll continu et retour arrière en jQuery »

Laisser un commentaire