CSS, auto numérotation des éléments : titres, listes, pagination

Logo

Introduction

L’auto numérotation des titres avec Word, OpenOffice, LibreOffice… est facile et bien connu des utilisateurs depuis des décennies.

Comment y parvenir lors de la publication de pages HTML?

CSS est puissant, CSS peut faire le boulot. Toujours investiguer les fonctionnalités CSS avant de démarrer un développement (Javascript…), souvent CSS est capable de couvrir un besoin et très facilement.

Pour auto numéroter les éléments :

  • 3 propriétés CSS dont la plupart sont appliquées sur la pseudo classe ::before : content, counter-increment, counter-reset.
  • 2 fonctions CSS : counter(), counters().

Pas plus.

<tag>::before {
  content: counter(…);
  counter-increment: …;
  counter-reset: …;
}

Numérotation des titres

Comment l’auto numérotation des titres est réalisée dans cet article ?

L’auto numérotation des titres est appliquée dans cet article avec uniquement CSS. Toutes les balises de titres à numéroter sont encapsulées dans la balise main ici, bien entendu cela peut être une autre balise parente (body, div…):

<main>
    <h2>Introduction</h2>
    <h2>Numérotation des titres</h2>
        <h3>Comment l’auto numérotation des titres est réalisée dans cet article ?</h3>
        <h3>Désactiver l’auto numérotation</h3>
        <h3>Changer le format</h3>
    <h2>Numérotation des listes</h2>
    <h2>Pagination</h2>
    <h2>Conclusion</h2>
</main>

La balise <h1> n’est pas numérotée. Dans l’usage, il n’y a qu’une seule balise <h1> dans une page et elle sert de titre.

Un compteur est défini pour chaque balise <hx> et la numérotation est appliquée jusqu’à la balise <h4> (il est recommandé d’appliquer un niveau de profondeur raisonnable dans les balises de titres).

<h2>h2counter
<h3>h3counter
<h4>h4counter

Les compteurs des sous-titres sont remis à zéro dans la propriété counter-reset de la balise titre parent :

main    { counter-reset: h2counter; }
main h2 { counter-reset: h3counter; }
main h3 { counter-reset: h4counter; }

Dans la pseudo classe ::before des balises <hx>, le compteur est incrémenté et le contenu de la numérotation est défini :

main    { counter-reset: h2counter; }
main h2 { counter-reset: h3counter; }
main h3 { counter-reset: h4counter; }

main h2::before {
    counter-increment: h2counter;
    content: counter(h2counter) ".";
}
main h3::before {
    counter-increment: h3counter;
    content: counter(h2counter) "." counter(h3counter) ".";
}
main h4::before {
    counter-increment: h4counter;
    content: counter(h2counter) "." counter(h3counter) "." counter(h4counter) ".";
}

C’est tout.

Désactiver l’auto numérotation

Pour certains titres (introduction, conclusion, annexe…), la désactivation de l’auto numérotation est préférable.

Définir un attribut utilisateur, par exemple data-nocount :

<h2 data-nocount>Introduction</h2>

et appliquer la numérotation uniquement si la balise ne contient pas cet attribut :

main                        { counter-reset: h2counter; }
main h2:not([data-nocount]) { counter-reset: h3counter; }
main h3:not([data-nocount]) { counter-reset: h4counter; }

main h2:not([data-nocount])::before {
    counter-increment: h2counter;
    content: counter(h2counter) ".";
}
main h3:not([data-nocount])::before {
    counter-increment: h3counter;
    content: counter(h2counter) "." counter(h3counter) ".";
}
main h4:not([data-nocount])::before {
    counter-increment: h4counter;
    content: counter(h2counter) "." counter(h3counter) "." counter(h4counter) ".";
}

Changer le format

Le format de la numérotation peut être différent dans les pages, exemples : 1.1.1., 1-1-1-.

Un séparateur est alors défini avec une variable, plus personnalisable :

:root {
    --sep-num : '-';
}

main                        { counter-reset: h2counter; }
main h2:not([data-nocount]) { counter-reset: h3counter; }
main h3:not([data-nocount]) { counter-reset: h4counter; }

main h2:not([data-nocount])::before {
    counter-increment: h2counter;
    content: counter(h2counter) var(--sep-num);
}
main h3:not([data-nocount])::before {
    counter-increment: h3counter;
    content: counter(h2counter) var(--sep-num) counter(h3counter) var(--sep-num);
}
main h4:not([data-nocount])::before {
    counter-increment: h4counter;
    content: counter(h2counter) var(--sep-num) counter(h3counter) var(--sep-num) counter(h4counter) var(--sep-num);
}

Pour changer le séparateur de numérotation pour une page, il suffira dans cette page de redéfinir la variable après le chargement du CSS contenant les règles de numérotation :

<link href="./css/style.css" rel="stylesheet">
<style>:root { --sep-num : '.'; }</style>

Une variable est également très utile si le format de numérotation est dépendant de la langue. Dans l’exemple ci-dessous, le séparateur par défaut est modifié si le document est en français :

:root {
    --sep-num : '.';
}

:lang(fr) { --sep-num : '-'; }

La numérotation peut être d’un autre type que numérique : alphabétique, romain, grec, katakana… (Mozilla - list-style-type). Le type est le second argument optionnel de la fonction counter :

content: counter(h2counter, upper-roman) var(--sep-num);

Numérotation des listes

La balise <ol> est pratique pour numéroter les éléments dans une liste, malheureusement la numérotation n’est pas celle attendue lorsque les balises <ol> sont imbriquées :

  1. Introduction
  2. Section
    1. Subsection
    2. Subsection
  3. Conclusion

Une autre problèmatique, on peut souhaiter appliquer une numérotation automatique sur les éléments d’une liste <ul> construite par une librairie tierce (javascript…) : TocBot par exemple, une librairie Javascript pour la génération dynamique de tables des matières.

Les compteurs CSS viennent à la rescousse !

Dans le cas pratique qui suit, la table des matières est générée dans un élément div (classe js-toc):

<div class="js-toc">
	<ul>
		<li><a href="#…">Introduction</a></li>
		<li><a href="#…">Numérotation des titres
			<ul>
				<li><a href="#…">Comment l’auto numérotation des titres est réalisée dans cet article ?</a></li>
				<li><a href="#…">Désactiver l’auto numérotation</a></li>
				<li><a href="#…">Changer le format</a></li>
			</ul>
		</li>
		<li><a href="#…">Numérotation des listes</a></li>
		<li><a href="#…">Pagination</a></li>
    <li><a href="#…">Conclusion</a></li>
	</ul>
</div>
Autonumbering UL lists

Pour réaliser la numérotation, un compteur licounter est réinitialisé à chaque fois qu’un bloc ul est créé dans le conteneur de la table des matières (<div class="js-toc">) :

div[class*="js-toc"] ul {
  list-style-type: none;
  counter-reset: licounter 0;
}

Pour chaque bloc li, le compteur est incrémenté et la fonction counters est utilisée pour remplir le contenu de la pseudo-class ::before :

:root {
    --sep-num : '.';
}

:lang(fr) { --sep-num : '-'; }

div[class*="js-toc"] ul {
  list-style-type: none;
  counter-reset: licounter 0;
}

div[class*="js-toc"] ul li::before {
  counter-increment: licounter;
  content: counters(licounter, var(--sep-num)) var(--sep-num);
  padding-right: 8px;
}

La fonction CSS counters active les compteurs imbriqués et retourne une chaîne concaténée des compteurs en cours.

Pour désactiver l’auto numérotation, comme pour les titres, un attribut utilisateur data-nocount est défini :

<li data-nocount><a href="#…">Introduction</a></li>

et la numérotation n’est appliquée que si la balise li ne contient pas l’attribut data-nocount :

div[class*="js-toc"] ul li:not([data-nocount])::before {
	  counter-increment: licounter;
	  content: counters(licounter, var(--sep-num)) var(--sep-num);
      padding-right: 8px;
}

Le troisième argument optionnel de la fonction counters change le type de la numérotation :

content: counters(licounter, var(--sep-num), upper-roman) var(--sep-num);

Pagination

Connaissant cette puissante fonctionnalité, les barres d’outils de pagination sont facilement construites sans avoir à calculer et afficher les compteurs :

ul[class*="paging"] {
    list-style-type: none;
    counter-reset: pgcounter var(--start-toolbar-paging);
}
ul[class*="paging"] li a:not([data-nocount])::before {
	  counter-increment: pgcounter;
	  content: counter(pgcounter);
}

Malheureusement, la variable --start-toolbar-paging doit être définie auparavant dans la page. Il est actuellement interdit d’utiliser des valeurs d’attributs qui sont de type string pour définir des compteurs qui sont de type integer, les nouveaux niveaux de versions de CSS à venir (CSS 4 ?) couvriront très probablement cette fonctionnalité très attendue.

ul[class*="paging"] {
    list-style-type: none;
    counter-reset: pgcounter attr(data-count-start);
}

Conclusion

Une conclusion ? Consulter les documentations CSS et rester à jour : puissant, CSS épargne très souvent des heures de programmation et maux de tête.