JavaScript Notes
Learn to build a nested threaded comment system in JavaScript with recursive rendering, reply, like, collapse/expand, and depth-limited nesting.
What You'll Learn
- How to model comments as a tree data structure (nodes with children)
- How to use recursion to render arbitrarily deep comment threads
- How to implement Reply, Like, and Delete on any comment
- How to collapse/expand branches of the comment tree
- How to limit nesting depth to prevent infinite indentation
Project Overview
Nested comments work like Reddit or YouTube: each comment can have replies, and each reply can have its own replies. The data is a tree, and rendering it requires a recursive function that handles any depth.
Component Structure
How It Works — Flow Diagram
Step-by-Step Build
Step 1 — Data Model
Step 2 — HTML Structure
<div class="comments-app">
<h1>Discussion</h1>
<form id="rootForm" class="comment-form">
<textarea id="rootInput" placeholder="Write a comment..." rows="3"></textarea>
<div class="form-row">
<input id="rootAuthor" type="text" placeholder="Your name" required>
<button type="submit">Post Comment</button>
</div>
</form>
<div id="commentList" class="comment-list"></div>
</div>Step 3 — Time Formatting Helper
function timeAgo(timestamp) {
const diff = Math.floor((Date.now() - timestamp) / 1000);
if (diff < 60) return `${diff}s ago`;
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
if (diff < 86400)return `${Math.floor(diff / 3600)}h ago`;
return `${Math.floor(diff / 86400)}d ago`;
}Step 4 — Build a Single Comment Element
function buildCommentEl(comment, depth) {
const el = document.createElement("div");
el.className = `comment depth-${Math.min(depth, MAX_DEPTH)}`;
el.dataset.id = comment.id;
el.innerHTML = `
<div class="comment-header">
<strong class="comment-author">${comment.author}</strong>
<span class="comment-time">${timeAgo(comment.timestamp)}</span>
</div>
<p class="comment-text">${comment.text}</p>
<div class="comment-actions">
<button class="btn-like">♥ <span class="like-count">${comment.likes}</span></button>
${depth < MAX_DEPTH ? `<button class="btn-reply">Reply</button>` : ""}
<button class="btn-delete">Delete</button>
${comment.replies.length > 0
? `<button class="btn-toggle">▾ Hide replies (${comment.replies.length})</button>`
: ""}
</div>
${depth < MAX_DEPTH ? `<div class="reply-form-container hidden"></div>` : ""}
<div class="replies-container"></div>
`;
return el;
}Step 5 — Recursive Render Function
Step 6 — Wire Actions (Like, Reply, Delete, Toggle)
Step 7 — Add New Comment / Reply
Step 8 — Delete by ID (Recursive Helper)
Step 9 — Initialize
renderComments(comments, document.getElementById("commentList"));Console Output
renderComments(comments, #commentList, depth=0)
→ buildCommentEl(Alice, depth=0) → append to #commentList
→ renderComments(Alice.replies, repliesContainer, depth=1)
→ buildCommentEl(Bob, depth=1) → append to repliesContainer
→ renderComments(Bob.replies, repliesContainer, depth=2)
→ buildCommentEl(Carol, depth=2) → append
// User likes Bob's comment (id: 2)
comment.likes++ → 3
like-count.textContent = "3"
// User deletes Carol (id: 3)
deleteComment(3, comments)
→ checks comments[0].replies → finds in Bob.replies → splices out
renderComments() → re-renders without CarolEdge Cases to Handle
| Situation | Expected Behavior |
|---|---|
| MAX_DEPTH reached | Reply button hidden |
| Delete comment with children | Entire subtree removed |
| Toggle with 0 replies | Toggle button not shown |
| Empty text reply | Blocked by trim() check |
| Very long username | CSS overflow: hidden / text-overflow |
Challenge Yourself
- Upvote/downvote — replace ♥ with ▲/▼ and allow negative like counts
- Sort by top/new — add sort buttons at the top level
- Markdown in comments — support
boldand_italic_formatting - Persist to localStorage — save the entire comment tree on every change
- Load more — show only 5 top-level comments and a "Load more" button
Best Practices
- Model comments as a tree — each node has an
id,text, andreplies[] - The recursive
renderComments()function is the heart of the project — keep it clean - Wire event listeners inside
buildCommentEl()orwireCommentActions(), not globally - Use
deleteComment(id, list)recursively — avoids needing to pass parent references - Limit max depth to prevent CSS indentation overflow
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Build JavaScript Nested Chat Comments - Step by Step Project Tutorial.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this JavaScript Master Course topic.
Search Terms
javascript-complete-guide, javascript master course, javascript, complete, guide, mini, projects, nested
Related JavaScript Master Course Topics