Passer au contenu principal

Settings

Color Mode

Theme Skin

Background

Appearance preferences are saved in this browser only.

Environment

Current Environment Dev

Built with JEKYLL_ENV=development. Changes auto-reload under jekyll serve.

Theme & Build

Jekyll v3.10.0
Last BuildNov 03, 07:01

Page Location

Page Info

Layout default
Collection none
Path fr/docs/features/toc.md
URL /fr/docs/features/toc/
Cette page a été traduite automatiquement depuis l'original en anglais. Voir l'original

Table des matières avec Scroll Spy

Table des matières automatique générée à partir des titres h1-h3 d'une page, avec un scroll spy positionnel qui met en gras la section en cours de lecture et un défilement fluide des ancres.

Table des matières

Génération automatique d’une table des matières à partir des titres de la page, avec mise en évidence de la section active.

Une page de documentation avec la table des matières « Sur cette page » dans la colonne de droite, listant les titres de la page ; la section courante est mise en évidence au fil du défilement

Le panneau Sur cette page à droite est la table des matières, construite à partir des titres h1–h3 de la page.

Aperçu

  • Génération automatique : extraite des titres h1-h3 (la plage est un paramètre, voir Niveaux de titre)
  • Scroll Spy : met en évidence la section courante
  • Défilement fluide : navigation animée
  • Responsive : barre latérale sur ordinateur, offcanvas sur mobile

Implémentation

Modèle d’inclusion

{% include content/toc.html %}

Génération de la table des matières

L’include toc.html utilise la table des matières intégrée de Kramdown :

<nav id="TableOfContents" class="toc">
  <h2 class="toc-title">On This Page</h2>
  {{ content | toc_only }}
</nav>

Ou une extraction manuelle :

<nav id="TableOfContents">
  <ul class="toc-list">
    {% for heading in page.content | split: '<h' %}
      {% if heading contains 'id="' %}
        {% assign id = heading | split: 'id="' | last | split: '"' | first %}
        {% assign level = heading | slice: 0, 1 %}
        {% assign text = heading | split: '>' | last | split: '<' | first %}
        <li class="toc-item toc-level-{{ level }}">
          <a href="#{{ id }}" class="toc-link">{{ text }}</a>
        </li>
      {% endif %}
    {% endfor %}
  </ul>
</nav>

Configuration

Activer la table des matières

Dans le front matter :

---
toc: true
---

Ou pour l’ensemble du site dans _config.yml :

defaults:
  - scope:
      type: docs
    values:
      toc: true

Niveaux de titre

La plage est définie à l’endroit où la table des matières est incluse, avec les paramètres h_min / h_max. _includes/navigation/sidebar-right.html affiche le panneau de droite avec h_min=1 h_max=3, de sorte que le titre de la page et ses sections h2/h3 apparaissent :

{% include content/toc.html html=content h_min=1 h_max=3 %}

L’include lui-même utilise par défaut h_min=1 et h_max=6 ; tout ce qui se trouve en dehors de la plage est ignoré, tout comme tout titre dépourvu d’un id.

Mise en forme

Styles de base

.toc {
  position: sticky;
  top: 80px;
  max-height: calc(100vh - 100px);
  overflow-y: auto;
}

.toc-title {
  font-size: 0.875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 1rem;
}

.toc-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.toc-link {
  display: block;
  padding: 0.25rem 0;
  color: var(--bs-secondary);
  text-decoration: none;
  font-size: 0.875rem;
  border-left: 2px solid transparent;
  padding-left: 0.75rem;
}

.toc-link:hover {
  color: var(--bs-primary);
}

.toc-link.active {
  color: var(--bs-primary);
  border-left-color: var(--bs-primary);
  font-weight: 500;
}

Niveaux imbriqués

.toc-level-3 {
  padding-left: 1rem;
}

.toc-level-4 {
  padding-left: 2rem;
  font-size: 0.8125rem;
}

Scroll Spy

assets/js/modules/navigation/scroll-spy.js met en gras l’entrée de la section que vous lisez. La règle est positionnelle plutôt que basée sur la visibilité : le titre actif est le dernier dont le haut a franchi la ligne de lecture — une ligne située à scroll-padding-top sous le haut de la fenêtre, c’est-à-dire juste sous l’en-tête fixe, qui est aussi l’endroit où un clic sur une entrée de la table des matières positionne son titre. Une fois la page défilée jusqu’en bas, le dernier titre l’emporte, de sorte que les sections finales plus courtes que la fenêtre restent accessibles.

// Simplified: the active heading, recomputed on each scroll frame.
function activeHeading(headings) {          // headings sorted by document offset
  const pad = parseFloat(getComputedStyle(document.documentElement).scrollPaddingTop);
  const atBottom =
    window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 4;
  if (atBottom) return headings[headings.length - 1];

  const line = window.scrollY + pad + 4;
  let active = headings[0];
  for (const heading of headings) {
    if (heading.top > line) break;
    active = heading;
  }
  return active;
}

Les décalages des titres sont mesurés une fois puis remesurés lors d’un redimensionnement ou d’un remaniement du contenu (ResizeObserver), et le recalcul est limité à une image d’animation par défilement, de sorte que toute la page est réévaluée à chaque image sans mesurer le DOM à chaque fois.

Demander à IntersectionObserver le titre « le plus visible » paraît plus simple mais ne fonctionne pas : les titres ne font que quelques pixels de haut, donc chaque titre à l’intérieur de la bande de l’observateur rapporte le même intersectionRatio, et les titres qui quittent la bande ne déclenchent aucun callback. La mise en évidence se retrouve alors sur le titre qui figurait dans le dernier lot de callbacks.

Deux détails comptent pour le confort. Une entrée cliquée reste active pendant l’animation du défilement fluide, au lieu de faire clignoter chaque titre traversé en chemin. Et maintenir l’entrée active visible dans une longue table des matières ajuste le propre scrollTop du conteneur de la table des matières — scrollIntoView() remonterait et ferait défiler la page, ce qui rétroagirait directement sur le spy.

Configuration (assets/js/modules/navigation/config.js) :

Clé Valeur par défaut Rôle
scrollSpy.offset null Distance de la ligne de lecture par rapport au haut de la fenêtre. null la déduit de scroll-padding-top ; définissez un nombre pour la fixer.
scrollSpy.tolerance 4 Marge (px) au niveau de la ligne de lecture et lors de la détection du bas de la page.

Défilement fluide

Méthode CSS

html {
  scroll-behavior: smooth;
}

Méthode JavaScript

document.querySelectorAll('.toc-link').forEach((link) => {
  link.addEventListener('click', (e) => {
    e.preventDefault();
    const targetId = link.getAttribute('href').slice(1);
    const target = document.getElementById(targetId);
    const headerOffset = 80;
    const position = target.offsetTop - headerOffset;
    
    window.scrollTo({
      top: position,
      behavior: 'smooth'
    });
    
    history.pushState(null, '', `#${targetId}`);
  });
});

Comportement responsive

Ordinateur

La table des matières apparaît dans la barre latérale droite :

<aside class="d-none d-lg-block">
  {% include content/toc.html %}
</aside>

Mobile

Table des matières en offcanvas (voir Table des matières mobile) :

<div class="offcanvas offcanvas-end d-lg-none" id="tocSidebar">
  {% include content/toc.html %}
</div>

Accessibilité

Attributs ARIA

<nav id="TableOfContents" 
     aria-label="Table of contents"
     role="navigation">
  • Tabulation à travers les liens de la table des matières
  • Entrée pour naviguer vers la section
  • Le focus se déplace vers le titre

Dépannage

La table des matières ne se génère pas

  1. Vérifiez que les titres ont des ID
  2. Vérifiez toc: true dans le front matter
  3. Assurez-vous d’utiliser le processeur Kramdown

Le Scroll Spy ne fonctionne pas

  1. Vérifiez que les ID des titres correspondent aux hrefs de la table des matières — le spy résout le href de chaque lien de la table des matières en un titre via id, de sorte qu’un lien sans élément correspondant est ignoré
  2. Vérifiez la ligne de lecture — scroll-padding-top sur html (ou config.scrollSpy.offset) détermine quand un titre devient courant
  3. Vérifiez que rien d’autre ne revendique #TableOfContents — un second scroll spy (un élément data-bs-spy="scroll" le ciblant) se dispute la classe .active

Problèmes de mise en forme

  1. Vérifiez le positionnement sticky
  2. Vérifiez le z-index
  3. Testez le comportement de débordement

Connexes

Voir aussi

  • [[Features]]
  • [[Mobile TOC Floating Action Button]]
  • [[Sidebar Navigation System]]