Using loops for creating accordions
Here is how to achieve it:
//Step 1: Select all FAQ headlines
const faqHeaders = document.querySelectorAll(".faqs .faq-header");
//Step 2: Loop through all selected FAQ headlines
for (let i = 0; i < faqHeaders.length; i++) {
const faqHeader = faqHeaders[i];
//Step 3: Add "click" event listener to each of them
faqHeader.addEventListener("click", function () {
/*
Step 4: When someone clicks on an individual FAQ headline,
toggle the "is-open" class on its parent element to make the
FAQ content visible or hide it if it is already open
*/
faqHeader.parentElement.classList.toggle("is-open");
});
}
Come on, let's go through it step-by-step.
Step 1: Select all FAQ headlines
const faqHeaders = document.querySelectorAll(".faqs .faq-header");
The faqHeaders
variable now contains a NodeList of individual FAQ's headlines:
Step 2: Loop through all selected FAQ headlines so that we can add a "click" event listener to each FAQ headline.
for (let i = 0; i < faqHeaders.length; i++) {
//...
}
Step 3: Add "click" event listener to each FAQ headline
for (let i = 0; i < faqHeaders.length; i++) {
const faqHeader = faqHeaders[i];
faqHeader.addEventListener("click", function () {
//...
});
}
To achieve it, first, inside the loop body, we are getting access to each FAQ's headline:
const faqHeader = faqHeaders[i];
And then, we are attaching a "click" event listener to it:
faqHeader.addEventListener("click", function () {
//...
});
You could also do it in a single step like this:
faqHeaders[i].addEventListener("click", function () {
//...
});
faqHeader
variable contains the respective FAQ's headline as an HTML object. This is useful for getting its parent element using the parentElement
property.Anyway, just to be clear, four FAQ headlines were selected, right?
So...
During iteration 1: The first FAQ's headline will be accessed:
<h3 class="faq-header">
<button>Is it easy to learn Javascript?</button>
</h3>
Then, a "click" event handler is attached to it.
During iteration 2: The second FAQ's headline will be accessed:
<h3 class="faq-header">
<button>Do I need to know programming?</button>
</h3>
Then, a "click" event handler is attached to it.
The same thing happens for the rest of the iterations until the loop runs out of all the FAQ headlines.
Step 4: When someone clicks on an individual FAQ's headline, toggle the "is-open" class on its parent element.
To achieve this, when an FAQ's headline is clicked, We select its parent element dynamically using the parentElement
property, and then we toggle the class is-open
on it:
faqHeader.parentElement.classList.toggle("is-open");
For example, if the user has clicked on the first FAQ's headline:
Inside the loop body, calling the parentElement
property on the "clicked" FAQ headline would return:
<div class="faq faq-1">
...
</div>
And then we are toggling the class is-open
on it:
If the concept of the parentElement
property is confusing for you, you can clear it out by console logging the parentElement
property inside the event handler:
const faqHeaders = document.querySelectorAll(".faqs .faq-header");
for (let i = 0; i < faqHeaders.length; i++) {
const faqHeader = faqHeaders[i];
faqHeader.addEventListener("click", function () {
//Log parent element of the "clicked" faq headline
console.log(faqHeader.parentElement);
faqHeader.parentElement.classList.toggle("is-open");
});
}
And now, when you click on any FAQ headline, you can see its parent element inside the Browser console:
The same thing happens when other FAQ headlines are clicked because the parentElement
property is contextual.
Inside the loop, the parentElement
property would only return the direct parent element of the FAQ headline we clicked:
Challenge: Use "forEach" instead of a "for" loop to implement the accordion functionality
As I said during the previous lesson, if you're using a modern browser, you can still access the forEach
method on a NodeList.
So, go ahead and change the previous code to use a forEach
loop.
...
...
Solution
const faqHeaders = document.querySelectorAll(".faqs .faq-header");
faqHeaders.forEach(function(faqHeader){
faqHeader.addEventListener("click", function () {
faqHeader.parentElement.classList.toggle("is-open");
});
});
The code is much more concise and easier to read, right?
The Conclusion
That's all.
That's exactly how the bulk interactivity works using a "for" loop or "forEach" method.
If you don't understand what's happening, don't worry.
It could take some time to wrap your head around what's happening, but you'll get habituated to it with practice.
To help you with more practice, we will work on the following projects in the upcoming lessons:
- Image Gallery
- Tabs
- Re-implementing the skills learned so far on an e-commerce website.
Also, if you notice the way we are toggling the class name on the parent element, we are using something called the "Dot Notation Chaining" technique to access object properties in a sequential way:
faqHeader.parentElement.classList.toggle("is-open");
We will explore this idea in detail in the next lesson because it will change how you write Javascript code in future lessons.