Changing CSS Styles with JavaScript DOM Methods
Learn how to change CSS styles dynamically with JavaScript. Master inline styles, classList, getComputedStyle, and CSS custom properties for responsive, interactive web pages.
Dynamic styling is the foundation of interactive web pages. Dropdown menus, dark mode toggles, animation triggers, form validation indicators: they all work by changing CSS through JavaScript. This guide covers every approach from inline styles to CSS custom properties, with clear examples showing when to use each technique.
Method 1: The style Property (Inline Styles)
Every DOM element has a style property that lets you read and write individual CSS properties directly. These become inline styles on the element.
const box = document.getElementById("box");
// Set individual CSS properties
box.style.backgroundColor = "blue";
box.style.width = "200px";
box.style.height = "200px";
box.style.borderRadius = "8px";
box.style.transition = "all 0.3s ease";CSS Property Name Conversion
CSS properties use kebab-case (background-color). JavaScript uses camelCase (backgroundColor):
const element = document.querySelector(".card");
// CSS property → JavaScript property
element.style.backgroundColor = "#f0f0f0"; // background-color
element.style.fontSize = "16px"; // font-size
element.style.marginTop = "20px"; // margin-top
element.style.borderBottomWidth = "2px"; // border-bottom-width
element.style.zIndex = "10"; // z-index
element.style.boxShadow = "0 2px 4px rgba(0,0,0,0.1)"; // box-shadowYou can also use bracket notation with the original CSS property names:
element.style["background-color"] = "#f0f0f0";
element.style["font-size"] = "16px";
element.style["margin-top"] = "20px";| CSS Property | JavaScript Property | Bracket Notation |
|---|---|---|
background-color | backgroundColor | style["background-color"] |
font-size | fontSize | style["font-size"] |
border-radius | borderRadius | style["border-radius"] |
z-index | zIndex | style["z-index"] |
max-width | maxWidth | style["max-width"] |
line-height | lineHeight | style["line-height"] |
Reading Inline Styles
The style property only reads inline styles that were set via JavaScript or the HTML style attribute. It does NOT read styles from CSS files.
// HTML: <div id="box" style="width: 100px;"></div>
// CSS file: #box { height: 200px; color: red; }
const box = document.getElementById("box");
console.log(box.style.width); // "100px" (inline style exists)
console.log(box.style.height); // "" (empty! set in CSS file, not inline)
console.log(box.style.color); // "" (empty! set in CSS file, not inline)Removing Inline Styles
// Remove a single style property
element.style.backgroundColor = ""; // Resets to CSS/default value
// Remove all inline styles
element.style.cssText = "";
// Or use removeProperty
element.style.removeProperty("background-color");Setting Multiple Styles with cssText
const card = document.querySelector(".card");
// Set multiple properties at once (overwrites ALL existing inline styles)
card.style.cssText = `
background-color: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`;
// Append to existing inline styles (preserves existing)
card.style.cssText += "margin-top: 20px;";Method 2: getComputedStyle (Reading Applied Styles)
To read the actual computed styles (from CSS files, browser defaults, and inline), use getComputedStyle:
const box = document.getElementById("box");
// Get ALL computed styles
const styles = getComputedStyle(box);
// Read specific properties (always returns resolved values)
console.log(styles.width); // "200px" (computed pixel value)
console.log(styles.backgroundColor); // "rgb(255, 0, 0)" (computed color)
console.log(styles.fontSize); // "16px"
console.log(styles.display); // "block"
console.log(styles.margin); // "10px" (shorthand may vary by browser)Computed Style vs Inline Style
// CSS: .box { width: 50%; padding: 1em; color: var(--primary); }
const box = document.querySelector(".box");
// Inline style returns what you SET (empty if not set inline)
console.log(box.style.width); // "" (not set inline)
console.log(box.style.padding); // "" (not set inline)
// Computed style returns what the browser CALCULATED
const computed = getComputedStyle(box);
console.log(computed.width); // "312px" (50% resolved to pixels)
console.log(computed.padding); // "16px" (1em resolved to pixels)
console.log(computed.color); // "rgb(59, 130, 246)" (variable resolved)| Read Method | Returns | Includes CSS files | Includes defaults | Resolves units |
|---|---|---|---|---|
element.style.prop | Inline value or "" | No | No | No |
getComputedStyle(el).prop | Final computed value | Yes | Yes | Yes (px, rgb, etc.) |
Method 3: classList (CSS Class Toggling)
The most maintainable approach to dynamic styling is toggling CSS classes rather than setting inline styles. Keep your styles in CSS and use JavaScript only to add or remove classes.
const button = document.querySelector(".menu-toggle");
const nav = document.querySelector(".mobile-nav");
button.addEventListener("click", () => {
nav.classList.toggle("open");
});/* CSS handles all the styling */
.mobile-nav {
transform: translateX(-100%);
transition: transform 0.3s ease;
}
.mobile-nav.open {
transform: translateX(0);
}classList Methods
const element = document.querySelector(".card");
// Add one or more classes
element.classList.add("active");
element.classList.add("highlighted", "expanded"); // Multiple at once
// Remove one or more classes
element.classList.remove("active");
element.classList.remove("highlighted", "expanded");
// Toggle a class (add if missing, remove if present)
element.classList.toggle("visible");
// Toggle with force parameter
element.classList.toggle("dark", true); // Always add
element.classList.toggle("dark", false); // Always remove
// Check if class exists
if (element.classList.contains("active")) {
console.log("Element is active");
}
// Replace one class with another
element.classList.replace("old-class", "new-class");Why classList Is Better Than Inline Styles
// APPROACH 1: Inline styles (hard to maintain)
function showError(input) {
input.style.borderColor = "red";
input.style.backgroundColor = "#fff5f5";
input.style.boxShadow = "0 0 0 3px rgba(255, 0, 0, 0.1)";
}
function hideError(input) {
input.style.borderColor = "";
input.style.backgroundColor = "";
input.style.boxShadow = "";
}
// APPROACH 2: CSS classes (clean, maintainable)
function showError(input) {
input.classList.add("input-error");
}
function hideError(input) {
input.classList.remove("input-error");
}| Criteria | Inline Styles (element.style) | CSS Classes (classList) |
|---|---|---|
| Separation of concerns | Mixes JS and CSS | Keeps styles in CSS |
| Specificity | High (overrides most CSS) | Normal (follows cascade) |
| Maintainability | Hard to update | Easy to update in CSS |
| Reusability | Must copy code for each element | Apply class to any element |
| Media queries | Not possible | Fully supported |
| Transitions/animations | Must set each property | Define once in CSS |
| Performance | Triggers individual reflows | Single reflow for class change |
Method 4: CSS Custom Properties (Variables)
CSS custom properties (variables) bridge the gap between JavaScript and CSS beautifully. Set a variable in JavaScript, and CSS handles the rendering:
// Set a CSS variable on the root element
document.documentElement.style.setProperty("--primary-color", "#3b82f6");
document.documentElement.style.setProperty("--font-size-base", "16px");
document.documentElement.style.setProperty("--sidebar-width", "280px");/* CSS uses the variables */
.button {
background-color: var(--primary-color);
font-size: var(--font-size-base);
}
.sidebar {
width: var(--sidebar-width);
}Dark Mode Toggle with CSS Variables
function toggleDarkMode() {
const root = document.documentElement;
const isDark = root.classList.toggle("dark");
if (isDark) {
root.style.setProperty("--bg-color", "#1a1a2e");
root.style.setProperty("--text-color", "#e0e0e0");
root.style.setProperty("--card-bg", "#16213e");
root.style.setProperty("--border-color", "#2a2a4a");
} else {
root.style.setProperty("--bg-color", "#ffffff");
root.style.setProperty("--text-color", "#1a1a1a");
root.style.setProperty("--card-bg", "#f8f9fa");
root.style.setProperty("--border-color", "#e0e0e0");
}
// Save preference
localStorage.setItem("darkMode", isDark);
}
// Read a CSS variable value
const primaryColor = getComputedStyle(document.documentElement)
.getPropertyValue("--primary-color")
.trim();
console.log(primaryColor); // "#3b82f6"Scoped CSS Variables
// Set variables on specific elements (scoped, not global)
function setCardTheme(cardElement, color) {
cardElement.style.setProperty("--card-accent", color);
}
// Each card can have a different accent color
const cards = document.querySelectorAll(".card");
const colors = ["#ef4444", "#22c55e", "#3b82f6", "#f59e0b"];
cards.forEach((card, index) => {
setCardTheme(card, colors[index % colors.length]);
});.card {
border-left: 4px solid var(--card-accent, #ccc);
}
.card h3 {
color: var(--card-accent, #333);
}Method 5: Stylesheet Manipulation
For advanced cases, you can create and modify entire stylesheets from JavaScript:
// Create a new stylesheet
const sheet = new CSSStyleSheet();
sheet.replaceSync(`
.dynamic-card {
background: white;
border-radius: 8px;
padding: 16px;
}
.dynamic-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
`);
// Adopt the stylesheet
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
// Modify rules later
sheet.insertRule(".dynamic-card.featured { border: 2px solid gold; }", sheet.cssRules.length);Best Practices
1. Prefer CSS Classes Over Inline Styles
// GOOD: Toggle a class
element.classList.toggle("expanded");
// AVOID: Set multiple inline properties
element.style.height = "auto";
element.style.opacity = "1";
element.style.overflow = "visible";2. Use CSS Variables for Theming
// GOOD: One variable change updates many elements
document.documentElement.style.setProperty("--accent", newColor);
// AVOID: Update each element individually
buttons.forEach(btn => { btn.style.backgroundColor = newColor; });
links.forEach(link => { link.style.color = newColor; });
headers.forEach(h => { h.style.borderColor = newColor; });3. Batch Style Changes
// SLOW: Multiple individual style changes trigger multiple reflows
element.style.width = "100px";
element.style.height = "100px";
element.style.margin = "10px";
element.style.padding = "5px";
// FAST: Single class toggle triggers one reflow
element.classList.add("compact");
// ALSO FAST: cssText sets all at once
element.style.cssText = "width: 100px; height: 100px; margin: 10px; padding: 5px;";Common Mistakes to Avoid
Mistake 1: Forgetting Units
// WRONG: No unit, style is ignored
element.style.width = 200; // Does nothing
element.style.fontSize = 16; // Does nothing
// CORRECT: Always include units
element.style.width = "200px";
element.style.fontSize = "16px";
// Exception: unitless properties
element.style.opacity = "0.5"; // No unit needed
element.style.zIndex = "10"; // No unit needed
element.style.lineHeight = "1.5"; // Unitless ratio is validMistake 2: Reading Styles Before Browser Calculates Them
// PROBLEM: Reading computed style immediately after setting it
element.style.width = "50%";
const width = getComputedStyle(element).width;
// May return the OLD value if browser hasn't recalculated yet
// SOLUTION: Force a reflow by reading a layout property
element.style.width = "50%";
element.offsetHeight; // Forces reflow
const width = getComputedStyle(element).width; // Now returns updated valueMistake 3: Using className Instead of classList
// DANGEROUS: Overwrites ALL existing classes
element.className = "active"; // Removes every other class!
// SAFE: Adds/removes without affecting other classes
element.classList.add("active");
element.classList.remove("active");Real-World Example: Interactive Theme Customizer
function createThemeCustomizer() {
const root = document.documentElement;
const controls = {
primary: document.getElementById("color-primary"),
background: document.getElementById("color-bg"),
fontSize: document.getElementById("font-size"),
borderRadius: document.getElementById("border-radius")
};
// Apply theme changes in real time
function applyTheme() {
root.style.setProperty("--primary", controls.primary.value);
root.style.setProperty("--bg-color", controls.background.value);
root.style.setProperty("--font-base", controls.fontSize.value + "px");
root.style.setProperty("--radius", controls.borderRadius.value + "px");
}
// Listen for changes on all controls
Object.values(controls).forEach(control => {
control.addEventListener("input", applyTheme);
});
// Save theme to localStorage
document.getElementById("save-theme").addEventListener("click", () => {
const theme = {};
Object.entries(controls).forEach(([key, control]) => {
theme[key] = control.value;
});
localStorage.setItem("customTheme", JSON.stringify(theme));
const status = document.getElementById("save-status");
status.textContent = "Theme saved!";
status.classList.add("visible");
setTimeout(() => status.classList.remove("visible"), 2000);
});
// Load saved theme
const saved = localStorage.getItem("customTheme");
if (saved) {
const theme = JSON.parse(saved);
Object.entries(theme).forEach(([key, value]) => {
if (controls[key]) controls[key].value = value;
});
applyTheme();
}
}
createThemeCustomizer();Rune AI
Key Insights
- classList first: Toggle CSS classes as your default approach; keep styling logic in your CSS files
- CSS variables for themes: Use
setProperty("--var", value)to update many elements through a single variable change - Inline styles for dynamic values: Use
element.styleonly for values that change frequently or cannot be predefined (drag positions, color pickers) - getComputedStyle for reading: The
styleproperty only reads inline styles; usegetComputedStyle()to read the actual rendered values - Performance: Batch style changes with
classListorcssTextto avoid multiple reflows; never alternate reading and writing layout properties
Frequently Asked Questions
Should I use inline styles or CSS classes in JavaScript?
How do I animate styles with JavaScript?
What is the performance impact of changing styles with JavaScript?
Can I use JavaScript to change styles in a CSS file?
How do CSS custom properties differ from inline styles?
Conclusion
JavaScript provides multiple ways to change CSS, and choosing the right approach depends on the situation. Use classList for toggling predefined visual states like "active," "expanded," or "dark." Use CSS custom properties for theme-wide changes where one variable controls many elements. Use the style property for truly dynamic values that cannot be predefined in CSS. Use getComputedStyle to read the final rendered values when you need to measure elements. The pattern that produces the cleanest, most maintainable code is keeping your styles in CSS and using JavaScript only to toggle classes or update variables.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.