Skip to content

Commit 44b9523

Browse files
committed
fixed
1 parent 2cdc171 commit 44b9523

File tree

3 files changed

+779
-0
lines changed

3 files changed

+779
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
importance: 5
2+
3+
---
4+
5+
# Create a notification
6+
7+
Write a function `showNotification(options)` that creates a notification: `<div class="notification">` with the given content. The notification should automatically disappear after 1.5 seconds.
8+
9+
The options are:
10+
11+
```js
12+
// shows an element with the text "Hello" near the right-top of the window
13+
showNotification({
14+
top: 10, // 10px from the top of the window (by default 0px)
15+
right: 10, // 10px from the right edge of the window (by default 0px)
16+
html: "Hello!", // the HTML of notification
17+
className: "welcome" // an additional class for the div (optional)
18+
});
19+
```
20+
21+
[demo src="solution"]
22+
23+
24+
Use CSS positioning to show the element at given top/right coordinates. The source document has the necessary styles.

Diff for: 2-ui/1-document/08-styles-and-classes/article.md

+303
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
# Styles and classes
2+
3+
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.
4+
5+
There are generally two ways to style an element:
6+
7+
1. Create a class in CSS and add it: `<div class="...">`
8+
2. Write properties directly into `style`: `<div style="...">`.
9+
10+
JavaScript can modify both classes and `style` properties.
11+
12+
We should always prefer CSS classes to `style`. The latter should only be used if classes "can't handle it".
13+
14+
For example, `style` is acceptable if we calculate coordinates of an element dynamically and want to set them from JavaScript, like this:
15+
16+
```js
17+
let top = /* complex calculations */;
18+
let left = /* complex calculations */;
19+
20+
elem.style.left = left; // e.g '123px', calculated at run-time
21+
elem.style.top = top; // e.g '456px'
22+
```
23+
24+
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.
25+
26+
## className and classList
27+
28+
Changing a class is one of the most often used actions in scripts.
29+
30+
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`.
31+
32+
So for classes the similar-looking property `"className"` was introduced: the `elem.className` corresponds to the `"class"` attribute.
33+
34+
For instance:
35+
36+
```html run
37+
<body class="main page">
38+
<script>
39+
alert(document.body.className); // main page
40+
</script>
41+
</body>
42+
```
43+
44+
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.
45+
46+
There's another property for that: `elem.classList`.
47+
48+
The `elem.classList` is a special object with methods to `add/remove/toggle` a single class.
49+
50+
For instance:
51+
52+
```html run
53+
<body class="main page">
54+
<script>
55+
*!*
56+
// add a class
57+
document.body.classList.add('article');
58+
*/!*
59+
60+
alert(document.body.className); // main page article
61+
</script>
62+
</body>
63+
```
64+
65+
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.
66+
67+
Methods of `classList`:
68+
69+
- `elem.classList.add/remove("class")` -- adds/removes the class.
70+
- `elem.classList.toggle("class")` -- adds the class if it doesn't exist, otherwise removes it.
71+
- `elem.classList.contains("class")` -- checks for the given class, returns `true/false`.
72+
73+
Besides, `classList` is iterable, so we can list all classes with `for..of`, like this:
74+
75+
```html run
76+
<body class="main page">
77+
<script>
78+
for (let name of document.body.classList) {
79+
alert(name); // main, and then page
80+
}
81+
</script>
82+
</body>
83+
```
84+
85+
## Element style
86+
87+
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`.
88+
89+
For multi-word property the camelCase is used:
90+
91+
```js no-beautify
92+
background-color => elem.style.backgroundColor
93+
z-index => elem.style.zIndex
94+
border-left-width => elem.style.borderLeftWidth
95+
```
96+
97+
For instance:
98+
99+
```js run
100+
document.body.style.backgroundColor = prompt('background color?', 'green');
101+
```
102+
103+
````smart header="Prefixed properties"
104+
Browser-prefixed properties like `-moz-border-radius`, `-webkit-border-radius` also follow the same rule: a dash means upper case.
105+
106+
For instance:
107+
108+
```js
109+
button.style.MozBorderRadius = '5px';
110+
button.style.WebkitBorderRadius = '5px';
111+
```
112+
````
113+
114+
## Resetting the style property
115+
116+
Sometimes we want to assign a style property, and later remove it.
117+
118+
For instance, to hide an element, we can set `elem.style.display = "none"`.
119+
120+
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 = ""`.
121+
122+
```js run
123+
// if we run this code, the <body> will blink
124+
document.body.style.display = "none"; // hide
125+
126+
setTimeout(() => document.body.style.display = "", 1000); // back to normal
127+
```
128+
129+
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.
130+
131+
````smart header="Full rewrite with `style.cssText`"
132+
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.
133+
134+
To set the full style as a string, there's a special property `style.cssText`:
135+
136+
```html run
137+
<div id="div">Button</div>
138+
139+
<script>
140+
// we can set special style flags like "important" here
141+
div.style.cssText=`color: red !important;
142+
background-color: yellow;
143+
width: 100px;
144+
text-align: center;
145+
`;
146+
147+
alert(div.style.cssText);
148+
</script>
149+
```
150+
151+
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.
152+
153+
The same can be accomplished by setting an attribute: `div.setAttribute('style', 'color: red...')`.
154+
````
155+
156+
## Mind the units
157+
158+
Don't forget to add CSS units to values.
159+
160+
For instance, we should not set `elem.style.top` to `10`, but rather to `10px`. Otherwise it wouldn't work:
161+
162+
```html run height=100
163+
<body>
164+
<script>
165+
*!*
166+
// doesn't work!
167+
document.body.style.margin = 20;
168+
alert(document.body.style.margin); // '' (empty string, the assignment is ignored)
169+
*/!*
170+
171+
// now add the CSS unit (px) - and it works
172+
document.body.style.margin = '20px';
173+
alert(document.body.style.margin); // 20px
174+
175+
alert(document.body.style.marginTop); // 20px
176+
alert(document.body.style.marginLeft); // 20px
177+
</script>
178+
</body>
179+
```
180+
181+
Please note: the browser "unpacks" the property `style.margin` in the last lines and infers `style.marginLeft` and `style.marginTop` from it.
182+
183+
## Computed styles: getComputedStyle
184+
185+
So, modifying a style is easy. But how to *read* it?
186+
187+
For instance, we want to know the size, margins, the color of an element. How to do it?
188+
189+
**The `style` property operates only on the value of the `"style"` attribute, without any CSS cascade.**
190+
191+
So we can't read anything that comes from CSS classes using `elem.style`.
192+
193+
For instance, here `style` doesn't see the margin:
194+
195+
```html run height=60 no-beautify
196+
<head>
197+
<style> body { color: red; margin: 5px } </style>
198+
</head>
199+
<body>
200+
201+
The red text
202+
<script>
203+
*!*
204+
alert(document.body.style.color); // empty
205+
alert(document.body.style.marginTop); // empty
206+
*/!*
207+
</script>
208+
</body>
209+
```
210+
211+
...But what if we need, say, to increase the margin by `20px`? We would want the current value of it.
212+
213+
There's another method for that: `getComputedStyle`.
214+
215+
The syntax is:
216+
217+
```js
218+
getComputedStyle(element, [pseudo])
219+
```
220+
221+
element
222+
: Element to read the value for.
223+
224+
pseudo
225+
: A pseudo-element if required, for instance `::before`. An empty string or no argument means the element itself.
226+
227+
The result is an object with styles, like `elem.style`, but now with respect to all CSS classes.
228+
229+
For instance:
230+
231+
```html run height=100
232+
<head>
233+
<style> body { color: red; margin: 5px } </style>
234+
</head>
235+
<body>
236+
237+
<script>
238+
let computedStyle = getComputedStyle(document.body);
239+
240+
// now we can read the margin and the color from it
241+
242+
alert( computedStyle.marginTop ); // 5px
243+
alert( computedStyle.color ); // rgb(255, 0, 0)
244+
</script>
245+
246+
</body>
247+
```
248+
249+
```smart header="Computed and resolved values"
250+
There are two concepts in [CSS](https://drafts.csswg.org/cssom/#resolved-values):
251+
252+
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%`.
253+
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`.
254+
255+
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.
256+
257+
So nowadays `getComputedStyle` actually returns the resolved value of the property, usually in `px` for geometry.
258+
```
259+
260+
````warn header="`getComputedStyle` requires the full property name"
261+
We should always ask for the exact property that we want, like `paddingLeft` or `marginTop` or `borderTopWidth`. Otherwise the correct result is not guaranteed.
262+
263+
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.
264+
265+
There are other inconsistencies. As an example, some browsers (Chrome) show `10px` in the document below, and some of them (Firefox) -- do not:
266+
267+
```html run
268+
<style>
269+
body {
270+
margin: 10px;
271+
}
272+
</style>
273+
<script>
274+
let style = getComputedStyle(document.body);
275+
alert(style.margin); // empty string in Firefox
276+
</script>
277+
```
278+
````
279+
280+
```smart header="Styles applied to `:visited` links are hidden!"
281+
Visited links may be colored using `:visited` CSS pseudoclass.
282+
283+
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.
284+
285+
JavaScript may not see the styles applied by `:visited`. And also, there's a limitation in CSS that forbids applying geometry-changing styles in `:visited`. That's to guarantee that there's no side way for an evil page to test if a link was visited and hence to break the privacy.
286+
```
287+
288+
## Summary
289+
290+
To manage classes, there are two DOM properties:
291+
292+
- `className` -- the string value, good to manage the whole set of classes.
293+
- `classList` -- the object with methods `add/remove/toggle/contains`, good for individual classes.
294+
295+
To change the styles:
296+
297+
- 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).
298+
299+
- The `style.cssText` property corresponds to the whole `"style"` attribute, the full string of styles.
300+
301+
To read the resolved styles (with respect to all classes, after all CSS is applied and final values are calculated):
302+
303+
- The `getComputedStyle(elem, [pseudo])` returns the style-like object with them. Read-only.

0 commit comments

Comments
 (0)