Command Palette

Search for a command to run...

Hectal
PHASE 8Intermediate ~21 min· topic 5 of 8

Topic 8.5

Changing Styles from JavaScript

In one line

element.style reads and writes an element's inline styles, getComputedStyle reads the final styles the browser actually applied, CSS variables can be set from JavaScript with setProperty, and toggling classes is usually the cleanest way to change looks.

Think of it like this

Dressing for school. Your school has a uniform rule book (the stylesheet): everyone wears a blue shirt. On sports day, a teacher pins a note on one student saying "wear red today" (an inline style). That note beats the rule book for that one student. Better still, the rule book can say "anyone in the sports team wears red", and the teacher only has to add the student to the team (a class). If you want to know what someone is actually wearing, you look at them, not at the rule book or the note (that's getComputedStyle).

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Inline style
A style written in the element's own style attribute, which element.style reads and writes.
element.style
The object for one element's inline styles. Property names are camelCase, values are strings.
camelCase
Dashes removed and the next letter made capital: background-color becomes backgroundColor.
getComputedStyle
A function that returns the final styles the browser really uses for an element, after all CSS rules.
Resolved value
The value getComputedStyle reports, with units worked out, such as rgb(255, 0, 0) or 16px.
CSS custom property
A CSS variable like --accent, set once and used with var(--accent) in many rules.
setProperty
A method on element.style that sets a property by its real CSS name, including custom properties.
hidden
An HTML attribute (and property) that hides an element; the browser gives it display: none.

Step by step

01Set inline styles

Write camelCase names and string values with units. Look at the element in DevTools: you'll see a style attribute appear.

app.jswhole filejavascript
const bar = document.querySelector(".progress-bar");
bar.style.width = "65%";
bar.style.backgroundColor = "seagreen";
bar.style.setProperty("border-radius", "4px");
// <div class="progress-bar" style="width: 65%; background-color: seagreen; border-radius: 4px;">

02Read the real value

style only knows inline values. getComputedStyle knows everything, after the cascade.

app.jswhole filejavascript
const title = document.querySelector("h1");
title.style.color;                     // "" (the colour comes from the stylesheet)
getComputedStyle(title).color;          // "rgb(20, 20, 20)"
getComputedStyle(title).fontSize;       // "32px"
Read the real valuediagram
Rendering diagram…

03Prefer a class for a state

Put the look in CSS once, and switch it with one line of JavaScript.

styles.csswhole filecss
.card {
  border: 1px solid #ccc;
  transition: box-shadow 0.2s;
}
.card.selected {
  border-color: royalblue;
  box-shadow: 0 0 0 3px #c7d2fe;
}

04Change a theme with one variable

Every rule that uses var(--accent) updates when the variable changes, so one JavaScript line recolours the whole page.

app.jswhole filejavascript
document.documentElement.style.setProperty("--accent", "tomato");
const accent = getComputedStyle(document.documentElement).getPropertyValue("--accent").trim();
console.log(accent); // tomato

05Hide and show correctly

Use hidden, or clear the inline display instead of guessing the original value.

app.jswhole filejavascript
panel.hidden = true;          // hide
panel.hidden = false;         // show

panel.style.display = "none"; // hide
panel.style.display = "";     // show: back to whatever the CSS says

Try it yourself

  1. 1

    Override with !important

    In the first example, add #box { color: green !important; } to the CSS and box.style.color = "red"; to the JavaScript. Predict the computed colour, then run. Then use box.style.setProperty("color", "red", "important").

  2. 2

    Pick a theme from a list

    In the third example, loop over ["crimson", "royalblue", "darkorange"], set each as --accent and log the button's computed background each time.

  3. 3

    Read a pseudo-element

    In the second example, add .card::before { content: "New"; color: crimson; } to the CSS and log getComputedStyle(card, "::before").content. What do the quotes in the output tell you?

Code & diagrams

element.style versus getComputedStyle New tab

`style.color` was empty because the blue comes from the stylesheet. The width without a unit was silently ignored. Setting `backgroundColor` to `""` removed it from the inline style.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

""
rgb(0, 0, 255)
background-color: gold; border-radius: 8px;
rgb(255, 215, 0) 8px
border-radius: 8px;
Switching a state with a class New tab

JavaScript only switched the class; the CSS decided what "selected" looks like. The transition is turned off here only so the console can read the final colour at once; normally you'd keep it.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Before: rgb(204, 204, 204)
Selected: rgb(65, 105, 225) rgb(238, 242, 255)
After: rgb(204, 204, 204)
Theming with CSS variables New tab

One `setProperty` call recoloured both the button and the text. Writing `root.style.accent` only made an ordinary JavaScript property; the CSS variable didn't change.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

teal rgb(0, 128, 128)
tomato rgb(255, 99, 71)
rgb(255, 99, 71)
tomato

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget the unit

Write bar.style.width = 65; to set a progress bar to 65%.

terminal
$ the page shows
── what you'll see ──
The bar's width doesn't change at all. There is no error in the console.

Break #2

Show an element by setting display to block

A flex container hidden with nav.style.display = "none" is shown again with nav.style.display = "block";.

terminal
$ the page shows
── what you'll see ──
The menu comes back, but its links are stacked vertically instead of in a row.

Break #3

Expect element.style to show stylesheet values

With h1 { color: navy; } in CSS, write if (title.style.color === "navy") { ... }.

terminal
$ Console
── what you'll see ──
The condition is always false: title.style.color is "".

Myth vs fact

Myth

element.style tells you how an element looks.

Fact

It only shows the inline style attribute. Use getComputedStyle for the final values from all CSS.

Myth

Setting styles from JavaScript is the normal way to change looks.

Fact

Toggling classes keeps styling in CSS where media queries, dark mode, transitions and designers can work with it. Inline styles are for values that are only known at runtime.

Myth

getComputedStyle returns what I wrote in CSS.

Fact

It returns resolved values: red becomes rgb(255, 0, 0), 2em becomes pixels, and many shorthand properties are expanded. For custom properties it returns the value as written (trim it).

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    getComputedStyle returns a live CSSStyleDeclaration: keep it in a variable and it reflects later changes. But each read of a layout-dependent property (width, height, top) may force a synchronous style and layout recalculation if the DOM changed since the last frame. Group reads together (Topic 8.8).

  • ▸

    For animations, changing transform and opacity is cheapest: modern browsers can animate them on the compositor without layout or paint. Changing width, top or left each frame causes layout every frame. Prefer CSS transitions or the Web Animations API (el.animate(...)) over updating style in a timer.

  • ▸

    CSS.registerProperty (or @property in CSS) registers a custom property with a type, such as <color>, an initial value and inheritance. Registered properties can be animated smoothly and are returned in computed form by getComputedStyle.

  • ▸

    Strict Content Security Policies can block inline style attributes in HTML (style-src without 'unsafe-inline'), but setting styles through the CSSOM (el.style.x = ..., setProperty) is still allowed. That's another reason to change styles through properties instead of writing style="..." strings with innerHTML.

Remember this

  1. 1

    element.style is an object for the element's inline styles: the ones in its style="..." attribute. Setting a property writes to that attribute: box.style.backgroundColor = "gold" adds background-color: gold. CSS names with dashes become camelCase properties (font-size → fontSize, z-index → zIndex), or you can use style.setProperty("font-size", "18px") with the real CSS name. Values are strings with units: box.style.width = "200px". A bare number like 200 is ignored for lengths. Setting a property to "" removes it, so the stylesheet's value applies again.

  2. 2

    Inline styles have high priority in the cascade (see /css, Phase 2): they beat normal rules from stylesheets, but lose to !important rules. setProperty("color", "red", "important") adds an inline !important. el.style.cssText reads or replaces the whole inline style string at once, wiping anything else that was inline.

  3. 3

    Reading el.style.color only tells you about inline styles. If the colour comes from a stylesheet, el.style.color is "". To know the real value, use getComputedStyle(el), which returns a read-only object with the resolved value of every property after the cascade, inheritance and units are worked out: colours come back as rgb(...), lengths usually in px. getComputedStyle(el, "::before") reads a pseudo-element. Reading layout-related values can force the browser to calculate layout (Topic 8.8).

  4. 4

    The cleanest pattern is to keep looks in CSS and use JavaScript only to switch states with classes: card.classList.toggle("selected"), with .card.selected { ... } in CSS. Designers can change the look without touching JavaScript, transitions defined in CSS just work, media queries and dark mode still apply, and you avoid long chains of inline style lines. Use inline styles for values that are truly dynamic and can't be listed in advance, like the position of a dragged item or the width of a progress bar.

  5. 5

    CSS custom properties (CSS variables, see /css) connect the two worlds. Define --accent: teal in CSS, use it with color: var(--accent), then change it from JavaScript with el.style.setProperty("--accent", "tomato"); every rule that uses it updates. Set it on document.documentElement (the <html> element) to change a theme for the whole page. Read it with getComputedStyle(el).getPropertyValue("--accent"). Custom properties need setProperty/getPropertyValue: el.style["--accent"] and el.style.accent don't work. Their values are inherited by descendants.

  6. 6

    Showing and hiding: el.hidden = true (the HTML hidden attribute, display: none by default) is clear and accessible. el.style.display = "none" also works, but to show the element again set display back to "", not to "block", which would break a flex or grid item or a table row. If your CSS sets display on the element, it beats hidden; add [hidden] { display: none !important; } to make hidden reliable.

Explain it without notes

01

What is the difference between element.style and getComputedStyle(element)?

02

Why is toggling a class usually better than setting inline styles?

03

How do you change a CSS custom property from JavaScript, and why is that useful for theming?

04

How should you hide and show an element from JavaScript?

Practice

01

Write setProgress(percent) that sets the width of #bar to that percentage and its background to crimson below 30, otherwise seagreen. Log the inline style after setProgress(20).

02

Write toggleDark() that toggles the class dark on <html> and returns the computed background colour of <body>, with CSS .dark body { background: rgb(17, 17, 17); }.

Trade-offs

  • ↔

    Inline styles are quick and precise for one element but hard to override and to theme; classes and custom properties keep design in CSS at the cost of naming a class or variable for each state.

  • ↔

    getComputedStyle gives the truth but can force layout when read after changes; keeping your own state in JavaScript (or in a class) avoids asking the browser at all.

Done when you can

  • I can set inline styles with camelCase names, string values and units.

  • I know element.style only shows inline styles, and use getComputedStyle to read real values.

  • I prefer toggling classes for states and keep the look in CSS.

  • I can set and read CSS custom properties with setProperty and getPropertyValue.

  • I can hide and show elements with hidden or by clearing display.