Skillversitas

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.style modifies inline styles — suitable for simple dynamic changes.
  • classList is 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.