Table of Contents
Markdown Parsing and HTML Generation
Generating the Table of Contents
Implementing the Intersection Observer
Smooth Scrolling and Link Click Handling
Animating the Table of Contents with Vue.js
Home Web Front-end CSS Tutorial Parsing Markdown into an Automated Table of Contents

Parsing Markdown into an Automated Table of Contents

Apr 01, 2025 am 04:21 AM

This tutorial demonstrates creating an interactive table of contents for long-form Markdown content. It leverages several techniques to enhance user experience, including Markdown parsing, Intersection Observer for active section detection, smooth scrolling animation, and Vue.js transitions for animated list updates.

Parsing Markdown into an Automated Table of Contents

A table of contents provides quick navigation within a page. This is particularly useful for lengthy articles, offering users a clear overview and easy access to different sections. This guide details how to transform Markdown text into an HTML table of contents with linked headings, highlighting the currently viewed section. We'll also implement smooth scrolling and animated list updates using Vue.js.

Markdown Parsing and HTML Generation

Web content often uses Markdown. We'll employ the marked library (though alternatives are suitable) to parse Markdown into HTML. The Markdown content is fetched from a GitHub Gist. The marked function converts the fetched Markdown text to HTML, which is then injected into the DOM.

async function fetchAndParseMarkdown() {
  const url = 'https://gist.githubusercontent.com/lisilinhart/e9dcf5298adff7c2c2a4da9ce2a3db3f/raw/2f1a0d47eba64756c22460b5d2919d45d8118d42/red_panda.md';
  const response = await fetch(url);
  const data = await response.text();
  const htmlFromMarkdown = marked(data, { sanitize: true });
  return htmlFromMarkdown;
}
Copy after login

The parsed HTML is inserted into a designated DOM element using innerHTML.

async function init() {
  const $main = document.querySelector('#app');
  const htmlContent = await fetchAndParseMarkdown();
  $main.innerHTML = htmlContent;
}

init();
Copy after login

Generating the Table of Contents

The next step is to create the table of contents from the headings (H1 and H2) within the parsed HTML. We use querySelectorAll to select these headings, extract their text content, depth (H1 or H2), and ID attributes.

function generateLinkMarkup($contentElement) {
  const headings = [...$contentElement.querySelectorAll('h1, h2')];
  const parsedHeadings = headings.map(heading => ({
    title: heading.innerText,
    depth: heading.nodeName.replace(/\D/g, ''),
    id: heading.getAttribute('id')
  }));
  // ... (rest of the function to generate HTML markup)
}
Copy after login

This generates an array of objects, each representing a heading. This data is then used to create the HTML for the table of contents using ES6 template literals, adding indentation for subheadings (H2). The resulting HTML is added to the aside element.

Implementing the Intersection Observer

To dynamically highlight the active section in the table of contents, we use the Intersection Observer API. This API allows us to monitor when an element (in this case, a heading) intersects with the viewport.

function createObserver($links) {
  const options = { rootMargin: "0px 0px -200px 0px", threshold: 1 };
  const callback = (entries, observer) => handleObserver(entries, observer, $links); // Callback handles updates
  return new IntersectionObserver(callback, options);
}

function handleObserver(entries, observer, $links) {
  entries.forEach(entry => {
    if (entry.isIntersecting && entry.intersectionRatio >= 1) {
      updateLinks(`#${entry.target.getAttribute('id')}`, $links);
    }
  });
}

function updateLinks(visibleId, $links) {
  $links.forEach(link => {
    link.classList.remove('is-active');
    if (link.getAttribute('href') === visibleId) link.classList.add('is-active');
  });
}
Copy after login

The handleObserver function checks for intersection and calls updateLinks to add/remove the is-active class from the corresponding link in the table of contents.

Clicking a link in the table of contents should smoothly scroll to the corresponding section. We add an event listener to each link, preventing default behavior and using window.scroll with the smooth behavior option (unless the user prefers reduced motion).

const motionQuery = window.matchMedia('(prefers-reduced-motion)');

$links.forEach(link => {
  link.addEventListener("click", evt => handleLinkClick(evt, $headings, motionQuery));
});

function handleLinkClick(evt, $headings, motionQuery) {
  evt.preventDefault();
  const id = evt.target.getAttribute("href").replace('#', '');
  const section = $headings.find(heading => heading.getAttribute('id') === id);
  section.focus();
  window.scroll({
    behavior: motionQuery.matches ? 'instant' : 'smooth',
    top: section.offsetTop - 20
  });
}
Copy after login

Animating the Table of Contents with Vue.js

Finally, Vue.js's <transition-group></transition-group> component is used to animate the addition and removal of list items in the table of contents. This provides smooth transitions when sections become active or inactive. Appropriate CSS transitions are defined to handle the animation. (The code for Vue integration and CSS is omitted for brevity but forms the final part of the implementation.) A debounce function is recommended to prevent animation issues with rapid scrolling.

This complete approach delivers a highly interactive and user-friendly table of contents for long-form Markdown content.

The above is the detailed content of Parsing Markdown into an Automated Table of Contents. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Vue 3 Vue 3 Apr 02, 2025 pm 06:32 PM

It&#039;s out! Congrats to the Vue team for getting it done, I know it was a massive effort and a long time coming. All new docs, as well.

Can you get valid CSS property values from the browser? Can you get valid CSS property values from the browser? Apr 02, 2025 pm 06:17 PM

I had someone write in with this very legit question. Lea just blogged about how you can get valid CSS properties themselves from the browser. That&#039;s like this.

Stacked Cards with Sticky Positioning and a Dash of Sass Stacked Cards with Sticky Positioning and a Dash of Sass Apr 03, 2025 am 10:30 AM

The other day, I spotted this particularly lovely bit from Corey Ginnivan’s website where a collection of cards stack on top of one another as you scroll.

A bit on ci/cd A bit on ci/cd Apr 02, 2025 pm 06:21 PM

I&#039;d say "website" fits better than "mobile app" but I like this framing from Max Lynch:

Using Markdown and Localization in the WordPress Block Editor Using Markdown and Localization in the WordPress Block Editor Apr 02, 2025 am 04:27 AM

If we need to show documentation to the user directly in the WordPress editor, what is the best way to do it?

Comparing Browsers for Responsive Design Comparing Browsers for Responsive Design Apr 02, 2025 pm 06:25 PM

There are a number of these desktop apps where the goal is showing your site at different dimensions all at the same time. So you can, for example, be writing

Why are the purple slashed areas in the Flex layout mistakenly considered 'overflow space'? Why are the purple slashed areas in the Flex layout mistakenly considered 'overflow space'? Apr 05, 2025 pm 05:51 PM

Questions about purple slash areas in Flex layouts When using Flex layouts, you may encounter some confusing phenomena, such as in the developer tools (d...

How to select a child element with the first class name item through CSS? How to select a child element with the first class name item through CSS? Apr 05, 2025 pm 11:24 PM

When the number of elements is not fixed, how to select the first child element of the specified class name through CSS. When processing HTML structure, you often encounter different elements...

See all articles