Webflow CMS의 동적 목차 – 가장 쉬운 방법

게시 됨: 2022-03-22
최근에 끈적한 목차에 활성 상태를 추가할 수 있게 되었습니다. 지침은 기사 끝을 참조하세요!

배경

Flowrite에서 우리는 최근 더 긴 콘텐츠를 쓰기 시작했고 가장 기본적인 요소인 목차부터 시작하여 블로그를 더 읽기 쉽게 만들고 싶었습니다.

우리는 Webflow를 사용하고 있고 그것을 절대적으로 좋아하지만 CMS 항목에 대해 확장 가능한 ToC를 만드는 것이 그렇게 간단하지 않다는 것이 밝혀졌습니다. 기존의 모든 솔루션은 ID와 링크를 수동으로 설정하거나 타사 플러그인을 사용합니다.

우리는 블로그의 제목을 기반으로 목차를 자동으로 생성하는 간단한 시스템을 원했습니다. 그것이 우리가 구축한 것이며, 여기 당신도 똑같이 할 수 있는 방법이 있습니다!

어떻게 작동합니까?

  1. 목차 항목을 디자인하십시오. 원하는 대로 만들 수 있습니다. 호버 상태, 애니메이션 등을 만듭니다.
  2. CMS 페이지의 아무 곳에서나 특정 ID로 ToC용 div를 만듭니다.
  3. CMS 페이지의 <body> 태그 끝에 코드를 삽입합니다.
  4. ToC는 각 CMS 항목에 대해 자동으로 생성됩니다.

동적 목차 만들기

목차 항목 디자인

ToC 항목에 클래스를 지정하고(우리는 "tocitem"을 사용함) 원하는 방식으로 스타일을 지정합니다.

내 권장 사항은 이러한 요소를 디자인하기 위해 별도의 개인 페이지를 사용하는 것입니다. 이렇게 하면 나중에 필요한 경우 디자인을 쉽게 수정할 수 있습니다.

우리는 Notion의 목차 요소의 단순함이 마음에 들었으므로 이를 벤치마크로 사용했습니다.


하나 이상의 제목 유형을 포함하려면 추가 클래스("toc-h2", "toc-h3" 등)를 지정해야 합니다.

목차에 대한 요소 만들기

ToC를 원하는 곳에 div를 추가하고 ID를 "toc"로 지정합니다. 다시 말하지만 이 요소의 스타일을 원하는 대로 지정할 수 있습니다.

서식 있는 텍스트 요소 안에 목차를 포함하려면 이 문서의 끝 부분에서 자세한 내용을 참조하세요.

마지막으로 서식 있는 텍스트 요소에 id "content"를 지정합니다. 이것은 나중에 목차에서 올바른 제목을 사용하는지 확인하는 데 필요합니다.

두 가지 옵션 중에서 선택

ToC는 자동으로 생성되므로 포함할 제목을 지정해야 합니다. 이 게시물에서는 두 가지 옵션을 다룹니다.

  1. 단일 정적 제목 유형(예: 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를 생성하는 데 사용할 제목을 사례별로 결정할 수 있습니다.

컬렉션에 새 텍스트 필드를 만드는 것으로 시작해 보겠습니다. 우리는 그것을 "...에 기반한 목차"라고 부릅니다. 여기에서 각 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 based on..." CMS 필드 값으로 대체해야 합니다.


이제 게시할 준비가 되었습니다.

커스터마이징

고정 목차의 활성 상태

CSS-Tricks에 대한 Chris Coyier의 게시물에서 영감을 받아 우리는 가시적인 ToC 항목에 활성 상태를 추가하는 방법을 구축했습니다. 이는 끈적한 ToC를 위한 훌륭한 기능입니다.

먼저 "observer.observer(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"라는 클래스를 보이는 토사이트에 추가하고 새 토사이트가 나타나면 해당 클래스를 제거합니다.

마지막으로, 예를 들어 다른 배경색을 지정하여 "활성" 클래스의 스타일을 지정해야 합니다.

일부 CMS 항목에만 목차 표시

일부 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') 옵션 하나만 있는 옵션 선택기를 만듭니다 . 서식 있는 텍스트 안에 목차를 포함하려는 CMS 항목에서 선택합니다.

2. 선택기가 설정되지 않은 경우에만 "일반" ToC를 표시합니다.


3. 선택기의 값을 코드의 다음 부분에 추가합니다. 이제 선택기가 선택되면 코드가 올바른 ToC div 내부에 ToC 항목을 추가합니다.

스크롤 시 navbar 오버레이 피하기

목차 링크를 클릭하면 페이지가 올바른 제목으로 스크롤되지만 탐색 표시줄에 의해 차단되는 것을 알 수 있습니다.

이 문제를 해결하려면:

  1. 탐색 요소의 위치가 "고정"으로 설정되어 있는지 확인하십시오.
  2. 탐색 요소의 태그를 "헤더"로 변경

그게 다야! 도중에 문제가 발생하면 주저하지 말고 저에게 이메일을 보내거나 Twitter/LinkedIn을 통해 연락하십시오.

나는 또한 당신이 이 시스템을 사용하게 되는지 알고 싶습니다. 만약 당신이 이 시스템을 사용했다면 저에게 핑을 보내주시고 이 시스템의 혜택을 받을 수 있는 다른 사람과 게시물을 공유하십시오 ️