JavascriptIntermediate
Accordion
Build an accordion component where clicking a section header expands or collapses its content.
Accordion
Problem
Build an accordion with multiple sections. Clicking a section header expands it to show its content and collapses any other open section. Clicking an open section's header collapses it.
Page loads
→ All sections collapsed
User clicks "What is JavaScript?"
→ That section expands, shows its content
→ Any other open section collapses
User clicks the same header again
→ Section collapses
User clicks "What is Python?"
→ "What is Python?" expands
→ "What is JavaScript?" automatically collapsesLogic
- Select all accordion headers
- Listen for
clickon each header using event delegation - Check if the clicked section is already open
- Close all sections first
- If it was not already open — open the clicked one
- Use
max-heightwith a CSS transition for smooth expand/collapse - Rotate an arrow icon to indicate open/closed state
Flow
HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Accordion</title>
<style>
body {
font-family: sans-serif;
max-width: 520px;
margin: 60px auto;
padding: 0 20px;
}
/* each accordion section */
.accordion-item {
border: 1px solid #e0e0e0;
border-radius: 8px;
margin-bottom: 10px;
overflow: hidden;
}
/* clickable header */
.accordion-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 18px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
color: #222;
background: #fafafa;
user-select: none;
}
.accordion-header:hover {
background: #f0f0f0;
}
/* arrow icon — rotates when open */
.arrow {
font-size: 0.85rem;
color: #888;
transition: transform 0.25s ease;
}
/* rotate arrow when item is open */
.accordion-item.open .arrow {
transform: rotate(90deg);
}
/* content wrapper — collapsed by default */
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
/* inner padding — separate from content wrapper
so max-height transition is not affected by padding */
.accordion-content-inner {
padding: 0 18px 16px;
font-size: 0.95rem;
color: #555;
line-height: 1.6;
}
</style>
</head>
<body>
<h1>FAQ</h1>
<!-- accordion container -->
<div id="accordion">
<!-- each item: header + content -->
<div class="accordion-item">
<div class="accordion-header">
<span>What is JavaScript?</span>
<span class="arrow">▶</span>
</div>
<div class="accordion-content">
<div class="accordion-content-inner">
JavaScript is a programming language that runs in browsers and
servers. It is used to build interactive websites, web apps,
and even backend systems with Node.js.
</div>
</div>
</div>
<div class="accordion-item">
<div class="accordion-header">
<span>What is Python?</span>
<span class="arrow">▶</span>
</div>
<div class="accordion-content">
<div class="accordion-content-inner">
Python is a beginner friendly programming language known for
its simple syntax. It is widely used in data science, automation,
and backend development.
</div>
</div>
</div>
<div class="accordion-item">
<div class="accordion-header">
<span>What is MongoDB?</span>
<span class="arrow">▶</span>
</div>
<div class="accordion-content">
<div class="accordion-content-inner">
MongoDB is a NoSQL database that stores data as flexible,
JSON-like documents instead of tables and rows like traditional
databases.
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>Solution
// Step 1 — select the accordion container and all items
const accordion = document.querySelector("#accordion");
const items = document.querySelectorAll(".accordion-item");
// Step 2 — use event delegation
// one listener on the container handles clicks on any header
accordion.addEventListener("click", (e) => {
// find the header that was clicked
const header = e.target.closest(".accordion-header");
if (!header) return; // clicked somewhere else — ignore
// Step 3 — find the parent accordion item
const clickedItem = header.closest(".accordion-item");
// Step 4 — check if this item is already open
const isAlreadyOpen = clickedItem.classList.contains("open");
// Step 5 — close all items first
closeAllItems();
// Step 6 — if it was not already open, open it now
// this creates the toggle effect — click again to close
if (!isAlreadyOpen) {
openItem(clickedItem);
}
});
// Step 7 — close all accordion items
function closeAllItems() {
items.forEach((item) => {
item.classList.remove("open");
const content = item.querySelector(".accordion-content");
// set max-height to 0 — triggers the collapse transition
content.style.maxHeight = "0px";
});
}
// Step 8 — open a specific accordion item
function openItem(item) {
item.classList.add("open");
const content = item.querySelector(".accordion-content");
const inner = item.querySelector(".accordion-content-inner");
// Step 9 — set max-height to the actual content height
// scrollHeight gives the full height of the content
// including parts that are currently hidden by overflow
content.style.maxHeight = inner.offsetHeight + "px";
}Code Execution
Trace through clicking "What is JavaScript?" (first time, all closed):
| Step | Code | Result |
|---|---|---|
| Find header | e.target.closest(".accordion-header") | JS header element |
| Find item | header.closest(".accordion-item") | JS accordion item |
| Check open | item.classList.contains("open") | false |
| Close all | closeAllItems() | all max-height = 0px |
| Open clicked | openItem(item) | adds "open" class |
| Set max-height | content.style.maxHeight = inner.offsetHeight + "px" | e.g. "96px" |
| Result | JS section expands, arrow rotates |
Trace through clicking "What is JavaScript?" again (now open):
| Step | Code | Result |
|---|---|---|
| Check open | item.classList.contains("open") | true |
| Close all | closeAllItems() | max-height = 0px, class removed |
isAlreadyOpen check | true → skip openItem() | item stays closed |
| Result | JS section collapses |
Trace through clicking "What is Python?" while JS section is open:
| Step | Code | Result |
|---|---|---|
| Close all | closeAllItems() | JS section collapses too |
| Check open | Python item was not open | false |
| Open Python | openItem(pythonItem) | Python section expands |
| Result | only Python is open now |
Output
Page loads → all sections collapsed, arrows pointing right
Click "JavaScript" → JavaScript section expands, arrow rotates down
Click "JavaScript" again → JavaScript section collapses
Click "Python" → Python expands, JavaScript stays closed
Click "MongoDB" → MongoDB expands, Python collapses