Skip to content
tezvyn:

Build a dynamic table of contents from article h2 tags

Source: developer.mozilla.orgEasyHow cards are made

Build a dynamic table of contents from article h2 tags
Summary

Basic DOM querying and dynamic element creation in vanilla JS.

Key points

Query h2s with querySelectorAll, assign ids, map to anchor links, append a nav list.

Watch out for

Importing jQuery or a framework for a six-line native task.

What's really being asked

This question checks whether you understand the Document Object Model as a programming interface that represents the page as nodes and objects so programs can change document structure and content. The interviewer wants to see you use core DOM methods to query a live tree, create new elements, and mutate attributes without relying on external libraries. At the senior level it also reveals whether you think about scoping, accessibility, and progressive enhancement.

The full answer

A strong response follows four steps in order. First, scope the search by calling querySelectorAll on the article container rather than document, which prevents accidentally capturing h2 tags from the site header or footer. Second, iterate the resulting NodeList and ensure every heading has a unique id attribute; if an id is missing, generate a URL-friendly slug from the heading textContent and assign it. Third, construct the table of contents by creating a nav or ul element with document.createElement, then for each heading append an li containing an anchor whose href is a fragment pointing to that id. Fourth, inject the finished list into the DOM at the desired mount point, typically before the article body. Mentioning that querySelectorAll returns a static NodeList and that textContent avoids HTML parsing overhead shows depth.

The mistakes people make

Red flags include suggesting jQuery or a framework for a task solvable in roughly ten lines of vanilla JavaScript. Another mistake is using innerHTML to assemble the list, which bypasses the DOM tree and risks XSS if heading text contains malicious markup. Failing to generate or validate ids means the anchor links will be broken. Some candidates query the entire document for h2 tags without scoping, which breaks when the page layout changes. A subtle error is using innerText instead of textContent, which introduces layout-dependent behavior that can fail in hidden or script-blocked contexts.

What usually comes next

The interviewer may ask how you would handle duplicate heading text, which requires appending an index to keep ids unique. They might ask how to make the table of contents update if the article is edited dynamically, leading to a MutationObserver or a re-render function. Accessibility follow-ups include asking whether you would add aria-labelledby or a skip link, or how you would preserve focus management when a user activates a table-of-contents link. Performance questions could cover whether you would debounce the build step on resize or if you would offload this work to a web worker, though for typical article lengths that is usually unnecessary.

A concrete example

Imagine an article container with the class content. You would write const headings = document.querySelectorAll(".content h2"); const toc = document.createElement("ul"); headings.forEach((h, i) => { if (!h.id) { const slug = h.textContent.toLowerCase().replace(/\s+/g, "-").replace(//g, "") || "heading"; h.id = slug + "-" + i; } const li = document.createElement("li"); const a = document.createElement("a"); a.href = "#" + h.id; a.textContent = h.textContent; li.appendChild(a); toc.appendChild(li); }); document.querySelector(".content").prepend(toc); This uses the DOM tree methods the HTML DOM API provides to read and write the document structure directly.

Interview question

When dynamically building a table of contents from article headings, which practice risks XSS if the heading text contains malicious markup?

  • a.Injecting the finished list before the article body
  • b.Querying headings with document.querySelectorAll scoped to the article container
  • c.Generating URL-friendly slugs from the heading textContent
  • d.Assembling the list with innerHTML instead of document.createElementCorrect
Why?

The card warns that innerHTML bypasses the DOM tree and risks XSS when heading text contains malicious markup, whereas createElement safely constructs nodes. Option B is wrong because scoping querySelectorAll to the article container is actually the recommended first step to avoid capturing header or footer headings.

Just read this? Test yourself on what you have been reading.

Read the original → developer.mozilla.org

You just looked this up. Could you explain it out loud?

That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on dom — each one lists the topics its interview covers.

See open roles