Za pomocą Javascript tworzyć dynamiczną tablicę treści długich artykułów

Why a Dynamic Table of Contents Matters for Long-Form Content

Długie artykuły, tutorials, tutorials, and documentation speatures can impotent readers if vigation is limited to manual scrolling. A present 1; incorporation 1; FLT: 0; 3; FLT: updates; directic table of contents enters enter1; extra1; FLT: 1 metri3; (TOC) solves this thy provisingg a clickable outline that automatically highlights thee section thee reade is viewing. Thies improwises usability, reduces bounce rates, and make you content more accessiblessible tusers whinto.

For example, a technical guidee with 30 sections becomes much easier to digest when readers s see a side bar menu that tracks their progress. The same principle applices to single-page applications, API documentation, or even blog posts witch multiple sub-themes. By implementing a dynamic TOC, u give readers control over their reading experience while reducing thee contritiva load of searching forequiant parts.

Core Concepts Behind a Dynamic Table of Contents

Tu build a dynamic TOC, you need to understand three foundational pieces:

Te części work together to produce a TOC that feels nativie te te page, requires minimal l server-side logic, andworks across modern browsers.

Krok 1: Przygotowanie struktury HTML Your

Before any JavaScript runs, you need two things in your HTML:

Assign Unique ID to Headings

Every heading thatt should d appear in thee TOC (typically indic1; indic1; FLT: 2 indic3; indic3; FLT: 3 indic3; indic3;, or indic1; FLT: 4 indic3; endic3;) mutt have a uniquie 1; endic1; FLT: 5 indic3; endic3; endic3;. This is essential because thee TOC links use frament identifiers (e.g., endic1; enti1; FLT: 6 indicrl; enti3;) tso thee correcret position. Here 's amen example:

<h2 id="introduction">Introduction</h2>
<p>...</p>
<h2 id="setup">Setting Up the Environment</h2>
<p>...</p>
<h3 id="installing-dependencies">Installing Dependencies</h3>
<p>...</p>
<h2 id="implementation">Implementation</h2>
<p>...</p>

If you cannot t modify the HTML directly, you can generate Ids frem heading text using JavaScript (np., Xi1; Xion1; FLT: 8 Xion3; exiondion), but it 's cleaner to add them manually or with a static site generator.

Stworzenie kontenera for thee TOC

Place an empty element (typically a indic1; indic1; FLT: 9 indic3; indic3; or a indic1; indic1; FLT: 10 indic3; indic3;) where you want the TOC to appear. For example:

<nav id="table-of-contents" aria-label="Table of Contents"></nav>

Thee Booking 1; Xion1; FLT: 12 XI3; XI3; improwizuje accessibility by giving screen readers a descriptive name for the vigation region. Later you 'll populate this container with thee generated ligt.

Step 2: Generating thee TOC with JavaScript

Nowe we write thee JavaScript that scans the headings andbuilds thee list. The following snippet creates a flat ligt of indi.1; Ig1; FLT: 13 condition; Igl; headings. For a more advanced TOC that includes subheadings, you would nested lists, which we 'll cover later.

Basic Flat TOC Example

const tocContainer = document.getElementById('table-of-contents');
const headings = document.querySelectorAll('h2');

// Bail early if there's no container or no headings
if (!tocContainer || headings.length === 0) return;

const ul = document.createElement('ul');
ul.setAttribute('role', 'list'); // accessibility enhancement

headings.forEach((heading, index) => {
 // Ensure the heading has an id; if not, generate one
 if (!heading.id) {
 heading.id = 'section-' + index;
 }
 const li = document.createElement('li');
 const a = document.createElement('a');
 a.textContent = heading.textContent;
 a.href = '#' + heading.id;
 a.setAttribute('data-section', heading.id); // useful for active detection
 li.appendChild(a);
 ul.appendChild(li);
});

tocContainer.appendChild(ul);

Xi1; Xi1; FLT: 0 Xi3; Xi3; Key points: Xi1; Xi1; FLT: 1 Xi3; Xi3;

Handling Nested Headings (H2, H3, H4)

A more useful TOC reflects the document 's hierarchy. To create nested lists, track thee current present 1; Xi1; FLT: 19 context 3; Xi3; and insert present 1; Xi1; FLT: 20 context 3; Xi3; for its children. Here' s a simplified approach using a stack:

const tocContainer = document.getElementById('table-of-contents');
const headings = document.querySelectorAll('h2, h3, h4');
if (!tocContainer || headings.length === 0) return;

const root = document.createElement('ul');
const stack = [{ element: root, level: 2 }]; // level refers to heading level

headings.forEach((heading) => {
 const level = parseInt(heading.tagName.substring(1), 10); // 'H2' -> 2
 if (!heading.id) heading.id = 'section-' + Math.random().toString(36).substr(2, 9);

 const li = document.createElement('li');
 const a = document.createElement('a');
 a.textContent = heading.textContent;
 a.href = '#' + heading.id;
 li.appendChild(a);

 // Pop stack until we reach the parent level
 while (stack.length > 0 && stack[stack.length - 1].level >= level) {
 stack.pop();
 }
 const parent = stack[stack.length - 1].element;
 parent.appendChild(li);

 // If next heading is lower, we need a nested list
 const nextLevel = headings.item(Array.from(headings).indexOf(heading) + 1);
 if (nextLevel && parseInt(nextLevel.tagName.substring(1), 10) > level) {
 const nestedUl = document.createElement('ul');
 li.appendChild(nestedUl);
 stack.push({ element: nestedUl, level: level });
 }
});

tocContainer.appendChild(root);

Algorytm Thii ensures that each subheading appears indented under its parent. For production, you may want to refripe the logic to avoid deep stacks andd handle edge cases (np., missing heading levels).

Step 3: Highlighting thee Activite Section on Scroll

Te highlight mechanic lets readers which part of thee article they 're currently reading. The idea is to loop through gh all headings, find the ne the one that is closesto to thee top of thee viewport (with some offset), and apprey an eng1; FLT: 22 contribution 3; class to the corresponding TOC link.

Efficient Scroll Listener

const tocLinks = document.querySelectorAll('#table-of-contents a');
const sections = Array.from(headings).map(h => ({
 id: h.id,
 top: h.offsetTop
}));

function updateActiveLink() {
 const scrollY = window.pageYOffset || document.documentElement.scrollTop;
 let currentId = '';

 // Iterate backwards for better performance
 for (let i = sections.length - 1; i >= 0; i--) {
 if (scrollY >= sections[i].top - 150) {
 currentId = sections[i].id;
 break;
 }
 }

 tocLinks.forEach(link => {
 link.classList.remove('active');
 if (link.getAttribute('href') === '#' + currentId) {
 link.classList.add('active');
 }
 });
}

// Throttle scroll events for performance
let ticking = false;
window.addEventListener('scroll', () => {
 if (!ticking) {
 window.requestAnimationFrame(() => {
 updateActiveLink();
 ticking = false;
 });
 ticking = true;
 }
});

Xiv1; Xiv1; FLT: 0 Xiv3; Xiv3; Optimizations: Xiv1; Xiv1; FLT: 1 Xiv3; Xiv3;

Step 4: Adding Smooth Scrolling andd Accessibility

Smooth scrolling makes jumping between section pleasant. You can accessé this with CSS, but also via JavaScript for finer control.

// Add click handler on the TOC container to use smooth scrolling
tocContainer.addEventListener('click', (e) => {
 const link = e.target.closest('a');
 if (link && link.getAttribute('href').startsWith('#')) {
 e.preventDefault();
 const targetId = link.getAttribute('href').substring(1);
 const target = document.getElementById(targetId);
 if (target) {
 target.scrollIntoView({ behavior: 'smooth' });
 // Update the URL hash without causing a scroll jump
 history.pushState(null, '', '#' + targetId);
 }
 }
});

Ulepszenia w zakresie dostępności:

Step 5: Styling thee Dynamic TOC

While styling is nott part of thee JavaScript logic, a well-styld TOC contents usability. Below is a minimal CSS example that adds a sticky positioning for sidebar usage:

#table-of-contents {
 position: sticky;
 top: 2rem;
 max-height: calc(100vh - 4rem);
 overflow-y: auto;
 border-left: 2px solid #ccc;
 padding-left: 1rem;
 font-size: 0.9rem;
}
#table-of-contents ul {
 list-style: none;
 padding: 0;
}
#table-of-contents li {
 margin-bottom: 0.25rem;
}
#table-of-contents a {
 color: #333;
 text-decoration: none;
}
#table-of-contents a.active {
 font-weight: bold;
 color: #007bff;
}
#table-of-contents a[aria-current="location"] {
 border-left: 2px solid #007bff;
 margin-left: -1rem;
 padding-left: calc(1rem - 2px);
}

For a responsive design, consider hiding the TOC on small screens andd adding a toggle button, or fallsing it into a select-dropdown menu.

Zaawansowane udoskonalenia

1. Debouncing Resize Events

If the viewport hight changes (np., on mobile orientation change), thee indic1; indic1; fLT: 36 contribution 3; indic3; values of headings may shift. Recalculate the indic1; indic1; fLT: 37 contribution 3; indic3; array on a debounced resize:

let sections = [];
function recalcSections() {
 sections = Array.from(headings).map(h => ({
 id: h.id,
 top: h.offsetTop
 }));
}
let resizeTimer;
window.addEventListener('resize', () => {
 clearTimeout(resizeTimer);
 resizeTimer = setTimeout(recalcSections, 250);
});

2. Intersection Observer for Scroll-Based Highlighting

An entertivie to scroll listrols is the incorporate 1; Xi1; FLT: 39 contribution 3; Xion3; API. It 's more performant and easyr to manage. Example:

const observer = new IntersectionObserver((entries) => {
 entries.forEach(entry => {
 if (entry.isIntersecting) {
 const id = entry.target.id;
 tocLinks.forEach(link => {
 link.classList.remove('active');
 if (link.getAttribute('href') === '#' + id) {
 link.classList.add('active');
 }
 });
 }
 });
}, { rootMargin: '-80px 0px -70% 0px' });

headings.forEach(h => observer.observe(h));

This fires only when a heading enters or leaves a computed zone, reducing overhead. The mea1; Xi1; FLT: 41 measure3; Xi3; defines when a section is considered measurement quetle; active. Xenquote;

3. Lazy Loading or Dynamic Content

If your article loads sections dynamically (np., via AJAX), you mutt regenerate thee TOC after new content appenars. One way is to use a MutationObserver on thee article container and call thee TOC generation functionion again. However, be careful not t a MutationObserver othe article container and call thee TOC generation function again. However, be careful not to duplicate entries.

Rozważanie wydajności

Integrating with a Static Site Generator (SSG) or CMS

If you use a static site generator, you can pre-render thee TOC using built-in factores (np., Eleventy 's collections, Hugo' s betig.1; FLT: 47 haird3; Equid3;). However, then dynamic scroll-highlighting still requires client client-side JavaScript. The facigage of a server-side TOC is that it 's vavaiable difficately, even before JavaScript runs, aiding SEO and accessibility.

For a CMS like WordPress or Directus, you can use te same JavaScript approvach while storing heading Ids in then content. Directus, for example, supports custem interfaces that generate Ids automatically. You could create a hook that runs on content save to add Ids to headings, then reliy on thee front-end JavaScript to build thee TOC.

External resources for deeper undering:

Testing andDebugging

  1. Verify that every heading has a unique environ1; Xi1; FLT: 48 Xion3; Xion3;. Duplicate IDS cause the browser to scroll to the first match only.
  2. Sprawdź, czy TOC i Both Light i Dark themes to ensure link contrass meets WCAG AA standards.
  3. Test wigh keyboard navigation: pressing indition 1; indi1; FLT: 0 indis3; endis3; Tab indis1; indis1; FLT: 1 indis3; endis3; should move between TOC links, and indis1; endis1; FLT: 2 indis3; enter indis1; enter indis1; endis1; FLT: 3 indis3; indis3; should scroll to thee section.
  4. Usie thee browser 's DevTools Performance tab to ensure no jank during scroll.
  5. If the article contains images or iframes, thee ideas 1; Ig1; FLT: 49 contains 3; Ig3; may change after those elements load. Call a recalculation functionion on engine engine engine engine 1; Iglome1; FLT: 50 contained 3; Iglome3; Or after all imagies are loaded (e.g., Igload1; Igload1; FLT: 51 contaillumation function onytion onn on ongyons1; Igload3;).

Potential Pitfalls andHow to Avoid Them

Konkluzja

Building a dynamic table of contents with JavaScript transformats a long, linear article into an interacte, scannable resource. Byasigning Ids to headings, generating a nested litt of links, and highlighting thee contect section based on concert position, you give readers a clear roadmap. The code examples in this article provide a solid four endation, but you can esily expile them - add smooth scrolling, use IntersectionObserver for ter performance, sole incitate, en exprecident, build procres.

Wdrożenie tego podejścia nie jest zgodne z testem your stack: pure JavaScript for sites sites sites, or a hybrid witch SSG for initiatival TOC structure plus client-side highlighting. Regardless of the methode, a dynamic TOC is a small investment that yields signitant usability gains.