fastclemmy.com

Vous êtes ici !

# 06-01-2005

Qu'il est agréable de trouver sur un plan le fameux macaron rouge "vous êtes ici". Sur un site web, c'est un peu pareil, il est de bon ton de dire au visiteur où il se situe dans le site. Généralement cela consiste à différencier dans la barre de navigation l'item correspondant à la page active.

Une pratique courante consiste à effectuer cette opération côté serveur. Comme dans l'exemple donné dans A List Apart, on renseigne sur chaque page une variable appelée par exemple $pageCourante ($pageCourante = "accueil") puis on vérifie à l'affichage de la navigation pour chaque item si il s'agit de la $pageCourante. Si c'est le cas, on affecte un id spécifique pour différencier l'item des autres. Grossièrement, le code PHP ressemble à ça :


<ul id="nav">
	<li<?php if ($pageCourante=="accueil") 
		echo " id=\"pageActive\""; ?>>
	<a href="index.php">Accueil</a></li>
	<li<?php if ($pageCourante=="produits") 
		echo " id=\"pageActive\""; ?>>
	<a href="produits.php">Produits</a></li>
	<li<?php if ($pageCourante=="services") 
		echo " id=\"pageActive\""; ?>>
	<a href="services.php">Services</a></li>
	<li<?php if ($pageCourante=="contact") 
		echo " id=\"pageActive\""; ?>>
	<a href="contact.php">Contact</a></li>
</ul>

Côté serveur ou côté client ?

Ca fonctionne bien, d'ailleurs j'ai moi-même adopté cette technique sur de nombreux sites. Néanmoins, je me demande maintenant si le fait que ce soit le serveur qui s'en occupe offre vraiment un intérêt et ce, pour au moins deux raisons. D'une part parce que cela oblige à rajouter pour chaque fichier du site le nom de la page en cours ($pageCourante = "accueil"), ce qui implique au pire de la maintenance fastidieuse ou au mieux un développement supplémentaire pour le gérer automatiquement. D'autre part, parce que je ne vois pas de valeur ajoutée à l'utilisation d'un langage serveur pour ça. Pas de problème de sécurité, simple information visuelle, bref, tout me fait plutôt pencher pour une solution côté client avec javascript.

La structure du code

Avant de nous plonger dans le code, dégageons la structure du script que nous allons écrire.

  1. Récupérer le nom la page en cours
  2. Faire une boucle pour récupérer le lien href contenu dans les balises <a>
  3. Comparer le lien avec le nom de la page en cours
  4. Modifier l'apparence du lien pointant vers la page en cours

Côté résultat final, ça se passe par là. Côté code, ça donne :


function highlightNav() {
	var chemin = "";
	var cheminComplet = "";
	
	// on essaye de détecter la page en cours pour modifier le style du lien actif
	if(document.getElementById("nav")) {
		
		// on récupère la page en cours
		
		// si l'URL comporte une query string, on la retire
		if(document.location.search) {
			cheminComplet = document.location.href;
			cheminCompletSansQueryString = cheminComplet.split(document.location.search);
			cheminComplet = cheminCompletSansQueryString[0];
		} else {
			cheminComplet = document.location.href;
		}

		// si il n'y a pas de fichier après le dernier slash, on doit être sur la page index.php
		cheminCompletDecoupe = cheminComplet.split("/");
		if (cheminCompletDecoupe[cheminCompletDecoupe.length-1] == "") {
			chemin = cheminComplet;
			cheminComplet += "index.php";
		} else {
			chemin = cheminCompletDecoupe.splice(0,cheminCompletDecoupe.length-1) 
			chemin = chemin.join("/");
			chemin += "/";
		}
		
		// on boucle sur les balises <a> pour récupérer leur href
		listeDesLiens = document.getElementById("nav").getElementsByTagName("a");
		for (var i=0; i<listeDesLiens.length; i++) {
			// on compare le href avec le chemin de la page en cours
			if(listeDesLiens[i].getAttribute("href") == cheminComplet || (chemin + listeDesLiens[i].getAttribute("href")) == cheminComplet) {
				// on modifie le style du lien actif
				listeDesLiens[i].style.color = "#cc0000";

				// on sort de la boucle
				break;
			}
		}
	}
}

Les beaux chemins ne mènent pas loin (proverbe chinois)

Parmi les problèmes à gérer, il faut noter que Firefox diffère d'Opera ou d'Internet Explorer sur un point important de notre script. Au moment de demander le contenu de l'attribut href des balises <a>, Firefox nous retourne le contenu exact, à savoir "produits.php" par exemple, tandis que les 2 autres nous retournent "http://www.monserveur.com/monRepertoire/produits.php". Au moment de comparer le lien avec le nom de la page en cours, il faut donc faire les deux tests, ce qui implique d'avoir retenu au préalable deux variables : le chemin (http://www.monserveur.com/monRepertoire/) et le chemin complet (http://www.monserveur.com/monRepertoire/produits.php).

Un beau livre, c'est celui qui sème à foison les points d'interrogation (Cocteau)

Par ailleurs, il faut s'assurer lorsque nous manipulons l'URL de la page que celle-ci ne comprenne page un paramètre qui nous compliquerait la vie (mapage.php?monparametre=mavaleur). Ce qui nous intéresse c'est le chemin du fichier, pas le paramètre. On teste donc si l'URL comprend une query string et on la supprime.


if(document.location.search) {
	cheminComplet = document.location.href;
	cheminCompletSansQueryString = cheminComplet.split(document.location.search);
	cheminComplet = cheminCompletSansQueryString[0];
}

L'arbre suit sa racine (proverbe berbère)

Autre souci, si on se trouve à la racine d'un répertoire (http://www.monserveur.com/monRepertoire/), c'est la page par défaut qui sera appelée, index.htm, index.html, index.php, index.asp, etc. Pour autant, document.location.href ne nous retournera toujours que http://www.monserveur.com/monRepertoire/, sans plus de précision. Empiriquement, on déterminera donc dans notre cas précis qu'utilisant PHP, c'est la page index.php qui sera appelée si on se trouve dans ce cas de figure.


cheminCompletDecoupe = cheminComplet.split("/");
if (cheminCompletDecoupe[cheminCompletDecoupe.length-1] == "") {
	chemin = cheminComplet;
	cheminComplet += "index.php";
} else {
	chemin = cheminCompletDecoupe.splice(0,cheminCompletDecoupe.length-1) 
	chemin = chemin.join("/");
	chemin += "/";
}

Comme d'habitude ce code est fait rapidement et sans doute sujet à discussion. Les commentaires sont donc les bienvenus !

#javascript

Pour faire suite à un article d'Openweb sur des pop-ups intelligentes et accessibles, j'ai voulu pousser un peu plus loin la logique en déclinant une version "non-intrusive".

"Non-intrusif", derrière ce terme barbare se cache une nouvelle philosophie sous-tendant l'utilisation de javascript. Que celui-ci soit activé ou non chez votre visiteur, votre site doit pouvoir fonctionner : javascript pour l'inutile et non pour l'indispensable. L'exemple d'Openweb le démontre bien : l'utilisation d'une pop-up n'est pas indispensable, elle est possible pour les gens utilisant javascript, mais le lien est simplement cliquable pour tous.

Seulement l'article d'Openweb fait l'impasse sur une autre nouvelle approche que tout bon concepteur web doit intégrer dans ses bonnes pratiques : la séparation contenu/interactivité. Vous êtes sans doute déjà familier avec le concept de la séparation structure/présentation : la structure et le contenu d'un document se trouvent dans le fichier HTML tandis que toutes les informations de présentation sont spécifiées dans la feuille de style. Séparer l'interactivité (gérée par javascript) du contenu revient de la même manière à :

  • mettre toutes les fonctions dans un fichier javascript séparé (*.js)
  • retirer tous les appels à ces fonctions dans le fichier HTML (attributs onmouseover, onclick, etc.)

Reprenons donc l'exemple d'Openweb :

<a href="/img/logo.png" onclick="window.open(this.href, 'exemple', 'height=200, width=400, top=100, left=100, toolbar=no, menubar=yes, location=no, resizable=yes, scrollbars=no, status=no'); return false;">Mon lien</a>

En donnant un id à notre lien, on se donne non seulement la possibilité de le styler avec CSS, mais de le manipuler facilement avec javascript. Notre HTML se résume donc maintenant à :

<a id="monLien" href="/img/logo.png">Mon lien</a>

Pour appeler notre fonction de pop-up, il ne faut pas oublier de rajouter dans la balise <head> de notre document HTML :

<script type="text/javascript" src="outils.js"></script>

Attaquons maintenant la fonction javascript proprement dite que nous enregistrerons dans un fichier "outils.js" :


function zoom() {
	// on récupère l'adresse vers laquelle on pointe dans le lien
	var lienPrecedent = document.getElementById("monLien").href;
	
	// on fixe une taille pour la pop-up
	var largeur = 100;
	var hauteur = 200;
	
	// on fixe les coordonnées pour centrer la pop-up
	var gauche = ( screen.width - largeur ) / 2;
	var haut = ( screen.height - hauteur ) / 2;

	// on ouvre la pop-up avec les bons paramètres
	var urlImage = lienPrecedent;
	var nomFenetre = "zoomModele";
	var options = "left="+gauche+"px,top="+haut+"px,width="+largeur+",height="+hauteur+",toolbar=0,resizable=0";
	window.open(urlImage, nomFenetre, options);
	
	// on empêche le lien de s'ouvrir en double en plus de la pop-up
	return false;
}

function initOnclick() {
	// si la page contient le lien avec l'id "monLien"
	if(document.getElementById("monLien")) {
	
		// on lance la fonction zoom au clic sur l'élément "monLien"
		document.getElementById("monLien").onclick = function () { return zoom();};
	}
}

// on initialise le comportement onclick au chargement de la page
window.onload =	function () {initOnclick()};

Et voilà le résultat ! Bien entendu, l'exemple est ici limité, libre à vous de modifier la fonction pour la rendre plus générique ou plus complète...

#javascript