skip to Main Content

I want only display one answer when I click on a question, but all answers are being shown.

let ques = document.querySelectorAll(".question")
let ans = document.querySelectorAll(".answer")

ques.forEach((question) => {
  question.addEventListener("click", () => {
    ans.forEach((answer) => {
      answer.classList.toggle("display")
    })
  })
})
.answer {
  display: none;
}

.answer.display {
  display: block;
}
<div class="faq">
  <div class="question">question</div>
  <div class="answer">answer</div>
</div>
<div class="faq">
  <div class="question">question</div>
  <div class="answer">answer</div>
</div>
<div class="faq">
  <div class="question">question</div>
  <div class="answer">answer</div>
</div>

5

Answers


  1. The issue in your code is because you’re looping through all the .answer elements, not the one related to the clicked .question.

    To do what you need you can use closest() and querySelector() to find the .answer within the same parent as the .question which was clicked and toggle the display class on it. You can then loop through all other .answer elements and remove the display class from them.

    Here’s a working example:

    const questions = document.querySelectorAll(".question");
    const answers = document.querySelectorAll(".answer");
    
    questions.forEach(q => {
      q.addEventListener("click", () => {
        const answer = q.closest('.faq').querySelector('.answer');
        answer.classList.toggle('display');  
        answers.forEach(a => a != answer && a.classList.remove("display"))
      })
    })
    .answer {
      display: none;
    }
    
    .answer.display {
      display: block;
    }
    <div class="faq">
      <div class="question">question</div>
      <div class="answer">answer</div>
    </div>
    <div class="faq">
      <div class="question">question</div>
      <div class="answer">answer</div>
    </div>
    <div class="faq">
      <div class="question">question</div>
      <div class="answer">answer</div>
    </div>
    Login or Signup to reply.
  2. Shorter and with delegated event handling

    document.body.addEventListener('click', e =>
      e.target.closest('.faq .question')?.
        nextElementSibling.classList.toggle('display'));
    

    Explanation: When someone clicks the body find out if a question was clicked, and if so get its next sibling (the answer) and toggle its display class.

    Login or Signup to reply.
  3. Here’s a solution using an adjacent css sibling selector and event delegation. Now you only have to manipulate the active class on the .question element to show the right answer.

    document.addEventListener(`click`, handle);
    
    function handle(evt) {
      if (evt.target.classList?.contains(`question`)) {
        document.querySelector(`.active`)?.classList.remove(`active`);
        return evt.target.classList.add(`active`);
      }
    }
    .question {
      cursor: pointer;
    }
    
    .question + .answer {
      display: none;
    }
    
    .question.active + .answer {
      display: block;
      color: red;
      margin-left: 1rem;
    }
    <div class="faq">
      <div class="question">question</div>
      <div class="answer">answer</div>
    </div>
    <div class="faq">
      <div class="question">question</div>
      <div class="answer">answer</div>
    </div>
    <div class="faq">
      <div class="question">question</div>
      <div class="answer">answer</div>
    </div>
    Login or Signup to reply.
  4. Delegate from the nearest static container – it is never useful to add an eventListener to many identical elements in the same container.

    Use hidden for simplicity. Alternatively use classList.toggle("display",tgt.nextElementSibling === answer)

    const container = document.getElementById("faqContainer");
    const answers = container.querySelectorAll(".answer");
    container.addEventListener("click", (e) => {
      const tgt = e.target.closest(".question");
      if (!tgt) return; // not a question
      answers.forEach(answer => answer.hidden = tgt.nextElementSibling !== answer);
    });
    <div id="faqContainer">
      <div class="faq">
        <div class="question">question 1</div>
        <div class="answer" hidden>answer 1</div>
      </div>
      <div class="faq">
        <div class="question">question 2</div>
        <div class="answer" hidden>answer 2</div>
      </div>
      <div class="faq">
        <div class="question">question 3</div>
        <div class="answer" hidden>answer 3</div>
      </div>
    </div>
    Login or Signup to reply.
  5. Instead of attaching listeners to all the elements wrap the FAQ sections in a container, add one listener to that (this is known as event delegation), and catch events from its child elements as they "bubble up" the DOM.

    If the element clicked on is a question you can toggle the display class on its nextElementSibling.

    You can cache the answer elements and then iterate over them with forEach in the handler.

    const faqs = document.querySelector('.faqs');
    const answers = document.querySelectorAll('.answer');
    
    faqs.addEventListener('click', handleClick);
    
    function handleClick(e) {
      if (e.target.matches('.question')) {
        answers.forEach(a => a.classList.remove('display'));
        e.target.nextElementSibling.classList.toggle('display');
      }
    }
    .faq { margin: 0.5rem 0; }
    .question:hover { cursor: pointer; }
    .answer { padding: 0.25rem; display: none; background-color: lightblue; }
    .display { display: block; }
    <section class="faqs">
      <section class="faq">
        <h3 class="question">Question 1</h3>
        <p class="answer">Answer 1</p>
      </section>
      <section class="faq">
        <h3 class="question">Question 2</h3>
        <p class="answer">Answer 2</p>
      </section>
      <section class="faq">
        <h3 class="question">Question 3</h3>
        <p class="answer">Answer 3</p>
      </section>
    </section>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search