JavaScriptWorking with Styles & Classes

DOM Styles and Classes

There are three layers to "styling" an element from JavaScript: setting inline styles directly, toggling classes the stylesheet already defines, and reading the actual styles the browser computed. Picking the right layer keeps your CSS where it belongs and your code small. This page walks through each.

Inline styles via element.style

element.style is a CSSStyleDeclaration — a JavaScript view of the element's style="…" attribute. Each CSS property maps to a camelCase property name.

JS
const box = document.querySelector(".box");

box.style.color = "white";
box.style.backgroundColor = "tomato";  // background-color → backgroundColor
box.style.padding = "12px 16px";
box.style.borderRadius = "8px";

Inline styles win against most stylesheet rules (specificity-wise they're high), which makes them powerful but heavy-handed. Use them for dynamic, calculated values — positions during a drag, colours derived from data, a width set from JavaScript.

Removing inline styles

JS
box.style.color = "";           // empty string clears that property
box.style.removeProperty("padding");

// Read just what is inline (not what CSS does):
box.style.color;                // "white"
box.style.fontSize;             // "" if not set inline
CSS custom properties (variables)

CSS variables don't fit the camelCase mould — they keep their dashes. Use setProperty / getPropertyValue.

JS
box.style.setProperty("--accent", "rebeccapurple");
box.style.getPropertyValue("--accent"); // "rebeccapurple"

This is the standard way to drive a CSS-variable-based theme from JavaScript.

classList — the right tool for the job

Most of the time you don't want to set styles from JavaScript at all — you want to toggle a class that your stylesheet already defines. The classList API makes that easy:

JS
const btn = document.querySelector(".btn");

btn.classList.add("active");
btn.classList.remove("disabled");
btn.classList.toggle("open");           // returns the new state
btn.classList.toggle("open", true);     // force add
btn.classList.toggle("open", false);    // force remove
btn.classList.contains("active");       // true
btn.classList.replace("size-sm", "size-lg");

Each method is a no-op when it doesn't need to do anything (add on an already-present class, remove on a missing class), which keeps callers simple.

a tiny toggle pattern

JS
document.querySelector(".menu-toggle").addEventListener("click", (e) => {
  const open = document.body.classList.toggle("menu-open");
  e.currentTarget.setAttribute("aria-expanded", String(open));
});
Why classes beat inline styles
Inline styles bake the design into your JavaScript. Classes keep the look in CSS, where designers and code can see it, where media queries and `:hover` still work, and where a single rename changes every component at once.
className: bulk set or read

Before classList there was element.className — a plain string of all the classes. It is still useful when you want to replace the whole set at once:

JS
btn.className;                 // "btn primary active"
btn.className = "btn ghost";   // replaces everything

But for add/remove/toggle, classList is clearer and safer (no risk of duplicates, no string surgery).

Reading the styles the browser actually applied

element.style only sees what you set inline. To read the resolved value — what the browser actually used after applying every stylesheet — use getComputedStyle:

JS
const box = document.querySelector(".box");
const cs = getComputedStyle(box);

cs.color;            // "rgb(255, 255, 255)"
cs.fontSize;         // "16px"  — always in absolute units, computed
cs.display;          // "flex"  — even if your CSS says "var(--display)"
cs.getPropertyValue("--accent"); // CSS variables work here too
color    -> rgb(255, 255, 255)
fontSize -> 16px
display  -> flex
  • Values are returned as strings, always in computed/absolute form ("16px", not "1em").

  • getComputedStyle is read-only. Setting a property on the returned object does nothing.

  • It forces a style recalculation, so avoid calling it in tight loops or inside scroll handlers.

Measuring elements

Closely related, but not on style: every element has read-only size properties.

JS
const r = box.getBoundingClientRect();
r.width;  r.height;
r.top;    r.left;   // position relative to the viewport

box.offsetWidth;  box.offsetHeight;  // include padding and border
box.clientWidth;  box.clientHeight;  // include padding only
box.scrollWidth;  box.scrollHeight;  // including overflowed content
When to use each layer
  • Static look — write it in CSS, use a class.

  • State that has a name (open, active, error, loading) — toggle a class with classList.

  • Dynamic numeric value computed at runtime (a width, a position, a hue) — el.style.left = "120px".

  • Theme variablesstyle.setProperty("--accent", …) on :root or a wrapper.

  • Reading what is on screengetComputedStyle or getBoundingClientRect.

Performance
Modifying styles invalidates layout. Batch your reads first, then your writes, to avoid forcing the browser to recompute geometry between every line. The pattern is sometimes called *read-then-write* — a common trick in animation libraries.