Paris, photo, humeurs, un peu de HTML/CSS pour faire sérieux. Feinte innocence et vraie naïveté (ou vice-versa). Encore un blog qui agite ses petits bras en couinant "Viens me lire !"
Troisième (!) mouture, en octobre 2008 grâce à Fred & Ben, de cet article de juillet 2007 déjà refondu en juillet 2008 sous l'influence de Marcovaldo (voir les commentaires – les vôtres sont tout aussi bienvenus !).
Attention, gros morceau !
La question est : comment est bâti mon biniou
de navigation, alias le petit machin infernal en haut à droite ? C'est réalisable au niveau Confiance, mais oui.
Ce qui ne signifie pas que c'est facile – désolé. Le procédé repose sur un JavaScript certes assez simple et surtout qui ne demande que très peu d'adaptation, mais il emploie du CSS un peu délicat et touche de près à la structure du blog. Le chantier est donc gros, vous ne vous en sortirez pas avec un copier/coller/hop magie ça marche tout seul. Par conséquent lisez les explications avant de vous lancer, et ne vous lancez que si la chose vous paraît claire. Si ce n'est pas le cas, n'hésitez pas à poser vos questions en commentaires : cet article est imparfait, il lui manque sûrement un tas de petites choses qui vont sans dire mais tellement mieux en les disant. Votre aide est donc bienvenue.
Les divers menus du biniou sont des modules habituels : derniers articles, derniers commentaires, etc. . Sur un blog normal
ils s'empilent dans une interminable colonne, d'où l'idée de permettre au lecteur de n'afficher que le ou les modules qu'il désire, idéalement en allant promener sa souris dessus.
La solution la plus propre, de pur CSS, serait d'afficher les modules réduits (= cacher leur .box-content) et de ne les développer (= montrer leur .box-content) que si on les survole, quelque chose comme :
.articlerecent .box-content {display:none} .articlerecent:hover .box-content {display:block} Simple et expressif, marche partout… sauf hélas sous IE qui (au moins jusqu'à IE7) ne sait pas traiter :hover sur autre chose que des liens.
Dans ces cas-là on s'en sort en réalisant en JavaScript ce qu'IE ne sait pas faire en CSS. Ici, on grefferait
donc aux modules un petit gestionnaire JavaScript (onmouseover ou onclick) pour les réduire ou les développer quand on les survole ou les clique.
Comment ajouter ces gestionnaires ? On ne peut pas modifier à sa guise le HTML que produit OB – ce qui est bien compréhensible. Si l'on est autorisé à insérer des balises script, cette tâche d'ajouter les petits gestionnaires
peut se déporter dans un script externe.
Mais on n'a pas droit à ces balises au niveau Confiance, niveau auquel se trouvait ce blog lorsque je me suis attaqué à la question.
Est alors venue l'idée de placer la gestion de l'affichage des modules escamotables dans un module supplémentaire de texte libre.
Ce module auxiliaire, le biniou navigatoire
(sic, appellation déposée), a une structure très simple (ce n'est qu'une série de liens) mais une forte teneur en JavaScript : chaque lien du biniou
commande l'affichage d'un module, par une technique similaire à celle utilisée pour faire un lire la suite
.
Les plus anciens lecteurs de ce blog se rappellent peut-être la période où le petit bateau s'accompagnait de quelques hideux gros boutons où l'on cliquait pour afficher ou masquer tel ou tel module ? Dans les entrailles du monstre (= JavaScript) rien ou presque n'a changé depuis cette époque, or j'en savais moins qu'aujourd'hui en JavaScript. Le passage des hideux gros boutons au système actuel repose uniquement sur un travail de CSS. Le prochain chapitre en explique le principe, le suivant en donnera les détails, le dernier chapitre tentera de vous guider dans la mise en place du système.
lire le reste de la présentation…
Puisque le système de menus est un proche cousin des "lire la suite", révisons nos classiques. Faire un "lire la suite", comme d'ailleurs son symétrique "cacher la fin", repose sur les idées suivantes :
Clair ? Quatre idées en tout, et cette architecture peut servir à faire autre chose que cacher ou masquer le bout de page (par exemple changer sa taille de caractères ou sa couleur de fond, plus généralement le faire passer d'une classe à une autre).
Le point important, pour ne pas désorienter le lecteur, est de n'afficher que le bouton de commande pertinent : "lire la suite" si le texte est caché, "cacher la fin" si le texte est affiché. C'est d'ailleurs ce qui se passe dans l'article que vous êtes en train de lire… et c'est ça qu'il est délicat de réaliser pour le système de menus : les liens "cacher le module" ne peuvent pas se trouver dans les modules puisque leur HTML, on l'a dit, est produit automatiquement par OB.
Les boutons "afficher tel module" et "cacher ce même module" seront forcément dans le nouveau module de texte libre (= le biniou), charge à chaque bouton de cacher l'autre. On peut dès lors envisager deux grandes voies :
menu glissant
À première vue, le biniou regroupant les boutons de commande ne peut qu'être séparé des modules dont il commande l'affichage, puisqu'il est un module à part. Or, tel que je l'ai construit, mon biniou semble entremêler les deux, avec des boutons au-dessus et au-dessous du module affiché. J'espère que l'effet est joli et l'utilisation intuitive (c'était le plus important) mais il a fallu un peu de jus de méninges
… et de tromperie optique à base de positionnement absolu. Je vous avais bien dit que c'était du CSS délicat, faites chauffer le café.
Première idée : à coups de CSS, les modules à commander reçoivent tous la même hauteur, quitte à y faire apparaître une barre de défilement si cette hauteur est trop faible pour eux (exemples : les liens et les archives). Autrement dit, pour ces modules, on a une règle de CSS avec un looooong sélecteur :
#module_1 .box-content, #module_2.box-content /* tous les modules concernés */ {height:une_hauteur_donnee; overflow-y:auto} Deuxième idée : le module "biniou navigatoire" ne contient que les liens de commande d'affichage, ne contient que des éléments a. À ce stade, le biniou ressemble à un gros bouzin informe.
Troisième idée : encore à coups de CSS, ces liens occupent la pleine largeur du module (autrement dit display:block pour tout le monde) et reçoivent l'aspect et les dimensions des barres de titre des modules. À ce stade, le biniou ressemble donc à une pile de barres de titre qui sont, en réalité, des boutons de commande.
Quatrième idée : le lien correspondant au module actif (car on postule qu'on le connaît, ce module !) est d'une classe différente de celle des autres liens. La seule différence entre cette classe et celle des boutons "au repos" porte sur sa hauteur, qu'on se débrouille pour rendre égale à celle d'un module complet (titre et contenu). À ce stade, le biniou ressemble donc à un module normal, en pleine hauteur, avec les barres de titres des modules cachés empilées en-dessus et en-dessous. C'est presque bien, mais le module n'est toujours pas là…
Cinquième et grandiose idée, préparée par les précédentes : les modules concernés sont mis en position:absolute, et puis :
Peut-être qu'un petit schéma ?
Tous les modules sont ici montrés, décalés vers la droite dans l'espoir d'y voir clair. En réalité tout le monde est aligné verticalement et invisible à l'exception du module actif (sur la figure, c'est le quatrième).
Notez que
mimela barre de titre du module correspondant ;
Petit avantage technique : comme le module visible cache son bouton de commande, ce bouton n'a plus besoin de se cacher lui-même : un peu de JavaScript en moins.
Petit inconvénient technique : comme la hauteur des boutons change à tout bout de champ, ce procédé conduit à recalculer le biniou, et donc la page, à chaque changement de module – et ceci bien que les dimensions du biniou soient constantes. Cela ralentit sensiblement l'affichage, la solution vous sera donnée plus bas : un peu de CSS en plus.
… masquer la fin de la présentationSi vous avez pigé l'idée, passons maintenant à la technique bestiale.
Pour l'exposé le biniou se réduira à l'essentiel, quatre modules usuels : derniers articles, derniers commentaires, archives et liens. Au chargement de la page le module Derniers articles
sera visible et les autres, cachés. Pas de décoration raffinée : les bordures et leurs petits pixels font faire beaucoup de calculs bourrins pour que rien ne déborde. À vous d'ajuster les valeurs numériques à vos besoins et de compléter la décoration.
Enfin, dans ce qui suit, le mot module
sans autre précision ne désigne que les modules dont l'affichage est géré par le biniou – les autres modules, tels le calendrier dans mon blog, ne sont évidemment pas concernés.
Chaque module DOIT avoir un id, ce qui n'est pas le cas par défaut. Dans l'admin OB, il faut donc aller dans Configurer/ Options globales / Réglages avancés et cocher la case permettre la configuration des id XHTML des modules
. Après quoi, toujours dans l'onglet configurer
, donnez un id de votre choix aux modules (petit marteau). Deux modules ne sauraient avoir le même identifiant, évidemment, et nous les baptiserons ArticleRecent, CommentRecent, Archives et Liens. Vous prenez bien ce qui vous plaît, mais il faudra vous en resservir dans la suite.
Le biniou portera l'identifiant biniou (pourquoi pas ? même procédure que pour les autres). Il sera commode, pour commencer, de placer ce biniou au début de la colonne dans la config OB – justification et compléments plus loin.
Voici une première version du code source du biniou. Attention : le JavaScript ne sera donné qu'un peu plus tard, ne recopiez donc pas ce HTML.
<a href="#" class="btn_on" id="btn_ArticleRecent">Derniers articles</a> <a href="#" class="btn_off" id="btn_CommentRecent">Commentaires</a> <a href="#" class="btn_off" id="btn_Archive">Archives</a> <a href="#" class="btn_off" id="btn_Liens">Ailleurs ...</a>
Ça, c'est le niveau zéro du langage HTML : une suite de liens, même pas une liste (pourquoi ne pas faire une liste ? J'y ai renoncé après avoir observé qu'IE laisse toujours un ou deux pixels entre les éléments d'une liste, même en mettant à zéro les marges et remplissages. Ils sont un peu directifs, chez Microsoft).
Notez tout de même que :
btn_(restons cohérent) à l'identifiant du module associé. Respectez les majuscules et minuscules !
Et comment sait-on que btn_on ou off correspond à un menu visible ou caché ? C'est le JavaScript qui fera respecter la correspondance, nous verrons ça le moment venu.
Maintenant, le CSS de tout ça, en plusieurs sections qu'il faudra évidemment assembler.
Le noyau dur pour les modules :
#ArticleRecent, #CommentRecent, #Archives, #Liens {visibility:hidden; position:absolute; width:210px} #ArticleRecent {visibility:visible;} /* module initialement visible */ On joue ici sur le visibility des modules alors que "lire la suite" joue sur leur display . Pourquoi ? Premièrement : les modules sont en positionnement absolu, donc on sait que leur apparition ou disparition ne changera rien à la page, donc les deux options sont ici équivalentes. Deuxièmement : l'expérience m'a montré que jouer sur visibility donne un meilleur temps de réponse, alors voilà.
La mention de width, si la largeur des modules n'est pas déjà définie ailleurs dans le CSS, est nécessaire pour que les modules en positionnement absolu gardent la même largeur que les autres.
Les deux classes possibles pour un bouton
.btn_off { /*détails divers*/ } /* bouton inactif */ .btn_on { /*détails divers*/ } /* bouton actif */ Les "détails divers" sont ce que vous voulez, sauf la hauteur et le padding (voir plus loin).
Mise en forme minimale du biniou :
#biniou a {display:block;} /* Les boutons en colonne */ #biniou .box-titre {display:none} /* Pas de titre pour le biniou */ Hauteurs et remplissages pour faire coïncider modules et boutons :
/* Hauteur des modules (200px pour l'exemple) */ #ArticleRecent, #CommentRecent, #Archives, #Liens {height:200px;} /* Hauteur des boutons selon leur état */ #biniou a, .box-titre {height:15px;} /* Boutons et titres ont la même hauteur */ .btn_off {padding-bottom:0px;} /* inactif haut comme un titre : 15 + 0 = 15 */ .btn_on {padding-bottom:185px;} /* actif haut comme un module : 185 +15 = 200 */ L'important est ici que la hauteur totale (height et padding) soit la même pour, d'une part un bouton inactif et une barre de titre, d'autre part un bouton actif et un module complet. Ainsi le module affiché pourra se superposer exactement à son bouton de commande.
Complément pour gérer les débordements dans les modules :
#ArticleRecent .box-content, #CommentRecent .box-content, #Archives .box-content, #Liens .box-content {overflow-y:auto;} /* Rétrécir les listes pour laisser place à une éventuelle scrollbar verticale */ #Archives li, #Liens li {width:195px;} Dans la dernière règle, la valeur de 195px n'est bien sûr qu'une indication, elle dépend de la largeur des modules : prévoyez 15 ou 20 pixels de diminution pour être tranquille. Notez aussi qu'il n'est pas obligatoire de faire porter cette règle sur tous les modules, seulement sur ceux qui risquent de déborder. Ici je suppose le gabarit de 185 pixels assez haut pour que les 10 liens maximum des derniers articles
et derniers commentaires
veuillent bien y rentrer.
Il faut se prémunir contre le recalcul de toute la page à chaque changement dans le biniou. La solution est de placer le biniou lui aussi en absolu : il sort ainsi du flux et le navigateur saura qu'il ne devra recalculer que le biniou plutôt que toute la page. Mais placer le biniou en absolu crée un grand vide dans la colonne de modules, vide compensé par un gros padding-top pour cette colonne.
Concrètement :
#biniou {position:absolute} #biniou {height:245px} /* Hauteur biniou = un module + (4-1) titres */ #cl_1_1 {padding-top:262px} /* padding supérieur à la hauteur du biniou */ Avoir placé le biniou en début de colonne dans la config OB explique que le gros padding soit ajouté à cette colonne plutôt qu'au module qui suit ou précède le biniou. Cette place en début de colonne est importante aussi parce qu'elle garantit que les modules escamotables suivront le biniou – et viendront donc le recouvrir bien qu'il soit devenu absolu comme eux. Sans quoi, on s'en sortirait tout de même à coup de z-index mais c'est déjà bien assez tordu comme ça.
Reste enfin à étager les modules, en les espaçant d'une hauteur de bouton/barre de titre, soit 15 pixels dans notre exemple :
#biniou, #ArticleRecent {top:170px;} #CommentRecent {top:185px;} #Archives {top:200px;} #Liens {top:215px;} L'ordre vertical des modules suit l'ordre vertical des boutons, le sommet du premier module coïncide avec le sommet du biniou (et donc du premier bouton) : chaque module recouvrira son bouton, le tour est joué !
Pas trop assommé ? Allez, on avance.
Jusqu'ici le système est complètement inerte : le premier module s'affichera, il s'affichera au bon endroit, mais rien ne pourra changer. En ajoutant à chaque bouton du biniou un gestionnaire onmouseover (ou onclick si on préfère un système à clic), on va mettre un peu d'animation.
Ce gestionnaire, revenons-y, doit accomplir les tâches suivantes :
Orientation;
noter dans un coinque c'est désormais lui le module affiché ;
Le gestionnaire n'a pas besoin de masquer le bouton qui le porte, puisque c'est son menu associé qui s'en charge. Il n'a pas non plus besoin de se demander si le menu associé est déjà affiché, puisqu'alors le bouton est masqué et donc impossible à survoler ou cliquer.
Je vous balance ici le texte complet du premier bouton de commande, copiable/collable en l'état, nous disséquerons le JavaScript ensuite.
<a href="#" id="btn_ArticleRecent" class="btn_on" onmouseover=" var cemodule=this.id.substring(4); if (typeof(moduleactif)=='undefined') {moduleactif='ArticleRecent';}; document.getElementById('btn_'+moduleactif).className='btn_off'; document.getElementById(moduleactif).style.visibility='hidden'; this.className='btn_on'; document.getElementById(cemodule).style.visibility='visible'; moduleactif=cemodule; return false; ">Orientation</a> Comme dit plus haut, le onmouseover doit être ajouté, tel quel, à tous les autres boutons du biniou. Exemple sur le bouton suivant :
<a href="#" id="btn_CommentRecent" class="btn_off" onmouseover=" var cemodule=this.id.substring(4); if (typeof(moduleactif)=='undefined') {moduleactif='ArticleRecent';}; document.getElementById('btn_'+moduleactif).className='btn_off'; document.getElementById(moduleactif).style.visibility='hidden'; this.className='btn_on'; document.getElementById(cemodule).style.visibility='visible'; moduleactif=cemodule; return false; ">Derniers articles</a> Remarquez que, dans la troisième ligne, on n'a pas remplacé ArticleRecent
par CommentRecent
.
Dissection : vous n'êtes pas du tout obligé de lire la fin de cette section mais il peut être intéressant de voir, instruction après instruction, comment se réalisent les points précédents :
ArticleRecentet l'on donne donc cette valeur à moduleactif ;
Voilà, c'est tout.
Quelques contresens possibles :
Orientation) :
biniouen sommet de colonne n'est pas obligatoire mais seulement commode. S'il est précédé par des modules
normaux, il faudra remplacer le gros padding sur la colonne de modules par une grosse marge basse sur le module qui précède le biniou ou haute sur celui qui le suit. S'il est précédé par certains des modules escamotables, il faudra donner de bons z-index aux uns et aux autres pour s'assurer que le biniou s'affiche en-dessous des modules escamotables plutôt qu'au-dessus.
Ce point est important pour un système à survol. On en a déjà parlé au fil de l'article, résumons ici.
L'affichage et le masquage des modules sont instantanés, mais le changement de padding des boutons prend du temps parce que le navigateur doit réarranger toute la page – et c'est très lent.
Mettre le biniou en positionnement absolu (et donc le sortir du flux) arrange un peu les choses puisque le changement d'état d'un bouton ne fait plus recalculer que le biniou : on passe de très lent
à encore un peu trop lent
.
En imposant une hauteur au biniou (ce qui n'était pas indispensable en principe) j'ai constaté qu'on arrivait à acceptable
(mais pas encore instantané
). Si vous avez une meilleure idée, je prends !
C'est pas simple, hein ?
Quelques indications pratiques…
Bien que ce gadget se contente de combiner des techniques assez connues (positionnement absolu, JavaScript simple), je ne crois pas qu'il puisse se mettre en place à l'aveuglette. Il m'a semblé important d'exposer les idées sous-jacentes (et puis j'aime bavarder…) pour comprendre ce à quoi riment les modifications nécessaires, qui sont volumineuses. J'aimerais vraiment savoir ce qui vous a semblé clair (s'il y en a) et moins clair dans ce papier.