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
styleattribute, whichelement.stylereads 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-colorbecomesbackgroundColor. - getComputedStyle
- A function that returns the final styles the browser really uses for an element, after all CSS rules.
- Resolved value
- The value
getComputedStylereports, with units worked out, such asrgb(255, 0, 0)or16px. - CSS custom property
- A CSS variable like
--accent, set once and used withvar(--accent)in many rules. - setProperty
- A method on
element.stylethat 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.
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.
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"03Prefer a class for a state
Put the look in CSS once, and switch it with one line of JavaScript.
.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.
document.documentElement.style.setProperty("--accent", "tomato");
const accent = getComputedStyle(document.documentElement).getPropertyValue("--accent").trim();
console.log(accent); // tomato05Hide and show correctly
Use hidden, or clear the inline display instead of guessing the original value.
panel.hidden = true; // hide
panel.hidden = false; // show
panel.style.display = "none"; // hide
panel.style.display = ""; // show: back to whatever the CSS saysTry it yourself
- 1
Override with !important
In the first example, add
#box { color: green !important; }to the CSS andbox.style.color = "red";to the JavaScript. Predict the computed colour, then run. Then usebox.style.setProperty("color", "red", "important"). - 2
Pick a theme from a list
In the third example, loop over
["crimson", "royalblue", "darkorange"], set each as--accentand log the button's computed background each time. - 3
Read a pseudo-element
In the second example, add
.card::before { content: "New"; color: crimson; }to the CSS and loggetComputedStyle(card, "::before").content. What do the quotes in the output tell you?
Code & diagrams
`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
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;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
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)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
Console
Nothing logged yet.Expected console output
teal rgb(0, 128, 128)
tomato rgb(255, 99, 71)
rgb(255, 99, 71)
tomatoBreak 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%.
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";.
Break #3
Expect element.style to show stylesheet values
With h1 { color: navy; } in CSS, write if (title.style.color === "navy") { ... }.
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.
- ▸
getComputedStylereturns a liveCSSStyleDeclaration: 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
transformandopacityis cheapest: modern browsers can animate them on the compositor without layout or paint. Changingwidth,toporlefteach frame causes layout every frame. Prefer CSS transitions or the Web Animations API (el.animate(...)) over updatingstylein a timer. - ▸
CSS.registerProperty(or@propertyin 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 bygetComputedStyle. - ▸
Strict Content Security Policies can block inline
styleattributes in HTML (style-srcwithout'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 writingstyle="..."strings withinnerHTML.
Remember this
- 1
element.styleis an object for the element's inline styles: the ones in itsstyle="..."attribute. Setting a property writes to that attribute:box.style.backgroundColor = "gold"addsbackground-color: gold. CSS names with dashes become camelCase properties (font-size→fontSize,z-index→zIndex), or you can usestyle.setProperty("font-size", "18px")with the real CSS name. Values are strings with units:box.style.width = "200px". A bare number like200is ignored for lengths. Setting a property to""removes it, so the stylesheet's value applies again. - 2
Inline styles have high priority in the cascade (see
/css, Phase 2): they beat normal rules from stylesheets, but lose to!importantrules.setProperty("color", "red", "important")adds an inline!important.el.style.cssTextreads or replaces the whole inline style string at once, wiping anything else that was inline. - 3
Reading
el.style.coloronly tells you about inline styles. If the colour comes from a stylesheet,el.style.coloris"". To know the real value, usegetComputedStyle(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 asrgb(...), lengths usually inpx.getComputedStyle(el, "::before")reads a pseudo-element. Reading layout-related values can force the browser to calculate layout (Topic 8.8). - 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
CSS custom properties (CSS variables, see
/css) connect the two worlds. Define--accent: tealin CSS, use it withcolor: var(--accent), then change it from JavaScript withel.style.setProperty("--accent", "tomato"); every rule that uses it updates. Set it ondocument.documentElement(the<html>element) to change a theme for the whole page. Read it withgetComputedStyle(el).getPropertyValue("--accent"). Custom properties needsetProperty/getPropertyValue:el.style["--accent"]andel.style.accentdon't work. Their values are inherited by descendants. - 6
Showing and hiding:
el.hidden = true(the HTMLhiddenattribute,display: noneby default) is clear and accessible.el.style.display = "none"also works, but to show the element again setdisplayback to"", not to"block", which would break a flex or grid item or a table row. If your CSS setsdisplayon the element, it beatshidden; add[hidden] { display: none !important; }to makehiddenreliable.
Explain it without notes
What is the difference between element.style and getComputedStyle(element)?
Why is toggling a class usually better than setting inline styles?
How do you change a CSS custom property from JavaScript, and why is that useful for theming?
How should you hide and show an element from JavaScript?
Practice
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).
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.
- ↔
getComputedStylegives 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.styleonly shows inline styles, and usegetComputedStyleto read real values.I prefer toggling classes for states and keep the look in CSS.
I can set and read CSS custom properties with
setPropertyandgetPropertyValue.I can hide and show elements with
hiddenor by clearingdisplay.