DOM Classes and Attributes — Complete Guide
In this tutorial, you will learn about DOM Classes and Attributes. We cover key concepts, practical examples, and best practices to help you master this topic.
Managing CSS classes and HTML attributes through the DOM involves classList methods like add, remove, toggle, and the dataset API for custom data attributes on elements.
What You'll Learn
- How to add, remove, toggle, and check CSS classes with classList
- How to read and write standard HTML attributes
- How to use the dataset API for custom data-* attributes
- When to use className vs classList
Why It Matters
Classes and attributes control appearance, behavior, and data on elements. Without proper management, toggling visibility, updating states, or storing metadata becomes error-prone and hard to maintain.
Real-World Use
- A tab component toggles the "active" class on the selected tab
- A modal uses aria-hidden attribute for Accessibility
- A dropdown stores options as data attributes and reads them on selection
flowchart LR A[Element] --> B[classList API] A --> C[Attributes] A --> D[dataset API] B --> E[add] B --> F[remove] B --> G[toggle] B --> H[contains] C --> I[getAttribute] C --> J[setAttribute] C --> K[removeAttribute] D --> L[data-* properties]
Managing CSS Classes with classList
The classList property provides a clean API for manipulating classes. It does not affect other classes on the element.
const panel = document.querySelector('.panel');
// Add a class
panel.classList.add('active');
console.log('After add:', panel.className);
// Remove a class
panel.classList.remove('inactive');
console.log('After remove:', panel.className);
// Toggle (add if missing, remove if present)
panel.classList.toggle('visible');
console.log('After toggle:', panel.classList.contains('visible'));
// Toggle returns a boolean indicating if the class is now present
const isNowVisible = panel.classList.toggle('highlight');
console.log('Highlight is now:', isNowVisible);
// Check if a class exists
if (panel.classList.contains('active')) {
console.log('Panel is active');
}
// Add multiple classes at once
panel.classList.add('rounded', 'shadow', 'border');
Expected output: The class string changes with each operation. contains returns true or false. toggle returns whether the class is present after the toggle.
Using className
The className property sets all classes at once as a space-separated string. Use it when replacing all classes, but prefer classList for individual changes.
const element = document.querySelector('.box');
// Get all classes
console.log('All classes:', element.className);
// Replace all classes
element.className = 'new-class another-class';
console.log('After replace:', element.className);
// Add a class via string concatenation (error-prone)
element.className += ' third-class';
console.log('After concat:', element.className);
// This approach has a bug: if the element already had
// 'third-class', it gets duplicated to 'third-class third-class'
// Safer: use classList.add for individual additions
Expected output: The className property shows the full class string. Concatenation works but risks duplicates and spacing errors.
Working with Standard Attributes
Use the attribute API for non-standard attributes or when you need precise control.
const link = document.querySelector('a');
// Standard properties (preferred for known attributes)
console.log('Href:', link.href);
link.href = 'https://example.com';
link.target = '_blank';
link.rel = 'noopener noreferrer';
// Generic attribute methods
link.setAttribute('download', 'document.pdf');
console.log('Has download:', link.hasAttribute('download'));
// Boolean attributes
const checkbox = document.querySelector('input[type="checkbox"]');
checkbox.setAttribute('checked', '');
console.log('Checked:', checkbox.checked);
// Remove attribute
link.removeAttribute('target');
console.log('Target after remove:', link.getAttribute('target'));
Expected output: The console shows the href value, confirms the download attribute, and shows that target becomes null after removal.
Working with Data Attributes
The dataset property provides camelCase access to data-* attributes. This is the modern, clean way to store custom data on elements.
const card = document.querySelector('.product-card');
// Setting data attributes via dataset
card.dataset.productId = '12345';
card.dataset.productName = 'Wireless Mouse';
card.dataset.productPrice = '29.99';
// The DOM reflects these as:
// data-product-id="12345"
// data-product-name="Wireless Mouse"
// data-product-price="29.99"
// Reading data attributes
console.log('Product ID:', card.dataset.productId);
console.log('Product Name:', card.dataset.productName);
console.log('Price:', card.dataset.productPrice);
// Check all data attributes
console.log(card.dataset);
// Removing a data attribute
delete card.dataset.productPrice;
console.log('After delete:', card.dataset);
Expected output: The console shows each data attribute value individually, the full DOMStringMap object, and then the map without the price property.
Styling with Inline Styles
The style property gives access to inline styles as a CSSStyleDeclaration object.
const box = document.querySelector('.box');
// Individual style properties (camelCase)
box.style.backgroundColor = '#3498db';
box.style.padding = '20px';
box.style.borderRadius = '8px';
box.style.fontSize = '16px';
// Check a computed value
console.log('Background:', box.style.backgroundColor);
console.log('Padding:', box.style.padding);
// Remove a style property
box.style.removeProperty('border-radius');
console.log('Border radius after remove:', box.style.borderRadius);
// Set multiple styles at once
box.style.cssText = 'color: white; margin: 10px; border: 1px solid black;';
// Note: cssText replaces ALL inline styles
Expected output: The background color and padding values appear in the console. The border radius becomes empty after removal. The cssText assignment replaces all previous inline styles.
Common Mistakes
- Using className with string concatenation — This causes duplicate classes and spacing errors. Use classList.add/remove/toggle instead.
- Forgetting that dataset uses camelCase —
data-user-namebecomesdataset.userName, notdataset.user-name. The hyphen is removed and the next letter capitalizes. - Setting boolean attributes with non-empty strings —
setAttribute('disabled', 'false')actually enables the attribute because any non-empty string is truthy. UseremoveAttributeto disable. - Mixing inline styles and CSS classes — Inline styles override class-based styles. Using both on the same element leads to confusing specificity battles.
- Reading style for computed values — The
styleproperty reads only inline styles. For computed styles, usegetComputedStyle(element).
Practice Questions
- How do you add a class without affecting existing classes? Use
element.classList.add('classname'). This preserves all existing classes. - What is the difference between className and classList? className sets/replaces all classes as a string. classList provides methods for individual class manipulation.
- How do you convert a data attribute name to its dataset property? Hyphenated names become camelCase:
data-user-idbecomesdataset.userId. - Challenge: Write a function that takes an element and returns an object with all data attributes as key-value pairs, with proper type conversion for numbers and booleans.
FAQ
Mini Project
Build a product card component with a "favorite" toggle button. Clicking the button toggles a "favorited" class (changing the heart icon color). Store the product ID, name, and price as data attributes on the card. When favorited, read the dataset and log the product info to the console. Use classList for styling and dataset for data storage.
What's Next
Continue with Lesson 7: CSS Styles and the DOM to learn how to read and set computed styles and manage CSS custom properties through JavaScript.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro