JavaScript Tutorial

JavaScript Project: Accordion

FAQ panels that open one at a time. Click a question to show its answer and hide the others.

What you will build

A short FAQ. Each item is a question button and an answer panel. Click a question to open its answer. Opening one item closes the others, so only one panel is visible.

Clicking the already-open question closes it. That gives you a way back to an all-closed list without a separate collapse-all control.

Open an example with Try it in JavaScript. That loads /javascript/try — a live page and a console.

Methods you will use

MethodJob on this page
classListAdds or removes open on a panel
querySelectorAllFinds every FAQ item so others can close
clickToggles the item that was pressed
containsChecks whether the clicked item was already open
aria-expandedTells assistive tech which question is expanded

CSS hides answers unless the parent has class open. JavaScript does not setdisplay on each panel. It only flips a class. Layout stays in the stylesheet.

Build in slices

Three items, same structure. The question is a button so it is keyboard reachable. The answer is a div under it, not a sibling elsewhere in the page.

Example

<div class="item">
  <button type="button" class="question" aria-expanded="false">What is an accordion?</button>
  <div class="answer">A stack of panels where opening one closes the rest.</div>
</div>
<div class="item">
  <button type="button" class="question" aria-expanded="false">Why not use details?</button>
  <div class="answer">details can open several at once. This script keeps one open.</div>
</div>

Hide answers with CSS. The open class on the item reveals the panel. Start with everything closed.

Example

<style>
  .answer { display: none; padding: 0.5rem 0.75rem 0.85rem; }
  .item.open .answer { display: block; }
  .question { width: 100%; text-align: left; }
</style>
<div class="item open">
  <button type="button" class="question">What is an accordion?</button>
  <div class="answer">This panel is open because the item has class open.</div>
</div>

On click, remember whether this item was open, close every item, then reopen this one only if it was closed. Update aria-expanded to match.

Example

<style>
  .answer { display: none; }
  .item.open .answer { display: block; }
  .question { width: 100%; text-align: left; }
</style>
<div class="item">
  <button type="button" class="question" aria-expanded="false">First question</button>
  <div class="answer">First answer.</div>
</div>
<div class="item">
  <button type="button" class="question" aria-expanded="false">Second question</button>
  <div class="answer">Second answer. Opening this closes the first.</div>
</div>
<script>
  const items = document.querySelectorAll(".item");
  items.forEach(function (item) {
    const button = item.querySelector(".question");
    button.addEventListener("click", function () {
      const wasOpen = item.classList.contains("open");
      items.forEach(function (other) {
        other.classList.remove("open");
        other.querySelector(".question").setAttribute("aria-expanded", "false");
      });
      if (!wasOpen) {
        item.classList.add("open");
        button.setAttribute("aria-expanded", "true");
      }
    });
  });
</script>

Open the first panel, then the second, at /javascript/try. The first answer must hide. Use Try it in JavaScript.

Complete document

This file is the FAQ you would keep. Four questions about the JavaScript studio. The open panel gets a darker amber header so you can see which item is active.

Example

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Accordion</title>
  <style>
    body {
      margin: 0;
      min-height: 100vh;
      display: grid;
      place-items: center;
      background: #fefce8;
      font-family: "Segoe UI", system-ui, sans-serif;
      color: #1c1917;
    }
    .card {
      width: min(28rem, 92vw);
      background: #fffbeb;
      border: 1px solid #fde68a;
      padding: 1.3rem 1.3rem 1.4rem;
    }
    h1 { margin: 0 0 0.85rem; font-size: 1.4rem; }
    .item { border: 1px solid #fde68a; margin-bottom: 0.45rem; background: #fff; }
    .question {
      width: 100%;
      text-align: left;
      padding: 0.65rem 0.75rem;
      border: 0;
      background: #fef9c3;
      font: inherit;
      font-weight: 700;
      cursor: pointer;
    }
    .item.open .question { background: #854d0e; color: #fffbeb; }
    .answer { display: none; padding: 0.65rem 0.75rem 0.85rem; line-height: 1.5; }
    .item.open .answer { display: block; }
  </style>
</head>
<body>
  <article class="card">
    <h1>JavaScript FAQ</h1>
    <div class="item">
      <button type="button" class="question" aria-expanded="false">Where does Try it open?</button>
      <div class="answer">Try it in JavaScript opens /javascript/try. It is not /try and not /html/try.</div>
    </div>
    <div class="item">
      <button type="button" class="question" aria-expanded="false">Why keep one panel open?</button>
      <div class="answer">A single open panel keeps the FAQ short on small screens. Two answers at once bury the next question.</div>
    </div>
    <div class="item">
      <button type="button" class="question" aria-expanded="false">What does classList do?</button>
      <div class="answer">classList.add and classList.remove change the open class. CSS decides whether the answer is visible.</div>
    </div>
    <div class="item">
      <button type="button" class="question" aria-expanded="false">Can I click the open question again?</button>
      <div class="answer">Yes. The second click closes it. Every panel can be shut without a separate button.</div>
    </div>
  </article>
  <script>
    const items = document.querySelectorAll(".item");

    function closeAll() {
      items.forEach(function (item) {
        item.classList.remove("open");
        item.querySelector(".question").setAttribute("aria-expanded", "false");
      });
    }

    items.forEach(function (item) {
      const button = item.querySelector(".question");
      button.addEventListener("click", function () {
        const wasOpen = item.classList.contains("open");
        closeAll();
        if (!wasOpen) {
          item.classList.add("open");
          button.setAttribute("aria-expanded", "true");
        }
      });
    });
  </script>
</body>
</html>

One open panel

Close all first, then open the clicked item. If you open first and close the others second, a loop that includes the current item would immediately close what you just opened.

Native details / summary can do an FAQ without a script, but several can stay open. This project is specifically exclusive open. That is why the script walks every item on each click.

Common mistakes

  • Toggling only the clicked item. Two panels stay open. That is a disclosure list, not this accordion.
  • Putting the listener on .answer. Clicks on the question would do nothing.
  • Using display in JavaScript. Then CSS and script fight. Flip a class.
  • Selecting document.querySelector(".item") (one node) instead of querySelectorAll.
  • Forgetting type="button" if the FAQ later sits in a form. The page would submit.

Practice tasks

  1. Open the first panel on load by adding open and aria-expanded="true" after binding clicks.
  2. Add a fifth question. You should not need to change the script.
  3. Allow several panels open only when Shift is held during the click. Preview at /javascript/try.

FAQ: JavaScript Project: Accordion

Common questions about this page.

What is the StudyGrid JavaScript tutorial?

The StudyGrid JavaScript tutorial is a full beginner track: variables, functions, arrays, if/else, the DOM, events, storage, and fetch. Each chapter has copy-and-run examples.

Should I run javascript accordion project examples locally for better learning?

Yes. Use the browser editor on StudyGrid for a quick check, then Download the example and run it on your computer. Local runs show real errors and the real toolchain, which is one of the fastest ways to learn javascript accordion project in this JavaScript JavaScript lesson (JavaScript Project: Accordion).

Is the JavaScript editor the same as Try Python?

No. Try JavaScript is a live page plus a console at /javascript/try. Try Python stays at /try. JavaScript lessons never open the Python editor.

Do I need to install anything to learn JavaScript?

No. Open a chapter, click Try it in JavaScript, and the page and console update in the browser.

Where should I start the JavaScript tutorial?

Start at JavaScript Intro, then Where To, Output, and Syntax. After variables and functions, continue to the DOM and events. Use Next at the bottom of each chapter.

Is the JavaScript tutorial free?

Yes. The JavaScript studio on StudyGrid (studygrid.in) is free: dashboard, chapters, and the live editor.