# How to Use classList.toggle for Toggleable UI Components: 5 Patterns from the dom-projects Repository

> Master classList toggle for dynamic UI components. Explore 5 patterns from dom-projects and efficiently add/remove CSS classes for interactive elements.

- Repository: [Jisan Mia/dom-projects](https://github.com/jisan-mia/dom-projects)
- Tags: tutorial
- Published: 2026-03-04

---

**classList.toggle is a native DOM API method that adds a CSS class to an element when absent and removes it when present, returning a boolean indicating whether the class was added.**

The jisan-mia/dom-projects repository demonstrates practical implementations of interactive UI components using vanilla JavaScript. Throughout this collection, `classList.toggle` serves as the primary state management mechanism for everything from search field reveals to todo completion markers, eliminating the need for separate boolean flags in your application logic.

## Understanding the classList.toggle API

`Element.classList.toggle()` accepts a string argument representing the class name to toggle. When invoked, the method checks the element's current class list: if the specified class is absent, the browser adds it and returns `true`; if present, the browser removes it and returns `false`.

This declarative approach eliminates manual conditional logic. Instead of writing `if/else` blocks to check `element.classList.contains('active')` before adding or removing, a single call handles both operations atomically.

## Implementation Patterns from dom-projects

### Toggle Visibility for Search Components

In [`projects/search-field-reveal/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/search-field-reveal/script.js) (lines 5-8), the repository implements a search field that expands when clicking a magnifying glass button. The pattern attaches a click listener to the button and toggles the `active` class on the container element.

```javascript
const searchContainer = document.querySelector('.search');
const searchBtn = document.querySelector('.search-btn');

searchBtn.addEventListener('click', () => {
  searchContainer.classList.toggle('active');
});

```

The CSS defines `.search.active` to show the input field, while the default state hides it. This approach stores the visibility state directly in the DOM rather than in a JavaScript variable.

### Mark Items as Completed in Todo Lists

The [`projects/Todo-List-application/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/Todo-List-application/script.js) file (lines 31-34) demonstrates toggling completion states. When a user clicks a checkbox, the application toggles the `completed` class on the parent list item element.

```javascript
list.addEventListener('change', e => {
  if (!e.target.matches('[data-list-item-checkbox]')) return;
  const item = e.target.closest('.list-item');
  item.classList.toggle('completed');
});

```

Accompanying CSS applies `text-decoration: line-through` and reduced opacity to `.completed` elements, providing immediate visual feedback without re-rendering the entire list.

### Manage Exclusive Active States

For filter buttons in [`projects/advanced-todo/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/advanced-todo/script.js) (lines 90-100), the repository shows how to handle mutually exclusive selections. While `classList.toggle` works for binary states, exclusive groups require clearing the previous selection before activating the new one.

```javascript
let previousFilterElm = null;

filterSection.addEventListener('click', e => {
  if (!e.target.matches('button')) return;
  
  // Clear stale active state
  if (previousFilterElm) {
    previousFilterElm.classList.remove('active-filter');
  }
  
  // Activate new selection
  e.target.classList.add('active-filter');
  previousFilterElm = e.target;
});

```

This pattern uses `classList.remove` for the previous element and `classList.add` for the new one, rather than toggle, to ensure only one button remains active at a time.

### Coordinate Binary Mode Selections

In [`projects/scientific-calculator/js/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/scientific-calculator/js/script.js) (lines 371-376), the calculator switches between radians and degrees using coordinated toggles. Two buttons share the same `active-angle` class, but only one should display it at a time.

```javascript
radBtn.addEventListener('click', () => {
  radBtn.classList.toggle('active-angle');
  degBtn.classList.toggle('active-angle');
});

degBtn.addEventListener('click', () => {
  degBtn.classList.toggle('active-angle');
  radBtn.classList.toggle('active-angle');
});

```

Each click handler toggles both buttons simultaneously, transferring the active state from one to the other without explicit conditional checks.

### Control Accordion Expansion

The morse code translator in [`projects/morse-translator/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/morse-translator/script.js) (lines 262-266) implements collapsible FAQ sections. Clicking an accordion header toggles the `is-open` class, which CSS uses to show or hide the associated content panel.

```javascript
accordionHeader.addEventListener('click', function() {
  this.classList.toggle('is-open');
});

```

## Critical Implementation Details

When implementing toggleable components, several edge cases require attention:

- **Prevent Default Form Submissions**: In [`projects/advanced-todo/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/advanced-todo/script.js) (line 27), form submission handlers call `e.preventDefault()` before toggling classes. Without this, page reloads reset the DOM state and destroy any toggled classes.

- **Target the Correct Element**: The repository consistently uses `Element.closest()` to ensure classes toggle on the intended container rather than an inner element. For example, in todo applications, clicking a checkbox toggles the parent `<li>`, not the input itself.

- **Initialize State from Storage**: Applications like the advanced todo read from `localStorage` during initialization and render items with the correct initial classes. This prevents a mismatch between persisted data and UI state after page reload.

- **Handle Multiple Visual States**: In [`projects/js-todo/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/js-todo/script.js) (lines 118-124), the code toggles multiple classes simultaneously—`check`, `uncheck`, and `lineThrough`—to coordinate checkbox appearance with text styling.

## Summary

- **classList.toggle** atomically adds or removes a single class based on current presence, returning a boolean indicating the new state.
- The jisan-mia/dom-projects repository uses this API across six distinct implementations including search reveals, todo completions, and accordion panels.
- For mutually exclusive states, manually clear the previous selection with `classList.remove` before adding to the new element.
- Always call `preventDefault()` on form submissions that trigger toggles to avoid state loss from page reloads.
- Use `Element.closest()` to ensure event handlers target container elements rather than child triggers.

## Frequently Asked Questions

### Does classList.toggle work with multiple classes at once?

No, `classList.toggle()` accepts only one class name per invocation. To toggle multiple classes simultaneously, chain multiple calls: `element.classList.toggle('class1'); element.classList.toggle('class2');`. In [`projects/js-todo/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/js-todo/script.js), the repository handles multiple visual states by toggling several related classes in sequence within the same event handler.

### Can I use classList.toggle with CSS transitions?

Yes, `classList.toggle` pairs naturally with CSS transitions. When you toggle a class that changes properties with `transition` defined in CSS, the browser animates the change automatically. The dom-projects repository relies on this behavior for smooth search field reveals and accordion expansions, defining transition properties in CSS while JavaScript only manages the class state.

### How do I know if classList.toggle added or removed the class?

The method returns `true` if the class was added and `false` if it was removed. You can capture this boolean to branch logic: `const wasAdded = element.classList.toggle('active'); if (wasAdded) { console.log('Activated'); } else { console.log('Deactivated'); }`. This pattern appears in the scientific calculator implementation to synchronize binary button states.

### Is classList.toggle better than manually checking classList.contains?

For simple binary states, `classList.toggle` produces cleaner, more maintainable code than explicit `if/else` blocks using `classList.contains()`. However, for exclusive groups where only one item should be active, the repository shows that manually calling `classList.remove` on the previous element and `classList.add` on the new one provides clearer intent and prevents race conditions.