Pourquoi une table des matières dynamique est importante pour le contenu de longue durée

Une table des matières dynamique (TOC) résout cette situation en fournissant un aperçu cliquable qui met automatiquement en évidence la section que le lecteur regarde. Cela améliore la convivialité, réduit les taux de rebond et rend votre contenu plus accessible aux utilisateurs qui veulent sauter rapidement entre les sujets. Contrairement à un TOC statique écrit à la main, un TOC généré par JavaScript se met à jour lorsque de nouvelles sections sont ajoutées, maintient les liens en synchronisation avec les ID de la rubrique et répond au comportement de défilement en temps réel.

Par exemple, un guide technique avec 30 sections devient beaucoup plus facile à digérer lorsque le lecteur voit un menu sidebar qui suit leur progression. Le même principe s'applique aux applications à une page, à la documentation API ou même aux billets de blog avec plusieurs sous-thèmes. En mettant en place un TOC dynamique, vous donnez aux lecteurs le contrôle de leur expérience de lecture tout en réduisant la charge cognitive de la recherche de pièces pertinentes.

Concepts fondamentaux derrière une table des matières dynamique

Pour construire un TOC dynamique, vous devez comprendre trois éléments fondamentaux :

  • Sémantique structure HTML[ – chaque rubrique doit avoir un attribut unique pour que JavaScript puisse le cibler.
  • DOM traversal and manipulation – votre script scanne les entêtes, crée une liste imbriquée de liens et ajoute cette liste à un élément conteneur.
  • Scroll event handling[ – un auditeur efficace vérifie quel entête est actuellement visible et ajoute une classe au lien correspondant de TOC.

Ces pièces travaillent ensemble pour produire un TOC qui se sent natif de la page, nécessite une logique minimale côté serveur, et fonctionne sur les navigateurs modernes.

Étape 1: Préparer votre structure HTML

Avant de lancer un JavaScript, vous avez besoin de deux choses dans votre HTML:

Attribuer des ID uniques aux rubriques

Chaque titre qui devrait apparaître dans le TOC (généralement , ou ) doit avoir un unique], ce qui est essentiel parce que le TOC lie des identificateurs de fragments (p. ex. ) pour faire défiler vers la bonne position. Voici un exemple :

<h2 id="introduction">Introduction</h2>
<p>...</p>
<h2 id="setup">Setting Up the Environment</h2>
<p>...</p>
<h3 id="installing-dependencies">Installing Dependencies</h3>
<p>...</p>
<h2 id="implementation">Implementation</h2>
<p>...</p>

Si vous ne pouvez pas modifier le HTML directement, vous pouvez générer des ID à partir du texte en vedette en utilisant JavaScript (p. ex., la fonction ), mais il est plus propre de les ajouter manuellement ou avec un générateur de site statique.

Créer un conteneur pour le TOC

Placez un élément vide (généralement un ou un ) où vous voulez que le TOC apparaisse. Par exemple :

<nav id="table-of-contents" aria-label="Table of Contents"></nav>

Le améliore l'accessibilité en donnant aux lecteurs d'écran un nom descriptif pour la région de navigation. Plus tard, vous allez remplir ce conteneur avec la liste générée.

Étape 2: Générer le TOC avec JavaScript

Maintenant, nous écrivons le JavaScript qui scanne les en-têtes et construit la liste. L'extrait suivant crée une liste plate de en-têtes . Pour un TOC plus avancé qui comprend des sous-titres, vous aurez besoin de listes imbriquées, que nous couvrirons plus tard.

Exemple de base de TOC plat

const tocContainer = document.getElementById('table-of-contents');
const headings = document.querySelectorAll('h2');

// Bail early if there's no container or no headings
if (!tocContainer || headings.length === 0) return;

const ul = document.createElement('ul');
ul.setAttribute('role', 'list'); // accessibility enhancement

headings.forEach((heading, index) => {
 // Ensure the heading has an id; if not, generate one
 if (!heading.id) {
 heading.id = 'section-' + index;
 }
 const li = document.createElement('li');
 const a = document.createElement('a');
 a.textContent = heading.textContent;
 a.href = '#' + heading.id;
 a.setAttribute('data-section', heading.id); // useful for active detection
 li.appendChild(a);
 ul.appendChild(li);
});

tocContainer.appendChild(ul);

Points clés:

  • La méthode renvoie un statique ; elle fonctionne pour des pages où les rubriques ne changent pas dynamiquement.
  • Si un titre manque de , nous en générons automatiquement un en utilisant l'index. Cela empêche les liens brisés.
  • Nous ajoutons un attribut à chaque lien pour faciliter la sélection plus tard.

Manipulation des caps nissés (H2, H3, H4)

Pour créer des listes imbriquées, suivre le courant et insérer pour ses enfants. Voici une approche simplifiée à l'aide d'une pile :

const tocContainer = document.getElementById('table-of-contents');
const headings = document.querySelectorAll('h2, h3, h4');
if (!tocContainer || headings.length === 0) return;

const root = document.createElement('ul');
const stack = [{ element: root, level: 2 }]; // level refers to heading level

headings.forEach((heading) => {
 const level = parseInt(heading.tagName.substring(1), 10); // 'H2' -> 2
 if (!heading.id) heading.id = 'section-' + Math.random().toString(36).substr(2, 9);

 const li = document.createElement('li');
 const a = document.createElement('a');
 a.textContent = heading.textContent;
 a.href = '#' + heading.id;
 li.appendChild(a);

 // Pop stack until we reach the parent level
 while (stack.length > 0 && stack[stack.length - 1].level >= level) {
 stack.pop();
 }
 const parent = stack[stack.length - 1].element;
 parent.appendChild(li);

 // If next heading is lower, we need a nested list
 const nextLevel = headings.item(Array.from(headings).indexOf(heading) + 1);
 if (nextLevel && parseInt(nextLevel.tagName.substring(1), 10) > level) {
 const nestedUl = document.createElement('ul');
 li.appendChild(nestedUl);
 stack.push({ element: nestedUl, level: level });
 }
});

tocContainer.appendChild(root);

Cet algorithme permet de s'assurer que chaque sous-position apparaît en creux sous son parent. Pour la production, vous pouvez affiner la logique afin d'éviter les piles profondes et les cas de bord de poignée (p. ex., les niveaux manquants de cap).

Étape 3 : Mettre en valeur la section active sur défiler

Le mécanicien de surlignement permet aux lecteurs de savoir quelle partie de l'article ils lisent actuellement. L'idée est de boucler toutes les rubriques, trouver celle qui est la plus proche du haut du port de vue (avec un certain décalage), et appliquer une classe au lien correspondant de TOC.

Auditeur efficace de défilement

const tocLinks = document.querySelectorAll('#table-of-contents a');
const sections = Array.from(headings).map(h => ({
 id: h.id,
 top: h.offsetTop
}));

function updateActiveLink() {
 const scrollY = window.pageYOffset || document.documentElement.scrollTop;
 let currentId = '';

 // Iterate backwards for better performance
 for (let i = sections.length - 1; i >= 0; i--) {
 if (scrollY >= sections[i].top - 150) {
 currentId = sections[i].id;
 break;
 }
 }

 tocLinks.forEach(link => {
 link.classList.remove('active');
 if (link.getAttribute('href') === '#' + currentId) {
 link.classList.add('active');
 }
 });
}

// Throttle scroll events for performance
let ticking = false;
window.addEventListener('scroll', () => {
 if (!ticking) {
 window.requestAnimationFrame(() => {
 updateActiveLink();
 ticking = false;
 });
 ticking = true;
 }
});

Optimisations:

  • Utilisez pour limiter les mises à jour du cycle de peinture du navigateur. Cela évite le décalage sur les pages occupées.
  • Le décalage de 150 pixels assure que la section est un peu active avant qu'elle atteigne le sommet, ce qui se sent plus naturel.
  • L' itération à l'envers de la dernière rubrique est plus efficace parce que la section active est probablement près du bas de la zone visible.

Étape 4 : Ajouter un défilement lisse et l'accessibilité

Le défilement lisse rend le saut entre les sections agréable. Vous pouvez y parvenir avec CSS, mais aussi via JavaScript pour un contrôle plus fin.

// Add click handler on the TOC container to use smooth scrolling
tocContainer.addEventListener('click', (e) => {
 const link = e.target.closest('a');
 if (link && link.getAttribute('href').startsWith('#')) {
 e.preventDefault();
 const targetId = link.getAttribute('href').substring(1);
 const target = document.getElementById(targetId);
 if (target) {
 target.scrollIntoView({ behavior: 'smooth' });
 // Update the URL hash without causing a scroll jump
 history.pushState(null, '', '#' + targetId);
 }
 }
});

Améliorations de l'accessibilité :

  • S'assurer que le COT a un (p. ex., -Table des matières).
  • Ajouter au lien actif : au lieu de . Cela aide les lecteurs d'écran à annoncer la section actuelle.
  • Utilisez et si la sémantique par défaut / est dépassée par le style.

Étape 5 : Écheller le TOC dynamique

Bien que le style ne fasse pas partie de la logique JavaScript, un TOC bien style renforce la convivialité. Voici un exemple minimal CSS qui ajoute un positionnement collant pour l'utilisation de la barre latérale :

#table-of-contents {
 position: sticky;
 top: 2rem;
 max-height: calc(100vh - 4rem);
 overflow-y: auto;
 border-left: 2px solid #ccc;
 padding-left: 1rem;
 font-size: 0.9rem;
}
#table-of-contents ul {
 list-style: none;
 padding: 0;
}
#table-of-contents li {
 margin-bottom: 0.25rem;
}
#table-of-contents a {
 color: #333;
 text-decoration: none;
}
#table-of-contents a.active {
 font-weight: bold;
 color: #007bff;
}
#table-of-contents a[aria-current="location"] {
 border-left: 2px solid #007bff;
 margin-left: -1rem;
 padding-left: calc(1rem - 2px);
}

Pour une conception réactive, envisagez de cacher le TOC sur de petits écrans et d'ajouter un bouton de basculement, ou de l'effondrer dans un menu déroulant sélectionné.

Améliorations avancées

1. Dénonciation des événements de redimensionnement

Si la hauteur du port de vue change (p. ex., sur le changement d'orientation mobile), les valeurs des entêtes peuvent changer. Recalculer le tableau sur une nouvelle taille débonnée :

let sections = [];
function recalcSections() {
 sections = Array.from(headings).map(h => ({
 id: h.id,
 top: h.offsetTop
 }));
}
let resizeTimer;
window.addEventListener('resize', () => {
 clearTimeout(resizeTimer);
 resizeTimer = setTimeout(recalcSections, 250);
});

2. Observateur intersection pour les mises en évidence basées sur le défilement

Une alternative aux auditeurs parchemin est l'API . Il est plus performant et plus facile à gérer. Exemple:

const observer = new IntersectionObserver((entries) => {
 entries.forEach(entry => {
 if (entry.isIntersecting) {
 const id = entry.target.id;
 tocLinks.forEach(link => {
 link.classList.remove('active');
 if (link.getAttribute('href') === '#' + id) {
 link.classList.add('active');
 }
 });
 }
 });
}, { rootMargin: '-80px 0px -70% 0px' });

headings.forEach(h => observer.observe(h));

Cela ne fait feu que lorsqu'une position entre ou quitte une zone calculée, réduisant ainsi les frais généraux. définit lorsqu'une section est considérée comme active.

3. Chargement paresseux ou contenu dynamique

Si votre article charge dynamiquement des sections (par exemple via AJAX), vous devez régénérer le TOC après que le nouveau contenu apparaît. Une façon est d'utiliser un Observateur de Mutation sur le conteneur de l'article et d'appeler à nouveau la fonction de génération de TOC. Cependant, attention à ne pas dupliquer les entrées.

Considérations relatives aux performances

  • Éviter les requêtes lourdes DOM dans les gestionnaires de défilement. Cache tous les sélecteurs une fois à l'initialisation.
  • Utilisez des auditeurs passifs d'événements pour faire défiler : . Cela améliore les performances de défilement, en particulier sur mobile.
  • Don=t gaz avec – ] est plus efficace car il synchronise avec le navigateur=s rendu boucle.
  • Minimisez et reportez le script de sorte qu'il ne bloque pas la charge de page. Placez le script juste avant ou utilisez l'attribut .

Intégration avec un générateur statique de site (SSG) ou un CMS

Si vous utilisez un générateur statique de site, vous pouvez pré-céder le TOC en utilisant des fonctionnalités intégrées (p. ex. collections Onzety, Hugos ). Cependant, le défilement dynamique nécessite toujours JavaScript côté client. L'avantage d'un TOC côté serveur est qu'il est disponible immédiatement, même avant JavaScript, aidant le référencement et l'accessibilité.

Pour un CMS comme WordPress ou Directus, vous pouvez utiliser la même approche JavaScript tout en stockant les ID de cap dans le contenu. Directus, par exemple, prend en charge des interfaces personnalisées qui génèrent automatiquement des ID. Vous pouvez créer un crochet qui fonctionne sur le contenu enregistrant pour ajouter des ID aux entêtes, puis compter sur le JavaScript front-end pour construire le TOC.

Ressources externes pour une meilleure compréhension :

Essais et débogage

  1. Vérifier que chaque rubrique a un . Dupliquer les IDs font défiler le navigateur à la première correspondance seulement.
  2. Vérifiez le TOC dans les thèmes clairs et sombres pour vous assurer que le contraste des liens respecte les normes WCAG AA.
  3. Test avec navigation au clavier : appuyez sur Le tab[ devrait se déplacer entre les liens TOC, et Entrer[ devrait défiler vers la section.
  4. Utilisez l'onglet Exécution DevTools du navigateur pour vous assurer qu'il n'y a pas de jank pendant le défilement.
  5. Si l'article contient des images ou des iframes, le peut changer après le chargement de ces éléments. Appelez une fonction de recalcul sur ou après le chargement de toutes les images (par exemple ).

Pièges potentiels et comment les éviter

  • Les liens brisés lorsque les rubriques ne sont pas identifiées. Vérifiez toujours pour un et en générez un si vous manquez (utilisez un utilitaire de lugify).
  • TOC clignotant pendant le défilement – causé par trop de reflows. Utilisez et cachez valeurs.
  • Sections de chevauchement – le point fort actif peut basculer trop tôt ou trop tard. Ajustez le dans IntersectionObserver ou le décalage dans le gestionnaire de défilement.
  • Nested TOC indentation issues[ – test avec plusieurs niveaux (H2 → H3 → H4) et s'assurer que la liste rend correctement. L'approche basée sur la pile ci-dessus fonctionne mais peut être étendue pour gérer les lacunes (par exemple, H2 directement suivi par H4).
  • Performance sur les longues pages – si vous avez des centaines de titres, envisagez de limiter le TOC à H2 et H3 seulement, ou implémentez le défilement virtuel pour la barre latérale.

Conclusion

En inscrivant des ID dans des rubriques, en générant une liste imbriquée de liens et en mettant en évidence la section actuelle en fonction de la position du rouleau, vous donnez aux lecteurs une feuille de route claire. Les exemples de code dans cet article fournissent une base solide, mais vous pouvez facilement les étendre : ajouter un défilement en douceur, utiliser IntersectionObserver pour améliorer les performances ou s'intégrer à votre processus de construction existant.

Implémentez l'approche qui correspond le mieux à votre pile : un JavaScript pur pour des sites simples, ou un hybride avec SSG pour la structure initiale des COT et la mise en valeur côté client.