Knowledge Base/guides/classList in JavaScript: add, remove, toggle, and contains

classList in JavaScript: add, remove, toggle, and contains

classList in JavaScript is the preferred way to turn element state into CSS classes: add one class, remove one class, flip one class, or ask whether one class is present. The non-obvious part is that this keeps JavaScript responsible for state changes and CSS responsible for appearance. Assigning className blindly replaces the entire class string, which can erase layout, theme, or state classes the element already needed.

If the page already has an element like this:

<article id="plan-card" class="card plan-card">
  <h2>Team plan</h2>
  <button id="choose-plan">Choose plan</button>
</article>

Use classList to add a state class without touching the base classes:

const card = document.querySelector("#plan-card");

card.classList.add("is-selected");

The element now has all three classes:

<article id="plan-card" class="card plan-card is-selected">

That is the core idea. JavaScript changes a named state. CSS decides what that state looks like.

Contents

What classList gives you

Every element has a classList property. It is a DOMTokenList, which means the browser treats each class as a separate token instead of making you edit one space-separated string by hand.

The methods you will use most are:

MethodUse it when
classList.add("name")A class should be present
classList.remove("name")A class should be absent
classList.toggle("name")A class should flip on or off
classList.contains("name")You need to check whether a class is present
classList.replace("old", "new")One state class should become another

These methods operate on individual class names. They do not rebuild the whole class attribute unless you ask for that by assigning to className.

Use add for a state that should be present

Use add when an element should enter a state:

const step = document.querySelector("#step-payment");

step.classList.add("is-current");

This is safer than assigning className:

step.className = "is-current";

That assignment removes every existing class. If the element started as:

<li id="step-payment" class="step checkout-step">Payment</li>

After step.className = "is-current", it becomes:

<li id="step-payment" class="is-current">Payment</li>

The step and checkout-step classes are gone. That may break layout, spacing, colors, or selectors elsewhere on the page. classList.add says exactly what you mean: keep the element as it is, and add one more state class.

You can add more than one class by passing each class as its own argument:

step.classList.add("is-current", "is-highlighted");

Use remove for a state that should be gone

Use remove when an element should leave a state:

const banner = document.querySelector("#trial-banner");

banner.classList.remove("is-hidden");

If is-hidden is present, the browser removes it. If it is already absent, nothing breaks.

This is useful for temporary state classes:

<p id="trial-banner" class="banner is-hidden">
  Your trial ends tomorrow
</p>
const banner = document.querySelector("#trial-banner");

banner.classList.remove("is-hidden");

The result keeps the base styling:

<p id="trial-banner" class="banner">
  Your trial ends tomorrow
</p>

The intent is narrow. You are not saying "make this element a banner." It already is one. You are saying "this banner is no longer hidden."

Use toggle for on and off state

Use toggle when the same action can move an element into either state:

const button = document.querySelector("#menu-button");
const panel = document.querySelector("#menu-panel");

button.addEventListener("click", () => {
  panel.classList.toggle("is-open");
});

Each click flips the class:

  • If is-open is missing, toggle adds it.
  • If is-open is present, toggle removes it.

That maps cleanly to UI controls such as menus, accordions, filters, favorites, selected cards, and dark mode switches.

toggle also accepts a second boolean argument, often called the force argument:

panel.classList.toggle("is-open", shouldOpen);

When shouldOpen is true, the class is added. When shouldOpen is false, the class is removed. This is useful when you already have a boolean and do not want to flip the state blindly.

function setPanelOpen(panel, shouldOpen) {
  panel.classList.toggle("is-open", shouldOpen);
}

setPanelOpen(panel, true);
setPanelOpen(panel, false);

Use plain toggle("is-open") for "switch it." Use toggle("is-open", shouldOpen) for "make it match this boolean."

Use contains to read the current state

contains checks whether a class is currently present:

const card = document.querySelector("#resource-card");

console.log(card.classList.contains("is-favorited"));

It returns true or false. It does not change the element.

This matters after a toggle, especially when another part of the UI needs to match the class state:

const card = document.querySelector("#resource-card");
const button = document.querySelector("#favorite-button");

button.addEventListener("click", () => {
  card.classList.toggle("is-favorited");

  if (card.classList.contains("is-favorited")) {
    button.textContent = "Remove favorite";
  } else {
    button.textContent = "Save favorite";
  }
});

The class is the source of truth for the visual state. The button label is updated from that state, so repeated clicks do not leave the card and label disagreeing.

toggle also returns the final state:

const isFavorited = card.classList.toggle("is-favorited");

button.textContent = isFavorited ? "Remove favorite" : "Save favorite";

Either version is fine. Use the one that reads more clearly in the code around it. For text update tradeoffs, see textContent vs innerHTML vs innerText.

Use replace for one state becoming another

Use replace when one class should become another:

const status = document.querySelector("#deploy-status");

status.classList.replace("status-pending", "status-success");

This avoids leaving contradictory state classes on the same element:

<p id="deploy-status" class="status-pill status-pending">
  Pending
</p>

After replace, the shared class remains and the state class changes:

<p id="deploy-status" class="status-pill status-success">
  Pending
</p>

The text still says "Pending" because classes do not update text. If the visible label should change too, update the label deliberately:

const status = document.querySelector("#deploy-status");

status.classList.replace("status-pending", "status-success");
status.textContent = "Deployed";

replace returns true if it found and replaced the old class. It returns false if the old class was not present.

const changed = status.classList.replace("status-pending", "status-success");

if (!changed) {
  console.warn("Expected deploy status to be pending first.");
}

That return value is handy when the old state is supposed to be known.

Keep styling decisions in CSS

The clean classList pattern has two parts:

  1. CSS defines how each state looks.
  2. JavaScript adds, removes, or checks state classes.
.menu {
  display: none;
}

.menu.is-open {
  display: block;
}

.favorite-button {
  border-color: #aeb7c2;
}

.resource-card.is-favorited .favorite-button {
  border-color: #f59e0b;
  background: #fff7ed;
}
const menuButton = document.querySelector("#menu-button");
const menu = document.querySelector("#menu");

menuButton.addEventListener("click", () => {
  menu.classList.toggle("is-open");
});

That is usually better than changing many inline styles from JavaScript:

menu.style.display = "block";
menu.style.padding = "1rem";
menu.style.border = "1px solid #aeb7c2";

Inline styles have their place, but they make state harder to scan. A class name like is-open, is-selected, is-loading, or has-error tells you what state the element is in. CSS can then handle the details.

When a visual class also changes meaning for assistive technology, update the relevant attribute too:

const button = document.querySelector("#menu-button");
const menu = document.querySelector("#menu");

button.addEventListener("click", () => {
  const isOpen = menu.classList.toggle("is-open");

  button.setAttribute("aria-expanded", String(isOpen));
});

Classes control styling. Attributes such as aria-expanded communicate state to users and tools that do not rely on your CSS.

Common classList mistakes

The first mistake is assigning className when you only meant to add a state:

card.className = "is-selected";

That line replaces the whole class string. Prefer:

card.classList.add("is-selected");

The second mistake is including the selector dot:

card.classList.add(".is-selected");

classList.add wants the class name, not the CSS selector. Use:

card.classList.add("is-selected");

The third mistake is passing several classes as one space-separated string:

card.classList.add("is-selected is-highlighted");

Class tokens cannot contain spaces. Pass each class separately:

card.classList.add("is-selected", "is-highlighted");

The fourth mistake is toggling when you already know the desired state:

function openMenu(menu) {
  menu.classList.toggle("is-open");
}

That function is named openMenu, but it closes the menu if the menu is already open. Make the state explicit:

function openMenu(menu) {
  menu.classList.add("is-open");
}

function closeMenu(menu) {
  menu.classList.remove("is-open");
}

Or use the force argument:

function setMenuOpen(menu, isOpen) {
  menu.classList.toggle("is-open", isOpen);
}

The fifth mistake is calling classList on a missing element:

const card = document.querySelector("#resource-card");

card.classList.add("is-favorited");

That code assumes the selector matched. If the element is optional, guard it:

const card = document.querySelector("#resource-card");

if (card) {
  card.classList.add("is-favorited");
}

If you are unsure whether you need one element or many, read querySelector vs querySelectorAll.

Practice path

Start with the lesson on styling elements in the DOM if you want the guided version inside the DOM course.

Then practice the individual classList moves:

The goal is not to memorize method names. The goal is to see CSS classes as the browser-friendly place to represent visible element state.

Key Takeaways

  • Use classList when JavaScript needs to manage CSS classes one at a time.
  • Prefer state classes such as is-open, is-selected, is-loading, and has-error over piles of inline style changes.
  • Do not assign className unless you really mean to replace every class on the element.
  • Use toggle("class", boolean) when the desired state is already known.
  • If a class changes user-facing meaning, update the matching text or ARIA attribute deliberately.
Share this article:
javascriptdomcssbrowser-apis