I have made a small script in JavaScript for tabbed content.
(function() {
let contentElms = document.querySelectorAll('.content > div');
let tabs = document.querySelectorAll('.tabs li');
// Show first tab initially
contentElms[0].classList.add("active");
tabs.forEach((tab, index) => {
tab.addEventListener("click", () => {
// Toggle tabs
tabs.forEach((tab) => {
tab.classList.remove("active")
});
tab.classList.add("active");
// Toggle content
contentElms.forEach((el) => {
el.classList.remove("active")
});
contentElms[index].classList.add("active");
});
});
})();
body {
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
}
body * {
box-sizing: content-box;
}
.tabbed-content {
max-width: 600px;
margin: 10px auto;
}
.tabbed-content .tabs ul {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
}
.tabbed-content .tabs ul li {
flex: 1;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 4px 4px 0 0;
overflow: hidden;
}
.tabbed-content .tabs ul li.active {
border-bottom: none;
font-weight: bold;
}
.tabbed-content .tabs ul a {
display: block;
padding: 10px;
text-align: center;
text-decoration: none;
color: #7f7f7f;
}
.tabbed-content .content {
padding: 10px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-top: none;
border-radius: 0 0 4px 4px;
}
.tabbed-content .content > div {
font-size: 14px;
line-height: 1.5;
text-align: justify;
display: none;
}
.tabbed-content .content > div.active {
display: block;
}
<div class="tabbed-content">
<nav class="tabs">
<ul>
<li class="active"><a href="#">Tab 1</a></li>
<li><a href="#">Tab 2</a></li>
<li><a href="#">Tab 3</a></li>
</ul>
</nav>
<div class="content">
<div>Nesciunt velit a hic, officia animi veritatis quis obcaecati tempora omnis iusto.</div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Molestias dolorem, sequi quos unde tempora corporis voluptatibus officia atque voluptatum consequuntur necessitatibus fuga quidem nihil. Perspiciatis reiciendis impedit cupiditate odit veritatis!</div>
<div>Deserunt est in, ipsum possimus dolorum! Numquam suscipit laborum, reiciendis delectus. Labore?</div>
</div>
</div>
The tabs work fine.
The problem
I am unhappy with the fact that there are nested forEach
loops in the script.
Questions
- Is there any (reliable) way to avoid that?
- Can I have just one
forEach
loop in the script?
2
Answers
Yeah. You can use this code
Instead of loops we will save references to active content and tab elements and update them on click
As mentioned in comments, you can use the event delegation, to achieve the desired result.
So, the
tabsContainer
(which is the parent of all tabs) listens for click events. When a click happens, it checks if the target(event.target)
is a tab(li)
. Then the index of the clicked tab is determined by converting theNodeList
of tabs into an array withArray.from
and usingindexOf
to find the index. Lastly, all tabs and content elements have theactive
class removed first. Then, the clicked tab and the corresponding content are set to active.