Sharpen your development skills by recreating the well known tab component using nothing more then vanilla JavaScript and your unflappable wit!
On the surface, tabs may seem quite boring. We all know what they do and use them on a daily basis in our web browsers without giving them much thought, however, designing a modern, reusable tab component that works well on mobile devices using only native browser technologies can actually be quite challenging and is a great opportunity to build some “real world” code.
Our tab component needs to be:
Lets get to it! First I’ll start with the code, and then a break down what’s happening:
Here’s the basic HTML markup for our tab component:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Gator tabs</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="gator-tabs-container">
<ul class="gator-tabs-header">
<li>The nest</li>
<li class="default-gator-tab">Hello world!</li>
<li>Rise of the gator</li>
</ul>
<ul class="gator-tabs">
<li class="gator-tabs-container">
<ul class="gator-tabs-header">
<li>Nested 1</li>
<li class="default-gator-tab">Nested 2</li>
</ul>
<ul class="gator-tabs">
<li class="gator-tab">Some eggs in nest one</li>
<li class="gator-tab">Some eggs in nest two</li>
</ul>
</li>
<li class="gator-tab">Hello world from tab one!</li>
<li class="gator-tab">Believe me I know tabs, I have the best tabs. Nobody does tabs like I do.</li>
<li class="gator-tab">For now the eggs lay dormant but soon the gators will rise from the swamps.</li>
</ul>
</div>
<script src="main.js"></script>
</body>
</html>
And some CSS styles to make it all look good:
/* minimal reset */
* {
margin:0;
border:0;
padding:0;
box-sizing:border-box;
list-style:none;
}
body{
background-color: #333;
/* because serifs are gross (IMO) */
font-family: sans-serif;
}
.gator-tabs-container{
display:flex;
flex-direction:column;
width:100%;
}
.gator-tabs-header {
background-color:#DFA612;
display:flex;
flex-wrap:wrap;
padding:.375rem;
}
.gator-tabs-header > li {
color:#fff;
cursor:pointer;
flex-grow:1;
padding:.375rem;
font-size:1.125rem;
}
.gator-tabs {
display:flex;
}
.gator-tab {
padding:1rem;
color:#fff;
}
And now let’s let the magic happen with some simple JavaScript:
function tabify( element ){
const header = element.querySelector('.gator-tabs-header');
const content = element.querySelector('.gator-tabs');
const tab_headers = [...header.children];
const tab_contents = [...content.children];
tab_contents.forEach( x => x.style.display = 'none');
let current_tab_index = -1;
function setTab( index ){
if( current_tab_index > -1 ){
tab_headers[ current_tab_index ].style.fontWeight = 400;
tab_contents[ current_tab_index ].style.display = 'none';
}
tab_headers[ index ].style.fontWeight = 800;
tab_contents[ index ].style.display = 'flex';
current_tab_index = index;
}
default_tab_index = tab_headers.findIndex( x => {
return [...x.classList].indexOf('default-gator-tab') > -1;
});
default_tab_index = default_tab_index === -1 ? 0 : default_tab_index;
setTab( default_tab_index );
tab_headers.forEach((x,i) => x.onclick = event => setTab(i));
}
// this is where the magic happens!
[...document.querySelectorAll('.gator-tabs-container')]
.forEach(x => tabify(x));
Here’s a screenshot of the app on mobile:
Our first requirement was to build something responsive and this was easily solved using Flexbox with the flex-wrap
property so the tab headers will now stack on top of each other on mobile.
Making the code reusable may seem tricky at first but by wrapping our code into a function called tabify
we can make anything into a tab that satisfied the required class/tag structure. This also makes our next requirement, nestable, fall into place automatically.
The last requirement is the ability to set a tab as the default selected when the page opens. This was accomplished by adding the default-gator-tab
class to the desired tab header and our script will find the index of the first tab header with this class and use it to set the initial tab.
In a previous article, we described building tabs using React, but honestly, I’m hard pressed to find any significant advantages React has over plain ol’ Javascript in this instance. We’re able to pass any configuration to our script such as the default tab using class names (instead of props) and we are using about the same amount of code as the React version without the overhead of a framework!
If we wanted to, we could easily extend what we have to include animations or more complex feature but I’ll leave that up to you.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!