Overblog Suivre ce blog
Editer l'article Administration Créer mon blog
9 décembre 2010 4 09 /12 /décembre /2010 23:00

Nous allons démonter et remonter le programme reformatComm.js présenté dans "Raccourcir les dates du module 'Commentaires'". Lisez cet article (il est bref !) si vous ne voyez pas de quoi il est question, ensuite revenez ici.

Point de départ : le module des commentaires

Le module des commentaires récents se compose d'une liste ul, avec un élément li par commentaire indiqué (10 commentaires au maximum). Il est disséqué dans cet article, ainsi que d'autres modules.

Voici un exemple de code source d'un tel élément :

 <li> <div> 05/12/2010 22:58:29 </div> <a href="http://brendufat.over-blog.com/article-61840870-comments.html#comment72433613"> HTML de blog [5.1] Modules </a> </li> 

Le li se compose de deux parties :

  1. un div plein d'espaces (et encore : j'en ai supprimés !) contenant la date du commentaire,
  2. un lien a vers le commentaire de l'article.

Le but est de remplacer le div sans classe par un span de classe dateComm et contenant la date raccourcie. Et même de mettre cette nouvelle date dans le lien plutôt qu'avant. Bref, on veut arriver à ça :

 <li> <a href="http://brendufat.over-blog.com/article--61840870-comments.html#comment72433613"> <span class="dateComm">05/12 22:58</span> HTML de blog [5.1] Modules </a> </li> 

Pour une fois que j'expose clairement la question, chuis content :-)

Les choses à faire

Les grandes lignes de l'histoire sont celles de nombreux autres de mes petits programmes qui retouchent la structure d'un blog :

  • d'abord vérifier qu'il y a bien quelque chose à retoucher,
  • ensuite fabriquer le texte de la retouche,
  • enfin appliquer la retouche au bon endroit, généralement celui trouvé lors de la première étape.

Dans le cas présent, la  liste de courses  détaillée coule de source, si ce n'est que nous voulons appliquer plusieurs retouches :

  1. trouver le module des commentaires. Si absent, abandon ;
  2. trouver tous les li de ce module ;
  3. pour chacun d'eux successivement :
    • trouver le div de date et récupérer son contenu (date et heure du commentaire) ;
    • fabriquer la date/heure au nouveau format ;
    • l'emballer dans un span qu'on dote de la classe dateComm ;
    • supprimer le div désormais inutile ;
    • greffer le span au tout début du lien.

Ossature du programme

Comme d'habitude, l'ébauche de programme commence par une paraphrase de cette liste de courses :

 (function(id) { /* trouver le module, s'il existe */ /* trouver les li */ /* traiter chacun d'eux */ })('commentrecent'); 
… et tout le travail va consister à traduire ces commentaires en code JS.

Comme pour les autres programmes, les traitements sont  emballés  dans une fonction anonymequ'on déclare  à la volée  pour l'exécuter juste ensuite. Cette technique ne déclare aucune variable globale et sauve un peu de mémoire – un tout petit peu car la fonction sera brève.

Notez aussi que cette fonction a un argument : id, l'identifiant du module à traiter. La valeur donnée à cet argument est ici 'commentrecent', il suffira de modifier la dernière ligne du programme si l'identifiant du module n'est pas 'commentrecent' .

Premières étapes

Pour chercher le module, un appel à getElementById exploite l'identifiant et renvoie la référence du module :

 var m=document.getElementById(id); 

Pour abandonner s'il est introuvable :

 if (!m) return; 

Pour récupérer tous les li, un appel à getElementsByTagName nous fournira les références de tous ces éléments, rangées dans un tableau :

 var rr=m.getElementsByTagName('li'); 

Pour traiter tous ces éléments, une classique boucle for fera l'affaire :

 var i,r; for (i=0;r=rr[i];i++) { /* traitement de l'élément référencé par r */ } 

Sans surprise, l'ébauche de programme ressemble donc à ceci, en regroupant toutes les déclarations de variables et en préparant la suite :

 (function(id) { var i,r,rr,m=document.getElementById(id); if (!m) return; rr=m.getElementsByTagName('li'); for (i=0;r=rr[i];i++) { /* traitement de l'élément li de référence r : */ // trouver le div et récupérer son contenu (date et heure du commentaire), // fabriquer la date/heure au nouveau format, // l'emballer dans un span qu'on dote de la classe dateComm, // supprimer le div désormais inutile, // greffer le span à la place voulue : au tout début du lien. } })('commentrecent'); 

Trouver et récupérer la date de chaque commentaire

Comme cette date est dans l'unique div que contient l'élément de référence r, obtenons la référence de ce div :

 var d=r.getElementsByTagName('div')[0]; 

Et comme ce div ne contient que la date, sans autre balisage, il nous suffit de récupérer son contenu :

 var t=d.innerHTML; 

Avancement des travaux :

 (function(id) { var i,r,rr,d,t,m=document.getElementById(id); if (!m) return; rr=m.getElementsByTagName('li'); for (i=0;r=rr[i];i++) { d=r.getElementsByTagName('div')[0]; t=d.innerHTML; // fabriquer la date/heure au nouveau format // l'emballer dans un span qu'on dote de la classe dateComm // supprimer le div désormais inutile // greffer le span à sa place, au tout début du lien } })('commentrecent'); 

Reformatter la date

Nettoyage préalable : ce texte est plein d'espaces, tabulations et sauts de ligne, et leur nombre peut changer au cours du temps (on ne va pas demander à OB de garantir le nombre d'espaces dans son HTML !). Il suffit d'éliminer les premiers de ces parasites, et il existe plusieurs techniques pour ça. J'ai choisi d'employer une expression régulière, pour la frime et parce que ça va nous instruire :

 t=t.replace(/\W*/m,''); 

Il s'agit de remplacer (replace)  quelque chose  (/\W*/) par rien (une chaîne vide).

Le  quelque chose  est une expression régulière, reconnaissable aux deux barres de fraction qui l'encadrent. Le 'm' après la dernière barre de fraction signifie que le  quelque chose  peut s'étendre sur plusieurs lignes (soyons prudent).

Éliminons ces accessoires : que signifie le  \W*  restant ? \W, en jargon d'expression régulière, désigne tout caractère autre qu'une lettre, un chiffre ou le tiret bas (_), * signifie que ce caractère peut être répété zéro, une ou plusieurs fois.

Résumons : on remplace par rien (=on supprime) la première apparition d'une chaîne composée de zéro ou plusieurs caractères non alphanumériques. De deux choses l'une :

  • si t commence par le premier chiffre de la date, la sous-chaîne recherchée est vide (ce que nous avons permis), et on remplace ce vide par rien (!)
  • si t commence par des espaces, la sous-chaîne recherchée s'étend jusqu'au premier chiffre de la date, par-dessus les éventuels sauts de ligne, et on la remplace par rien.

Dans les deux cas la date vient se cadrer à gauche dans t, c'est gagné. Condensons les deux étapes précédentes en une seule instruction :

 var t=d.innerHTML.replace(/\W*/m,''); 

Avancement des travaux :

 (function(id) { var i,r,rr,d,t,m=document.getElementById(id); if (!m) return; rr=m.getElementsByTagName('li'); for (i=0;r=rr[i];i++) { d=r.getElementsByTagName('div')[0]; t=d.innerHTML.replace(/\W*/m,''); // fabriquer la date/heure au nouveau format // l'emballer dans un span qu'on dote de la classe dateComm // supprimer le div désormais inutile // greffer le span à sa place, au tout début du lien } })('commentrecent'); 

Fabriquer la nouvelle date

Nous venons de récupérer dans t une date organisée ainsi : jj/mm/aaaa hh:mm:ss. La nouvelle date doit se composer des 5 premiers caractères jj/mm et de 6 caractères à partir du 11e (l'espace) :  hh:mm. En se rappelant que les caractères sont numérotés à partir de zéro et que la méthode substr nous tend les bras :

 var newT=t.substr(0,5)+t.substr(10,6); 

Avancement des travaux :

 (function(id) { var i,r,rr,d,t,m=document.getElementById(id); if (!m) return; rr=m.getElementsByTagName('li'); for (i=0;r=rr[i];i++) { d=r.getElementsByTagName('div')[0]; t=d.innerHTML.replace(/\W*/m,''); var newT=t.substr(0,5)+t.substr(10,6); // l'emballer dans un span qu'on dote de la classe dateComm // supprimer le div désormais inutile // greffer le span à sa place, au tout début du lien } })('commentrecent'); 

Créer le nouveau span

On le crée  en l'air , sans encore le greffer dans la page :

 var s=document.createElement('span'); 

On lui donner une classe :

 s.className='dateComm'; 

On le garnit de la nouvelle date 

 s.innerHTML=newT; 

Simple et direct. En regroupant tout on peut se passer de l'intermédiaire newT, ce qui donne :

 s=document.createElement('span'); s.className='dateComm'; s.innerHTML=t.substr(0,5)+t.substr(10,6); 

Avancement des travaux :

 (function(id) { var i,r,rr,d,t,m=document.getElementById(id); if (!m) return; rr=m.getElementsByTagName('li'); for (i=0;r=rr[i];i++) { d=r.getElementsByTagName('div')[0]; t=d.innerHTML.replace(/\W*/m,''); s=document.createElement('span'); s.className='dateComm'; s.innerHTML=t.substr(0,5)+t.substr(10,6); // supprimer le div désormais inutile // greffer le span à sa place, au tout début du lien } })('commentrecent'); 

Reste maintenant à remanier l'arborescence du document.

Supprimer le vieux div

Cela se fait non pas en disant au div  disparais !  mais en disant au parent du div  enlève-le de ta descendance . Comme le parent du div n'est autre que le li référencé par r, comme nous connaissons déjà la référence du div, à savoir d, nous avons tout ce qu'il faut. Vous suivez ? C'est plus long à expliquer qu'à comprendre, et plus long à comprendre qu'à écrire :

 r.removeChild(d); 

Avancement des travaux :

 (function(id) { var i,r,rr,d,t,m=document.getElementById(id); if (!m) return; rr=m.getElementsByTagName('li'); for (i=0;r=rr[i];i++) { d=r.getElementsByTagName('div')[0]; t=d.innerHTML.replace(/\W*/m,''); s=document.createElement('span'); s.className='dateComm'; s.innerHTML=t.substr(0,5)+t.substr(10,6); r.removeChild(d); // greffer le span à sa place, au tout début du lien } })('commentrecent'); 

Repérer le lien

L'élément de liste ne contient plus qu'un lien mais aussi, peut-être, des espaces, sauts de ligne, etc. Il serait donc imprudent de supposer que le lien est le premier et seul enfant de l'élément de liste, il est plus sage de récupérer sa référence par un moyen bien connu :

 t=r.getElementsByTagName('a')[0]; 

Encore t ? Oui : puisque nous en avons fini avec sa précédente valeur (la date d'origine), j'ai paresseusement choisi de réutiliser cette variable plutôt que d'en créer une nouvelle. Ce n'est pas spécialement élégant ni astucieux, juste de la paresse.

Greffer la nouvelle date dans le lien

Pour le moment, le lien est ainsi bâti :

 <a href="....">Texte du lien</a> 

Ce lien n'a qu'un seul enfant : son texte. Je souhaite que le nouveau span apparaisse avant le texte (qui est le premier enfant du lien, puisque c'est le seul), alors je dis :

 t.insertBefore(s,t.firstChild); 
… qui produit l'équivalent de ceci :
 <a href="..."><span class="dateComm">05/12 22:58</span>Texte du lien</a> 

Si j'avais voulu l'insérer après le texte (qui est aussi le dernier enfant du lien, puisque c'est le seul), j'aurais dit :

 t.appendChild(r); 
… qui aurait produit ceci :
 <a href="...">Texte du lien<span class="dateComm">05/12 22:58</span></a> 

Limpide, non ? Avec ça, le programme est terminé :

 (function(id) { var i,r,rr,d,t,m=document.getElementById(id); if (!m) return; rr=m.getElementsByTagName('li'); for (i=0;r=rr[i];i++) { d=r.getElementsByTagName('div')[0]; t=d.innerHTML.replace(/\W*/m,''); s=document.createElement('span');s.className='dateComm'; s.innerHTML=t.substr(0,5)+t.substr(10,6); r.removeChild(d); t=r.getElementsByTagName('a')[0]; t.insertBefore(s,t.firstChild); } })('commentrecent'); 

À toutes fins utiles : quelques bonnes adresses pour en savoir plus sur JavaScript.

par Aïe mes doigts ! - dans JavaScript Mania
commenter cet article

commentaires

Archives