Parsing Markdown into an Automated Table of Contents
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.
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; }
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();
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) }
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'); }); }
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.
Smooth Scrolling and Link Click Handling
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 }); }
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!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

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

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

It'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.

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's like this.

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.

I'd say "website" fits better than "mobile app" but I like this framing from Max Lynch:

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

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

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

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