# Styles and classes Before we get into JavaScript's ways of dealing with styles and classes -- here's an important rule. Hopefully it's obvious enough, but we still have to mention it. There are generally two ways to style an element: 1. Create a class in CSS and add it: `
` 2. Write properties directly into `style`: `
`. JavaScript can modify both classes and `style` properties. We should always prefer CSS classes to `style`. The latter should only be used if classes "can't handle it". For example, `style` is acceptable if we calculate coordinates of an element dynamically and want to set them from JavaScript, like this: ```js let top = /* complex calculations */; let left = /* complex calculations */; elem.style.left = left; // e.g '123px', calculated at run-time elem.style.top = top; // e.g '456px' ``` For other cases, like making the text red, adding a background icon -- describe that in CSS and then add the class (JavaScript can do that). That's more flexible and easier to support. ## className and classList Changing a class is one of the most often used actions in scripts. In the ancient time, there was a limitation in JavaScript: a reserved word like `"class"` could not be an object property. That limitation does not exist now, but at that time it was impossible to have a `"class"` property, like `elem.class`. So for classes the similar-looking property `"className"` was introduced: the `elem.className` corresponds to the `"class"` attribute. For instance: ```html run ``` If we assign something to `elem.className`, it replaces the whole string of classes. Sometimes that's what we need, but often we want to add/remove a single class. There's another property for that: `elem.classList`. The `elem.classList` is a special object with methods to `add/remove/toggle` a single class. For instance: ```html run ``` So we can operate both on the full class string using `className` or on individual classes using `classList`. What we choose depends on our needs. Methods of `classList`: - `elem.classList.add/remove("class")` -- adds/removes the class. - `elem.classList.toggle("class")` -- adds the class if it doesn't exist, otherwise removes it. - `elem.classList.contains("class")` -- checks for the given class, returns `true/false`. Besides, `classList` is iterable, so we can list all classes with `for..of`, like this: ```html run ``` ## Element style The property `elem.style` is an object that corresponds to what's written in the `"style"` attribute. Setting `elem.style.width="100px"` works the same as if we had in the attribute `style` a string `width:100px`. For multi-word property the camelCase is used: ```js no-beautify background-color => elem.style.backgroundColor z-index => elem.style.zIndex border-left-width => elem.style.borderLeftWidth ``` For instance: ```js run document.body.style.backgroundColor = prompt('background color?', 'green'); ``` ````smart header="Prefixed properties" Browser-prefixed properties like `-moz-border-radius`, `-webkit-border-radius` also follow the same rule: a dash means upper case. For instance: ```js button.style.MozBorderRadius = '5px'; button.style.WebkitBorderRadius = '5px'; ``` ```` ## Resetting the style property Sometimes we want to assign a style property, and later remove it. For instance, to hide an element, we can set `elem.style.display = "none"`. Then later we may want to remove the `style.display` as if it were not set. Instead of `delete elem.style.display` we should assign an empty string to it: `elem.style.display = ""`. ```js run // if we run this code, the will blink document.body.style.display = "none"; // hide setTimeout(() => document.body.style.display = "", 1000); // back to normal ``` If we set `style.display` to an empty string, then the browser applies CSS classes and its built-in styles normally, as if there were no such `style.display` property at all. ````smart header="Full rewrite with `style.cssText`" Normally, we use `style.*` to assign individual style properties. We can't set the full style like `div.style="color: red; width: 100px"`, because `div.style` is an object, and it's read-only. To set the full style as a string, there's a special property `style.cssText`: ```html run
Button
``` This property is rarely used, because such assignment removes all existing styles: it does not add, but replaces them. May occasionally delete something needed. But we can safely use it for new elements, when we know we won't delete an existing style. The same can be accomplished by setting an attribute: `div.setAttribute('style', 'color: red...')`. ```` ## Mind the units Don't forget to add CSS units to values. For instance, we should not set `elem.style.top` to `10`, but rather to `10px`. Otherwise it wouldn't work: ```html run height=100 ``` Please note: the browser "unpacks" the property `style.margin` in the last lines and infers `style.marginLeft` and `style.marginTop` from it. ## Computed styles: getComputedStyle So, modifying a style is easy. But how to *read* it? For instance, we want to know the size, margins, the color of an element. How to do it? **The `style` property operates only on the value of the `"style"` attribute, without any CSS cascade.** So we can't read anything that comes from CSS classes using `elem.style`. For instance, here `style` doesn't see the margin: ```html run height=60 no-beautify The red text ``` ...But what if we need, say, to increase the margin by `20px`? We would want the current value of it. There's another method for that: `getComputedStyle`. The syntax is: ```js getComputedStyle(element, [pseudo]) ``` element : Element to read the value for. pseudo : A pseudo-element if required, for instance `::before`. An empty string or no argument means the element itself. The result is an object with styles, like `elem.style`, but now with respect to all CSS classes. For instance: ```html run height=100 ``` ```smart header="Computed and resolved values" There are two concepts in [CSS](https://drafts.csswg.org/cssom/#resolved-values): 1. A *computed* style value is the value after all CSS rules and CSS inheritance is applied, as the result of the CSS cascade. It can look like `height:1em` or `font-size:125%`. 2. A *resolved* style value is the one finally applied to the element. Values like `1em` or `125%` are relative. The browser takes the computed value and makes all units fixed and absolute, for instance: `height:20px` or `font-size:16px`. For geometry properties resolved values may have a floating point, like `width:50.5px`. A long time ago `getComputedStyle` was created to get computed values, but it turned out that resolved values are much more convenient, and the standard changed. So nowadays `getComputedStyle` actually returns the resolved value of the property, usually in `px` for geometry. ``` ````warn header="`getComputedStyle` requires the full property name" We should always ask for the exact property that we want, like `paddingLeft` or `marginTop` or `borderTopWidth`. Otherwise the correct result is not guaranteed. For instance, if there are properties `paddingLeft/paddingTop`, then what should we get for `getComputedStyle(elem).padding`? Nothing, or maybe a "generated" value from known paddings? There's no standard rule here. There are other inconsistencies. As an example, some browsers (Chrome) show `10px` in the document below, and some of them (Firefox) -- do not: ```html run ``` ```` ```smart header="Styles applied to `:visited` links are hidden!" Visited links may be colored using `:visited` CSS pseudoclass. But `getComputedStyle` does not give access to that color, because otherwise an arbitrary page could find out whether the user visited a link by creating it on the page and checking the styles. JavaScript may not see the styles applied by `:visited`. And also, there's a limitation in CSS that forbids to apply geometry-changing styles in `:visited`. That's to guarantee that there's no sideway for an evil page to test if a link was visited and hence to break the privacy. ``` ## Summary To manage classes, there are two DOM properties: - `className` -- the string value, good to manage the whole set of classes. - `classList` -- the object with methods `add/remove/toggle/contains`, good for individual classes. To change the styles: - The `style` property is an object with camelCased styles. Reading and writing to it has the same meaning as modifying individual properties in the `"style"` attribute. To see how to apply `important` and other rare stuff -- there's a list of methods at [MDN](mdn:api/CSSStyleDeclaration). - The `style.cssText` property corresponds to the whole `"style"` attribute, the full string of styles. To read the resolved styles (with respect to all classes, after all CSS is applied and final values are calculated): - The `getComputedStyle(elem, [pseudo])` returns the style-like object with them. Read-only.