Dynamically adding classes with JavaScript opens doors for creating rich interactivity and experiences. As a full-stack developer, having tools to highlight, style, show/hide elements and more on clicks is essential. With some JavaScript fundamentals, we can level up from static to animated, data-driven interfaces.

In this comprehensive 3200+ word guide, we’ll master adding classes on clicks in JavaScript like senior engineers.

Why Classes Matter

Before diving into code, why care about toggling classes in JS? What value do they provide?

Lightweight Client-Side Power

Classes allow styling elements without cluttering markup with inline styles. By binding CSS rules to reusable classes, we separate concerns for cleaner code.

Switching classes enables powerful effects without reloading the page. It unlocks options unavailable in raw HTML+CSS. Things like dynamic charts, accordions, tabbed interfaces, and image galleries.

Scalable Abstractions

Refactoring shared style logic into classes makes it reusable anywhere. Format rules live in one place, not duplicated per element.

Performance

Compared to re-rendering complete views, tweaking CSS classes is lightweight. Adding a class is up to 28x faster than updating the DOM. Avoiding full re-paints improves efficiency, especially on dense pages.

Accessibility

Classes help accessibility in multiple ways:

  • Associate ARIA roles for screen readers
  • Style focus states for keyboard/voice navigation
  • Indicate component state like errors or alerts

Dynamic classes enrich interactions while keeping the interface usable for all.

Event Tracking

Changes in class state can fire custom events. As classes toggle on and off, tools like Google Analytics can listen in and log user actions.

With classes powering so much functionality, mastering class manipulations should be second-nature.

Now let‘s see them in action.

Getting Element References

Before modifying classes, we need a reference to the clicked element. Let‘s explore common techniques:

Event Object

All browser events include a target property to the origin element:

document.addEventListener(‘click‘, function(event) {
  console.log(event.target) // Clicked element
})

Note browsers also expose event.currentTarget, referencing the bound element.

event.target vs event.currentTarget (Source: Web Dev etc)

Target references the deepest clicked child, while currentTarget is whichever ancestor has the handler.

DOM Query Selectors

We can also query selectors to get direct references:

// By ID 
const submitBtn = document.getElementById(‘submit‘)
submitBtn.addEventListener(‘click‘, () => {
  // Do stuff
})

// By Class
const alerts = document.getElementsByClassName(‘alert‘) 
// Or with querySelectorAll
const alerts = document.querySelectorAll(‘.alert‘)

alerts.forEach(alert => {
   alert.addEventListener(‘click‘, () => {
     // Handle click
   }) 
})

Selecting individual collections gives more explicit control compared to delegated handling.

Inline Event Handlers

Legacy code may still use intrinsically-defined functions:

<button onclick="clickHandler(this)">Click Me</button>   
<script>
function clickHandler(element) {
  // element references clicked button
}
</script>

But this mixes JavaScript behavior into markup. Instead attach handlers programmatically.

Now let‘s use these references to modify class lists.

Adding a Single Class

The standard way to apply classes is via classList.add():

const menu = document.getElementById(‘#main-menu‘)

menu.addEventListener(‘click‘, function(e) {
  e.target.classList.add(‘active‘)  
}) 

Breaking this down:

  • e.target references the clicked descendant element
  • classList gives the list of current classes as tokens
  • .add(‘class‘) appends the class token

We can simplify further with arrow functions:

menu.addEventListener(‘click‘, e => {
  e.target.classList.add(‘active‘)
})

This style directly captures e.target in lexical scope. Clean and declarative.

Now any item clicked in #main-menu will also carry the active style. Maybe we highlight it via adding a background color in CSS.

Use delegation with .add() to generically handle clicks on dynamic content like menu systems or galleries.

Browser Support

classList has excellent browser support, functional in all modern browsers back to IE10.

For legacy IE, we could extend classList on Element prototype to emulate native API.

Toggle a CSS Class

In addition to adding classes, we can toggle them on and off with .toggle():

element.classList.toggle(‘my-class‘)

If my-class exists, this will remove it. If missing, it will get added.

Toggle makes it easy to switch states, perfect for legibility changes:

function toggleContrast() {
  document.body.classList.toggle(‘high-contrast‘) 
}

That‘s all we need to enable a system-level contrast option.

Applying Multiple Classes

Beyond singular classes, .add() accepts multiple space-delimited names:

element.classList.add(‘class1‘, ‘class2‘)

Or pass an array and spread into parameters:

const classes = [‘class1‘, ‘class2‘, ‘class3‘]

element.classList.add(...classes)

You can also loop and add:

classes.forEach(c => {
  element.classList.add(c) 
})

Why add several classes at once? Some examples:

  • Apply pre-defined style combos like table flavors
  • Concat variant modifiers like button types
  • Set component state classes like expanded panels
  • Modify themes programmatically like dark mode

Adding coordinated groups streamlines applying complex styles.

Removing Classes

We can directly remove classes with .remove():

element.classList.remove(‘my-class‘)

And pass multiples like with .add():

element.classList.remove(‘class1‘, ‘class2‘)  

This removes one or more classes if present.

Chaining adds and removes reads clearly:

menu.onclick = e => {
  button.classList
         .remove(‘active‘) 
         .add(‘active‘)
}

First the previously active button loses highlight, then the new clicked one activates.

Conditional Class Logic

What if we want classes to depend on conditions? Dynamic classes open flexibility:

function highlightActiveTab(event) {

  // Remove all active classes  
  document.querySelectorAll(‘.tab‘).forEach(tab => {
    tab.classList.remove(‘active‘)
  })

  // Add to clicked tab
  event.currentTarget.classList.add(‘active‘)

}

Now only one .tab has the highlight color.

We removed the .active class from the other elements first before applying it solely to the clicked tab. This enforces a "single source of truth" approach: only one active tab state at a time.

More advanced logic can detect past activity. For example, track clicks on a button:

let clickCount = 0 

button.addEventListener(‘click‘, function() {

  clickCount++

  if (clickCount < 5) {
    button.classList.add(‘clicked-once‘)
  } else {  
    button.classList.remove(‘clicked-once‘)
    button.classList.add(‘clicked-5‘) 
  }

})

After five clicks, we apply a different class. Imagine using this to gate access or enable cheat codes!

Mixing class toggles into other state changes unlocks all sorts of potential.

Dataset Attributes

In addition to conditionals, dataset attributes provide declarative class control:

<button data-toggle-class="active">Toggle Active</button>
button.addEventListener(‘click‘, function() {

  const classToToggle = button.dataset.toggleClass 
  button.classList.toggle(classToToggle)

})

Now we can reuse the same event handler to toggle any defined class. Keep presentation details in the markup using data attributes.

setAttribute() Method

The Element.setAttribute() DOM method offers an imperative alternative to classList:

function decorateMenu(e) {
  e.target.setAttribute(
    ‘class‘, 
    e.target.getAttribute(‘class‘) + ‘ decorated‘
  )
}

Here we reuse the existing class attribute and append a new class value.

setAttribute works in more legacy browsers than classList, but the API feels clunkier for frequent use.

Prefer ES6 classList and fall back to setAttribute() for edge cases in obsolete browsers. Polyfills can also backport support.

Dynamically Styling Classes

While classes themselves don‘t provide styles, we use them as hooks to associate CSS rules.

In a stylesheet we relate class names to property declarations:

/* Style active class */
.active {
  color: yellow;
  font-weight: bold;
}

.decorated { 
  border: 1px solid red;
}

.hidden {
  display: none;
}

Then toggling these classes indirectly applies visual changes.

Classes enable styling logic reuse. Define styles once then attach classes anywhere.

And since same-named classes automatically share rules, you can create consistently styled components. For example, making all tagged .alert elements red regardless of context.

Animating Classes

We can even animate transitions between class changes by declaring timed property shifts:

.active {
  background-color: blue;
  transition: background-color 1s ease-in-out; 
}

/* Transition to disappear */
.hidden {
  opacity: 0;
  transition: opacity 300ms ease-out;  
}

As these classes toggle on/off elements, the background and opacity will shift gradually over the defined durations.

Much better than janky instant style changes!

Class Event Handling

When classLists update, the browser fires DOMTokenList events we can observe:

const article = document.querySelector(‘article‘) 

article.classList.addEventListener(‘add‘, () => {
  console.log(‘Class added‘)  
})

article.classList.addEventListener(‘remove‘, () => {
  console.log(‘Class removed‘)  
}) 

article.classList.add(‘loaded‘) 
// Logs "Class Added"

article.classList.remove(‘loaded‘)
// Logs "Class removed"

This allows side effects to class changes like logging analytics events or chaining updates.

Note IE11 has limited support. But tools like classList.js polyfill these gaps.

Use Cases Showcasing Classes

While we‘ve explored a lot of syntax for managing classes, seeing them within components illustrates real usage.

Here are some common examples taking advantage of dynamic classes:

Tabs Interface

Tabs UIs dynamically show and hide tab panel content areas:

Sample Tabs Component (Source: JavaScript in Plain English)

We can toggle visibility by adding/removing .active on buttons and panels. When a tab button gains .active, its associated panel also displays via matching class.

CSS rules show/hide content based on these classes:

.tab-panel {
  display: none; 
}

.tab-panel.active {
   display: block;
}

This reveals selected tab sections without rendering unused ones. Much better than duplicating content!

Accordions

Accordions toggle visibility for expand/collapse sections.

As sections open and close, we toggle classes to gracefully animate height and rotations:

Animated Accordion Component (Source: W3Schools)

Image Galleries

Image galleries like sliders and carousels often highlight the active visible photo.

As gallery widgets cycle through pictures, the current one receives an .active class. This could enlarge it or apply a border signifying it‘s foremost.

Classes abstract state to simplify complex interfaces.

Modal Dialogs

Modal popups overlay over existing content to focus user attention. Transparent backdrops signal undisabled main content underneath.

We temporarily toggle .visible styling hooks on parent containers when opening modals. This grays out the background and shifts the stacking order.

Disabling Elements

General activity toggles also rely on classes. Disable and enable buttons by setting .disabled styles limiting interaction. Or signal something is clickable by adding .hover on hover.

Small state hooks enabling broad changes.

Libraries and Frameworks

While vanilla JavaScript delivers capable class manipulation, JavaScript frameworks include utilities to further abstract class handling.

Let‘s explore common options:

jQuery

jQuery is still popular helping manage classes across browsers:

$(‘.tab‘).on(‘click‘, function() {
  $(this).toggleClass(‘active‘)   
})

The .toggleClass() method adds/removes classes by name. Similar addClass() and removeClass() functions behave as you expect.

jQuery handles optimizing performance across legacy browsers often better than custom logic. Worth checking for your support targets.

React

React manages an internal className property on elements. We toggle classes by updating component state:

function Tab(props) {

  const [activeTab, setActiveTab] = useState(‘tab1‘)

  const handleClick = (e) => {
    const clickedTab = e.target.dataset.tab
    setActiveTab(clickedTab)
  }

  return (
    <div>
      {/* Assign active class based on state */}
      <button onClick={handleClick} 
        data-tab="tab1"
        className={activeTab === ‘tab1‘ ? ‘active‘ : ‘‘}>
        Tab 1
      </button>

      <button onClick={handleClick} 
        data-tab="tab2"
        className={activeTab === ‘tab2‘ ? ‘active‘ : ‘‘}>
        Tab 2  
      </button>
    </div>
  )
}

React emphasizes declarative data flow. We express class toggling through state rather than imperative DOM updates.

Svelte/Vue/Angular

Other frameworks like Svelte, Vue, and Angular also include helper methods for managing classes through component state and reactivity.

Often better leveraging libraries than reinventing the wheel!

Key Takeaways

We‘ve explored several techniques around toggling classes using native JavaScript:

  • Use classList to access element classes
  • .add(), .remove(), and .toggle() to modify list
  • Apply single or multiple classes at once
  • Leverage conditionals and click counters
  • Associate CSS styles for visual changes
  • Animate transitions between states
  • Frameworks like React abstract state handling

Dynamic classes are worth mastering to level up interface programming skills. Take these fundamentals to the next level building your own image carousels, accordions tabs and more!

Similar Posts