32. Changing CSS with JavaScript
JavaScript lets you modify an element's CSS styles dynamically — changing colors, sizes, layouts, and animations based on user interaction or specific conditions.
Code Example
element.style — Inline Styles
const card = document.querySelector(".card");
// Change styles directly (only for inline styles)
card.style.backgroundColor = "#2196f3";
card.style.color = "white";
card.style.padding = "20px";
card.style.borderRadius = "8px";
card.style.boxShadow = "0 2px 8px rgba(0,0,0,0.2)";
// CamelCase for multi-word properties
card.style.fontSize = "16px"; // font-size
card.style.fontWeight = "bold"; // font-weight
card.style.textAlign = "center"; // text-align
// Use backticks for complex values
const color = "#1976d2";
card.style.background = `linear-gradient(135deg, ${color}, #bbdefb)`;
// Reset styles
card.style.cssText = ""; // remove all inline styles
classList — Managing CSS Classes
const element = document.querySelector(".sidebar");
// Add a class
element.classList.add("active");
element.classList.add("visible", "expanded"); // multiple
// Remove a class
element.classList.remove("active");
element.classList.remove("visible", "collapsed");
// Toggle a class
element.classList.toggle("collapsed");
// If it exists → remove it, if it doesn't → add it
// Toggle with a condition
const isDarkMode = true;
element.classList.toggle("dark-mode", isDarkMode);
// If isDarkMode is true → add it, if false → remove it
// Check a class
const isActive = element.classList.contains("active"); // boolean
// Replace a class
element.classList.replace("old-class", "new-class");
Dark Mode Toggle
class DarkModeToggle {
constructor() {
this.btn = document.querySelector("#darkModeToggle");
this.isDark = localStorage.getItem("darkMode") === "true";
this.init();
}
init() {
// Apply the saved preference
if (this.isDark) {
document.body.classList.add("dark-mode");
}
this.updateIcon();
this.btn.addEventListener("click", () => {
this.isDark = !this.isDark;
document.body.classList.toggle("dark-mode", this.isDark);
localStorage.setItem("darkMode", this.isDark);
this.updateIcon();
});
}
updateIcon() {
this.btn.textContent = this.isDark ? "☀️" : "🌙";
}
}
document.addEventListener("DOMContentLoaded", () => {
new DarkModeToggle();
});
getComputedStyle — Reading Active Styles
const card = document.querySelector(".card");
// Read the computed style (the values actually applied by the browser)
const computedStyle = getComputedStyle(card);
console.log(computedStyle.color); // "rgb(33, 33, 33)"
console.log(computedStyle.fontSize); // "16px"
console.log(computedStyle.display); // "flex"
console.log(computedStyle.backgroundColor); // "rgb(255, 255, 255)"
// Useful for debugging
const header = document.querySelector("h1");
const headerStyle = getComputedStyle(header);
console.log({
color: headerStyle.color,
fontSize: headerStyle.fontSize,
fontWeight: headerStyle.fontWeight,
margin: headerStyle.margin
});
// Check whether an element is visible
function isVisible(el) {
const style = getComputedStyle(el);
return (
style.display !== "none" &&
style.visibility !== "hidden" &&
parseFloat(style.opacity) > 0
);
}
console.log(isVisible(document.querySelector(".hidden-card"))); // false
Animations with JavaScript
// Slide toggle animation
function slideToggle(el, duration = 300) {
if (el.classList.contains("slide-open")) {
// Close
el.style.transition = `max-height ${duration}ms ease-out`;
el.style.maxHeight = el.scrollHeight + "px";
requestAnimationFrame(() => {
el.style.maxHeight = "0";
el.classList.remove("slide-open");
});
} else {
// Open
el.classList.add("slide-open");
el.style.maxHeight = "0";
el.style.overflow = "hidden";
requestAnimationFrame(() => {
el.style.maxHeight = el.scrollHeight + "px";
});
}
}
// Smooth number counter
function animateNumber(element, target, duration = 1000) {
const start = parseInt(element.textContent) || 0;
const startTime = performance.now();
function update(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Ease out cubic
const eased = 1 - Math.pow(1 - progress, 3);
const current = Math.round(start + (target - start) * eased);
element.textContent = current;
if (progress < 1) {
requestAnimationFrame(update);
}
}
requestAnimationFrame(update);
}
// Usage
document.addEventListener("DOMContentLoaded", () => {
const counter = document.getElementById("counter");
animateNumber(counter, 1500, 1500);
});
CSS Custom Properties (Variables)
// Change CSS variables from JavaScript
document.documentElement.style.setProperty("--primary-color", "#1976d2");
document.documentElement.style.setProperty("--sidebar-width", "280px");
// Read a CSS variable
const primaryColor = getComputedStyle(document.documentElement)
.getPropertyValue("--primary-color");
console.log(primaryColor); // "#1976d2"
// Theme switching with CSS variables
class ThemeManager {
constructor() {
this.themes = {
light: {
"--bg-color": "#ffffff",
"--text-color": "#212121",
"--primary": "#1976d2",
"--surface": "#f5f5f5",
"--border": "#e0e0e0"
},
dark: {
"--bg-color": "#121212",
"--text-color": "#e0e0e0",
"--primary": "#90caf9",
"--surface": "#1e1e1e",
"--border": "#333333"
},
campus: {
"--bg-color": "#fafafa",
"--text-color": "#333333",
"--primary": "#d32f2f",
"--surface": "#ffffff",
"--border": "#ffcdd2"
}
};
this.currentTheme = localStorage.getItem("theme") || "light";
this.apply(this.currentTheme);
}
apply(themeName) {
const theme = this.themes[themeName];
if (!theme) return;
const root = document.documentElement;
for (const [prop, value] of Object.entries(theme)) {
root.style.setProperty(prop, value);
}
this.currentTheme = themeName;
localStorage.setItem("theme", themeName);
}
getAvailableThemes() {
return Object.keys(this.themes);
}
}
// Usage
const themeManager = new ThemeManager();
document.getElementById("themeLight")?.addEventListener("click", () => {
themeManager.apply("light");
});
document.getElementById("themeDark")?.addEventListener("click", () => {
themeManager.apply("dark");
});
document.getElementById("themeCampus")?.addEventListener("click", () => {
themeManager.apply("campus");
});
Dynamic Styling Patterns
// 1. Responsive without media queries
function updateLayout() {
const width = window.innerWidth;
const sidebar = document.querySelector(".sidebar");
if (width < 768) {
sidebar.classList.add("collapsed");
document.body.classList.add("mobile");
} else {
sidebar.classList.remove("collapsed");
document.body.classList.remove("mobile");
}
}
window.addEventListener("resize", updateLayout);
updateLayout(); // initial check
// 2. Loading indicator
function showLoading(el) {
el.classList.add("loading");
el.style.position = "relative";
el.style.pointerEvents = "none";
el.style.opacity = "0.6";
}
function hideLoading(el) {
el.classList.remove("loading");
el.style.pointerEvents = "";
el.style.opacity = "";
}
// 3. Staggered animation
function staggerReveal(selector, delay = 50) {
const elements = document.querySelectorAll(selector);
elements.forEach((el, index) => {
el.style.opacity = "0";
el.style.transform = "translateY(20px)";
el.style.transition = "opacity 0.3s ease, transform 0.3s ease";
setTimeout(() => {
el.style.opacity = "1";
el.style.transform = "translateY(0)";
}, index * delay);
});
}
Code Explanation
| Method | Explanation |
|---|---|
element.style.prop |
Set/get an inline style (camelCase) |
classList.add() |
Add a CSS class |
classList.remove() |
Remove a CSS class |
classList.toggle() |
Toggle a class (exists → remove, missing → add) |
classList.contains() |
Check whether a class exists (boolean) |
classList.replace() |
Replace an old class with a new one |
getComputedStyle() |
Read the computed style from the browser |
style.setProperty() |
Set a CSS custom property (variable) |
style.getPropertyValue() |
Read a CSS custom property |
Best Practices
✅ Use classes for frequently changed styles
✅ Use CSS variables for themes and global values
✅ Use classList, not innerHTML, to toggle styles
✅ Use getComputedStyle() to debug layouts
❌ Avoid element.style for many changes
❌ Avoid inline styles for complex styling
/* Styles should go in CSS, not in JS */
.card {
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
// JS only toggles the class; CSS handles the transition
card.addEventListener("mouseenter", () => {
card.classList.add("hovered");
});
card.addEventListener("mouseleave", () => {
card.classList.remove("hovered");
});
Real-World Examples
A dark mode toggle for an academic portal:
class PortalTheme {
constructor() {
this.body = document.body;
this.toggleBtn = document.querySelector("#themeToggle");
this.currentTheme = this.load();
this.apply();
this.bindEvents();
}
load() {
return localStorage.getItem("portalTheme") || "light";
}
save() {
localStorage.setItem("portalTheme", this.currentTheme);
}
apply() {
// Toggle dark mode on the body
this.body.classList.toggle("dark-mode", this.currentTheme === "dark");
// Update the icon
if (this.toggleBtn) {
this.toggleBtn.textContent = this.currentTheme === "dark" ? "☀️" : "🌙";
}
// Update CSS variables
const root = document.documentElement;
if (this.currentTheme === "dark") {
root.style.setProperty("--bg-main", "#1a1a2e");
root.style.setProperty("--bg-card", "#16213e");
root.style.setProperty("--text-primary", "#e0e0e0");
root.style.setProperty("--text-secondary", "#a0a0a0");
root.style.setProperty("--border-color", "#2a2a4a");
root.style.setProperty("--accent", "#4fc3f7");
} else {
root.style.setProperty("--bg-main", "#f5f7fa");
root.style.setProperty("--bg-card", "#ffffff");
root.style.setProperty("--text-primary", "#212121");
root.style.setProperty("--text-secondary", "#757575");
root.style.setProperty("--border-color", "#e0e0e0");
root.style.setProperty("--accent", "#1976d2");
}
}
toggle() {
this.currentTheme = this.currentTheme === "dark" ? "light" : "dark";
this.save();
this.apply();
}
bindEvents() {
if (this.toggleBtn) {
this.toggleBtn.addEventListener("click", () => this.toggle());
}
// Auto-detect the system preference
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
mediaQuery.addEventListener("change", (e) => {
if (!localStorage.getItem("portalTheme")) {
this.currentTheme = e.matches ? "dark" : "light";
this.apply();
}
});
}
}
document.addEventListener("DOMContentLoaded", () => {
new PortalTheme();
});
Conclusion
element.stylemodifies inline styles — suitable for simple dynamic changes.classListis the best way to toggle/manage CSS classes — let CSS handle the styling details.getComputedStyle()reads the styles that are actually active (including those from CSS files).- CSS custom properties (
--variable) enable dynamic theming from JavaScript. - Separate concerns: CSS for visual styling, JavaScript for logic and interaction.