Une introduction simple à l’API d’historique en JavaScript
L’API d’historique est l’une de ces techniques JavaScript que tout développeur Web doit connaître à froid. Sans cela, un seul clic sur le bouton Précédent sortira directement de votre application. Vous perdrez tout travail en cours et l’utilisateur sera obligé de tout recommencer.
Heureusement, il existe une solution facile. Depuis la formalisation de HTML5 (et la sortie d’Internet Explorer 10), nous avons une API d’historique qui vous permet d’ajouter à la liste d’historique et de réagir à la navigation de retour et de retour. Dans cet article, vous apprendrez comment cela fonctionne — et vous l’essayerez avec un exemple en direct.
Mais avant d’aller de l’avant, examinons de plus près le problème.
Dans l’ancien temps du web, une page Web faisait une chose : afficher des choses. Lorsque vous vouliez regarder autre chose, vous avez cliqué sur un lien et vous êtes allé à une nouvelle URL.
Au fur et à mesure que JavaScript devenait plus puissant, les développeurs ont réalisé que chaque page Web pouvait être une application complète à part entière. Une page web pourrait rivaliser avec une application de bureau! Le problème était que les boutons Précédent et suivant du navigateur ne correspondaient pas à ce nouveau modèle d’application. Par exemple, un utilisateur peut effectuer une séquence de tâches dans une application Web d’une seule page, puis s’attendre à utiliser le bouton Retour pour revenir en arrière d’une seule étape. Au lieu de cela, le bouton Retour reviendrait à la page Web précédente, fermant efficacement l’application JavaScript au milieu de tout ce qu’elle faisait.
Souvent, ce comportement n’était pas intuitif. Par exemple, considérons une application qui utilise l’objet XMLHttpRequest
pour récupérer de nouvelles données et mettre à jour la page. Pour l’utilisateur, il peut sembler qu’il se déplace vers une nouvelle page. Mais s’ils font l’erreur d’utiliser le bouton de retour, l’illusion est brisée et tout se passe sur le côté.
Regardons de plus près. Voici un exemple qui vous permet de parcourir le Dictionnaire Infernal, un livre célèbre (et maintenant du domaine public) de 1818 décrivant des bêtes étranges de la superstition et de la mythologie. L’astuce est que les liens précédents et suivants ne vous mènent pas à une autre page. Au lieu de cela, ils déclenchent un appel asynchrone via XMLHttpRequest
qui obtient les informations pour la diapositive suivante. Ensuite, le code JavaScript met à jour la boîte de diapositives, sans perturber le contenu du reste de la page.
Voici une version live de l’exemple, complète avec le support de l’API d’historique. Même si tout se déroule sur une seule page Web, vous pouvez utiliser les boutons Avant et Arrière pour passer d’une image à l’autre de manière transparente. Vous pouvez même utiliser le bouton Recharger (recharge la diapositive actuelle, plutôt que de redémarrer toute l’application), et vous pouvez placer des signets qui conservent l’état actuel — en d’autres termes, ils vous ramènent directement à la diapositive que vous regardez actuellement.
Et voici une version ancienne de l’exemple qui n’utilise pas l’API d’historique. Peu importe à quelle distance vous vous trouvez dans le spectacle latéral – lorsque vous utilisez le bouton Retour, vous revenez à la page Web précédente. Les boutons Avant et de rechargement sont également cassés. Signets redémarrez l’application depuis le début.
Vous pouvez télécharger tout le code source ici pour jouer avec. (Cependant, cela ne fonctionnera pas localement, car il utilise l’objet XMLHttpRequest
pour effectuer des requêtes Web.) Dans le reste de cet article, je vais décomposer comment tout fonctionne.
Préparation de l’exemple
Avant d’entrer dans l’API d’historique, il est important d’avoir une compréhension de base du fonctionnement de cet exemple. Tout est assez simple.
La page utilise un numéro pour garder une trace de la diapositive qu’elle affiche actuellement. Lorsque vous cliquez sur Suivant ou Précédent, le code modifie le numéro de diapositive actuel. Ensuite, il utilise sa propre fonction goToSlide()
pour mettre à jour la page.
La fonction goToSlide()
est l’endroit où le travail réel a lieu. Il démarre une requête asychrone en utilisant le tristement célèbre objet XMLHttpRequest
.
C’est à vous de décider comment le contenu de la diapositive est généré et renvoyé au code d’appel de la page. Habituellement, vous aurez une sorte de cadre de routage qui déclenche le bon code côté serveur. Ce code côté serveur peut ensuite extraire le contenu de la diapositive d’un cache, d’une base de données, d’un fichier ou même d’un bloc de balisage codé en dur.
Dans cet exemple, j’ai gardé les choses aussi simples que possible. La requête saisit le balisage HTML à partir d’un fichier statique. Donc, si vous demandez la troisième diapositive (avec une URL relative comme Slides / 3), vous obtenez un extrait de balisage HTML pour cette diapositive. Doucement !
Une fois la requête terminée, le navigateur déclenche le gestionnaire d’événements newSlideReceived()
. Ensuite, il s’agit simplement de prendre le balisage reçu et de l’insérer dans la page, en utilisant un espace réservé <div>
.
C’est tout ce que vous devez savoir pour créer la version naïve de cette page, qui ne prend pas en charge l’historique du navigateur.
L’objet historique
L’objet history
existe presque depuis l’aube de JavaScript. Mais pendant une grande partie de sa vie, il était beaucoup moins puissant (et beaucoup moins utile) qu’il ne l’est aujourd’hui.
L’objet history
d’origine n’a qu’une seule propriété et trois méthodes de base. La propriété est length
, et elle vous indique le nombre d’entrées dans la liste d’historique du navigateur :
alert("You have " + history.length + " pages in the history.");
Ce détail est généralement inutile.
Les trois méthodes d’origine history
sont back()
(envoie un visiteur un pas en arrière dans l’historique de navigation), forward()
(envoie un visiteur un pas en avant) et go()
(prend un décalage numérique qui indique au navigateur combien d’étapes il faut avancer ou reculer). Tout cela représente relativement peu, à moins que vous ne souhaitiez concevoir vos propres boutons de retour et de transfert personnalisés sur une page Web.
L’HistoryAPI ajoute deux ingrédients beaucoup plus utiles : la méthode pushState()
et l’événement onPopState
.
Ajout d’une entrée à la liste d’historique
La méthode pushState()
est la pièce maîtresse de l’API d’historique. Il vous permet d’ajouter un nouvel élément dans la liste d’historique de la page en cours. Voici à quoi cela ressemble:
À ce stade, vous pouvez vous demander — à quoi bon avoir plus d’une entrée pour la même page? L’astuce est que chaque entrée a un état attaché avec elle — une information ou un objet lié que vous définissez. Lorsque l’utilisateur revient dans la liste de l’historique, vous obtenez les informations d’état correspondantes afin que vous puissiez retourner la page à sa version précédente.
La méthode pushState()
prend trois arguments:
- Data. Le premier argument est toute donnée que vous souhaitez stocker pour représenter l’état actuel de cette page. Vous utiliserez ces informations pour restaurer la page lorsque l’utilisateur parcourra la liste d’historique.
- Titre. Le deuxième argument est le titre de la page que vous souhaitez que le navigateur affiche. Actuellement, tous les navigateurs sont unifiés en ignorant ce détail. (Vous pouvez donc simplement passer
null
.) - URL. Le troisième argument est la nouvelle version de l’URL que vous souhaitez afficher pour la page dans la barre d’adresse du navigateur. Cela vous permet d’ajouter une meilleure prise en charge du bouton de rechargement et des signets du navigateur.
Dans l’exemple de diaporama Dictionnaire Infernal, il est facile d’ajouter un support d’historique Tout ce dont vous avez besoin pour garder une trace est le numéro de la diapositive. Vous ajoutez à la liste d’historique chaque fois que la diapositive change.
Voici la ligne de code que vous devez ajouter:
history.pushState(slideNumber, null, null);
Vous remarquerez que ce code ne change pas le titre de la page avec le deuxième argument (car cela ne fonctionne pas de toute façon) et ne change pas l’URL (vous verrez comment le faire dans un instant). Tout ce qu’il fait est de stocker le numéro de diapositive.
Pour une image complète, voici les deux appels à history.pushState()
à leur place — les gestionnaires d’événements pour les liens suivants et précédents:
Pour revenir à une page précédente
Lorsque vous utilisez la méthode pushState()
, vous devez également penser à son homologue naturel, l’événement onPopState
. Alors que la méthode pushState()
place une nouvelle entrée dans la liste d’historique du navigateur, l’événement onPopState
vous donne la possibilité de la gérer lorsque l’utilisateur revient.
Pour comprendre comment cela fonctionne, réfléchissez à ce qui se passe si un visiteur parcourt toutes les diapositives de l’exemple du Dictionnaire Infernal. Lorsque chaque nouvelle diapositive est chargée, la page ajoute une entrée d’historique. Mais si l’utilisateur recule avec le bouton de retour, rien ne se passe.
Pour corriger cette lacune, vous devez gérer l’événement onPopState
, qui est déclenché après chaque navigation dans l’historique. (Cela inclut si vous utilisez l’une des méthodes d’historique, comme history.back()
, ainsi que lorsque l’utilisateur clique sur les boutons de navigation du navigateur.)
L’événement onPopState
fournit à votre code les informations d’état que vous avez stockées précédemment avec pushState()
. Dans ce cas, c’est juste le numéro de diapositive, que vous pouvez récupérer à partir de la propriété state
de l’argument event, comme ceci:
var slideNumber = e.State;
Votre travail consiste à utiliser les informations d’état pour restaurer la version appropriée de la page. Dans l’exemple actuel, cela signifie charger la diapositive correspondante en appelant la fonction pratique goToSlide()
qui gère toute la navigation de la diapositive. Le code complet est court et simple:
Notez que vous devez vérifier si e.State
est null
, ce qui se produit sur certains navigateurs lorsque la page est chargée pour la première fois et qu’aucun état n’est enregistré.
Modification de l’URL
La version actuelle de cet exemple prend en charge la liste d’historique mais ne fonctionne pas correctement avec le bouton Recharger. Par exemple, si vous cliquez sur la troisième diapositive, puis actualisez la page, vous reviendrez au début. Vous vous retrouverez dans la même situation si vous essayez de mettre en signet l’une des diapositives — revenez au signet et vous recommencerez sur la première diapositive.
La solution à ces problèmes consiste à utiliser l’API d’historique pour modifier l’URL. Mais il y a une raison importante pour laquelle j’ai quitté cette étape jusqu’à la fin. Souvent, les exemples qui utilisent l’API d’historique modifient l’URL mais n’utilisent pas la nouvelle URL. Cela peut conduire à une application Web qui se casse lorsque l’utilisateur l’actualise (avec une vilaine erreur 404), ou passe à une version similaire mais légèrement différente de la page. Si vous ne savez pas comment gérer différentes URL, il n’y a pas de honte à utiliser l’approche simplifiée de l’exemple précédent.
Alors disons que vous êtes prêt à plonger, à créer de meilleures URL et à bien jouer avec les signets et les rafraîchissements du navigateur. Vous pouvez adopter deux approches de base:
- Changez complètement le nom de la page URL. Dans l’exemple Dictionnaire Infernal, vous pouvez changer l’URL en Slide1.html, Slide2.html, et ainsi de suite lorsque l’utilisateur se déplace dans les diapositives. Le hic est que vous devez soit avoir des pages réelles avec ces noms (ne faites pas cela, c’est lourd), soit vous devez configurer le routage de sorte que lorsque votre serveur Web reçoit une demande de Slide2.html il exécute du code qui crée la bonne version de la page.
- Conservez le même nom de page mais ajoutez des informations à la chaîne de requête. Il s’agit d’une approche plus simple et à plus petite échelle. Au fur et à mesure que l’utilisateur parcourt le diaporama, vous vous retrouverez avec des URL comme le diaporama.html ?slide=1, puis DiapoRama.html ?slide= 2, DiapoRama.html ?slide = 3, et ainsi de suite. L’avantage de cette approche est qu’il est trivialement facile d’écrire un petit morceau de JavaScript qui vérifie la chaîne de requête et s’assure que vous êtes sur la bonne diapositive lorsque la page est rechargée. Vous n’avez besoin d’aucun code de serveur pour que cela fonctionne — une page peut tout faire.
Voici une version modifiée de l’appel pushState()
que vous avez vu plus tôt qui utilise la deuxième approche. Ce code insère le numéro de diapositive à la fin de l’URL:
history.pushState(slide, null, "Dictionnaire.html?slide=" + slide);
Et voici le code qui s’exécute lorsque la page est rechargée. Il vérifie les informations de la diapositive dans l’URL et, s’il trouve un numéro approprié, appelle la fonction goToSlide()
pour charger la diapositive.
Maintenant, vous ne perdrez jamais votre place dans le diaporama. Vous pouvez essayer la version complète de cet exemple ou tout télécharger ici.
Détails finaux
L’API d’historique est facile à utiliser, mais elle facilite également la création de problèmes plus importants que vous résolvez. Maintenant que vous savez comment tout va bien, voici quelques derniers conseils:
- Si vous poussez, vous devez sauter. La méthode
pushState()
et l’événementonPopState
sont des partenaires. Ce que vous mettez avec l’un, vous le récupérez avec l’autre. - Ne modifiez pas l’URL sauf si vous êtes prêt à la gérer. Il est agréable de modifier votre URL pour refléter votre page changeante, mais vous devez être capable de gérer les demandes pour chaque URL que vous utilisez. Si votre URL pointe vers une page qui n’existe pas, votre application se cassera lors des actualisations et des signets.
- Utilisez un objet d’état personnalisé pour stocker plus d’informations. Vous n’avez pas besoin de vous en tenir à un simple numéro. Vous pouvez mettre en état un objet personnalisé entier, qui peut regrouper toutes les informations dont vous avez besoin pour une tâche complexe.