Skip to content

Commit 66682ac

Browse files
authored
Merge pull request #242 from vplentinax/sync-ui-loading-and-99uimisc
sync-ui-loading-99misc
2 parents cde0572 + bb5fef9 commit 66682ac

File tree

18 files changed

+1326
-84
lines changed

18 files changed

+1326
-84
lines changed

2-ui/5-loading/01-onload-ondomcontentloaded/article.md

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
The lifecycle of an HTML page has three important events:
44

55
- `DOMContentLoaded` -- the browser fully loaded HTML, and the DOM tree is built, but external resources like pictures `<img>` and stylesheets may be not yet loaded.
6-
- `load` -- the browser loaded all resources (images, styles etc).
7-
- `beforeunload/unload` -- when the user is leaving the page.
6+
- `load` -- not only HTML is loaded, but also all the external resources: images, styles etc.
7+
- `beforeunload/unload` -- the user is leaving the page.
88

99
Each event may be useful:
1010

1111
- `DOMContentLoaded` event -- DOM is ready, so the handler can lookup DOM nodes, initialize the interface.
12-
- `load` event -- additional resources are loaded, we can get image sizes (if not specified in HTML/CSS) etc.
12+
- `load` event -- external resources are loaded, so styles are applied, image sizes are known etc.
1313
- `beforeunload` event -- the user is leaving: we can check if the user saved the changes and ask them whether they really want to leave.
1414
- `unload` -- the user almost left, but we still can initiate some operations, such as sending out statistics.
1515

@@ -49,7 +49,7 @@ In the example the `DOMContentLoaded` handler runs when the document is loaded,
4949

5050
But it doesn't wait for the image to load. So `alert` shows zero sizes.
5151

52-
At the first sight `DOMContentLoaded` event is very simple. The DOM tree is ready -- here's the event. There are few peculiarities though.
52+
At first sight, the `DOMContentLoaded` event is very simple. The DOM tree is ready -- here's the event. There are few peculiarities though.
5353

5454
### DOMContentLoaded and scripts
5555

@@ -60,7 +60,7 @@ So DOMContentLoaded definitely happens after such scripts:
6060
```html run
6161
<script>
6262
document.addEventListener("DOMContentLoaded", () => {
63-
console.log("DOM ready!");
63+
alert("DOM ready!");
6464
});
6565
</script>
6666

@@ -73,11 +73,10 @@ So DOMContentLoaded definitely happens after such scripts:
7373

7474
In the example above, we first see "Library loaded...", and then "DOM ready!" (all scripts are executed).
7575

76-
```warn header="Scripts with `async`, `defer` or `type=\"module\"` don't block DOMContentLoaded"
77-
78-
Script attributes `async` and `defer`, that we'll cover [a bit later](info:script-async-defer), don't block DOMContentLoaded. [Javascript modules](info:modules) behave like `defer`, they don't block it too.
79-
80-
So here we're talking about "regular" scripts, like `<script>...</script>`, or `<script src="..."></script>`.
76+
```warn header="Scripts that don't block DOMContentLoaded"
77+
There are two exceptions from this rule:
78+
1. Scripts with the `async` attribute, that we'll cover [a bit later](info:script-async-defer), don't block `DOMContentLoaded`.
79+
2. Scripts that are generated dynamically with `document.createElement('script')` and then added to the webpage also don't block this event.
8180
```
8281

8382
### DOMContentLoaded and styles
@@ -86,15 +85,15 @@ External style sheets don't affect DOM, so `DOMContentLoaded` does not wait for
8685

8786
But there's a pitfall. If we have a script after the style, then that script must wait until the stylesheet loads:
8887

89-
```html
88+
```html run
9089
<link type="text/css" rel="stylesheet" href="style.css">
9190
<script>
9291
// the script doesn't not execute until the stylesheet is loaded
9392
alert(getComputedStyle(document.body).marginTop);
9493
</script>
9594
```
9695

97-
The reason is that the script may want to get coordinates and other style-dependent properties of elements, like in the example above. Naturally, it has to wait for styles to load.
96+
The reason for this is that the script may want to get coordinates and other style-dependent properties of elements, like in the example above. Naturally, it has to wait for styles to load.
9897

9998
As `DOMContentLoaded` waits for scripts, it now waits for styles before them as well.
10099

@@ -109,13 +108,13 @@ So if `DOMContentLoaded` is postponed by long-loading scripts, then autofill als
109108

110109
## window.onload [#window-onload]
111110

112-
The `load` event on the `window` object triggers when the whole page is loaded including styles, images and other resources.
111+
The `load` event on the `window` object triggers when the whole page is loaded including styles, images and other resources. This event is available via the `onload` property.
113112

114113
The example below correctly shows image sizes, because `window.onload` waits for all images:
115114

116115
```html run height=200 refresh
117116
<script>
118-
window.onload = function() {
117+
window.onload = function() { // same as window.addEventListener('load', (event) => {
119118
alert('Page loaded');
120119
121120
// image is loaded at this time
@@ -150,15 +149,15 @@ window.addEventListener("unload", function() {
150149
```
151150
152151
- The request is sent as POST.
153-
- We can send not only a string, but also forms and other formats, as described in the chapter <info:fetch-basics>, but usually it's a stringified object.
152+
- We can send not only a string, but also forms and other formats, as described in the chapter <info:fetch>, but usually it's a stringified object.
154153
- The data is limited by 64kb.
155154
156155
When the `sendBeacon` request is finished, the browser probably has already left the document, so there's no way to get server response (which is usually empty for analytics).
157156
158-
There's also a `keepalive` flag for doing such "after-page-left" requests in [fetch](info:fetch-basics) method for generic network requests. You can find more information in the chapter <info:fetch-api>.
157+
There's also a `keepalive` flag for doing such "after-page-left" requests in [fetch](info:fetch) method for generic network requests. You can find more information in the chapter <info:fetch-api>.
159158
160159
161-
If we want to cancel the transition to another page, we can't do it here. But we can use another event -- `onbeforeunload`.
160+
If we want to cancel the transition to another page, we can't do it here. But we can use another event -- `onbeforeunload`.
162161
163162
## window.onbeforeunload [#window.onbeforeunload]
164163
@@ -174,7 +173,7 @@ window.onbeforeunload = function() {
174173
};
175174
```
176175
177-
For historical reasons, returning a non-empty string also counts as canceling the event. Some time ago browsers used show it as a message, but as the [modern specification](https://html.spec.whatwg.org/#unloading-documents) says, they shouldn't.
176+
For historical reasons, returning a non-empty string also counts as canceling the event. Some time ago browsers used to show it as a message, but as the [modern specification](https://html.spec.whatwg.org/#unloading-documents) says, they shouldn't.
178177
179178
Here's an example:
180179
@@ -218,7 +217,7 @@ if (document.readyState == 'loading') {
218217
}
219218
```
220219
221-
There's also `readystatechange` event that triggers when the state changes, so we can print all these states like this:
220+
There's also the `readystatechange` event that triggers when the state changes, so we can print all these states like this:
222221
223222
```js run
224223
// current state
@@ -273,12 +272,12 @@ The numbers in square brackets denote the approximate time of when it happens. E
273272
274273
Page load events:
275274
276-
- `DOMContentLoaded` event triggers on `document` when DOM is ready. We can apply JavaScript to elements at this stage.
275+
- The `DOMContentLoaded` event triggers on `document` when the DOM is ready. We can apply JavaScript to elements at this stage.
277276
- Script such as `<script>...</script>` or `<script src="..."></script>` block DOMContentLoaded, the browser waits for them to execute.
278277
- Images and other resources may also still continue loading.
279-
- `load` event on `window` triggers when the page and all resources are loaded. We rarely use it, because there's usually no need to wait for so long.
280-
- `beforeunload` event on `window` triggers when the user wants to leave the page. If we cancel the event, browser asks whether the user really wants to leave (e.g we have unsaved changes).
281-
- `unload` event on `window` triggers when the user is finally leaving, in the handler we can only do simple things that do not involve delays or asking a user. Because of that limitation, it's rarely used. We can send out a network request with `navigator.sendBeacon`.
278+
- The `load` event on `window` triggers when the page and all resources are loaded. We rarely use it, because there's usually no need to wait for so long.
279+
- The `beforeunload` event on `window` triggers when the user wants to leave the page. If we cancel the event, browser asks whether the user really wants to leave (e.g we have unsaved changes).
280+
- The `unload` event on `window` triggers when the user is finally leaving, in the handler we can only do simple things that do not involve delays or asking a user. Because of that limitation, it's rarely used. We can send out a network request with `navigator.sendBeacon`.
282281
- `document.readyState` is the current state of the document, changes can be tracked in the `readystatechange` event:
283282
- `loading` -- the document is loading.
284283
- `interactive` -- the document is parsed, happens at about the same time as `DOMContentLoaded`, but before it.

2-ui/5-loading/02-script-async-defer/article.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
In modern websites, scripts are often "heavier" than HTML: their download size is larger, and processing time is also longer.
55

6-
When the browser loads HTML and comes across a `<script>...</script>` tag, it can't continue building DOM. It must execute the script right now. The same happens for external scripts `<script src="..."></script>`: the browser must wait until the script downloads, execute it, and only after process the rest of the page.
6+
When the browser loads HTML and comes across a `<script>...</script>` tag, it can't continue building the DOM. It must execute the script right now. The same happens for external scripts `<script src="..."></script>`: the browser must wait until the script downloads, execute it, and only after process the rest of the page.
77

88
That leads to two important issues:
99

10-
1. Scripts can't see DOM elements below them, so can't add handlers etc.
10+
1. Scripts can't see DOM elements below them, so they can't add handlers etc.
1111
2. If there's a bulky script at the top of the page, it "blocks the page". Users can't see the page content till it downloads and runs:
1212

1313
```html run height=100
@@ -29,11 +29,11 @@ There are some workarounds to that. For instance, we can put a script at the bot
2929
</body>
3030
```
3131

32-
But this solution is far from perfect. For example, the browser actually notices the script (and can start downloading it) only after it downloaded the full HTML document. For long HTML documents, that may be a noticeable delay.
32+
But this solution is far from perfect. For example, the browser notices the script (and can start downloading it) only after it downloaded the full HTML document. For long HTML documents, that may be a noticeable delay.
3333

34-
Such things are invisible for people using very fast connections, but many people in the world still have slower internet speeds and far-from-perfect mobile connectivity.
34+
Such things are invisible for people using very fast connections, but many people in the world still have slow internet speeds and use a far-from-perfect mobile internet connection.
3535

36-
Luckily, there are two `<script>` attributes that solve the problem for us: `defer` and `async`
36+
Luckily, there are two `<script>` attributes that solve the problem for us: `defer` and `async`.
3737

3838
## defer
3939

@@ -68,34 +68,34 @@ The following example demonstrates that:
6868
```
6969

7070
1. The page content shows up immediately.
71-
2. `DOMContentLoaded` waits for the deferred script. It only triggers when the script `(2)` is downloaded is executed.
71+
2. `DOMContentLoaded` waits for the deferred script. It only triggers when the script `(2)` is downloaded and executed.
7272

7373
Deferred scripts keep their relative order, just like regular scripts.
7474

7575
So, if we have a long script first, and then a smaller one, then the latter one waits.
7676

7777
```html
78-
<script async src="https://javascript.info/article/script-async-defer/long.js"></script>
79-
<script async src="https://javascript.info/article/script-async-defer/small.js"></script>
78+
<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
79+
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>
8080
```
8181

8282
```smart header="The small script downloads first, runs second"
8383
Browsers scan the page for scripts and download them in parallel, to improve performance. So in the example above both scripts download in parallel. The `small.js` probably makes it first.
8484
85-
But the specification requres scripts to execute in the document order, so it waits for `long.js` to execute.
85+
But the specification requires scripts to execute in the document order, so it waits for `long.js` to execute.
8686
```
8787

8888
```smart header="The `defer` attribute is only for external scripts"
89-
The `defer` attribute is ignored if the script has no `src`.
89+
The `defer` attribute is ignored if the `<script>` tag has no `src`.
9090
```
9191
9292
9393
## async
9494
95-
The `async` attribute means that a script is completely independant:
95+
The `async` attribute means that a script is completely independent:
9696
97-
- The page doesn't wait for async scripts, the contents is processed and displayed.
98-
- `DOMContentLoaded` and async scripts don't wait each other:
97+
- The page doesn't wait for async scripts, the contents are processed and displayed.
98+
- `DOMContentLoaded` and async scripts don't wait for each other:
9999
- `DOMContentLoaded` may happen both before an async script (if an async script finishes loading after the page is complete)
100100
- ...or after an async script (if an async script is short or was in HTTP-cache)
101101
- Other scripts don't wait for `async` scripts, and `async` scripts don't wait for them.
@@ -120,16 +120,17 @@ So, if we have several `async` scripts, they may execute in any order. Whatever
120120
2. `DOMContentLoaded` may happen both before and after `async`, no guarantees here.
121121
3. Async scripts don't wait for each other. A smaller script `small.js` goes second, but probably loads before `long.js`, so runs first. That's called a "load-first" order.
122122

123-
Async scripts are great when we integrate an independant third-party script into the page: counters, ads and so on.
123+
Async scripts are great when we integrate an independent third-party script into the page: counters, ads and so on, as they don't depend on our scripts, and our scripts shouldn't wait for them:
124124

125125
```html
126+
<!-- Google Analytics is usually added like this -->
126127
<script async src="https://google-analytics.com/analytics.js"></script>
127128
```
128129

129130

130131
## Dynamic scripts
131132

132-
We can also create a script dynamically using Javascript:
133+
We can also add a script dynamically using JavaScript:
133134

134135
```js run
135136
let script = document.createElement('script');
@@ -145,7 +146,6 @@ That is:
145146
- They don't wait for anything, nothing waits for them.
146147
- The script that loads first -- runs first ("load-first" order).
147148

148-
We can change the load-first order into the document order by explicitly setting `async` to `false`:
149149

150150
```js run
151151
let script = document.createElement('script');
@@ -177,7 +177,7 @@ loadScript("/article/script-async-defer/small.js");
177177

178178
## Summary
179179

180-
Both `async` and `defer` have one common thing: they don't block page rendering. So the user can read page content and get acquanted with the page immediately.
180+
Both `async` and `defer` have one common thing: downloading of such scripts doesn't block page rendering. So the user can read page content and get acquainted with the page immediately.
181181

182182
But there are also essential differences between them:
183183

@@ -187,11 +187,11 @@ But there are also essential differences between them:
187187
| `defer` | *Document order* (as they go in the document). | Execute after the document is loaded and parsed (they wait if needed), right before `DOMContentLoaded`. |
188188

189189
```warn header="Page without scripts should be usable"
190-
Please note that if you're using `defer`, then the page is visible before the script loads and enables all the graphical components.
190+
Please note that if you're using `defer`, then the page is visible *before* the script loads.
191191
192-
So, buttons should be disabled by CSS or by other means, to let the user
193-
194-
In practice, `defer` is used for scripts that need DOM and/or their relative execution order is important.
192+
So the user may read the page, but some graphical components are probably not ready yet.
195193
194+
There should be "loading" indications in the proper places, and disabled buttons should show as such, so the user can clearly see what's ready and what's not.
195+
```
196196

197-
So `async` is used for independent scripts, like counters or ads, that don't need to access page content. And their relative execution order does not matter.
197+
In practice, `defer` is used for scripts that need the whole DOM and/or their relative execution order is important. And `async` is used for independent scripts, like counters or ads. And their relative execution order does not matter.

2-ui/5-loading/02-script-async-defer/window-onbeforeunload.view/index.html

Lines changed: 0 additions & 19 deletions
This file was deleted.

2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.view/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
}
3636

3737
// for each image,
38-
// let's create another img with the same src and check that we have its width immediately
38+
// let's create another img with the same src and check that we have its width
3939
function testLoaded() {
4040
let widthSum = 0;
4141
for (let i = 0; i < sources.length; i++) {

0 commit comments

Comments
 (0)