Webflow CMS 上的动态目录 - 最简单的方法

已发表: 2022-03-22
我们最近可以将活动状态添加到粘性目录中 - 请参阅文章末尾的说明!

背景

在 Flowrite,我们最近开始编写更长的内容片段,并希望从最基本的元素——目录开始,让我们的博客更具可读性。

我们正在使用 Webflow 并且非常喜欢它,但事实证明,为 CMS 项目创建可扩展的 ToC 并不是那么简单。 所有现有的解决方案都依赖于手动设置 id 和链接或使用 3rd-party 插件。

我们想要一个简单的系统,可以根据我们博客中的标题自动生成 ToC。 这就是我们构建的,这就是你如何做同样的事情!

这个怎么运作?

  1. 设计您的 ToC 项目。 以任何你想要的方式制作它们——创建悬停状态、动画等。
  2. 在 CMS 页面上的任何位置为 ToC 创建一个具有特定 ID 的 div。
  3. 在您的 CMS 页面上的 <body> 标记末尾插入一段代码。
  4. 为每个 CMS 项目自动生成 ToC。

创建动态目录

设计您的 ToC 项目

给你的 ToC 项目一个类(我们使用“tocitem”)并以你想要的任何方式设置它。

我的建议是使用单独的私有页面来设计这些元素。 这样,您以后可以在需要时轻松修改设计。

我们喜欢 Notion 目录元素的简单性,因此我们将其用作基准。


如果您想包含不止一种类型的标题,请确保给它们一个额外的类(“toc-h2”、“toc-h3”等)

为您的 ToC 创建元素

在您希望 ToC 所在的任何位置添加一个 div,并为其指定一个 id “toc”。 同样,您可以根据需要为该元素设置任何样式。

如果您希望将 ToC 包含在富文本元素中,请参阅本文末尾以获取更多详细信息。

最后,给 Rich Text 元素一个 id “content”。 稍后将需要这样做,以确保我们在 ToC 中使用正确的标题。

从两个选项中选择

由于 ToC 将自动生成,因此我们需要指明要包含在其中的标题。 在这篇文章中,我们介绍了两个选项:

  1. 仅基于单个静态标题类型生成 ToC,例如 H2s。
  2. 根据您选择的多个标题生成 ToC——例如,一篇博文的 H2 和 H3 以及另一篇博文的 H2 和 H4。

选项 1:单标题 ToC

在此选项中,将始终基于相同的标题生成 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 项目的 ToC 中使用的标题。 用逗号分隔,不要包含任何空格。


现在,在集合页面上的 <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 based on...”CMS 字段的值。


就是这样,您已准备好发布。

定制

粘性目录中的活动状态

受 Chris Coyier 在 CSS-Tricks 上的帖子的启发,我们构建了一种将活动状态添加到可见 ToC 项目的方法——这是粘性 ToC 的一个很好的功能。

首先,添加“observer.observe(heading);” 在您之前实现的代码的开头。 请参阅屏幕截图以了解正确的位置。

然后,在所有现有 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>

该代码将一个名为“active”的类添加到可见的 tocitem 并在出现新的 tocitem 时删除该类。

最后,确保通过赋予不同的背景颜色来设置“活动”类的样式。

仅在某些 CMS 项目上显示 ToC

如果您不希望 ToC 出现在某些 CMS 项目上,请完成以下步骤:

‍ 1. 将“ToC based on..”字段留空。
如果您使用选项 1,请在 CMS 集合中添加一个开关,以指示是否应显示 ToC。

2. 根据步骤 1 中的选择器使 ToC div 的可见性成为条件

3. 在 ToC 代码片段的开头创建 if 语句,以避免在字段为空时运行函数。 记得用“}”结束。

富文本元素中的 ToC

如果要将 ToC 放在富文本元素中,可以使用嵌入代码复制 ToC div 元素。

在您的富文本中创建一个自定义代码元素,然后在代码字段中复制并粘贴“<div id="toc" class="toc"></div>”。


如果您只想在某些 CMS 项目上使用内联 ToC,而在其他项目上使用“正常”ToC,则需要做一些额外的技巧:

1. 创建一个只有一个选项的选项选择器:“.getElementById('content')。在您希望在富文本中包含 ToC 的那些 CMS 项目上选择它。

2. 仅当未设置选择器时才显示“正常”ToC。


3.将选择器的值添加到代码的以下部分。 现在,如果选择器已被选中,代码会将 ToC 项附加到正确的 ToC div 中。

避免滚动时导航栏覆盖

您可能会注意到,单击 ToC 链接时,页面会向下滚动到正确的标题,但会被导航栏挡住。

要解决这个问题:

  1. 确保导航元素的位置设置为“固定”
  2. 将导航元素的标签更改为“标题”

而已! 如果您在此过程中遇到任何问题,请随时给我发送电子邮件或通过 Twitter / LinkedIn 联系。

我也很想知道你是否最终使用了这个系统,所以如果你这样做了,请联系我,并与可能从中受益的其他人分享帖子️