Build an Accessible Toggle
Build an Accessible Toggle
An interactive control should expose its state as well as changing its appearance. A native button can open a school notice using aria-expanded and a panel's hidden state.
Before this topic, review Handle Events on a List Parent if its terms are unfamiliar.
What you’ll learn
- Explain build an accessible toggle through the school example.
- Trace the shown operations and distinguish their result from displayed output.
- Make the practice change and explain its consequence: Two activations return the control to closed and hide its panel.
Work through the school example
This is JavaScript in a browser. Read the initial values, then follow each operation to its displayed result. A declaration names a value, a function call performs an operation, and console.log displays its arguments.
Create this markup in a separate index.html, then place the JavaScript in a script after these elements. Use this isolated practice page so the selectors refer to its markup.
<button id="toggle" aria-expanded="false" aria-controls="panel">Show notice</button><p id="panel" hidden>School opens at 9.</p>JavaScript · browser · complete script for the supplied markup
const button = document.querySelector("#toggle");
const panel = document.querySelector("#panel");
button.addEventListener("click", () => {
const open = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", String(!open));
panel.hidden = open;
});
button.click();
console.log(button.getAttribute("aria-expanded"));
console.log(panel.hidden);
Expected output
true
falseIntroduction
Creating interactive UI elements is a core part of building modern web applications. With JavaScript, you can manipulate content dynamically based on user interactions like clicks, mouse movements, and keyboard input.
Example 1: Toggle a Message Using a Button
Let’s simulate a button that shows and hides a message every time it's clicked. Since we are focusing on pure JavaScript, we'll emulate the UI using console logs to simulate behavior.
// Simulate button click to toggle message
let show = false;
function toggleMessage() {
show = !show;
if (show) {
console.log("Hello! The message is now visible.");
} else {
console.log("The message is hidden.");
}
}
// Simulate button clicks
toggleMessage(); // First click
toggleMessage(); // Second click
Hello! The message is now visible. The message is hidden.
Q: How does JavaScript remember the toggle state?
A: The variable show holds the current state. Each time toggleMessage() is called, it flips the boolean value using !show. This change is remembered across calls because the variable exists in the outer scope.
Example 2: Simulate a Dropdown Menu Toggle
Imagine a dropdown menu that opens and closes when you interact with it. We’ll use console logs to simulate open/close behavior.
let isDropdownOpen = false;
function toggleDropdown() {
if (isDropdownOpen) {
console.log("Dropdown closed.");
} else {
console.log("Dropdown opened with options: [Profile, Settings, Logout]");
}
isDropdownOpen = !isDropdownOpen;
}
toggleDropdown(); // Open
toggleDropdown(); // Close
Dropdown opened with options: [Profile, Settings, Logout] Dropdown closed.
Example 3: Simulate a Counter with Increase/Decrease Buttons
This is a common UI element where the user can increment or decrement a number displayed on screen.
let counter = 0;
function increase() {
counter++;
console.log("Counter:", counter);
}
function decrease() {
counter--;
console.log("Counter:", counter);
}
increase(); // 1
increase(); // 2
decrease(); // 1
Counter: 1 Counter: 2 Counter: 1
Q: Can we prevent the counter from going below zero?
A: Yes, add a check before decreasing:
function decreaseSafe() {
if (counter > 0) {
counter--;
console.log("Counter:", counter);
} else {
console.log("Counter can't go below 0.");
}
}
Example 4: Simulate Tab Switching
In UI, tabs are used to show different content. We can simulate this by switching between content blocks based on input.
const tabs = {
home: "Welcome to Home!",
about: "About Us: We love JavaScript.",
contact: "Contact us at support@example.com"
};
function switchTab(tabName) {
if (tabs[tabName]) {
console.log("Tab Content:", tabs[tabName]);
} else {
console.log("Tab not found.");
}
}
switchTab("home");
switchTab("about");
switchTab("contact");
switchTab("settings"); // Non-existent
Tab Content: Welcome to Home! Tab Content: About Us: We love JavaScript. Tab Content: Contact us at support@example.com Tab not found.
Example 5: Simulate a Modal (Popup)
Modals are dialog boxes that overlay the screen. Let’s simulate opening and closing a modal.
let isModalOpen = false;
function openModal() {
isModalOpen = true;
console.log("Modal Opened: Are you sure you want to proceed?");
}
function closeModal() {
isModalOpen = false;
console.log("Modal Closed.");
}
openModal();
closeModal();
Modal Opened: Are you sure you want to proceed? Modal Closed.
Conclusion
These examples demonstrate how to simulate interactive UI elements using pure JavaScript in a way that helps you build intuition for real browser-based behavior. In real apps, instead of using console.log(), you would manipulate the DOM, but the logic behind toggling states, handling events, and updating data remains the same.
Quick Recap
- Use booleans to track open/close or show/hide states.
- Encapsulate behavior in functions that can be triggered by events.
- Use objects to simulate multiple views like tabs or menus.
Practice Challenge
Try creating a simulated Light/Dark mode toggle that logs the current theme each time it's toggled.
Recognize the important boundary
This is a small disclosure demo, not hidden essential lesson text. Native buttons support keyboard activation; a styled div needs extra semantics and behavior.
Try a small variation
JavaScript · original source for practice
const button = document.querySelector("#toggle");
const panel = document.querySelector("#panel");
button.addEventListener("click", () => {
const open = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", String(!open));
panel.hidden = open;
});
button.click();
console.log(button.getAttribute("aria-expanded"));
console.log(panel.hidden);
Your task: Replace button.click(); with button.click(); button.click();. Predict the result before running the edited program.
The source is repeated here. If the edit targets the HTML rather than the script, use the markup repeated below; the worked answer also repeats the changed markup.
Markup used by this section:
<button id="toggle" aria-expanded="false" aria-controls="panel">Show notice</button><p id="panel" hidden>School opens at 9.</p>Worked answer and reasoning
Two activations return the control to closed and hide its panel.
Markup for this edited program:
<button id="toggle" aria-expanded="false" aria-controls="panel">Show notice</button><p id="panel" hidden>School opens at 9.</p>JavaScript · complete worked variation
const button = document.querySelector("#toggle");
const panel = document.querySelector("#panel");
button.addEventListener("click", () => {
const open = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", String(!open));
panel.hidden = open;
});
button.click();
button.click();
console.log(button.getAttribute("aria-expanded"));
console.log(panel.hidden);
Expected output
false
truePredict the original program
JavaScript · original program for this question
const button = document.querySelector("#toggle");
const panel = document.querySelector("#panel");
button.addEventListener("click", () => {
const open = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", String(!open));
panel.hidden = open;
});
button.click();
console.log(button.getAttribute("aria-expanded"));
console.log(panel.hidden);
Markup used by this section:
<button id="toggle" aria-expanded="false" aria-controls="panel">Show notice</button><p id="panel" hidden>School opens at 9.</p>Keep the useful idea
An interactive control should expose its state as well as changing its appearance. A native button can open a school notice using aria-expanded and a panel's hidden state.
Check your variation against this explanation: Two activations return the control to closed and hide its panel.
Use the JavaScript array reference library for individual array operations and boundary tests to check a rule independently.
Reaching the recap and completing the quizzes are separate milestones.