Lexington has been awarded a grant from Astro, to celebrate. Get a 30% discount. Apply code LEXINGTON30 ( uppercase ) at checkout.
Monday again, another day of tutorials! Today, we’ll be creating a persistent tabs component instead of using Alpine JS like we did on the past tutorial, we will be using JavaScript and Tailwind CSS.
Persistent tabs bring multiple advantages to web applications:
Persistent tabs are beneficial in a variety of situations:
By integrating persistent tabs, you can significantly improve the usability and user-friendliness of your web application across these and many other scenarios.
The wrapper is where we’ll add the snippet to persist the tab state. We’ll also add the id
and class
attributes to the wrapper.
id="tabsComponent"
: Assigns a unique ID to the wrapper. This ID will be used to target the wrapper in the JavaScript code.<div id="tabsComponent">
<!-- Tabs go here -->
</div>
The buttons are li
elements without the need of classes on this example. We’ll add the role="presentation"
attribute to the ul
element to indicate that the element is a presentational element.
role="presentation"
: Indicates that the element is a presentational element. <ul role="tablist">
<!-- Tabs go here -->
</ul>
Attributes
role="tab"
: Indicates that the element is a tab.id="tab-1"
: Assigns a unique ID to the tab.aria-controls="panel-1"
: Associates the tab with its corresponding panel.<button role="tab" id="tab-1" aria-controls="panel-1"> My account </button>
The panels are section
elements where the content of the tabs will be displayed.
Attributes
role="tabpanel"
: Indicates that the element is a tabpanel.id="panel-1"
: Assigns a unique ID to the panel.aria-labelledby="tab-1"
: Associates the panel with the corresponding tab.<section role="tabpanel" id="panel-1" aria-labelledby="tab-1"> Content 1 </section>
We will do the same with the other panel, only that the seconde panel will have it’s own attributes.
Note: Classes are removed for brevity and you can get the full code on the GitHub repository.
<div id="tabsComponent">
<!-- Tab List -->
<ul role="tablist">
<!-- Tab 1 -->
<li role="presentation">
<button role="tab" id="tab-1" aria-controls="panel-1"> My account </button>
</li>
<!-- Tab 2 -->
<li role="presentation">
<button role="tab" id="tab-2" aria-controls="panel-2"> Billing </button>
</li>
</ul>
<!-- Panels -->
<div>
<!-- Panel 1 -->
<section role="tabpanel" id="panel-1" aria-labelledby="tab-1"> Content 1 </section>
<!-- Panel 2 -->
<section role="tabpanel" id="panel-2" aria-labelledby="tab-2"> Content 2 </section>
</div>
</div>
The script is where we’ll define the logic for the tabs component.
We’ll add the addEventListener
function to the document
object to listen for the DOMContentLoaded
event.
const tabsComponent = document.getElementById("tabsComponent");
: This line of code selects the wrapper element with the ID tabsComponent
.const tabButtons = tabsComponent.querySelectorAll(".tab-button");
: This line of code selects all the buttons inside the wrapper.const tabPanels = tabsComponent.querySelectorAll(".tab-panel");
: This line of code selects all the panels inside the wrapper. const tabsComponent = document.getElementById("tabsComponent");
const tabButtons = tabsComponent.querySelectorAll(".tab-button");
const tabPanels = tabsComponent.querySelectorAll(".tab-panel");
function setActiveTab(index)
: This function sets the active tab based on the index passed as an argument.tabButtons.forEach((button, i) => {
: This line of code iterates over each button and sets the aria-selected
, tabindex
, and class
attributes based on the index passed as an argument.button.setAttribute("aria-selected", i === index);
: This line of code sets the aria-selected
attribute to true
if the index matches the current index.button.setAttribute("tabindex", i === index ? "0" : "-1");
: This line of code sets the tabindex
attribute to 0
if the index matches the current index, and -1
otherwise.button.classList.toggle("bg-orange-50", i === index);
: This line of code toggles the bg-orange-50
class on the button if the index matches the current index.button.classList.toggle("text-orange-600", i === index);
: This line of code toggles the text-orange-600
class on the button if the index matches the current index.tabButtons.forEach((button, i) => {
button.setAttribute("aria-selected", i === index);
button.setAttribute("tabindex", i === index ? "0" : "-1");
button.classList.toggle("bg-orange-50", i === index);
button.classList.toggle("text-orange-600", i === index);
});
tabPanels.forEach((panel, i) => {
: This line of code iterates over each panel and sets the display
style based on the index passed as an argument.panel.style.display = i === index ? "block" : "none";
: This line of code sets the display
style to block
if the index matches the current index, and none
otherwise. tabPanels.forEach((panel, i) => {
panel.style.display = i === index ? "block" : "none";
});
localStorage.setItem("activeTab", index.toString());
: This line of code sets the activeTab
key in the local storage to the index passed as an argument. localStorage.setItem("activeTab", index.toString());
tabButtons.forEach((button, index) =>
: This line of code iterates over each button and adds a click event listener to it.button.addEventListener("click", () => setActiveTab(index));
: This line of code adds a click event listener to each button and calls the setActiveTab
function with the index of the button as an argument. tabButtons.forEach((button, index) => {
button.addEventListener("click", () => setActiveTab(index));
});
const storedActiveTab = parseInt(localStorage.getItem("activeTab")) || 0;
: This line of code retrieves the value of the activeTab
key in the local storage and parses it as an integer.setActiveTab(storedActiveTab);
: This line of code calls the setActiveTab
function with the value of the activeTab
key as an argument.const storedActiveTab = parseInt(localStorage.getItem("activeTab")) || 0;
setActiveTab(storedActiveTab);
document.addEventListener("DOMContentLoaded", function () {
const tabsComponent = document.getElementById("tabsComponent");
const tabButtons = tabsComponent.querySelectorAll(".tab-button");
const tabPanels = tabsComponent.querySelectorAll(".tab-panel");
function setActiveTab(index) {
tabButtons.forEach((button, i) => {
button.setAttribute("aria-selected", i === index);
button.setAttribute("tabindex", i === index ? "0" : "-1");
button.classList.toggle("bg-orange-50", i === index);
button.classList.toggle("text-orange-600", i === index);
});
tabPanels.forEach((panel, i) => {
panel.style.display = i === index ? "block" : "none";
});
localStorage.setItem("activeTab", index.toString());
}
tabButtons.forEach((button, index) => {
button.addEventListener("click", () => setActiveTab(index));
});
// Set initial active tab
const storedActiveTab = parseInt(localStorage.getItem("activeTab")) || 0;
setActiveTab(storedActiveTab);
});
In this tutorial, we learned how to create a persistent tabs component using JavaScript and Tailwind CSS, and how to use JavaScript to iterate over elements and set attributes based on the index of the element. We also learned how to use the localStorage
API to store and retrieve data in the browser.
I hope you found this tutorial helpful and have a great day!
/Michael Andreuzza
Get lifetime access to every theme available today for $199 and own
them forever. Plus, new themes, lifetime updates, use on unlimited
projects and enjoy lifetime support.
— No subscription required!