Guide
du lecteur

La navigation interne dans une page web

par
le à 16h49

La navigation interne à une page web d'un site consiste à utiliser une ancre et y faire référence pour forcer l'affichage du navigateur sur la zone en question. Penchons nous d'abord sur l'utilisation « légacy* » de ce mécanisme et ensuite sur les améliorations ergonomiques envisageables avec du JavaScript (et du jQuery).

* tel qu'il fonctionne de manière standard depuis sa création.

L'ancre interprétée nativement par le navigateur

L'utilisation standard de la balise <a> est celle de permettre la navigation entre différentes pages d'un même site ou d'autre sites/ressources sur la toile.

Une seconde utilisation —celle qui nous intéresse dans cet article— est de permettre la navigation interne à une page. Cette seconde utilisation peu grosso-modo être divisée en 3 types de lien :

  • accès rapide : on va plus loin dans la page ("Aller au menu", "Aller à l'article"...),
  • navigation interne : réservée pour des intéractions ("Retour en haut de page", "Référence au commentaire 14"...),
  • évitement : plus de détails dans l'article Les liens d'évitement - Alsacreations.

Bien que la finalité de chaque utilisation soit différente, le mécanisme de mise en place est identique.

La balise a et l'attribut name

Dans les débuts, il n'y avait qu'une façon d'atteindre une zone précise d'un document : le couple balise <a> et attribut name. Prenons l'exemple d'une phrase présente plus haut dans cet article et voyons comment l'ancrer de manière à y accéder par un lien.

Pour atteindre l'ancre « adefinition » :

<a name="adefinition">*</a> tel qu'il fonctionne de manière standard depuis sa création.

On utilise le lien :

Cliquer sur <a href="la-navigation-interne-dans-une-page-web/#adefinition">Atteindre</a>

Résultat : Cliquer sur Atteindre

Gardez à l'esprit qu'en xHTML ou en HTML5 l'attribut name sur la balise <a> est obsolète.

Note : comme vous pouvez le constater dans notre exemple, la phrase est cachée par le menu. Malheureusement l'ancre ne permet pas de définir une valeur de décalage (offset). On peut facilement envisager d'ancrer un élément plus haut que la zone véritablement souhaitée pour la rendre bien lisible mais QUID de l'accessibilité et de la maintenance des ancres à posteriori.

L'attribut id comme ancre

Puis très vite les navigateurs se sont nativement affranchis de la balise <a name=""> en permettant de cibler n'importe quel élément HTML. Pour cela —parce que l'attribut name n'est pas disponible sur toutes les balises— on utilise l'attribut id disponible sur toutes les balises.

Pour atteindre l'ancre « pdefinition » :

<p id="pdefinition" class="eye-catching">
	<a name="adefinition">*</a> tel qu'il fonctionne de manière standard depuis sa création.
</p>

On utilise le lien :

Cliquer sur <a href="la-navigation-interne-dans-une-page-web/#pdefinition">Atteindre</a>

Résultat : Cliquer sur Atteindre

Note : vous remarquerez que le système natif impose d'ajouter l'ancre à l'url. Cela peut s'avérer être un avantage pour communiquer l'adresse et la position précise d'un commentaire en un seul lien cependant pour les mécanismes AJAX utilisant le détournement de l'ancre (la partie hash de l'adresse se situant après le « # ») cela est un handicape certain.

Les ancres HTML et le JavaScript

A présent voyons quelques exemples de manipulation d'ancres avec du JavaScript.

Décaller l'affichage de l'ancre du haut du navigateur

Comme noté précédemment, il serait possible de simplement placer son ancre plus haut que l'endroit réel où l'on souhaite arriver. Cependant pour des questions d'accessibilité, il vaut mieux que l'élément ancré soit réellement l'élément que l'on souhaite afficher.

Le script JavaScript ci-dessous va vous permettre de décaler l'affichage de vos ancres :

	// Pour tous les liens commençant par #.
	$("a[href^='#']").click(function (e) {
		// On annule le comportement initial au cas ou la base soit différente de la page courante.
		e.preventDefault(); 
				
		// On ajoute le hash dans l'url.
		window.location.hash = $(this).attr("href");
		
		// Une fois en place on va forcer l'affichage 40 pixels plus haut.
		$(window).scrollTop( $(window).scrollTop() - 40 );
	});

Exemple : Atteindre #pdefinition avec un offset de -40

Faire défiler la page jusqu'à une ancre

Nous allons rajouter un effet pour surcharger le comportement de base. La page ne sautera plus du lien vers l'ancre, mais accompagnera l'utilisateur jusqu'à l'ancre ce qui lui permettra de ne pas être perdu. Il pourra ainsi appréhender où se situe la nouvelle zone dans la page courante.

	// Pour tous les liens commençant par #.
	$("a[href^='#']").click(function (e) {
		var 
			yPos,
			yInitPos,
			target = ($($(this).attr("href") + ":first"));
			
		// On annule le comportement initial au cas ou la base soit différente de la page courante.
		e.preventDefault(); 
		
		yInitPos = $(window).scrollTop();
		
		// On ajoute le hash dans l'url.
		window.location.hash = $(this).attr("href");
		
		// Comme il est possible que l'ajout du hash perturbe le défilement, on va forcer le scrollTop à son endroit inital.
		$(window).scrollTop(yInitPos);
		
		// On cible manuellement l'ancre pour en extraire sa position.
		// Si c'est un ID on l'obtient.
		target = ($($(this).attr("href") + ":first"));

		// Sinon on cherche l'ancre dans le name d'un a.
		if (target.length == 0) {
			target = ($("a[name=" + $(this).attr("href").replace(/#/gi,"") + "]:first"))
		}
		
		// Si on a trouvé un name ou un id, on défile.
		if (target.length == 1) {
			yPos = target.offset().top; // Position de l'ancre.
		
			// On anime le défilement jusqu'à l'ancre.
			$('html,body').animate({ scrollTop: yPos - 40 }, 1000); // On décale de 40 pixels l'affichage pour ne pas coller le bord haut de l'affichage du navigateur et on défile en 1 seconde jusqu'à l'ancre.
		}
	});

Exemple :

Variante : ne pas conserver le hash dans l'url

Cela permet aux d'applications web avec de l'AJAX se servant du hash pour la navigation globale de ne pas perturber l'adresse. Il suffit de supprimer les lignes suivantes de l'exemple précédent :

	...
	yInitPos,
	...
	yInitPos = $(window).scrollTop();
	window.location.hash = $(this).attr("href");
	$(window).scrollTop(yInitPos);

Variante : ne pas conserver le hash dans l'url sans défilement

Au cas où vous préféreriez simplement annuler l'ajout du hash, mais conserver le comportement initial (sans défilement) il suffit de remplacer la ligne :

	$('html,body').animate({ scrollTop: yPos - 40 }, 1000);

par la ligne

	$(window).scrollTop(yPos - 40);

Quoi de plus simple :)

Problème d'ancre avec la balise base

Si vous ne la connaissez pas encore, sachez que la balise <base> permet de choisir la référence d'une url dans le cas d'un appel relatif à une page dans un lien. Elle est réellement pratique pour les fichiers appelés par réécriture d'url incluant donc de faux sous répertoires.

Résoudre un problème...

Voyez plutôt l'exemple :

Problème de chemin relatif avec réécriture d'url

Je décide de ne pas utiliser d'url absolue car mon site peut changer d'adresse racine. Je n'ai pas envie de changer tous mes liens en base de données si changement d'adresse par exemple. J'utilise donc l'url relative.

À cette adresse http://blog.haeresis.fr/, je dois inclure ma CSS comme suit :

<link type="text/css" rel="stylesheet" href="css/common.css" media="screen" />

Et à cette adresse http://blog.haeresis.fr/categories/, je dois inclure ma CSS comme suit :

<link type="text/css" rel="stylesheet" href="../css/common.css" media="screen" />

Le problème intervient donc dès lors que l'inclusion de <link> est dans un fichier commun à toutes les pages. Avec la réécriture d'url, je n'ai aucune idée du niveau d'arborescence qui sépare mon fichier de la page courante.

Exemple de résolution avec base

Grâce à l'utilisation de <base> avant tous mes liens je peux définir la base de l'appel d'un fichier qui se fait initialement depuis le dossier de l'adresse courante de manière identique à toutes les pages (quel que soit le niveau d'arborescence) et ceux pour tous les liens de la page.

<base href="http://blog.haeresis.fr/" />
<link type="text/css" rel="stylesheet" href="css/common.css" media="screen" />

Cela n'empêchera pas de devoir changer la base s'il y a changement d'adresse du site. Mais le travail est alors minime et les liens relatifs enregistrés en base (ceux d'un article par exemple) marcheront partout, quel que soit la profondeur de l'adresse de l'article.

...peut en apporter un autre

Tout semble aller pour le mieux dans le meilleur des mondes ; mais revenons à notre lien <a href="#pdefinition">. Il pointe maintenant vers la base du site et non plus vers la page courante à cause de <base> ! Cela a pour effet de bord de ne pas atteindre l'ancre dans la page mais de chercher à atteindre l'ancre à l'adresse de <base>. Il faut alors compléter son lien pour se ré-accorder à la base du site.

Mise en évidence de l'effet de bord

Nous sommes à l'adresse : http://blog.haeresis.fr/la-navigation-interne-dans-une-page-web/

<head>
	...
	<base href="http://blog.haeresis.fr/" />
	...
</head>
<body>
	...
	<a href="#pdefinition" title="Atteindre l'ancre">Atteindre l'ancre<a> 
	<a href="la-navigation-interne-dans-une-page-web/#pdefinition" title="Atteindre l'ancre">Atteindre l'ancre<a> 
	...
</body>

Problème : Cette ancre ne marche pas à cause de la base du site

Un peu de JavaScript et on oublit

Si vous voulez absolument éviter de réécrire l'url de la page courante (parce que si elle change d'adresse, le monde s'éteindra) vous pouvez remplacer :

	<a href="#pdefinition" title="Ancre">Ancre<a>

par :

	<a href="#pdefinition" onclick="window.location.hash = 'pdefinition'; return false" title="Ancre">Ancre</a>

ou automatiser le système avec jQuery :

	// Pour tous les liens commençant par #.
	$("a[href^='#']").click(function (e) {
		e.preventDefault(); // On annule le comportement initial.
		window.location.hash = $(this).attr("href"); // On change le hash de l'adresse.
	});

Note : Vous me direz que sans JavaScript on retournera à l'accueil et vous aurez raison...

En conclusion

La simplicité avec laquelle on peut atteindre une ancre s'envole dès l'instant où l'on souhaite faire plus que cela. Ainsi utiliser les ancres avec la balise base, décaler l'offset d'une ancre ou animer la page jusqu'à l'ancre sont autant de tâches réalisables mais qui nécessitent un minimum de connaissances et de précautions.

4 Commentaires

Choisir un Avatar depuis une url

Adresse : (64px x 64px)
L'url de votre image est incorrecte !

ou Changer la couleur de fond

Nouvelle couleur !

et Choisir un Avatar dans la liste

ChangerFermer
Je crois que vous avez oublié votre commentaire !
Votre email est bizarre !
L'url de votre site doit ressembler à ça : http(s)://(www.)votresite.ici !
Vous ne pouvez pas laisser d'email en restant Anonyme !
Vous ne pouvez pas laisser de site web sans votre email !

Votre commentaire a été ajouter ci-dessous ! Si vous désirez le supprimer ultérieurement, servez vous du code suivant :

Les commentaires sont actuellement affiché du plus rescent au plus ancien. Vous pouvez inverser l'ordre en cliquant ci-dessous :

  • Du plus rescent au plus ancien
  • Du plus ancien au plus rescent
111
Supprimer
Votre commentaire a été supprimé.
le à 21h02

Merci beaucoup pour ce tuto! vraiment utile!
Petite question, est-il possible de décaler l'affichage des ancres en vh plutôt qu'en px?
Merci!

Je trouve ce commentaire pertinent ?

Mauvais code de suppression
110
Supprimer
Votre commentaire a été supprimé.
le à 10h24

Un bon article clair et chose rare : un bonne exemple qui marche du premier coup quand on l'utilise dans un site. merci

Je trouve ce commentaire pertinent ?

Mauvais code de suppression
108
Supprimer
Votre commentaire a été supprimé.
le à 09h29

Dans quel fichier dois-je insérer le bout de code si je suis sur wordpress ?
J'ai essayé de mettre ça dans un fichier js (rangé dans un dossier js) de mon thème, mais ça semble ne pas le prendre en compte. Lorsque j'actualise ma page j'ai l'astuce qui fonctionne sur l'instant, puis, lorsque je clique sur mes liens, ils ne m'amènent pas aux ancres en scrollant et j'ai toujours pas de décalage de -40. :/

Je trouve ce commentaire pertinent ?

Mauvais code de suppression
104
Supprimer
Votre commentaire a été supprimé.
le à 09h28

merci je commence à comprendre une notion que j'appliquais sans trop comprendre.
Merci!!!!bien expliqué

Je trouve ce commentaire pertinent ?

Mauvais code de suppression
Supprimer
Votre commentaire a été supprimé.
A l'instant

Je trouve ce commentaire pertinent ?

Vous avez aimé cette article ? Commentez le ou .

A propos de l'auteur

Bonjour, je suis Bruno Lesieur, architecte web basé sur Annecy. Intégrateur HTML5, CSS3 et JavaScript ou encore développeur Front-End ; appelez moi comme vous le voulez mais sachez que je suis très friand de performance web, de Responsive Web Design, d'accessibilité web, de bonnes pratiques, de techniques et d'outils de gestion web, de factorisation et d'automatisation, de partage et casse-tête en tout genre ainsi que de boissons gazeuses.
Article Commentaires Avis 4
la-navigation-interne-dans-une-page-web

Quelques #IDet beaucoup de .CLASS

Année
Mois
http://blog.haeresis.fr default fr FR Janvier Février Mars Avril Mai Juin Juillet Août Septembre Octobre Novembre Décembre