Styles and classes
Before any of the JavaScript, one rule sets the whole chapter up. It reads as obvious once said, yet it decides how maintainable your UI code turns out.
An element can get its look from two places:
- A class defined in CSS, attached with
<div class="...">. - Inline properties, written straight into the element’s
styleattribute:<div style="...">.
JavaScript can drive both. It can flip classes on and off, and it can set inline style properties one by one.
The habit worth building: reach for a CSS class first, and only fall back to style when a class genuinely can’t express what you need.
Inline style earns its place when a value is computed at runtime and can’t be known ahead of time — element coordinates being the classic case:
let top = /* complex calculations */;
let left = /* complex calculations */;
elem.style.left = left; // e.g. '123px', figured out while running
elem.style.top = top; // e.g. '456px'
Everything else — turning text red, dropping in a background icon, spacing something out — belongs in a stylesheet behind a class name. Then JavaScript’s whole job is to add or remove that class. The visual detail stays in CSS where a designer can find it, and your script stays about behavior.
className and classList
Toggling a class is one of the things scripts do most. The DOM gives you two handles for it, and they solve slightly different problems.
Start with the older one. Early JavaScript had a restriction: a reserved word like class couldn’t be used as an object property, so elem.class was off the table. That restriction is gone in modern JavaScript, but by the time it lifted the DOM had already settled on a workaround name. The property elem.className maps to the element’s class attribute.
Read it back and you get the whole class string:
<body class="panel open">
<script>
alert(document.body.className); // panel open
</script>
</body>
The catch: assigning to className overwrites the entire string at once. Sometimes that’s exactly right — you’re setting a fresh, known set of classes. But often you only want to touch a single class and leave the rest alone. Do that through className and you’re stuck manually splitting the string, editing it, and joining it back.
That’s the job elem.classList exists for. It’s a special object with methods that add, remove, or toggle one class without disturbing the others.
<body class="panel open">
<script>
// add a class
document.body.classList.add('pinned');
alert(document.body.className); // panel open pinned
</script>
</body>
So you have both handles. Use className when you want the full set as one string; use classList when you’re editing classes individually.
The methods on classList:
elem.classList.add("class")/elem.classList.remove("class")— adds or removes the class.elem.classList.toggle("class")— adds the class if it’s missing, removes it if it’s present. This is the one you’ll wire to a click handler again and again.elem.classList.contains("class")— returnstrueorfalsedepending on whether the class is there.
classList is also iterable, so a for..of loop walks every class name on the element:
<body class="panel open">
<script>
for (let name of document.body.classList) {
alert(name); // panel, and then open
}
</script>
</body>
Here every button on classList in one place. Click Toggle a few times to watch the highlight class come and go, and notice how the read-out of className and contains stays in sync — the other classes on the card are never disturbed.
Element style
The elem.style property is an object that mirrors the element’s style attribute. Writing elem.style.width = "100px" has the same effect as if the markup carried style="width:100px".
CSS property names are hyphenated; JavaScript property names can’t contain hyphens (a hyphen means subtraction). So each multi-word CSS property becomes camelCase on the style object — the hyphen drops out and the next letter goes uppercase:
background-color => elem.style.backgroundColor
z-index => elem.style.zIndex
border-left-width => elem.style.borderLeftWidth
For example:
document.body.style.backgroundColor = prompt('background color?', 'teal');
Drag the sliders below. Each one writes a single camelCased property onto the box’s inline style, and the read-out shows the exact style string the DOM builds up as a result.
Resetting the style property
Sometimes you set a style property and later want it gone — not set to some other value, but unset, as if you’d never touched it.
Hiding an element is the everyday case: elem.style.display = "none".
To bring it back, you might reach for delete elem.style.display. Don’t. The right move is to assign an empty string: elem.style.display = "".
// run this and the <body> blinks
document.body.style.display = "none"; // hide
setTimeout(() => document.body.style.display = "", 1000); // back to normal
Setting style.display to "" clears that inline property. With the inline value gone, the element falls back to whatever its CSS classes and the browser’s built-in styles say — exactly as if the inline property had never been there.
There’s also a dedicated method, elem.style.removeProperty('propertyName'), which clears a single property:
document.body.style.background = 'red'; // set background to red
setTimeout(() => document.body.style.removeProperty('background'), 1000); // remove it after 1 second
Mind the units
CSS values often need a unit, and style won’t add one for you. Assign a bare number where CSS expects a length and the browser silently ignores it.
<body>
<script>
// doesn't work!
document.body.style.margin = 16;
alert(document.body.style.margin); // '' (empty string — the assignment was ignored)
// add the unit and it works
document.body.style.margin = '16px';
alert(document.body.style.margin); // 16px
alert(document.body.style.marginTop); // 16px
alert(document.body.style.marginLeft); // 16px
</script>
</body>
Notice the last two reads. You set the shorthand style.margin, and the browser unpacks it into the longhand pieces — marginTop, marginLeft, and the rest — so each one reads back the value derived from the shorthand.
Computed styles: getComputedStyle
Writing styles is the easy half. Reading them back has a trap that catches almost everyone once.
Say you want an element’s real size, its margins, its color. Your instinct is to read elem.style.something. That works only for values written into the inline style attribute.
elem.style reflects the style attribute alone — nothing from the CSS cascade.
So anything an element inherited from a class or a stylesheet rule is invisible to elem.style. Here the margin and color come from a <style> block, and style reports them as empty:
<head>
<style> body { color: blue; margin: 8px } </style>
</head>
<body>
The blue text
<script>
alert(document.body.style.color); // empty
alert(document.body.style.marginTop); // empty
</script>
</body>
To read the value the element actually ended up with — after every class, rule, and default has been resolved — use getComputedStyle.
The syntax:
getComputedStyle(element, [pseudo])
- element
The element whose value you want to read.
- pseudo
A pseudo-element, if you need one — for instance
::before. Leave it out (or pass an empty string) to read the element itself.
You get back a style-like object, same shape as elem.style, but this one accounts for every CSS rule that applied:
<head>
<style> body { color: blue; margin: 8px } </style>
</head>
<body>
<script>
let computedStyle = getComputedStyle(document.body);
// now the margin and color are readable
alert( computedStyle.marginTop ); // 8px
alert( computedStyle.color ); // rgb(0, 0, 255)
</script>
</body>
The demo below makes the split concrete. The paragraph gets its color from a stylesheet rule and its padding from an inline attribute. Click the button and compare the two columns: elem.style only knows about the inline padding, while getComputedStyle reports the resolved value of everything.
Summary
Two DOM properties manage classes:
className— the class attribute as a single string. Reach for it when you want to read or replace the whole set at once.classList— an object withadd/remove/toggle/contains, plus iteration. Reach for it when you’re editing individual classes.
Two ways to change styles:
style— an object of camelCased properties that mirrors the inlinestyleattribute. Reading and writing it is the same as editing that attribute. For!importantand the rarer operations, the CSSStyleDeclaration reference on MDN lists the methods.style.cssText— the entirestyleattribute as one string. Assigning it replaces everything.
One way to read the finished styles, after all CSS has applied and values are resolved:
getComputedStyle(elem, [pseudo])— returns a read-only, style-like object of the resolved values.