- 🧠 Changing button text makes things clearer and easier to use.
- ⚙️ JavaScript is needed to change button text as content shows or hides.
- 📱
aria-expandedandaria-controlsare key accessibility settings, as WCAG says. - ⚡ CSS transitions with JavaScript toggles make things feel faster.
- 🏗️ Clean code and event delegation help manage many toggles in big apps.
When you build user interfaces, being able to show or hide things simply is a basic interactive part. And doing it well means more than just showing or hiding elements. Using a JavaScript button to switch content visibility and change button text makes things easier to use for everyone. This guide looks at the HTML, JavaScript, and accessibility methods you need to make good, easy-to-use toggles.
HTML Setup for Toggle Buttons
Before writing any JavaScript, it's important to start with HTML elements that make sense and work for everyone. Here's an example of good HTML for a content toggle:
<button class="toggle-btn" aria-expanded="false" aria-controls="extraContent">
Show more
</button>
<div id="extraContent" class="hidden" hidden>
<p>This is extra content that can be shown or hidden.</p>
</div>
Why This Setup Is Important
<button>Element: It makes sense and you can use it with a keyboard, unlike<div>or<span>.aria-expanded: This ARIA (Accessible Rich Internet Applications) setting changes to show if the content is open or closed. Screen readers use this to tell people what's happening.aria-controls: This connects the button to its content. It helps screen readers give better information.hiddenAttribute: This makes sure the content is not seen or read by screen readers. It also works as a backup across different browsers when you use a class to control it..hiddenClass: You use this to style the hidden state with CSS. This helps with animations or showing what's happening.
Getting this setup right from the start helps developers give a better, stronger experience to all users.
JavaScript Toggle Button: How It Works
After you have the HTML, adding interactive parts is easy. Here's a basic JavaScript setup that changes both if content is seen and what the button says.
<script>
const toggleBtn = document.querySelector('.toggle-btn');
const content = document.getElementById('extraContent');
toggleBtn.addEventListener('click', () => {
const isHidden = content.hasAttribute('hidden');
if (isHidden) {
content.removeAttribute('hidden');
toggleBtn.innerText = 'Hide';
toggleBtn.setAttribute('aria-expanded', 'true');
} else {
content.setAttribute('hidden', '');
toggleBtn.innerText = 'Show more';
toggleBtn.setAttribute('aria-expanded', 'false');
}
});
</script>
Important Things to Know
innerTextchanges what the button says. This is key for telling the user what is happening.- The toggle uses
hasAttribute('hidden')to reliably figure out if content is seen across all main browsers. - ARIA attributes must change every time the state changes. This keeps screen readers up to date.
This way, by matching content visibility changes with button text updates and accessibility settings, this method takes care of the main need for people who can see and people who cannot.
Make It Better: Smooth Looks and Clear Text
User interfaces should feel natural, not jumpy. Adding small transitions and using clear button text makes things much better for the user.
Other Button Text Ideas
Try these clear options instead of "Show More" and "Hide":
- "Expand details" · "Collapse details"
- "Show less" · "Show more"
- "Read more" · "Hide content"
Clear labels on buttons make people less confused and more sure when they click.
Showing and Hiding Content with CSS
To make hidden content appear smoothly, use CSS transitions with JavaScript to add or remove classes:
.hidden {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.visible {
max-height: 500px; /* Adjust to match expected size */
}
Then change your JavaScript like this:
if (isHidden) {
content.classList.add('visible');
content.classList.remove('hidden');
content.removeAttribute('hidden');
toggleBtn.innerText = 'Hide';
} else {
content.classList.remove('visible');
content.classList.add('hidden');
content.setAttribute('hidden', '');
toggleBtn.innerText = 'Show more';
}
This method gives quick feedback. It also keeps things working for everyone and easy to understand.
Clean Code: Make It Reusable
When user interfaces get bigger, breaking code into smaller parts is key. Putting the toggle code into a function helps with handling more toggles and makes it easier to keep up.
Toggle Function
const SHOW_TEXT = 'Show more';
const HIDE_TEXT = 'Hide';
function toggleContent(button, contentId) {
const content = document.getElementById(contentId);
const isHidden = content.hasAttribute('hidden');
if (isHidden) {
content.removeAttribute('hidden');
button.innerText = HIDE_TEXT;
button.setAttribute('aria-expanded', 'true');
} else {
content.setAttribute('hidden', '');
button.innerText = SHOW_TEXT;
button.setAttribute('aria-expanded', 'false');
}
}
Connect Many Toggles
document.querySelectorAll('.toggle-btn').forEach(btn => {
btn.addEventListener('click', () => {
const targetId = btn.getAttribute('aria-controls');
toggleContent(btn, targetId);
});
});
You can now use this same code for any toggle button and its content. Just give them matching aria-controls attributes in your document.
Handle Many Toggles: Event Delegation
If your app shows many toggle buttons, like in a Q&A section or filter menu, adding a separate listener for each one can slow things down and make your JavaScript file bigger.
A Better Method: Event Delegation
document.body.addEventListener('click', (event) => {
if (!event.target.matches('.toggle-btn')) return;
const button = event.target;
const targetId = button.getAttribute('aria-controls');
toggleContent(button, targetId);
});
Good Points:
- Fewer event listeners mean less memory and CPU use.
- Buttons added later work right away.
- It's easier to find problems with all click logic in one spot.
Event delegation works well for big pages with lots of interaction or parts that show up later.
Accessibility Is Key: Follow WCAG Rules
Accessibility (a11y) is a must. Your button toggle needs to follow WCAG rules so it works for people who use keyboards, screen readers, and other helper tools.
Accessibility Check
- ✅ Use real
<button>elements so they make sense and work with keyboards. - ✅ Give
aria-expanded="true/false"to show if the toggle is open or closed. - ✅ Connect each button to its content using
aria-controls. - ✅ Do not hide content with only CSS display rules. Use
hiddenor ARIA methods to keep screen readers from seeing it.
Doing these things helps meet standards and gives a fair experience to all users.
When to Use a JavaScript Toggle Button
Adding a show/hide feature with text that changes is a small but strong way to interact. People use it often in today's web apps. Here are common times to use it:
- Q&A Sections: Show answers under each question with a "+" or "Show Answer" button.
- Dropdowns in Dashboards: Show or hide extra numbers or filters in parts of the UI that can open and close.
- "Read More" Sections on Blog Posts: Keep long paragraphs short and make it easier to scan.
- Mobile Menus or Sidebars: Open up navigation in small areas.
- Product Pages (Online Stores): Show or hide details, shipping rules, or reviews.
Every situation gets something special from clear buttons that change their text.
Works with Frameworks: React and Vue
React.js Toggle Example
function TogglePanel() {
const [open, setOpen] = React.useState(false);
return (
<>
<button
onClick={() => setOpen(prev => !prev)}
aria-expanded={open}
aria-controls="toggleSection"
>
{open ? 'Hide' : 'Show more'}
</button>
<div id="toggleSection" hidden={!open}>
<p>Additional details go here.</p>
</div>
</>
);
}
Use React hooks like useState to make toggles that change with JSX for the text.
Vue.js Toggle Example
<template>
<div>
<button
@click="show = !show"
aria-expanded="show"
aria-controls="moreText"
>
{{ show ? 'Hide' : 'Show more' }}
</button>
<div id="moreText" v-if="show">
<p>Extra content that can be toggled.</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
show: false
};
}
}
</script>
Vue’s v-if lets the DOM update as the component's state changes.
Mistakes to Avoid
User interfaces that change can cause errors fast if you don't follow good habits. Here are some mistakes to look out for:
- ❌ Using
<div>or<span>for clickable things—this stops people from using them easily. - ❌ Not using
aria-expanded, which means screen readers can't tell the right state. - ❌ Not changing button text when content shows or hides.
- ❌ Putting the same text labels in many places in your code.
Not making these mistakes keeps your interfaces strong, usable for everyone, and easy to change.
Browser Support
All new browsers (Chrome, Firefox, Safari, Edge, Opera) fully work with the DOM features, accessibility tools, and CSS transitions in this guide. For older browsers (like Internet Explorer), make sure the hidden attribute also has CSS support:
[hidden] {
display: none !important;
}
This makes sure that hidden content truly stays hidden both visually and for people using helper tools.
Toggling with Only CSS: The Checkbox Hack
If you can't use JavaScript or if what you need is very simple, here's a way to do it with only CSS:
<input type="checkbox" id="toggleCheck" hidden />
<label for="toggleCheck">Show Text</label>
<div class="toggle-content">This is the toggleable content.</div>
#toggleCheck:not(:checked) ~ .toggle-content {
display: none;
}
Downsides
✅ No JavaScript needed
❌ Cannot change the button text as things happen.
❌ Needs CSS changes and HTML that is not easy to understand.
Only use this method for quick examples or very small websites.
How to Make It Fast
As your app gets bigger, making it fast is very important. Here are some main tips:
- Save DOM references outside of event loops.
- Do less direct changes to the DOM.
- Use event delegation instead of many separate listeners.
- Do not force reflows with
.offsetHeightunless you really have to. - Use
requestAnimationFrame()when making animations or controlling UI updates.
These methods help your toggle buttons stay fast and work well, even with many things on the screen.
Final Notes + Full Code Example
Mixing text that changes, clear HTML, smooth transitions, and clean JavaScript shows what makes good user interfaces today. Here’s an example ready for use:
<button class="toggle-btn" aria-expanded="false" aria-controls="extraContent">
Show more
</button>
<div id="extraContent" class="hidden" hidden>
<p>This is extra content.</p>
</div>
<script>
const SHOW_TEXT = 'Show more';
const HIDE_TEXT = 'Hide';
function toggleContent(btn) {
const contentId = btn.getAttribute('aria-controls');
const content = document.getElementById(contentId);
const isHidden = content.hasAttribute('hidden');
if (isHidden) {
content.removeAttribute('hidden');
btn.innerText = HIDE_TEXT;
btn.setAttribute('aria-expanded', 'true');
} else {
content.setAttribute('hidden', '');
btn.innerText = SHOW_TEXT;
btn.setAttribute('aria-expanded', 'false');
}
}
document.querySelectorAll('.toggle-btn').forEach(btn => {
btn.addEventListener('click', () => toggleContent(btn));
});
</script>
Make your UIs helpful and easy to understand by using button labels that change, accessibility, and logic that can grow. The small parts are not just for looks—they are what good design is built on.
Citations
- Nielsen Norman Group. (n.d.). Visibility of system status. Retrieved from https://www.nngroup.com/articles/visibility-system-status/
- W3C. (2018). WAI-ARIA Authoring Practices 1.1. Retrieved from https://www.w3.org/TR/wai-aria-practices/#disclosure
- Mozilla Developer Network. (n.d.). Element.innerText – Web APIs. Retrieved from https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText