Table of Contents
Waarom een dynamische inhoudsopgave van belang is voor lange-vorm inhoud
Lange artikelen, tutorials en documentatiepagina's kunnen lezers overweldigen als de navigatie beperkt is tot handmatig scrollen. Een dynamische inhoudsopgave (TOC) lost dit op door een klikbare omtrek te geven die automatisch de sectie die de lezer bekijkt markeert. Dit verbetert de bruikbaarheid, vermindert de bounce rates en maakt uw inhoud toegankelijker voor gebruikers die snel tussen onderwerpen willen springen. In tegenstelling tot een statische TOC die met de hand wordt geschreven, wordt een JavaScript-gegenereerde TOC bijgewerkt wanneer nieuwe secties worden toegevoegd, houdt links sync met kop-ID's, en reageert op scrollgedrag in real time.
Een technische handleiding met 30 secties wordt bijvoorbeeld veel gemakkelijker te verwerken wanneer lezers een zijbalkmenu zien dat hun voortgang volgt. Hetzelfde principe geldt voor single-page toepassingen, API documentatie, of zelfs blog berichten met meerdere subthema's. Door een dynamische TOC te implementeren, geeft u lezers controle over hun leeservaring en vermindert u de cognitieve belasting van het zoeken naar relevante onderdelen.
Kernbegrippen achter een dynamische inhoudstabel
Om een dynamische TOC te bouwen, moet je drie basisstukken begrijpen:
- Semantische HTML structuur . .Elke sectie kop moet een unieke eigenschap hebben zodat JavaScript het kan richten.
- DOM doorlopende en manipulatie . .Je script scant rubrieken, maakt een geneste lijst van links, en voegt die lijst toe aan een containerelement.
- Scroll event handling . . een efficiënte luisteraar controleert welke koers momenteel zichtbaar is en voegt een klasse toe aan de bijbehorende TOC-link.
Deze stukken werken samen om een TOC te produceren die zich inheems voelt aan de pagina, een minimale logica aan de serverzijde vereist en werkt in moderne browsers.
Stap 1: Het voorbereiden van uw HTML-structuur
Voordat een JavaScript draait, heb je twee dingen nodig in je HTML:
Unieke ID's toewijzen aan kopstukken
Elke kop die in de TOC moet verschijnen (meestal , of ) moet een unieke hebben. Dit is essentieel omdat de TOC-links fragmentidentificaties gebruiken (bijv. ) om naar de juiste positie te scrollen. Hier een voorbeeld:
<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>
Als u de HTML niet direct kunt wijzigen, kunt u ID's genereren uit koptekst met behulp van JavaScript (bijv., functie), maar het is schoner om ze handmatig of met een statische site generator toe te voegen.
Een container aanmaken voor de TOC
Plaats een leeg element (doorgaans een of een ) waar u wilt dat de TOC verschijnt. Bijvoorbeeld:
<nav id="table-of-contents" aria-label="Table of Contents"></nav>
De verbetert de bereikbaarheid door schermlezers een beschrijvende naam te geven voor de navigatieregio. Later vul je deze container met de gegenereerde lijst.
Stap 2: Genereren van de TOC met JavaScript
Nu schrijven we het JavaScript dat de kopstukken scant en de lijst maakt. Het volgende knipsel maakt een platte lijst van koppen. Voor een meer geavanceerde TOC die subrubrieken bevat, zou je geneste lijsten nodig hebben, die we later behandelen.
Basis Flat TOC-voorbeeld
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);
Kernpunten:
- De methode geeft een statische terug; het werkt voor pagina's waar de rubrieken niet dynamisch veranderen.
- Als een kop een ontbreekt, dan wordt er automatisch een gegenereerd met behulp van de index. Dit voorkomt verbroken links.
- We voegen een attribuut toe aan elke link voor een makkelijkere selectie later.
Behandelen van genest hoofden (H2, H3, H4)
Een meer nuttige TOC weerspiegelt de documenthiërarchie. Om geneste lijsten te maken, volgt u de huidige en plaatst u voor haar kinderen. Hier vindt u een vereenvoudigde aanpak met behulp van een 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);
Dit algoritme zorgt ervoor dat elke onderverdeling onder de oorspronkelijke onderverdeling verschijnt. Voor de productie kunt u de logica verfijnen om diepe stapels en kant-en-klare gevallen te vermijden (bijvoorbeeld ontbrekende rubriekniveaus).
Stap 3: Het Active Section over Scroll markeren
De highlight monteur laat lezers weten welk deel van het artikel ze momenteel lezen. Het idee is om door alle rubrieken te lopen, degene te vinden die het dichtst bij de top van de viewport (met enige offset) staat, en een klasse van toe te passen op de bijbehorende TOC-link.
Efficiënte ScrollLuisteraar
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;
}
});
Optimidaties:
- Gebruik om updates van de verfcyclus van de browser te beperken. Dit voorkomt vertraging op drukke pagina's.
- De offset van 150 pixels zorgt ervoor dat de sectie een beetje actief is voordat het de top bereikt, wat natuurlijker aanvoelt.
- Achterwaarts itereren vanaf de laatste kop is efficiënter omdat het actieve deel waarschijnlijk vlakbij de onderkant van het zichtbare gebied ligt.
Stap 4: Het toevoegen van glad scrollen en toegankelijkheid
Glad scrollen maakt springen tussen secties aangenaam. Dit kunt u bereiken met CSS, maar ook via JavaScript voor fijnere controle.
// 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);
}
}
});
Verbeteringen van de toegankelijkheid:
- Zorg ervoor dat de TOC een heeft (bv. ., .Table of Inhoudsopgave
- Voeg toe aan de actieve link: in plaats van ]. Dit helpt schermlezers om de huidige sectie aan te kondigen.
- Gebruik en indien de standaard /] semantiek wordt overschreven door styling.
Stap 5: De dynamische TOC stylen
Hoewel styling geen deel uitmaakt van de JavaScript logica, versterkt een goed vormgegeven TOC de bruikbaarheid. Hieronder staat een minimaal CSS voorbeeld dat een kleverige positionering voor zijbalkgebruik toevoegt:
#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);
}
Voor een responsief ontwerp, overweeg het verbergen van de TOC op kleine schermen en het toevoegen van een knop, of het instorten in een select-dropdown menu.
Geavanceerde verbeteringen
1. Afbreken van grootte wijzigen van gebeurtenissen
Als de kijkhoogte verandert (bijvoorbeeld bij verandering van mobiele oriëntatie), kunnen de waarden van de rubrieken verschuiven. Herbereken de array op een gedebounceerde grootte:
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. Tussensectie Observer voor Scroll-Based Highlighting
Een alternatief voor scroll luisteraars is de API. Het is meer performant en gemakkelijker te beheren. Voorbeeld:
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));
Deze brandt alleen wanneer een kop een berekende zone binnenkomt of verlaat, waardoor de overhead wordt verminderd. De definieert wanneer een sectie als
3. Luie Laden of Dynamische Inhoud
Als uw artikel delen dynamisch laadt (bijvoorbeeld via AJAX), moet u de TOC regenereren nadat nieuwe inhoud verschijnt. Een manier is om een MutationObserver op de artikelcontainer te gebruiken en de TOC-generatiefunctie opnieuw te bellen. Wees echter voorzichtig om geen vermeldingen te dupliceren.
Prestatieoverwegingen
- Vermijd zware DOM-queries binnen scroll-verwerkers. Cache alle selectoren eenmaal bij initialisatie.
- Gebruik passieve event luisteraars voor scroll: . Dit verbetert scrollen prestaties, vooral op mobiele.
- Don.t gaspedaal met
- Minify en uitstel van het script zodat het de paginabelasting niet blokkeert. Plaats het script voor of gebruik het attribuut.
Integratie met een statische sitegenerator (SSG) of CMS
Als u een statische site generator gebruikt, kunt u de TOC vooraf renderen met ingebouwde functies (bijv., Elfty. collecties, Hugo... ). Echter, de dynamische scroll-verlichting vereist nog steeds client-side JavaScript. Het voordeel van een server-side TOC is dat het direct beschikbaar is, zelfs voordat JavaScript draait, waardoor SEO en bereikbaarheid worden bevorderd.
Voor een CMS zoals WordPress of Directus kunt u dezelfde JavaScript-aanpak gebruiken terwijl u hoofd-ID's in de inhoud opslaat. Directus ondersteunt bijvoorbeeld aangepaste interfaces die automatisch ID's genereren. U kunt een haak maken die draait op inhoud, opslaan om ID's toe te voegen aan rubrieken, en dan gebruik maken van de front-end JavaScript om de TOC te bouwen.
Externe middelen voor een beter begrip:
Testen en debuggen
- Controleer of elke kop een unieke heeft. Dubbele ID's veroorzaken dat de browser alleen naar de eerste wedstrijd scroll.
- Controleer de TOC in zowel lichte als donkere thema's om ervoor te zorgen dat link contrast voldoet aan de WCAG AA normen.
- Test met toetsenbordnavigatie: drukt op Tab moet tussen TOC-links bewegen, en Enter moet naar de sectie schuiven.
- Gebruik de browser tabblad DevTools Prestaties om ervoor te zorgen dat geen jank tijdens scroll.
- Als het artikel afbeeldingen of iframes bevat, kan de veranderen na het laden van die elementen. Roep een herberekeningsfunctie op of nadat alle afbeeldingen geladen zijn (bv. ).
Potentiële Pitfalls en Hoe ze te vermijden
- Verbroken links bij gebrek aan ID's. Controleer altijd op een en maak er een als er geen is (gebruik een slugify-hulpprogramma).
- TOC flikkeren tijdens scroll
- Overlappende secties . . . de actieve highlight zou kunnen schakelen te vroeg of te laat. Pas de in IntersectieObserver of de offset in scroll handler.
- Nested TOC inspringing problemen
- Prestatie op lange pagina's
Conclusie
Een dynamische inhoudsopgave bouwen met JavaScript transformeert een lang, lineair artikel in een interactieve, scanneerbare bron. Door ID's toe te wijzen aan rubrieken, een geneste lijst van links te genereren en de huidige sectie op basis van scrollpositie te markeren, geeft u lezers een duidelijke routekaart. De codevoorbeelden in dit artikel bieden een solide basis, maar u kunt ze eenvoudig uitbreiden. U kunt ze vloeiende scrollen toevoegen, Intersect Observer gebruiken voor betere prestaties of integreren met uw bestaande bouwproces. Het resultaat is een professionelere leeservaring die de gebruikerstijd en aandacht respecteert, vooral op inhouds-zware sites zoals documentatieportalen, tutorials of long-form journalistiek.
Implementeer de aanpak die het beste bij uw stack past: pure JavaScript voor eenvoudige sites, of een hybride met SSG voor initiële TOC structuur plus client-side highlighting. Ongeacht de methode, een dynamische TOC is een kleine investering die aanzienlijke usability winsten oplevert.