Un sommaire pour vos articles, sans plugin

Comment créer simplement un sommaire pour vos articles WordPress ?

Même si des plugins le font déjà, je vous propose de voir ici comment réaliser un système de sommaire automatique, avec deux petits bouts de code.

Il s’agit de la même fonction qui me sert à concevoir les indexes de ce site…

sommaire wordpress sans plugin
Créer un sommaire dans WordPress

Pourquoi un sommaire

Le sommaire est un élément ergonomique important dont la présence est nécessaire pour plusieurs raisons :

  • Il permet d’abord au lecteur de savoir rapidement de quoi traite un article, et il informe sur les chemins que va emprunter l’auteur pour délivrer son message.
  • S’il s’agit d’un contenu d’une longueur conséquente, il incite l’internaute à s’y intéresser.
  • Enfin c’est un outil qui permet d’aller directement de chapitre en chapitre pour trouver le passage qui nous intéresse, et de retourner à une partie précise si on a besoin de la relire.

Pour ces trois motifs, je vous conseille de mettre un sommaire dès que l’article comporte plus de deux sections.

Fonctionnement du sommaire

Pour réaliser un sommaire, il nous faut deux types d’éléments :

  1. Des ancres, qui vont nous servir à baliser le contenu
  2. Des liens vers ces ancres, qui vont constituer les entrées de l’index

Il est possible de réaliser ça à la main, bien évidemment, en ajoutant des ancres dans le texte et des liens juste après l’introduction, mais ce serait long et fastidieux.

Nous pouvons également d’utiliser un plugin comme Table of Content, cependant c’est vraiment plus fun de le coder sois-même :-P.

Pour baliser vos articles de façon pertinente, le plus logique est d’utiliser les niveaux de titre <h2>,<h3>,<h4>. Notre système va donc leur ajouter des ancres, puis il va automatiquement créer des liens vers celles-ci.

Into ze code !

Nous allons concevoir le système de sommaire en deux étapes : d’abord nous allons voir le code pour ajouter les ancres ; ensuite nous allons coder l’affichage de l’index.

Ajout automatique des ancres dans votre contenu

Pour ajouter les ancres sur les différentes sections, nous allons ajouter un filtre sur le contenu des posts qui va rechercher le h2,h3 et h4 et placer des id sur ces balises.

Il faut utiliser la fonction preg_replace_callback() de PHP qui permet de faire des modifications sur des éléments que notre regex va isoler dans la chaîne. L’avantage, par rapport à un simple preg_replace() c’est que l’on peut passer une fonction en paramètre et donc effectuer des transformations plus complexes.

Dans le fichier functions.php de votre thème, ajoutez le code suivant :

function replace_ca($matches){
  return '<h'.$matches[1].$matches[2].' id="'.sanitize_title($matches[3]).'">'.$matches[3].'</h'.$matches[4].'>';
}

//Ajout d'un filtre sur le contenu  
add_filter('the_content', 'add_anchor_to_title', 12);
function add_anchor_to_title($content){   
  if(is_singular('post')){ // s'il s'agit d'un article
    global $post;
    $pattern = "/<h([2-4])(.*?)>(.*?)<\/h([2-4])>/i";
    
    $content = preg_replace_callback($pattern, 'replace_ca', $content);
    return $content;
  }else{
    return $content;
  }
}

Notre regex /<h([2-4])(.*?)>(.*?)</h([2-4])>/ recherche tout ce qui correspond à des balise <h2>,<h3> ou <h4>, peut importe leur attribut ou leur contenu.
Le callback replace_ca() rajoute à ces balises un ID dont la valeur est une chaîne normalisé inspirée du texte du titre.

Pour prendre un exemple :

<h2>Mon titre de niveau 2</h2>

… va devenir…

<h2 id= »mon-titre-de-niveau-2″>Mon titre de niveau 2</h2>

Nos articles seront alors balisés automatiquement.

Une fonction pour ressortir le sommaire

Il faut donc maintenant coder la fonction pour afficher le sommaire, qui sera constitué de liens pointant vers les ancres précédemment crées.

Cette fonction va récupérer le contenu de l’article, puis isoler les balises qui nous intéresse afin de reconstituer les liens comme il faut.

On utilise pour ça la fonction preg_match_all() pour pousser tout ce qui correspond à notre regex (/<h([2-4])(.*?)>(.*?)</h([2-4])>/) dans la variable $results sous forme de tableau.

Ensuite, il reste à utiliser $results pour générer nos liens.

Ajoutez le code suivant dans le functions.php de votre thème :

function automenu( $echo = false ){
  global $post;
  $obj = '<nav id="sommaire-article">';
  $original_content = $post->post_content;

  //on récupère les titres
  $patt = "/<h([2-4])(.*?)>(.*?)<\/h([2-4])>/i";
  preg_match_all($patt, $original_content, $results);
        
  //on génère les liens
  foreach ($results[3] as $k=> $r) {
    $obj .= '<a class="title_lvl'.$results[1][$k].' " href="#'.sanitize_title($r).'">'.$r.'</a>';
  }

  $obj .= '</nav>';
  if ( $echo )
    echo $obj;
  else
    return $obj;
}

Cette fonction, utilisée dans la boucle, nous permet d’obtenir le sommaire de l’article. Mais il est encore possible de l’améliorer…

Avec des numéros s’il vous plait!

Comme vous pouvez le voir par exemple dans le sommaire de cet article, il est possible d’ajouter des numéro devant chaque entrée du sommaire, de manière à se repérer encore plus facilement.

Disons que les titres de niveau 2 sont ornés d’un numéro, et les titres de niveau 3 d’une lettre.

L’itération des lettres doit bien sûr être réinitialisée à chaque fois que l’on change de grand titre.

Modifions la function automenu() :

function automenu( $echo = false ){
  global $post;
  $obj = '<nav id="sommaire-article">';
  $original_content = $post->post_content;

  $patt = "/<h([2-4])(.*?)>(.*?)<\/h([2-4])>/i";
  preg_match_all($patt, $original_content, $results);

  $lvl1 = 0;
  $lvl2 = 0;
  $lvl3 = 0;

  foreach ($results[3] as $k=> $r) {
    switch($results[1][$k]){
      case 2:
        $lvl1++;
        $niveau = '<span class="title_lvl">'.$lvl1.'/</span>';
        $lvl2 = 0;
        $lvl3 = 0;
        break;

      case 3:
        $lvl2++;
        $niveau = '<span class="title_lvl">'.base_convert(($lvl2+9),10,36).'.</span>';
        $lvl3 = 0;
        break;

      case 4:
        $lvl3++;
        $niveau = '<span class="title_lvl">'.$lvl3.')</span>';
        break;
    }

    $obj .= '<a href="#'.sanitize_title($r).'" class="title_lvl'.$results[1][$k].'">'.$niveau.$r.'</a>';
  }

  $obj .= '</nav>';
  if ( $echo )
    echo $obj;
  else
    return $obj;
}

Par rapport à la version précédente, on a simplement ajouté 3 variables dans lesquelles vont s’incrémenter les niveaux de titre.
Un switch est utilisé pour itérer/réinitialiser les variables, ainsi que pour déterminer la lettre ou le numéro qui va bien.

La fonction base_convert() sert à faire ressortir une lettre selon sa position dans la table des caractères.

Le code complet

Notre système de sommaire est complet, il est temps de récapituler.
Le fichier functions.php doit contenir :

/**
Génèse des ancres
*/
function replace_ca($matches){
  return '<h'.$matches[1].$matches[2].' id="'.sanitize_title($matches[3]).'">'.$matches[3].'</h'.$matches[4].'>';
}

//Ajout d'un filtre sur le contenu  
add_filter('the_content', 'add_anchor_to_title', 12);
function add_anchor_to_title($content){   
  if(is_singular('post')){ // s'il s'agit d'un article
    global $post;
    $pattern = "/<h([2-4])(.*?)>(.*?)<\/h([2-4])>/i";
    
    $content = preg_replace_callback($pattern, 'replace_ca', $content);
    return $content;
  }else{
    return $content;
  }
}

/**
Function automenu( $echo = false )
*/
function automenu(){
  global $post;
  $obj = '<nav id="sommaire-article">';
  $original_content = $post->post_content;

  $patt = "/<h([2-4])(.*?)>(.*?)<\/h([2-4])>/i";
  preg_match_all($patt, $original_content, $results);

  $lvl1 = 0;
  $lvl2 = 0;
  $lvl3 = 0;

  foreach ($results[3] as $k=> $r) {
    switch($results[1][$k]){
      case 2:
        $lvl1++;
        $niveau = '<span class="title_lvl">'.$lvl1.'/</span>';
        $lvl2 = 0;
        $lvl3 = 0;
        break;

      case 3:
        $lvl2++;
        $niveau = '<span class="title_lvl">'.base_convert(($lvl2+9),10,36).'.</span>';
        $lvl3 = 0;
        break;

      case 4:
        $lvl3++;
        $niveau = '<span class="title_lvl">'.$lvl3.')</span>';
        break;
    }

    $obj .= '<a href="#'.sanitize_title($r).'" class="title_lvl'.$results[1][$k].'">'.$niveau.$r.'</a>';
  }

  $obj .= '</nav>';
  if ( $echo )
    echo $obj;
  else
    return $obj;
}

Parfait !

Mais nous n’en n’avons pas fini pour autant, il faut afficher le sommaire…

Appel du sommaire

Soit on décide d’afficher le sommaire systématiquement sur tous les articles, à un emplacement dédiée et inéchangeable, soit on préfère utiliser un shortcode pour placer le sommaire dans le contenu de l’article, après l’introduction par exemple.

Dans le template

Si on veut afficher l’index toujours au même endroit dans le thème, alors il suffit d’appeler la fonction automenu() à cet emplacement.
La fonction doit être utilisée dans la boucle WordPress, ou alors il faudra revoir le code de manière à préciser l’ID du post pour détailler le contenu voulu.

// template function
automenu( true );

Via un shortcode

Une autre solution est d’utiliser un shortcode. L’API des shortcodes de WordPress est schtroumphement bien faite, puisque schtroumphement simple.

Il suffit d’ajouter cette ligne dans le fichier functions.php :

// shortcode
add_shortcode('sommaire','automenu');

Dès lors, il suffira d’ajouter [sommaire] dans le contenu de notre article pour générer un sommaire à ce lieu précis.

Animation du scroll avec jQuery

Je pense qu’ergonomiquement, il peut être intelligent d’utiliser une transition pour rendre compte d’un déplacement dans le contenu, lorsque l’on clique sur une entrée du sommaire.

En effet :

  1. Ça permet déjà de visualiser si l’on se rend à un chapitre précédent ou suivant.
  2. Cela rend compte de la quantité de texte survolé (et potentiellement nécessaire à la compréhension de l’article).

Ce bout de javascript utilise le plugin jQuery scrollTo utilise un code jQuery de Geoffrey Crofte pour se déplacer verticalement de façon fluide dans la page.

Il suffit de place le code suivant dans votre fichier javascript principal :

jQuery(document).ready(function($){
    $(document).on('click','#sommaire-article a',function(){
        var h = $(this).attr('href');

        $('body,html').animate({  
            scrollTop:$(h).offset().top  
        }, 500); 
        return false;
    });
});

…Bonus

En bonus voici le détail du code pour réaliser des animations d’entrée du menu comme sur ce site.
Ici les éléments du menu surgissent de la gauche, mais il est possible de réaliser de nombreuses animations, ce n’est pas très compliqué…

26 commentaires
Daniel Roch, il y a 5 années
Je confirme : ce code fonctionne très bien et est bien plus léger que les autres codes donnés ici pour ajouter un sommaire à WordPress.
Greg, il y a 5 années
Sympa 🙂

J’ai fait un truc un peu similaire pour une cliente récemment, mais au lieu de construire un sommaire c’était une navigation par onglets à partir des h2.
L’idée reste la même, on cherche les titres et on joue avec ^^

Bien joué, çame servira sûrement pour la V3 de mon site 🙂
A+

Geoffrey, il y a 5 années
Hello,

Excellent code !
J’aurais juste organisé le sommaire en listes imbriquées (question de m’embêter un peu plus), mais sinon nickel. 🙂

Dommage de charger scrollTo pour faire ça, tu pourrais utiliser un petit script moins lourd et expliqué ici, au hasard : Effet smoothscroll

Le bonux ça serait de voir se colorer l’ancre du sommaire correspondant à la section de l’article dans laquelle on se trouve 🙂 (avec jQuery InView par exemple)

Mon commentaire fait très auto-promo, désolé :/

Encore merci pour le partage ! 🙂

Willy Bahuaud, il y a 5 années
@greg Merci ! C’est vrai que l’on peut faire pas mal de trucs en parsant le content 🙂

Tu as tout à fait raison ça fait très auto-promo Je vais tester avec ton petit script qui sera je suis sûr beaucoup plus léger pour faire mon scroll.

Je n’avais pas pensé à mettre en avant la partie en cours de lecture mais c’est vraiment un excellente idée !

Julio Potier (BoiteAWeb), il y a 5 années
Wow, super sympa ça ! Le code fourni est excellent !
Merci pour le partage, merci pour les idées, merci pour le cours.
Je garde en fav, obligé !
Willy Bahuaud, il y a 5 années
je viens de me débarrasser de jQuery.scrollTo, effectivement c’était vraiment des octets de gachés !

Au lieu de faire :

$.scrollTo(ancre,500);

…je fais maintenant…

$('body,html').animate({scrollTop:$(ancre).offset().top}, 500);

Pour l’instant ça à l’air de marcher correctement. Reste à voir si à l’usage ça ne va pas lagguer davantage…

Julien, il y a 5 années
Voilà qui est franchement optimisé. Merci pour le partage. Je l’ai implémenté sur mon petit blog en le modifiant un peu.

Comme le disait geoffrey, c’est vrai que l’on aurait plus vu cela dans une liste ordonnée mais bon c’est déjà bien et cela a l’avantage d’être bien plus court que la plupart des codes existants sur le sujet.

Merci encore.

Arnaud, il y a 5 années
Au moins c’est clair et bien structuré comme code. Je n’ai pas trop pensé à faire un sommaire avant, mais l’idée m’intéresse de plus en plus.

Rien que le fait pour le lecteur de trouver directement l’information qu’il lui sera utile.

Merci pour le code 🙂

Rodrigue, il y a 5 années
Magique, j’adore les effets, d’ailleurs j’adore simplement la structure de ton site, je viens de le découvrir via Fabrice.

Le sommaire sans plugin, beaucoup mieux.

Willy Bahuaud, il y a 5 années
J’avoue que de ne pas passer par une liste ordonnée raccourci considérablement le code (beaucoup de conditions en moins), et je ne suis pas spécialement fan du «tout en liste»

Oui, moi même depuis que je l’ai mis je m’y retrouve beaucoup plus ^^

Merci, je viens aussi (seulement) de voir l’article de Fabrice 🙂

Eric, il y a 5 années
Merci pour le partage, je cherchai justement comment ajouter un sommaire sans plugin. Chapeau bas pour ton site, il est juste monstrueux !! Un grand bravo et je m’en vais voter de ce pas pour toi 😉
Jerome, il y a 5 années
Merci pour ce tuto et le partage. C’est vrai que je suis assez partisan, moi aussi, des listes… mais finalement, ça me va bien très bien comme ça 😉 Allié au smoothscroll de Geoffrey, c’est de la tuerie. Bravo !
Christophe, il y a 5 années
Hello,

merci du partage.
Tout se passe à merveille, sauf que, quoi que je fasse, le sommaire se place systématiquement tout en haut de l’article (comme si il est était en position « absolute »).

Suis-je le seul à avoir ce type comportement ?

Merci

Julio Potier, il y a 4 années
Hello

pour le shortcode la fonction doit faire un « return » et non un « echo », il faut choisir si on l’utilise sont pour un shortcode ou en fonction directement.

Super code, je le teste là pour ma v3 :p

Willy Bahuaud, il y a 4 années
Merci pour la relecture ,
J’ai corrigé le point que tu as relevé 🙂

Je pense que le bug de était lié à cela.

sebastien, il y a 4 années
c’est vrai que ce serait cool que le chapitre affiché à l’ecran soit mis en valeur dans le menu, et change donc à mesure qu’on scrolle.
En tout cas, tu fais du beau boulot 🙂
Sacha, il y a 4 années
Bonjour,
Je débute sur WordPress et suis entrain de créer un site.
Merci pour toutes ces infos, ça à l’air top!
Peut-être une question un peu bête…
En mettant ce code là, je suppose qu’il l’applique à l’ensemble du site, non?
Et j’aimerai juste l’appliquer à une page où j’aimerai créer un deuxième menu pour faire défiler comme ici, une page un peu longue.
Comment faire ?
Merci beaucoup !
Sacha
Jonathan, il y a 3 années
Très bon code en effet, et très clair dans son explication

J’ai juste fait une petite modification sur la ligne suivante :

$obj .= '<a href="#'.sanitize_title($r).'" class="title_lvl'.$results[1][$k].'" rel="nofollow">'.$niveau.$r.'</a>';

qui passe à

$obj .= '<br><a href="#'.sanitize_title($r).'" class="title_lvl'.$results[1][$k].'">'.$niveau.$r.'</a>';

Cela me permet d’avoir le sommaire sous forme de liste en forçant le retour à la ligne pour chacun sans utiliser de jquery.
Le défaut de ma manip est que cela force un retour à la ligne dés le debut… Mais j’avance ^^

Merci à toi !

Willy Bahuaud, il y a 3 années
Pour cela on peut aussi utiliser simplement du CSS, mais effectivement, je vois que tu as saisi comment ça marche 🙂
Thierry, il y a 3 années
Bonjour Willy et merci pour ce partage vraiment intéressant!

J’essaie d’appliquer les codes au nouveau thème que je développe en local. Malheureusement ça beugue. Au lieu de présenter le sommaire, tous les posts reprennent le code des fonctions « sommaire » comme du texte.

En fait, je travaille sur un thème-enfant de twenty-fourteen et j’ajoute les codes proposés au functions.php de l’enfant. Hormis, les fonctions liées au sommaire, celui-ci reprend uniquement/pour l’instant des sidebars supplémentaires qui fonctionnent (fier de moi pour le coups).

Aurais-tu une idée de ce qui coince quand on applique ta solution à un child-theme?

Un tout grand merci d’avance pour ton aide!!!

Tche, il y a 3 années
Salut Willy,
Je ne suis pas certain que mon précédent message soit passé. Il attend certainement ta publication. Mais j’ai trouvé la solution pour faire fonctionner le sommaire à partir du thème enfant. Il s’agissait en fait d’un « > » à retirer avant d’y ajouter ton code.
Par contre je bloque toujours sur l’appel, à partir de mon single.php. J’ai essayé en ajoutant automenu( true ); // automenu(); à différents endroits de single.php et de content.php dans Twenty Fourteen mais sans succès.

lo, il y a 3 années
Salut,
Merci beaucoup pour ce tuto que je viens de tester et qui fonctionne nickel.
N’étant pas codeur je galère néanmoins sur un bête truc:
J’ai placé le sommaire dans une <div> qui affiche un cadre avec le css. Mon problème vient du fait que le cadre vide s’affiche sur certains articles qui ne possèdent pas de H2, H3 etc…
Pourriez m’indiquer quel genre de if() placer dans le code pour que le sommaire ne s’affiche que lorsque l’article possède des titres h2 etc…?
Willy Bahuaud, il y a 3 années
je te conseil de mettre le résultat du preg_match_all() de la function automenu dans une variable, puis de ne retourner (ou n’afficher) $obj seulement si cette variable est différente de 0 ou de false 🙂
lo, il y a 3 années
Mais tu sais que t’es un gars bien toi… 😉
Merci pour le conseil qui m’a mis les idées en place, ça fonctionne…
Au plaisir ici ou ailleurs…
Rub's, il y a 3 années
Code fonctionnel et original, tout comme ce site.

Merci

Yannick Altuna, il y a 3 années
Bonjour,
Je viens de découvrir ton site qui est excellent (j’aime particulièrement le design).
J’ai tenter l’aventure avec ton sommaire, il est correctement appelé avec le shortcode, par contre avec le :
automenu( true );

il n’apparaît pas, quelqu’un est dans le même cas?

Cordialement,
Yannick

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Publié le 17 octobre 2012
par Willy Bahuaud
Catégorie Développement WordPress, Intégration web