Динамическое оглавление на Webflow CMS — самый простой способ

Опубликовано: 2022-03-22
Недавно мы сделали возможным добавление активных состояний в прикрепленное оглавление — смотрите инструкции в конце статьи!

Задний план

Во Flowrite мы недавно начали писать более длинные статьи и хотели сделать наши блоги более читабельными, начиная с самого основного элемента — оглавления.

Мы используем Webflow, и нам это очень нравится, но оказалось, что создание масштабируемого ToC для элементов CMS не так просто. Все существующие решения полагаются либо на настройку идентификаторов и ссылок вручную, либо на использование сторонних плагинов.

Нам нужна была простая система, которая автоматически генерировала бы ToC на основе заголовков в наших блогах. Это то, что мы построили, и вот как вы можете сделать то же самое!

Как это работает?

  1. Создайте свои элементы ToC. Делайте их как хотите — создавайте состояния наведения, анимацию и т. д.
  2. Создайте div для ToC с определенным идентификатором в любом месте на странице вашей CMS.
  3. Вставьте фрагмент кода в конце тега <body> на странице вашей CMS.
  4. ToC автоматически генерируются для каждого элемента CMS.

Создание динамического оглавления

Создайте свои элементы ToC

Присвойте своему элементу ToC класс (мы используем «tocitem») и настройте его так, как хотите.

Я рекомендую использовать отдельную приватную страницу для разработки этих элементов. Таким образом, вы можете легко изменить дизайн позже, если это необходимо.

Нам понравилась простота элементов оглавления Notion, поэтому мы использовали ее в качестве эталона.


Если вы хотите включить более одного типа заголовков, обязательно дайте им дополнительный класс ("toc-h2", "toc-h3" и т. д.).

Создайте элемент для вашего ToC

Добавьте div туда, где вы хотите, чтобы ваш ToC был, и присвойте ему идентификатор «toc». Опять же, вы можете стилизовать этот элемент как хотите.

Если вы хотите, чтобы оглавление было внутри элемента форматированного текста, см. подробности в конце этой статьи.

Наконец, присвойте элементу Rich Text идентификатор «content». Это понадобится позже, чтобы убедиться, что мы используем правильные заголовки в ToC.

Выберите один из двух вариантов

Поскольку оглавления будут генерироваться автоматически, нам нужно указать, какие заголовки мы хотим включить в них. В этом посте мы рассмотрим два варианта:

  1. Генерация ToC на основе только одного статического типа заголовка, например H2.
  2. Создание ToC на основе нескольких заголовков по вашему выбору — например, H2 и H3 для одного сообщения в блоге и H2 и H4 для другого.

Вариант 1: ToC с одним заголовком

В этом варианте оглавления всегда будут создаваться на основе одного и того же заголовка.

Вставьте следующий код в конце тега <body> на страницах коллекции. Смотрите комментарии в конце каждой строки, если хотите понять, как это работает.

  <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>

Опубликуйте сайт, и все готово

Вариант 2: Содержание с несколькими заголовками

Эта опция дает вам возможность определить в каждом конкретном случае, какие заголовки будут использоваться для создания ToC.

Давайте начнем с создания нового текстового поля в вашей коллекции. Мы называем это «TOC на основе…». Здесь вы указываете заголовки, которые будут использоваться в ToC каждого элемента CMS. Разделяйте запятой и не включайте пробелы.


Теперь вставьте следующий код в конец тега <body> на страницах коллекции. Смотрите комментарии в конце каждой строки, если хотите понять, как это работает.

  <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>

Наконец, не забудьте заменить [HEADINGS] значением поля «ToC на основе…» CMS.


Вот и все, вы готовы к публикации.

Настройка

Активные состояния в прикрепленном оглавлении

Вдохновленные сообщением Криса Койера на CSS-Tricks, мы создали способ добавления активных состояний к видимым элементам ToC — отличная функция для закрепленных ToC.

Во-первых, добавьте «observer.observe (заголовок);» в начале кода, который вы реализовали ранее. Смотрите скриншот для правильного местоположения.

Затем добавьте следующий фрагмент кода поверх всего существующего кода ToC:

  <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>

Код добавляет класс с именем «активный» к видимому tocitem и удаляет класс, когда появляется новый tocitem.

Наконец, не забудьте стилизовать «активный» класс, например, задав ему другой цвет фона.

Отображать ToC только для некоторых элементов CMS

Если вы не хотите, чтобы оглавление отображалось в некоторых элементах вашей CMS, выполните следующие действия:

‍ 1. Оставьте поле «ToC на основе..» пустым.
Если вы используете вариант 1, добавьте переключатель в свою коллекцию CMS, чтобы указать, должен ли ToC отображаться или нет.

2. Сделайте видимость вашего блока ToC условной на основе селектора на шаге 1.

3. Создайте оператор if в начале фрагмента кода ToC, чтобы избежать запуска функции, если поле пусто. Не забудьте закрыть с помощью "}".

ToC внутри вашего элемента Rich Text

Если вы хотите поместить оглавление внутри элемента Rich Text, вы можете реплицировать элемент div оглавления с кодом для встраивания.

Создайте пользовательский элемент кода внутри форматированного текста и скопируйте и вставьте "<div id="toc" class="toc"></div>" в поле кода.


Если вы хотите иметь встроенный ToC только для некоторых элементов CMS и «обычный» ToC для других, вам нужно сделать несколько дополнительных трюков:

1. Создайте селектор параметров только с одним параметром: ".getElementById('content'). Выберите его для тех элементов CMS, для которых вы хотите иметь оглавление внутри форматированного текста.

2. Отображать «нормальный» ToC, только если селектор не установлен.


3. Добавьте значение селектора в следующую часть кода. Теперь, если селектор был выбран, код добавит элементы ToC внутри правильного div ToC.

Избегайте наложения панели навигации при прокрутке

Вы могли заметить, что при нажатии на ссылку ToC страница прокручивается до правильного заголовка, но блокируется вашей панелью навигации.

Чтобы исправить это:

  1. Убедитесь, что положение вашего навигационного элемента установлено на «фиксированное».
  2. Измените тег элемента навигации на «Заголовок».

Вот и все! Если у вас возникнут какие-либо проблемы по пути, не стесняйтесь, присылайте мне электронное письмо или обращайтесь через Twitter / LinkedIn.

Я также хотел бы знать, используете ли вы эту систему в конечном итоге, поэтому, пожалуйста, отправьте мне сообщение, если вы это сделали, и поделитесь публикацией с кем-то еще, кому это может быть полезно ️