The ability to dynamically add, remove and toggle CSS classes on HTML elements is an extremely useful technique for web developers. It allows us to change styling and behavior without manually editing HTML or CSS code. In this comprehensive guide, we’ll explore the ins and outs of manipulating classes from JavaScript.

Why Dynamic Classes Matter

Before diving into code specifics, it’s worth understanding why the ability to manipulate classes dynamically is so valuable:

Creating interactivity and UI states – Toggling classes allows building complex, interactive interfaces and UI components without much custom CSS code. For example, opening a modal overlay involves temporarily adding a "open" class.

Animations/Transitions – CSS animations are often triggered on class changes. For example, cascading content animation on accordions relies on toggling visibility classes in JS.

Adaptive design – Dynamic classes allow altering styling based on user interaction and application state. This enables use-cases like toggle themes, responsive design, and more.

Accessibility – Semantic classes can communicate non-visual state information for accessibility, like whether an accordion is open or closed for screen readers.

Performance – Applying bulk styling by changing a class is faster than directly manipulating many individual style properties. (More on this later…)

In summary, dynamic classes are integral to building maintainable, interactive websites and web apps. Understanding how to effectively manage classes from JavaScript is a must-have skill.

Accessing Elements

Before adding classes, we need to access the HTML elements we want to manipulate. Here are modern best practices for querying elements from JavaScript:

// Newer Methods ✅

// Get by ID
const modal = document.getElementById(‘modal‘); 

// CSS selector 
const panels = document.querySelectorAll(‘.panel‘);

// Older Methods ❌ 

// HTML collection - Requires iteration
const items = document.getElementsByClassName(‘item‘);  

// Avoids direct DOM manipulation
const modalTriggers = Array.from(document.querySelectorAll(‘.trigger‘));

Key reasons to prefer querySelector and getElementById:

  • Simplicity – No DOM collection processing required.
  • Versatility – Use any valid CSS selector to target elements.
  • Performance – Direct access without needing to iterate.

Legacy methods like getElementsByClassName return live HTMLCollections, which require conversion to arrays before we can iterate or access directly. This extra processing can get expensive, especially in hot code paths that run frequently.

Adding Classes with classList

Once we have an element reference, we can use the classList property to manipulate its classes:

const panel = document.querySelector(‘.panel‘);

// Add
panel.classList.add(‘active‘);

// Remove 
panel.classList.remove(‘active‘);

// Toggle  
panel.classList.toggle(‘active‘); 

classList has the following advantages compared to directly setting className:

  • Browser support is excellent – over 95% globally as of Feb 2023. Only issue is IE8 and below.
  • Simple API avoids dealing with strings directly.
  • Methods gracefully handle invalid class names without errors.
  • Batch adding classes avoids multiple separate DOM changes.

We can pass multiple classes to .add() and .remove() to change several at once:

// Add multiple classes  
panel.classList.add(‘expanded‘, ‘active‘); 

// Remove multiple
panel.classList.remove(‘expanded‘, ‘active‘);

And even check if an element contains a specific class:

if(panel.classList.contains(‘active‘)) {
  // Element has active class
}

There are also quite a few less commonly used classList methods, like .replace() and toggling based on a condition with .toggle(). See documentation for all available methods.

Browser Support and Polyfills

As mentioned earlier, classList support is excellent in all modern browsers. But there are some edge cases around legacy browser support worth mentioning:

  • IE9 and below do not support classList at all.
  • Some early Android Browser versions implemented an incomplete version missing some functionality.

For supporting these legacy browsers, we have a couple options:

  1. Use a Polyfill – Automatically patch and shim missing functionality. classList.js is a popular choice.

  2. Manually detect and fallback – Check if classList exists on element, and if not, use a custom className setter function with similar behavior.

Here is an example manual fallback helper function:

function setClass(el, className, isAdd) {
  if (el.classList) {
    el.classList[isAdd ? ‘add‘ : ‘remove‘](className);    
  } else {  
    const existing = el.className.split(‘ ‘);
    const index = existing.indexOf(className);
    if (isAdd && index < 0) {
      existing.push(className);
    }
    if (!isAdd && index >= 0) {
      existing.splice(index, 1);
    }
    el.className = existing.join(‘ ‘); 
  }
}

This simulates .add()/.remove() functionality by directly handling the className string.

So in summary – for modern browsers implementing classList natively use it directly. In edge cases of legacy browser support either use a polyfill or manually handle updates.

Setting class name Directly

We can also completely overwrite an element‘s classes by setting className directly:

const button = document.querySelector(‘.btn‘);

function toggleActive() {
  if(button.className === ‘btn‘) {
    // Set classes    
    button.className = ‘btn active pulse‘;
  } else {
    // Remove all classes
    button.className = ‘btn‘; 
  }
}

Performance Advantages

Changing an element‘s entire className can actually be faster than toggling individual classes in some cases.

Benchmarks have shown assigning className can outperform .add()/.toggle() by 2-4x depending on number of classes being changed.

For example, this simple className update:

el.className = ‘class1 class2‘;

…is significantly faster than:

el.classList.add(‘class1‘);
el.classList.add(‘class2‘); 

Reason being – updating classList requires changing multiple DOM properties behind the scenes. Wherease className acts on the single class attribute.

However, keep in mind manually building up long className strings can get messy. classList promotes cleaner code.

Pitfalls to Avoid

One thing to watch out for when directly setting className is all existing classes get replaced. For example:

el.className = ‘new-class‘;

…would wipe out any classes that were already applied to the element!

To safely append new classes, ensure you concatenate onto the previous string:

// Append class rather than overwrite
el.className += ‘ extra-class‘; 

Or manually compose strings while preserving existing classes:

const prevClasses = el.className;
el.className = `${prevClasses} new-class`.trim();

So in summary, although directly setting className can provide performance advantages, it requires more care to handle properly compared to classList methods.

Putting Into Practice

Now that we‘ve covered core concepts and APIs, let’s walk through some practical use cases for dynamic classes.

Toggling Interface States

A common example is toggling visibility of UI components, like opening and closing menus.

Here is sample markup for navigation menu with open/close buttons:

<button class="open-menu">≡</button>

<nav>
  <div class="menu">
    ...
  </div>

  <button class="close-menu">×</button>  
</nav>

We can style the menu to initially be hidden:

.menu {
  display: none; 
}

.menu.visible {
  display: block;
} 

Then toggle a visible class from JavaScript to show/hide:

let menuOpen = false;

function toggleMenu() {
  const menu = document.querySelector(‘.menu‘);

  if(!menuOpen) { 
    menu.classList.add(‘visible‘);
  } else {
    menu.classList.remove(‘visible‘);
  }

  menuOpen = !menuOpen; // Inverse boolean  
}

openMenuBtn.addEventListener(‘click‘, toggleMenu);
closeMenuBtn.addEventListener(‘click‘, toggleMenu); 

This workflow applies for all kinds of UI components – tabs, modals, dropdowns etc. Toggling classes allows clean separation of concerns between CSS, JS and markup.

Creating Image Sliders

Another great use case is image sliders and carousels – showing one slide at a time using classes.

Consider this basic slider markup:

<div class="slider">
  <img src="img1.jpg"> 
  <img src="img2.jpg">
  <img src="img3.jpg">
</div>

We use classes to decide which to show:

.slider img {
  display: none;  
}

.slider img.active {
  display: block;
}

And can now toggle the active class in JavaScript:

let currentIndex = 0; 

function showNextSlide() {
  // Remove active class from all
  slides.forEach(slide => slide.classList.remove(‘active‘));

  // Get current slide and loop index  
  currentIndex++;
  if(currentIndex >= slides.length) {
    currentIndex = 0; 
  }
  const currentSlide = slides[currentIndex];

  // Activate next slide 
  currentSlide.classList.add(‘active‘); 
}

So applying this pattern of toggle one element dynamically at a time is extremely powerful for sliders, tabs,accordions and more.

Complex Component Logic

Taking this concept even further, we can build entire components with logic driven by class toggling.

For example form elements to handle validation states:

<form>
  <input class="text-input">

  <div class="messages">
    <span class="error"></span>  
    <span class="success"></span>
  </div>

  <button class="submit-btn">Submit</button>
</form>  

Some simplified logic:

const input = document.querySelector(‘.text-input‘);
const errorMsg = document.querySelector(‘.error‘);
const submitBtn = document.querySelector(‘.submit-btn‘);

// Validation check  
function validateInput() {
  if(input.value.length > 5) {
    // Valid
    input.classList.add(‘valid‘);
    input.classList.remove(‘invalid‘);

    errorMsg.classList.remove(‘active‘); 

    // Allow submit
    submitBtn.disabled = false;
  } else {  
    // Invalid 
    input.classList.add(‘invalid‘);
    input.classList.remove(‘valid‘);

    errorMsg.classList.add(‘active‘);

    submitBtn.disabled = true;
  } 
}

input.addEventListener(‘input‘, validateInput);  

Here classes both communicate current validation state and alter styling/behavior accordingly.

This is a very common pattern for handling form logic and component state changes.

Optimization Considerations

One downside to excessive class manipulation is it can incur DOM reflows, where the browser must recalculate layout. This is especially true if changing styling that affects document flow, like toggling between display: none; and display: block;

We can reduce the quantity of reflows by avoiding active DOM manipulation and batching changes together.

Use DocumentFragment

For example instead of toggling classes on elements directly in the live DOM:

tabs.forEach(tab => {
  tab.classList.remove(‘active‘); 
});  

targetTab.classList.add(‘active‘);

…we can build up changes in a DocumentFragment instead:

// Fragment avoids live DOM mutations  
const docFrag = document.createDocumentFragment();

tabs.forEach(tab => {
  // Clone nodes to fragment
  const tabFrag = tab.cloneNode(true);  

  tabFrag.classList.remove(‘active‘);

  docFrag.appendChild(tabFrag);  
});

const activeTabFrag = targetTab.cloneNode(true);
activeTabFrag.classList.add(‘active‘);
docFrag.appendChild(activeTabFrag);

// Now, apply fragment in one shot
container.innerHTML = ‘‘; 
container.appendChild(docFrag);

This batches changes together into a single redraw instead of multiple, producing better performance.

There are also other advanced techniques like using requestAnimationFrame() for timing class application carefully in rendering cycles.

In summary – excessive DOM mutations can be expensive, so employ batching/fragment strategies in performance critical code.

Conclusion and Key Takeaways

The ability to dynamically manipulate CSS classes is an extremely valuable tool for web developers building interactive applications. Here is a recap of best practices covered in this guide:

  • Utilize classList for simple, cross-browser compliant class toggling. Methods like .add(), .remove() and .toggle() to manipulate classes.

  • Set className directly for better performance in certain edge cases. But handle with care to avoid clearing existing classes accidentally.

  • Optimize class toggling performance through batching, using document fragments, and avoiding unnecessary layout calculations.

  • Common applications include toggling interface states, creating image sliders/carousels, applying form validation, and more.

Of course we‘ve only scratched the surface of techniques and real-world use cases for dynamic classes. For further learning check out resources like:

But I hope this guide gives all the knowledge needed to start implementing dynamic class toggling effectively in your web projects! Let me know if you have any other questions.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *