jQuery / Plugin / jquery.toc — Créer un sommaire depuis les titres avec défilement doux
jquery.toc recueille les titres h1–h6 d’une zone et génère un sommaire lié. Appelez .toc({ container: "#contents" }) sur l’article pour insérer la liste ailleurs ; un clic défile vers le titre. Séparez article et sommaire et définissez les niveaux et la reconstruction.
Préparer la source et le document
Il s’agit de jquery.toc de jakobbossek, sans mélanger les options d’autres plugins similaires. La source v1.1 vérifiée est fixée à un commit. Enregistrez le fichier brut jquery.toc.js sous ce nom à côté du HTML, et non la page de consultation GitHub.
Les fichiers locaux sont toc-demo.html et jquery.toc.js. jQuery complet 4.0.0 vient du CDN. Respectez l’ordre jQuery, plugin, initialisation ; slim n’a pas les effets requis. La même source a été testée avec 4.0.0 et 3.7.1 sous Windows Edge.
Tous les :header de la zone sont recueillis. #article exclut sommaire, titre du document et aside. Deux h3 suivent un h2 pour représenter des sous-sujets réels. N’utilisez pas les niveaux comme simples styles. Cette source n’implémente pas de profondeur maximale configurable.
Exemple complet avec focus et reconstruction
Enregistrez toc-demo.html en UTF-8 et ouvrez-le sans serveur ni compilation. Internet charge jQuery. Les titres sont des textes simples contrôlés. Le plugin réécrit leur HTML et place des caractères dans des chaînes HTML ; cet exemple ne traite pas des titres arbitraires reçus en entrée ni des boutons et liens interactifs dans les titres.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Generate a scoped table of contents</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script src="jquery.toc.js"></script>
<style>
html { scroll-behavior: auto; }
body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 20px; }
#contents { border: 1px solid teal; padding: 16px; }
#contents ul { padding-left: 24px; }
#contents a { overflow-wrap: anywhere; }
#article p { min-height: 440px; }
h2:focus, h3:focus { outline: 3px solid teal; }
button { font: inherit; }
footer { min-height: 100vh; }
</style>
</head>
<body>
<h1>A focused table of contents</h1>
<nav id="contents" aria-label="Article contents"><p>Contents will be generated when scripts load.</p></nav>
<button type="button" id="rebuild">Rebuild original example</button>
<p id="status" role="status">Article text is available below.</p>
<main id="article">
<h2 id="existing-overview">Overview</h2><p>First main topic with an existing stable ID.</p>
<h3>Preparation</h3><p>First subtopic.</p>
<h3>Verification</h3><p>Second subtopic.</p>
<h2>Overview</h2><p>A different section with the same displayed title.</p>
</main>
<aside><h2>Outside the article</h2><p>This heading must not enter the contents.</p></aside>
<footer>Space for the final destination.</footer>
<script>
jQuery(function ($) {
if (typeof $.fn.toc !== "function") return;
const $article = $("#article"), $toc = $("#contents"), original = $article.html();
const $scroller = $(document.scrollingElement);
let generation = 0;
function build() {
generation++; $scroller.stop(true, false);
$article.html(original); $toc.empty().off("click.demo");
$article.toc({ container: "#contents", strip: false, stripAfter: 30, scrollSpeed: 400, scrollOffset: 20 });
// Replace this example's generated links' click handlers only.
$toc.find("a").off("click");
$toc.on("click.demo", "a[href^='#hl_']", function (event) {
const anchor = document.getElementById(this.hash.slice(1));
if (!anchor) return;
event.preventDefault();
const run = ++generation, heading = anchor.parentElement;
$scroller.stop(true, false);
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
$scroller.animate({ scrollTop: Math.max(0, $(heading).offset().top - 20) }, reduce ? 0 : 400, function () {
if (run !== generation) return;
heading.tabIndex = -1; heading.focus({ preventScroll: true });
$("#status").text("Arrived: " + heading.textContent);
});
});
$("#status").text("Entries: " + $toc.find("a").length);
}
$("#rebuild").on("click", function () { build(); window.scrollTo(0, 0); });
build();
});
</script>
</body>
</html>
Résultat initial ; les deux Overview sont des sections distinctes.
Entries: 4
- Vérifiez quatre entrées, les deux centrales imbriquées. Outside the article est absent.
- Le dernier Overview mène au titre en 400ms, avec 20px au-dessus et un contour de focus.
- Focalisez une entrée au clavier et appuyez sur Entrée.
- Avec mouvement réduit à reduce, l’arrivée est immédiate.
- Rebuild original example revient en haut et garde Entries: 4 même après répétition, sans listes ni ancres imbriquées supplémentaires.
- Reconstruisez pendant le mouvement : une ancienne fin ne doit pas écraser le nouvel état.
Après génération, l’exemple remplace seulement les gestionnaires de clic de ses liens. Le complément anime un seul élément de défilement réel et ajoute nettoyage de file, distinction des annulations, focus et mouvement réduit. Le fichier original n’est pas modifié. Fragment URL et historique ne changent pas.
Appel simple et comportement réel des options
Pour examiner l’original, gardez les balises script de l’initialisation et remplacez tout leur code intérieur par ceci. Conservez chargement, article et #contents, mais retirez le bouton de reconstruction et l’état. Cette variante utilise le défilement d’origine sans complément de focus, mouvement réduit ou reconstruction.
$("#article").toc({
container: "#contents",
strip: false,
stripAfter: 30,
scrollSpeed: 400,
scrollOffset: 20
});
#article désigne la source ; container, le lieu d’insertion. wrapWith règle le HTML enveloppant, de classe par défaut toc_container. Limitez les styles à #contents.
scrollSpeed est une durée en millisecondes. Dans l’appel simple, comparez 400 à 800 sur le même lien, puis rétablissez. scrollOffset est soustrait à la coordonnée de l’ancre interne : positif 20 laisse de l’espace au-dessus. Le complément complet aligne la boîte du titre elle-même à 20px, ce qui peut différer de quelques pixels de l’ancre en ligne.
Dans l’exemple complet, changer seulement ces options .toc() ne modifie pas le gestionnaire de remplacement. Changez la durée animate 400 ou la soustraction 20 à cet endroit ; gardez zéro pour le mouvement réduit.
La condition réelle de raccourcissement examine strip. Malgré shorten dans les valeurs initiales, shorten:true n’active pas cette branche. strip:true applique substring jusqu’à stripAfter et ajoute ... à toutes les entrées, même courtes. Ce sont des unités de chaîne, pas des mots ; des caractères combinés comme les emoji peuvent être coupés. Préférez strip:false pour des titres lisibles complets.
Ancres, ID existants et limites d’intégration
La source conserve l’ID du titre et insère à l’intérieur un a identifié comme hl_112. existing-overview subsiste. Les ID générés dépendent de la zone, de l’ordre et du niveau, pas du texte : les intitulés identiques de cet exemple ont donc des cibles distinctes. Unicode est aussi recueilli, mais réordonner peut changer les ID. Ce n’est pas une règle de permaliens stables.
L’original ajoute les listes et enveloppe encore le HTML à chaque appel. Le code complet restaure sa copie HTML initiale et vide le sommaire. Il ne conserve donc ni contenu ajouté ensuite, ni état de formulaire, ni événements directs. Un éditeur exige une autre gestion du contenu courant et des identifiants stables.
Ne l’initialisez pas en plus d’un thème ou shortcode WordPress produisant déjà un sommaire pour le même article. L’exercice est distinct du système de ce site. La sélection des clics originale atteint aussi globalement les listes .toc ; l’indépendance de plusieurs instances n’est pas acquise.
Si rien n’apparaît, contrôlez chemin et véritable contenu JavaScript, ordre, titres et cible. Pour un décalage, examinez menus fixes, images/polices tardives et place restante à la fin. Faites revenir les longs intitulés à la ligne en CSS et gardez l’article lisible sans sommaire.




