Use Sophia to knock out your gen-ed requirements quickly and affordably. Learn more
×

Interactive UI Components

Author: Sophia

what's covered
In this lesson, you will learn how to build interactive user interface components using JavaScript event handling. Toggles, accordions, and tabs are essential building blocks found on modern websites. Specifically, this lesson will cover the following:

Table of Contents

1. Toggles

A toggle is a user interface pattern that switches between two states, such as on and off or visible and hidden. Toggles form the foundation for more complex interactive patterns like accordions and tabs.

These components build on skills you have already learned, including selecting DOM elements, modifying content and attributes, managing classes with the classList API, and responding to user actions with event listeners. In this lesson, you will combine those skills to create complete interactive components.

term to know
Toggle
A user interface pattern that switches between two states, such as on/off or visible/hidden, typically activated by clicking a button or control.

1a. Show and Hide

The simplest interactive component is a toggle that shows or hides content when a user clicks a button. This pattern is common in mobile navigation menus, dropdown panels, and content reveal sections.

A show and hide toggle works by coordinating three parts: HTML provides a trigger and a content area, CSS defines how the content looks in each state, and JavaScript switches between those states when the user interacts with the page.

EXAMPLE

A simple toggle that reveals and hides a content panel:

<button id="toggle-btn">Show Details</button>
<div id="content-panel" class="hidden">
<p>This content can be toggled on and off.</p>
</div>

.hidden {
display: none;
}

const toggleBtn = document.getElementById('toggle-btn');
const contentPanel = document.getElementById('content-panel');

toggleBtn.addEventListener('click', function() {
contentPanel.classList.toggle('hidden');
});
In this code:

  • The HTML defines a button trigger and a panel that starts with the hidden class applied.
  • The CSS uses display: none to hide any element with the hidden class.
  • The JavaScript listens for clicks on the button and calls classList.toggle('hidden') to add or remove the class with each click.

key concept
The classList.toggle() method is the most efficient way to switch between two states. It removes the class if it is present and adds it if it is absent, eliminating the need for conditional logic.

try it
Open index.html in your Codespace.

Create a new file called toggle.js and link it before the closing </body> tag in your HTML using the following:

Copy
<script src="toggle.js" defer></script>
Add the following elements to your page:

Copy
<button id="reveal">Show Tip</button>
<p id="tip" class="hidden">Try saving often!</p>
  • In your CSS file, add a .hidden rule that sets display: none.
  • In toggle.js, select the button and paragraph, then add a click event listener to the button that toggles the hidden class on the paragraph.
  • Preview the page in Codespaces.
  • Click the button to confirm the tip appears and disappears.
This exercise shows how a single classList.toggle() call can switch between visible and hidden states, creating a simple interactive component.

1b. State With Classes

Effective toggle components manage state, or the current condition of a component, through CSS classes rather than inline styles. For example, a component might be in a visible or hidden state, and that state determines how it appears on the page.

Using classes to manage state keeps JavaScript focused on behavior while CSS handles presentation. It also makes your code easier to maintain because all visual changes are defined in your stylesheet. You can think of a toggle as a simple two-state system that switches between hidden and visible states.

Diagram showing a toggle switching between two states: a hidden state with a class applied and a visible state with no class, triggered by a click event that toggles the class.

When a toggle switches between these states, you often need to update several parts of the interface at the same time. This can include showing or hiding content, changing the button text, and updating visual cues such as icons or colors. Using CSS classes allows you to coordinate all of these changes in one place.

EXAMPLE

A menu toggle that updates both the menu visibility and the button label:

<button id="menu-toggle">
<span class="toggle-text">Open Menu</span>
</button>
<nav id="menu">
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>

#menu {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}

#menu.active {
max-height: 200px;
}

#menu-toggle.active {
background-color: #333;
color: #fff;
}

const menuToggle = document.getElementById('menu-toggle');
const menu = document.getElementById('menu');
const toggleText = menuToggle.querySelector('.toggle-text');

menuToggle.addEventListener('click', function() {
menu.classList.toggle('active');
menuToggle.classList.toggle('active');

if (menu.classList.contains('active')) {
toggleText.textContent = 'Close Menu';
} else {
toggleText.textContent = 'Open Menu';
}
});
In this code:

  • The CSS defines an active state for both the menu and the button, with a smooth transition for the menu height.
  • A single click toggles the active class on both elements at once.
  • classList.contains('active') checks the new state so the button label can be updated to match.

try it
What method would you use to check whether an element currently has a specific class applied?
Use element.classList.contains('className'). This method returns true if the class is present and false if it is not, which lets you make decisions based on the current state of the element.

big idea
Toggle components use CSS classes to manage state, with JavaScript handling the logic of when to add or remove those classes. The classList API provides add(), remove(), toggle(), and contains() methods for complete state control.

term to know
State
The current condition of a component at any given moment, such as whether a panel is open or closed, which determines how it appears and behaves.


2. Accordions

An accordion is a vertically stacked set of expandable panels. Users click a header to expand its content, and the design typically collapses other open panels at the same time. Accordions work well for FAQs, product specifications, and any content that benefits from progressive disclosure.

Each accordion is built from repeating sections. Every section includes a clickable header and a content panel that expands or collapses. The header is typically a <button> for accessibility, and the content is controlled using a CSS class that determines whether it is visible or hidden.

EXAMPLE

The HTML and CSS for an accordion with two items:

<div class="accordion">
<div class="accordion-item">
<button class="accordion-header">What is HTML?</button>
<div class="accordion-content">
<p>HTML is the standard language for creating web pages.</p>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header">What is CSS?</button>
<div class="accordion-content">
<p>CSS controls the visual presentation of HTML elements.</p>
</div>
</div>
</div>

.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
padding: 0 16px;
}

.accordion-item.active .accordion-content {
max-height: 200px;
padding: 16px;
}

.accordion-header {
width: 100%;
padding: 16px;
text-align: left;
background-color: #e9e9e9;
border: none;
cursor: pointer;
}
In this code:

  • Each .accordion-item pairs a header with a content panel.
  • The default max-height: 0 keeps content collapsed.
  • When the parent .accordion-item has the active class, the content expands.

Screenshot of an accordion with two items, one expanded and one collapsed, in a Codespaces preview.

To make the accordion interactive, JavaScript responds to user clicks and updates which panel is open. Rather than directly changing styles, the code adds or removes a class, allowing CSS to control how the panel appears.

EXAMPLE

JavaScript that allows only one panel to be open at a time:

const headers = document.querySelectorAll('.accordion-header');
const items = document.querySelectorAll('.accordion-item');

const header1 = headers[0];
const header2 = headers[1];

const item1 = items[0];
const item2 = items[1];

header1.addEventListener('click', function() {
const isActive = item1.classList.contains('active');

// Close all panels
item1.classList.remove('active');
item2.classList.remove('active');

// Open this panel if it was previously closed
if (!isActive) {
item1.classList.add('active');
}
});

header2.addEventListener('click', function() {
const isActive = item2.classList.contains('active');

// Close all panels
item1.classList.remove('active');
item2.classList.remove('active');

// Open this panel if it was previously closed
if (!isActive) {
item2.classList.add('active');
}
});
In this code:

  • querySelectorAll() selects all headers and accordion items.
  • Each header is handled individually so the logic is easier to follow.
  • The code checks whether the clicked panel is already open.
  • Removing the active class from both items ensures that only one panel is open at a time.
  • The selected panel is then opened only if it was previously closed.

This pattern is called a single-open accordion, where opening one panel closes the others. This helps users focus on one section at a time and keeps the interface organized. In some cases, you may want to allow multiple panels to remain open, depending on how users need to interact with the content.

try it
Now you will build your own accordion component by applying the same structure and behavior from the example.

Open accordion.html in your Codespace, or create a new file and add the HTML and CSS examples from above.

Open your index.html file and add a link so you can access your accordion page in the browser. Here is an example:

Copy
<a href="accordion.html">View Accordion</a>
Preview index.html, and click the link to open your accordion page.

Create a file called accordion.js and link it in your HTML using the following:

Copy
<script src="accordion.js" defer></script>
Open accordion.js in your Codespace.

  • Select all accordion headers using document.querySelectorAll('.accordion-header').
  • Select all accordion items using document.querySelectorAll('.accordion-item').
  • Access the first two headers and items using index positions [0] and [1].
  • Add a click event listener to each header.
  • Inside each event listener:
    • Check whether the selected item already has the active class.
    • Remove the active class from both items.
    • Add the active class back to the selected item only if it was previously closed.
Preview your page.

Click each header:

  • Only one panel should be open at a time.
  • Clicking an open panel should close it.
How would you modify this code to allow multiple panels to remain open at the same time?
Instead of closing all panels, toggle the active class on the clicked item:

header1.addEventListener('click', function() {
item1.classList.toggle('active');
});

header2.addEventListener('click', function() {
item2.classList.toggle('active');
});
Each panel then opens and closes independently without affecting the others.

big idea
Accordions organize content into expandable sections, reducing visual clutter while keeping information accessible. By using CSS classes to control state and JavaScript to respond to user interaction, you can build flexible components that support different interaction patterns.

term to know
Accordion
A UI component consisting of vertically stacked sections where each section has a header that, when clicked, expands or collapses the associated content panel.


3. Tabs

A tab component displays multiple content panels in the same space, with only one panel visible at a time. Like an accordion, tabs organize content into sections and allow users to control what is shown. However, instead of expanding panels vertically, tabs switch content within a fixed area. Users click tab buttons to move between panels, making tabs useful for comparing related content such as product details, settings categories, or content filters.

A tab component has two main parts: a set of tab buttons and a set of content panels. Each button is connected to a specific panel so that the correct content can be displayed when the user interacts with it. This connection is often created using data attributes, which store a reference to the panel that should be shown.

EXAMPLE

The HTML structure for a tab component:

<div class="tabs">
<div class="tab-list">
<button class="tab-button active" data-tab="html">HTML</button>
<button class="tab-button" data-tab="css">CSS</button>
<button class="tab-button" data-tab="js">JavaScript</button>
</div>
<div class="tab-panels">
<div class="tab-panel active" id="html">
<h3>HTML Basics</h3>
<p>HTML provides the structure for web content.</p>
</div>
<div class="tab-panel" id="css">
<h3>CSS Styling</h3>
<p>CSS controls colors, layouts, and visual design.</p>
</div>
<div class="tab-panel" id="js">
<h3>JavaScript Interactivity</h3>
<p>JavaScript adds dynamic behavior to web pages.</p>
</div>
</div>
</div>
In this code:

  • The data-tab attribute on each button stores the id of the panel it controls.
  • The active class on one button and one panel sets the initial visible tab.
  • Connecting buttons to panels through a data attribute keeps the link declarative and easy to read.

Each tab button is connected to a panel using matching values. This connection allows JavaScript to determine which panel to display when a button is clicked.

Diagram of tab UI: Buttons with data-tab values map to panels with matching data-panel IDs; JavaScript uses dataset and querySelector to show the linked panel dynamically.

To make the tabs interactive, JavaScript must update both the selected button and the visible panel. When a user clicks a tab, the interface needs to reset the previous state and apply a new one so only the selected content is visible.

EXAMPLE

The CSS and JavaScript that make the tab structure functional:

CSS:

.tab-button.active {
background-color: #fff;
border-bottom: 2px solid #007bff;
}

.tab-panel {
display: none;
}

.tab-panel.active {
display: block;
}

JavaScript:

const btnHTML = document.querySelector('[data-tab="html"]');
const btnCSS = document.querySelector('[data-tab="css"]');
const btnJS = document.querySelector('[data-tab="js"]');

const panelHTML = document.getElementById('html');
const panelCSS = document.getElementById('css');
const panelJS = document.getElementById('js');

function clearTabs() {
btnHTML.classList.remove('active');
btnCSS.classList.remove('active');
btnJS.classList.remove('active');

panelHTML.classList.remove('active');
panelCSS.classList.remove('active');
panelJS.classList.remove('active');
}

btnHTML.addEventListener('click', function() {
clearTabs();
btnHTML.classList.add('active');
panelHTML.classList.add('active');
});

btnCSS.addEventListener('click', function() {
clearTabs();
btnCSS.classList.add('active');
panelCSS.classList.add('active');
});

btnJS.addEventListener('click', function() {
clearTabs();
btnJS.classList.add('active');
panelJS.classList.add('active');
});
In this code:

  • Each tab button is selected individually using its data-tab attribute.
  • The clearTabs() function removes the active class from all buttons and panels.
  • Each button click calls clearTabs() to reset the interface.
  • The clicked button and its matching panel are then given the active class.
  • This ensures that only one tab and one panel are active at a time.

This pattern ensures that only one tab is active at a time. Resetting all elements before activating the selected ones keeps the interface predictable and prevents multiple panels from appearing at once.

before you start
Create a new file called tabs.html in your Codespace and add the following starter HTML:

Copy
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tabs Example</title>
<style>
.tab-button.active {
background-color: #fff;
border-bottom: 2px solid #007bff;
}

.tab-panel {
display: none;
}

.tab-panel.active {
display: block;
}
</style>
</head>
<body>
<h1>Tabs Example</h1>

<div class="tabs">
<div class="tab-list">
<button class="tab-button active" data-tab="html">HTML</button>
<button class="tab-button" data-tab="css">CSS</button>
<button class="tab-button" data-tab="js">JavaScript</button>
</div>

<div class="tab-panels">
<div class="tab-panel active" id="html">
<p>HTML provides structure.</p>
</div>
<div class="tab-panel" id="css">
<p>CSS controls appearance.</p>
</div>
<div class="tab-panel" id="js">
<p>JavaScript adds behavior.</p>
</div>
</div>
</div>

<script src="tabs.js" defer></script>

</body>
</html>
Then create a file called tabs.js in the same folder.

Open your index.html file and add a link so that you can access this page:

Copy
<a href="tabs.html">View Tabs</a>
Preview index.html, and click the link to open your tabs page.

try it
Now you will build your own tab component by applying the same structure and behavior from the example.

Open tabs.html in your Codespace, or create a new file and add the starter HTML provided above.

Open your index.html file and add a link so that you can access your tabs page in the browser:

Copy
<a href="tabs.html">View Tabs</a>
Preview index.html, and click the link to open your tabs page.

Create a file called tabs.js and link it in your HTML using the following:

Copy
<script src="tabs.js" defer></script>
Open tabs.js in your Codespace.

  • Select each tab button individually using its data-tab attribute.
  • Select each tab panel using document.getElementById().
  • Create a function called clearTabs() that removes the active class from all buttons and panels.
  • Add a click event listener to each button.
  • Inside each event listener:
    • Call clearTabs().
    • Add the active class to the clicked button.
    • Add the active class to the matching panel.
Preview your page.

Click each tab:

  • Only one tab button should appear active at a time.
  • Only one content panel should be visible at a time.
Why do you remove the active class from all buttons and panels before activating a new tab?
Removing the active class from all elements ensures that only one tab and one panel are active at a time. This prevents leftover states and keeps the interface consistent.

watch
Watch this to build interactive tabs with JavaScript.

big idea
Tab components connect buttons to content panels using data attributes and manage state with CSS classes. By resetting and applying state on each interaction, you ensure that only the selected content is visible and the interface remains clear and predictable.


4. Reusable Patterns

After building interactive components like toggles, accordions, and tabs, it is important to organize your code so that it is easy to maintain and reuse. Reusable components follow consistent patterns that separate responsibilities and ensure they work in different environments.

One key principle is separation of concerns, which means keeping structure (HTML), presentation (CSS), and behavior (JavaScript) in their appropriate roles. In practice, JavaScript should control when something changes, while CSS controls how it looks.

EXAMPLE

The same toggle written with poor separation and good separation:

// Poor separation -- JavaScript controls styles directly
button.addEventListener('click', function() {
panel.style.display = 'block';
panel.style.backgroundColor = '#f5f5f5';
panel.style.padding = '20px';
});

// Good separation -- JavaScript only manages classes
button.addEventListener('click', function() {
panel.classList.add('visible');
});
In this code:

  • The first version mixes styling and behavior by setting styles directly in JavaScript.
  • The second version uses a class, allowing CSS to control how the component appears.
  • This makes the code easier to update and reuse.

Another important principle is progressive enhancement, which ensures that content remains usable even if JavaScript is not available. Instead of relying entirely on scripts, you start with a functional HTML structure and then add interactivity.

Progressive enhancement builds functionality in layers, starting with a basic experience and adding more features as support becomes available.

Diagram of progressive enhancement: HTML baseline shows all content, CSS adds layout and styling, and JavaScript adds interactivity (accordion); content remains usable without JS.

Each layer builds on the previous one, ensuring that content remains accessible even if styling or JavaScript is unavailable.

EXAMPLE

An accordion can display all content by default and only hide panels when JavaScript is available:

<html class="no-js">
<head>
<style>
/* Content visible when JavaScript is unavailable */
.no-js .accordion-content {
max-height: none;
}

/* Content collapsed when JavaScript is available */
.accordion-content {
max-height: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script>
// Remove no-js class as soon as the script runs
document.documentElement.classList.remove('no-js');
</script>
<!-- Accordion HTML here -->
</body>
</html>
In this code:

  • The <html> element starts with a no-js class, so the page assumes JavaScript is unavailable.
  • A small inline script removes the no-js class immediately before the rest of the page renders.
  • The CSS uses the presence or absence of no-js to decide whether content is visible or collapsed.

think about it
Consider one of the components you built in this lesson.

  • Where is the structure defined?
  • Where are visual styles defined?
  • What role does JavaScript play in changing the component’s behavior?
If you were to update the design of the component, which part of your code would you need to change?

big idea
Reusable components follow consistent patterns that separate structure, style, and behavior and ensure functionality even without JavaScript. These principles help you build components that are easier to maintain, reuse, and scale.

terms to know
Separation of Concerns
A design principle that keeps different parts of a system responsible for different tasks, such as using HTML for structure, CSS for presentation, and JavaScript for behavior.
Progressive Enhancement
A development strategy that starts with a functional baseline experience accessible to all users, then layers on advanced features for browsers and devices that support them.

summary
In this lesson, you learned how to build interactive components using toggles, including show and hide behavior and managing state with classes to control how elements appear. You then applied these ideas to accordions, which organize content into expandable sections, and tabs, which switch between panels within the same space.

Finally, you explored reusable patterns that help structure your code, including separating responsibilities between HTML, CSS, and JavaScript and building components that remain usable in different environments. Together, these patterns allow you to create interactive interfaces that are flexible, maintainable, and user-friendly.

Source: THIS TUTORIAL WAS AUTHORED BY SOPHIA LEARNING. PLEASE SEE OUR TERMS OF USE.

REFERENCES

Mozilla Developer Network. (n.d.). How the web works. developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/How_the_Web_works

Terms to Know
Accordion

A UI component consisting of vertically stacked sections where each section has a header that, when clicked, expands or collapses the associated content panel.

Progressive Enhancement

A development strategy that starts with a functional baseline experience accessible to all users, then layers on advanced features for browsers and devices that support them.

Separation of Concerns

A design principle that keeps different parts of a system responsible for different tasks, such as using HTML for structure, CSS for presentation, and JavaScript for behavior.

State

The current condition of a component at any given moment, such as whether a panel is open or closed, which determines how it appears and behaves.

Toggle

A user interface pattern that switches between two states, such as on/off or visible/hidden, typically activated by clicking a button or control.