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 !"
Le programme reclasseBody.js dont le mode d'emploi se trouve dans cet article lit l'URL de la page, la découpe en fines lamelles et en déduit des classes qu'il ajoute au body de la page. Le mode d'emploi du programme se trouve dans l'autre article, merci de vous y reporter si c'est ça qui vous intéresse. Mais si vous voulez en apprendre un peu plus sur la tambouille interne ou prendre des idées pour vos propres recettes, bavardons encore un peu.
Encore ? Encore. Le mode d'emploi du programme ne donne que quelques principes très généraux, il y a aussi des cas particuliers, et il faut revoir tout ça avant de plonger dans la programmation.
Notez bien que ma science
des URL OverBlog ne vient que de la pratique et de la réflexion, que je ne suis pas dans le secret des dieux, que ce que je vais vous expliquer peut se périmer ou se compléter. Ça s'est d'ailleurs déjà produit, dans ce cas je m'adapte après un certain temps
;-)
L'URL se présente comme http://URLduBlog/nn-nature-identifiant.html. Détaillons :
nomdu fichier HTML affiché, donne une identité unique à la page :
motdu nom de fichier.
http://URLduBlog/sans aucun nom de fichier. Ce nom de fichier absent, par défaut, est index.html ou, si l'on veut faire apparaître un numéro d'ordre : 0-index.html. Faites l'essai sur votre blog, vous verrez que ça marche !
http://URLduBlog/article-titreDarticle-numeroDarticle.htmlLe blogueur peut modifier, voire supprimer complètement cette série de mots.
http://URLduBlog/pages/numeroDePage-titreDePage.html
http://URLduBlog/nn-archive-mm-jj-aaaa.html http://URLduBlog/nn-archive-mm-aaaa.htmlLe jour et le mois s'écrivent tantôt avec zéro initial, tantôt sans : il ne faut donc pas espérer s'y retrouver en comptant les caractères. Le numéro d'ordre va de 40 en 40, cet intervalle n'est pas réglable.
http://URLduBlog/nn-liste-articles.htmlPas de numéro d'identification, il serait bien inutile. Le numéro d'ordre va de 40 en 40, cet intervalle n'est pas réglable.
http://URLduBlog/top-articles.htmlPas de numéro d'identification, pas de numéro d'ordre.
http://URLduBlog/reglement-blog.phpNotez que l'extension est .php au lieu de .html, et que ça nous est complètement égal pour la suite.
De tout cet indispensable pinaillage émergent heureusement quelques régularités :
C'est toute cette littérature que nous allons maintenant traduire en instructions JavaScript.
C'est assez simple (je sais, je dis toujours que c'est assez simple
…) :
Le programme accomplit ces tâches en séquence, sans aiguillages très compliqués, nous le passerons donc en revue dans l'ordre où il est écrit. C'est un joli petit échantillon de trituration de chaînes de caractères et de tableaux, vous allez peut-être vous instruire…
Toutes les informations, absolument toutes, présentes dans la fenêtre du navigateur sont regroupées, aux yeux de l'interpréteur JavaScript, dans une seule structure de données (un objet
) nommée window. Cet objet comporte plusieurs subdivisions (ou attributs, ou facettes, ou sous-objets…).
La plus volumineuse et la plus utilisée est bien sûr le document proprement dit. On le nomme window.document ou, pour faire court, document. C'est une présentation à l'usage de JavaScript du source HTML soumis au navigateur, tout comme le contenu affiché à l'écran en est une présentation à l'usage de vos yeux. Dans les articles précédents nous n'avions exploité que window.document.
Mais l'objet window contient aussi des renseignements sur le navigateur utilisé, les dimensions de l'écran… toutes choses qui ne dépendent pas du document affiché et n'ont donc pas leur place dans le sous-objet document. Pour en savoir plus, vous pouvez vous reporter à la description de l'objet JavaScript qui figure dans ce très bon tutoriel.
Ce qui nous intéresse ici est window.location, copie exacte du contenu de la barre d'adresse de votre navigateur. Un petit exemple rapide :
Cet objet comporte plusieurs sous-objets pour décomposer l'URL. Le nom du fichier et son chemin d'accès, qui seuls nous intéressent ici, se trouvent dans window.location.pathname. Un autre petit exemple rapide :
Le résultat de ces essais (présence ou non d'un slash initial) peut dépendre du navigateur. On va donc programmer blindé
, sans hypothèse trop confiante, et commencer le programme par
function reclasseBody(){ var nomFichier = window.location.pathname; // nomFichier est une chaîne de caractères }
On veut découper la chaîne à hauteur des slash s'il y en a, justement il existe une méthode pour ça, nommée split. Elle renvoie un tableau contenant les différents tronçons (certains pouvant être vides) :
nomFichier = nomFichier.split('\/'); // nomFichier est maintenant un tableau
Le tout dernier élément est le nom du fichier (avec l'extension .html ou .php) et l'avant-dernier, si l'on a affaire à une OB-page, est le répertoire des OB-pages. Un test nous le dira, son résultat est conservé dans la variable booléenne isPage déclarée pour l'occasion :
var isPage=(nomFichier.length >=2) && (nomFichier[nomFichier.length - 2]=='pages') ;
Notez la précaution de vérifier d'abord qu'il y a au moins deux éléments avant de tester l'avant-dernier, et cochons la 2e ligne de la liste.
Récupérons aussi le tout dernier élément (=filename+fileext) :
nomFichier = nomFichier[nomFichier.length - 1] ; // nomFichier est à nouveau une chaîne de caractères
Il y a dans cette chaîne un point pour séparer le filename du fileext. Encore un coup de tronçonneuse split sur le point (seul le premier tronçon nous intéressera : le filename) :
nomFichier = nomFichier.split('.'); // résultat : un tableau nomFichier = nomFichier[0]; // résultat : une chaîne
En condensant quelques instructions le début du programme devient ceci :
function reclasseBody() { var nomFichier = window.location.pathname.split('\/') ,isPage=(nomFichier.length >=2) && (nomFichier[nomFichier.length - 2]=='pages') ; nomFichier = nomFichier[nomFichier.length - 1] ; nomFichier = nomFichier.split('.')[0]; }
On peut cocher la 3e ligne de la liste.
Première chose : il peut ne pas y avoir de filename du tout si l'on est arrivé sur la page d'accueil du blog. On sait que dans ce cas-là le filename est en réalité index (et l'extension .html). Hé bien on le dit :
if (!nomFichier) nomFichier='index';
Ensuite, que le nom de fichier soit déduit de l'URL ou reconstitué comme on vient de le faire, il faut tout découper à hauteur des tirets :
nomFichier = nomFichier.split('-');
Cochons la 4e ligne de la liste, et nous voilà devant un tableau de mots, à interpréter aussi intelligemment que possible.
Ce cas est facile : pas de numéro d'ordre, le nom de fichier se compose d'un numéro d'identité suivi d'un texte descriptif qu'on récupère aussi, à tout hasard :
var pgType, pgNumero, pgIdent, pgSubIdent ; if (isPage) { pgType='page'; pgNumero=0; pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-'); } else {/* traitement d'une page normale */ }
Vous voyez apparaître quatre variables où nous rangeons les informations tirées de nomFichier :
Tout le jeu ultérieur sera de calculer correctement ces quatre variables dans les divers cas de figure. Vous remarquez aussi deux nouvelles méthodes :
La section suivante est consacrée au traitement d'une page normale, autrement dit elle va remplir le else. Pour alléger un peu l'exposé je n'y répéterai pas ce else et ses accolades, nous les retrouverons au moment d'exploiter les informations collectées.
Si nous revenons à notre liste de principes, nous trouvons une règle universelle : si le 1er mot est un nombre c'est le numéro d'ordre de la page – et, implicitement, il ne change rien à l'interprétation du reste de l'URL. Bon, allons-y :
var mot; mot = nomFichier[0];
Comment savoir que mot, qui est une chaîne de caractères, peut se comprendre comme un nombre ou pas ? La technique la plus rapide, exploitant la grande tolérance de JavaScript en matière de types de données, est de le multiplier par 1 et de tester le résultat.
Gné ? Voui : 02 (ou 2 ou 0002) est un nombre mais '02' (qui n'est ni '2' ni '0002') est une chaîne de caractères. Si vous écrivez '02'+'02', vous obtiendrez '0202' et pas 4, tout comme 'deux'+'deux' fait 'deuxdeux' et pas 'quatre'. Mais si vous multipliez une chaîne par le nombre 1, JavaScript va faire de son mieux pour que l'expression ait un sens :
1* 'brendufat' c'est vraiment pas possible mais
1* '02', ça fait 2 (le nombre, pas la chaîne '2').
Deuxième exploitation du bon caractère de JavaScript, le test. Là aussi, faisant de son mieux pour comprendre ce qu'on lui demande, il considère que 2 et '2' sont égaux (alors que 2 et 'deux', voire 2 et '1+1' ne le sont pas, faut pas pousser). Au bout du compte, pour savoir si mot est un nombre, nous écrirons
if ( mot== 1*mot) {/* mot est un nombre */} else {/* mot n'est pas un nombre */};
Que mettre entre les accolades ? Si mot est un nombre : 1) c'est le numéro d'ordre et 2) nous l'éjectons du tableau. Si mot n'est pas un nombre 1) le numéro d'ordre est zéro et 2) nous ne touchons pas au tableau. Ça s'écrit brièvement :
if ( mot== 1*mot) nomFichier.shift(); else mot=0; pgNumero=mot;
Au suivant ! Dans le tableau ainsi débarrassé de l'éventuel numéro d'ordre, on sait que le premier mot indique la nature de la page. Donc on le récupère et le supprime du tableau, comme ceci :
pgType=nomFichier.shift();
La suite des opérations dépend de ce qu'on vient de trouver :
Ce genre d'aiguillage selon la valeur de quelque chose (ici pgType) se traite par une instruction switch :
switch (pgType) { case 'article' : /* traitement du cas où pgType == 'article' */ case 'archive' : /* traitement du cas où pgType == 'archive' */ default : /* tous les autres cas*/ }
Le cas article est le plus complexe :
case 'article' : mot= nomFichier[nomFichier.length-1]; if (mot=='6'|| mot=='comments') { pgType='comments'; nomFichier.pop(); } pgIdent= nomFichier.pop(); pgSubIdent = nomFichier.join('-'); break;
En paraphrasant :
Le cas archive est bien plus simple :
case 'archive' : pgIdent=nomFichier.join('-'); break;
Paraphrase : on recolle les morceaux de la date et roulez jeunesse…
Et maintenant, pour tous les autres cas, la voiture-balai :
default : pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-');
Paraphrase : on récupère/élimine le premier mot en guise de numéro d'identité. Le reste éventuel, après recollage, est un complément d'information.
Notez aussi qu'il n'y a pas, ici, d'instruction break : ce cas est le dernier du switch, après l'avoir exécuté on sort de toute manière du switch.
Voilà, nous avons fini d'être intelligents, nous avons garni les accolades du else :
else { mot = nomFichier[0]; if ( mot== 1*mot) nomFichier.shift(); else mot=0; pgNumero=mot; pgType=nomFichier.shift(); switch (pgType) { case 'article' : mot= nomFichier[nomFichier.length-1]; if (mot=='6'|| mot=='comments') {pgType='comments';nomFichier.pop()} pgIdent= nomFichier.pop(); pgSubIdent = nomFichier.join('-'); break; case 'archive' : pgIdent=nomFichier.join('-'); break; default : pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-'); } };
Bilan : le cas OB-page et le cas usuel sont couverts. Cochons la 4e ligne de la liste et redevenons bourrins pour former le nom des classes que nous allons donner au body.
Procédé bestial : repérer tous les éléments body du document (tous… façon de parler) et garder le premier :
var refBody=document.getElementsByTagName('body')[0];
Autrement dit, construire une jolie chaîne de caractères avec tous les éléments calculés précédemment, et la flanquer dans le className du body :
refBody.className = 'une_très_jolie_chaîne' ;
Euh… sait-on jamais, un autre script astucieux a peut-être déjà donné une classe au body, il serait fâcheux de perdre cette information. Donc, plutôt que de mettre la jolie chaîne dans className, on va la rajouter à la fin :
refBody.className += ' une_très_jolie_chaîne' ; // avé un espace de séparation au début de la chaîne
Si jamais vous installez après mon gadget un autre script génial qui redéfinit la classe du body sans prendre la même précaution, vous pourrez engueuler son auteur de ma part (et corriger son script au passage).
Et l'espace au début de la chaîne ? Il est là pour séparer les nouvelles classes de l'existant éventuel. Bien. Si le body n'a pas déjà de classe(s), la liste de classes après ajout commencera donc par un espace. Ce n'est pas défendu mais certains navigateurs ne le digèrent pas. Donc selon donc que refBody.className est déjà défini ou pas, on ajoutera ou pas cet espace initial :
refBody.className+=(refBody.className ? ' ' : '') /*+ les nouvelles classes */;
Ah, encore une construction exotique plutôt que des if ! Parlons du test rapide ? : si la condition est vraie (= il y a déjà une classe) le résultat suit immédiatement (= un espace), si elle est fausse(= il n'y a pas de classe) le résultat vient après le : (= chaîne vide : deux apostrophes consécutives, pas un guillemet !)
Bon, revenons à la jolie chaîne… L'intention est de donner au body des noms de classe qui contiennent l'information précédente. Notre "page 33 de la catégorie 84203", par exemple, recevra les classes pg_categorie (= c'est une page de catégorie), pgid_84203 (= de la catégorie 84203) et pgnum_33 (= la page numéro 33 de cette catégorie). Simple. Si une information manque (pas le numéro d'ordre, toujours défini quitte à valoir zéro) elle ne produira pas de classe.
En une seule instruction :
refBody.className+=(refBody.className ? ' ' : '') + 'pg_'+pgType+' pgnum_'+pgNumero +(pgIdent ? ' pgid_'+pgIdent :'') +(pgSubIdent ? ' pgsubid_'+pgSubIdent :'');
Le test rapide est bien commode…
En assemblant tous les fragments qui précèdent, on arrive à ça :
function reclasseBody() { var refBody=document.getElementsByTagName('body')[0] ,pgNumero,pgType,pgIdent,pgSubIdent ,mot,nomFichier = window.location.pathname.split('\/') ,isPage=(nomFichier.length >=2) && (nomFichier[nomFichier.length - 2]=='pages') ; nomFichier = nomFichier[nomFichier.length - 1] ; nomFichier = nomFichier.split('.')[0]; if (!nomFichier) nomFichier='index'; nomFichier = nomFichier.split('-'); if (isPage) {pgType='page'; pgNumero=0; pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-'); } else { mot = nomFichier[0]; if ( mot== 1*mot) nomFichier.shift(); else mot=0; pgNumero=mot; pgType=nomFichier.shift(); switch (pgType) { case 'article' : mot= nomFichier[nomFichier.length-1]; if (mot=='6'|| mot=='comments') {pgType='comments';nomFichier.pop()} pgIdent= nomFichier.pop(); pgSubIdent = nomFichier.join('-'); break; case 'archive' : pgIdent=nomFichier.join('-'); break; default : pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-'); } }; refBody.className+=(refBody.className ? ' ' : '') +'pg_'+pgType+' pgnum_'+pgNumero +(pgIdent ? ' pgid_'+pgIdent :'') +(pgSubIdent ? ' pgsubid_'+pgSubIdent :''); } reclasseBody();
Voilà, nous avons reconstruit le programme. Avec cette petite vingtaine de lignes de JavaScript, c'est comme si vous aviez un onglet de CSS pour chaque type de page de blog et même pour chaque page de blog, y compris la liste des articles et les CGU. Mignon, non ? Si vous combinez ça avec les possibilités qu'offrent, justement, les onglets, et celles que vous donne cet autre gadget analogue, mais pour les articles, il y a de quoi s'occuper.
À toutes fins utiles : quelques bonnes adresses pour en savoir plus sur JavaScript.