Table des matières dynamique sur Webflow CMS – Le moyen le plus simple

Publié: 2022-03-22
Nous avons récemment rendu possible l'ajout d'états actifs à une table des matières collante - voir la fin de l'article pour les instructions !

Contexte

Chez Flowrite, nous avons récemment commencé à écrire des éléments de contenu plus longs et voulions rendre nos blogs plus lisibles, en commençant par l'élément le plus basique - la table des matières.

Nous utilisons Webflow et nous l'adorons, mais il s'est avéré que la création de ToC de manière évolutive pour les éléments CMS n'était pas si simple. Toutes les solutions existantes reposent soit sur la définition manuelle des identifiants et des liens, soit sur l'utilisation de plugins tiers.

Nous voulions un système simple qui générerait automatiquement des TdC basés sur les titres de nos blogs. C'est ce que nous avons construit, et voici comment vous pouvez faire de même !

Comment ça fonctionne?

  1. Concevez vos éléments de table des matières. Faites-les comme vous le souhaitez - créez des états de survol, des animations, etc.
  2. Créez une div pour la table des matières avec un identifiant spécifique n'importe où sur votre page CMS.
  3. Insérez un morceau de code à la fin de la balise <body> sur votre page CMS.
  4. Les tables des matières sont générées automatiquement pour chaque élément CMS.

Création de votre table des matières dynamique

Concevez vos éléments de table des matières

Donnez à votre élément ToC une classe (nous utilisons "tocitem") et stylisez-le comme vous le souhaitez.

Ma recommandation est d'utiliser une page privée séparée pour concevoir ces éléments. De cette façon, vous pouvez facilement modifier la conception plus tard si nécessaire.

Nous avons adoré la simplicité des éléments de la table des matières de Notion, nous l'avons donc utilisée comme référence.


Si vous souhaitez inclure plusieurs types d'en-têtes, assurez-vous de leur attribuer une classe supplémentaire ("toc-h2", "toc-h3", etc.)

Créez l'élément pour votre table des matières

Ajoutez un div où vous voulez que votre table des matières soit et donnez-lui un identifiant "toc". Encore une fois, vous pouvez styliser cet élément comme vous le souhaitez.

Si vous souhaitez avoir votre table des matières dans votre élément de texte enrichi, consultez la fin de cet article pour plus de détails.

Enfin, donnez à l'élément Rich Text un identifiant "content". Cela sera nécessaire plus tard pour nous assurer que nous utilisons les titres corrects dans la table des matières.

Choisissez parmi deux options

Étant donné que les TdC seront générées automatiquement, nous devons indiquer les rubriques que nous souhaitons y inclure. Dans cet article, nous couvrons deux options :

  1. Génération de tables des matières basées sur un seul type d'en-tête statique, par exemple H2s.
  2. Génération de tables des matières basées sur plusieurs en-têtes de votre choix - par exemple, H2 et H3 pour un article de blog et H2 et H4 pour un autre.

Option 1 : TdC à en-tête unique

Dans cette option, les TdC seront toujours générées sur la base du même en-tête.

Insérez le code suivant à la fin de votre balise <body> sur vos pages de collection. Voir les commentaires à la fin de chaque ligne si vous voulez comprendre comment cela fonctionne.

  <script> document.getElementById("content").querySelectorAll("h2").forEach(function(heading, i) { // runs a function for all h2 elements inside your rich text element heading.setAttribute("id", "toc-" + i); // gives each h2 a unique id let str = heading.innerHTML; // adds section titles to slugs str = str.replace(/\s+/g, '-').replace(/[°&\/\\#,+()$~%.'":;*?<>{}]/g, "").toLowerCase(); // replaces spaces with hyphens, removes special characters and extra spaces from the headings, and applies lowercase in slugs heading.setAttribute("id", str); // gives each heading a unique id const item = document.createElement("a"); // creates an anchor element called "item" for each h2 item.innerHTML = heading.innerHTML // gives each item the text of the corresponding heading item.setAttribute("class", "tocitem"); // gives each item the correct class item.setAttribute("href", "#" + str); // gives each item the correct anchor link document.querySelector("#toc").appendChild(item); // places each item inside the Table of Contents div }); </script>
<script> document.getElementById("content").querySelectorAll("h2").forEach(function(heading, i) { // runs a function for all h2 elements inside your rich text element heading.setAttribute("id", "toc-" + i); // gives each h2 a unique id let str = heading.innerHTML; // adds section titles to slugs str = str.replace(/\s+/g, '-').replace(/[°&\/\\#,+()$~%.'":;*?<>{}]/g, "").toLowerCase(); // replaces spaces with hyphens, removes special characters and extra spaces from the headings, and applies lowercase in slugs heading.setAttribute("id", str); // gives each heading a unique id const item = document.createElement("a"); // creates an anchor element called "item" for each h2 item.innerHTML = heading.innerHTML // gives each item the text of the corresponding heading item.setAttribute("class", "tocitem"); // gives each item the correct class item.setAttribute("href", "#" + str); // gives each item the correct anchor link document.querySelector("#toc").appendChild(item); // places each item inside the Table of Contents div }); </script>

Publiez le site et vous êtes prêt à partir

Option 2 : Tables des matières multi-rubriques

Cette option vous permet de déterminer au cas par cas quelles rubriques seront utilisées pour générer les TdC.

Commençons par créer un nouveau champ de texte dans votre Collection. Nous l'appelons "TOC basé sur...". C'est ici que vous spécifiez les titres qui seront utilisés dans la table des matières de chaque élément du CMS. Séparez par une virgule et n'incluez aucun espace.


Maintenant, insérez le code suivant à la fin de votre balise <body> sur vos pages de collection. Voir les commentaires à la fin de chaque ligne si vous voulez comprendre comment cela fonctionne.

  <script> document.getElementById("content").querySelectorAll("[HEADINGS]").forEach(function(heading, i) { // runs a function for all headings inside your rich text element let str = heading.innerHTML; // adds section titles to slugs str = str.replace(/\s+/g, '-').replace(/[°&\/\\#,+()$~%.'":;*?<>{}]/g, "").toLowerCase(); // replaces spaces with hyphens, removes special characters and extra spaces from the headings, and applies lowercase in slugs heading.setAttribute("id", str); // gives each heading a unique id const item = document.createElement("a"); // creates an anchor element called "item" for each heading item.innerHTML = heading.innerHTML; // gives each item the text of the corresponding heading ("[HEADINGS]").split(",").forEach(function(x) { // runs a function for each item in your headings list if (heading.tagName.toLowerCase()==x) { item.classList.add("tocitem", "toc-" + x); // gives each item the correct class } }); item.setAttribute("href", "#" + str); // gives each item the correct anchor link document.querySelector("#toc").appendChild(item); // places each item inside the Table of Contents div }); </script>
<script> document.getElementById("content").querySelectorAll("[HEADINGS]").forEach(function(heading, i) { // runs a function for all headings inside your rich text element let str = heading.innerHTML; // adds section titles to slugs str = str.replace(/\s+/g, '-').replace(/[°&\/\\#,+()$~%.'":;*?<>{}]/g, "").toLowerCase(); // replaces spaces with hyphens, removes special characters and extra spaces from the headings, and applies lowercase in slugs heading.setAttribute("id", str); // gives each heading a unique id const item = document.createElement("a"); // creates an anchor element called "item" for each heading item.innerHTML = heading.innerHTML; // gives each item the text of the corresponding heading ("[HEADINGS]").split(",").forEach(function(x) { // runs a function for each item in your headings list if (heading.tagName.toLowerCase()==x) { item.classList.add("tocitem", "toc-" + x); // gives each item the correct class } }); item.setAttribute("href", "#" + str); // gives each item the correct anchor link document.querySelector("#toc").appendChild(item); // places each item inside the Table of Contents div }); </script>

Enfin, assurez-vous de remplacer [HEADINGS] par la valeur de votre champ CMS "ToC based on...".


Ça y est, vous êtes prêt à publier.

Personnalisation

États actifs dans une table des matières collante

Inspirés par le post de Chris Coyier sur CSS-Tricks, nous avons créé un moyen d'ajouter des états actifs aux éléments de table des matières visibles - une fonctionnalité intéressante pour les tables des matières collantes.

Tout d'abord, ajoutez "observer.observe(heading);" au début du code que vous avez implémenté plus tôt. Voir la capture d'écran pour l'emplacement correct.

Ensuite, ajoutez l'extrait de code suivant au-dessus de tout le code ToC existant :

  <script> const observer = new IntersectionObserver(entries => { entries.forEach(entry => { const id = entry.target.getAttribute("id"); if (entry.isIntersecting) { document.querySelectorAll(".active").forEach((z) => { z.classList.remove("active") }); document.querySelector(`a[href="#${id}"]`).classList.add("active"); } }); }, { rootMargin: '0px 0px -75% 0px' }); </script>
<script> const observer = new IntersectionObserver(entries => { entries.forEach(entry => { const id = entry.target.getAttribute("id"); if (entry.isIntersecting) { document.querySelectorAll(".active").forEach((z) => { z.classList.remove("active") }); document.querySelector(`a[href="#${id}"]`).classList.add("active"); } }); }, { rootMargin: '0px 0px -75% 0px' }); </script>

Le code ajoute une classe appelée "active" à un tocitem visible et supprime la classe lorsqu'un nouveau tocitem apparaît.

Enfin, assurez-vous de styliser la classe "active" en lui donnant une couleur de fond différente, par exemple.

Afficher les tables des matières uniquement sur certains éléments du CMS

Si vous ne souhaitez pas qu'une table des matières apparaisse sur certains de vos éléments CMS, procédez comme suit :

‍ 1. Laissez le champ "ToC based on.." vide.
Si vous utilisez l'option 1, ajoutez un commutateur dans votre collection CMS pour indiquer si la table des matières doit apparaître ou non.

2. Rendez la visibilité de votre div ToC conditionnelle en fonction du sélecteur à l'étape 1.

3. Créez une instruction if au début de votre extrait de code ToC pour éviter d'exécuter la fonction si le champ est vide. N'oubliez pas de fermer avec "}".

Table des matières à l'intérieur de votre élément de texte enrichi

Si vous souhaitez placer votre table des matières dans votre élément de texte enrichi, vous pouvez répliquer l'élément div de la table des matières avec un code d'intégration.

Créez un élément de code personnalisé dans votre texte enrichi et copiez-collez "<div id="toc" class="toc"></div>" dans le champ de code.


Si vous souhaitez avoir une table des matières en ligne sur certains éléments du CMS uniquement et une table des matières "normale" sur d'autres, vous devez effectuer quelques astuces supplémentaires :

1. Créez un sélecteur d'options avec une seule option : ".getElementById('content'). Sélectionnez-le sur les éléments CMS où vous souhaitez avoir une table des matières dans le texte enrichi.

2. Afficher la table des matières "normale" uniquement si le sélecteur n'est pas réglé.


3. Ajoutez la valeur du sélecteur à la partie suivante du code. Maintenant, si le sélecteur a été sélectionné, le code ajoutera les éléments ToC à l'intérieur de la bonne div ToC.

Éviter la superposition de la barre de navigation sur le défilement

Vous remarquerez peut-être que lorsque vous cliquez sur un lien ToC, la page défile jusqu'au bon titre mais est bloquée par votre barre de navigation.

Pour résoudre ce problème :

  1. Assurez-vous que la position de votre élément de navigation est définie sur "fixe"
  2. Changez la balise de l'élément de navigation en "Header"

C'est ça! Si vous rencontrez des problèmes en cours de route, n'hésitez pas à m'envoyer un e-mail ou à me contacter via Twitter / LinkedIn.

J'aimerais aussi savoir si vous finissez par utiliser ce système, alors s'il vous plaît contactez-moi si vous l'avez fait et partagez le message avec quelqu'un d'autre qui pourrait en bénéficier ️