diff --git a/1-js/13-modules/02-import-export/article.md b/1-js/13-modules/02-import-export/article.md
index dbeb1540..265bd1b9 100644
--- a/1-js/13-modules/02-import-export/article.md
+++ b/1-js/13-modules/02-import-export/article.md
@@ -204,7 +204,7 @@ We may have both default and named exports in a single module, but in practice p
**Another thing to note is that named exports must (naturally) have a name, while `export default` may be anonymous.**
-For instance, these are all perfecly valid default exports:
+For instance, these are all perfectly valid default exports:
```js
export default class { // no class name
@@ -433,6 +433,6 @@ if (something) {
}
```
-...But what if we really need to import something conditionally? Or at the right time? Like, load a module upon request, when it's really needed?
+...But what if we really need to import something conditionally? Or at the right time? Like, load a module upon request, when it's really needed?
We'll see dynamic imports in the next chapter.
diff --git a/2-ui/3-event-details/10-onload-ondomcontentloaded/article.md b/2-ui/3-event-details/10-onload-ondomcontentloaded/article.md
index 7c62da19..60d7bdb1 100644
--- a/2-ui/3-event-details/10-onload-ondomcontentloaded/article.md
+++ b/2-ui/3-event-details/10-onload-ondomcontentloaded/article.md
@@ -121,9 +121,34 @@ The example below correctly shows image sizes, because `window.onload` waits for
## window.onunload
-When a visitor leaves the page, the `unload` event triggers on `window`. We can do something there that doesn't involve a delay, like closing related popup windows. But we can't cancel the transition to another page.
+When a visitor leaves the page, the `unload` event triggers on `window`. We can do something there that doesn't involve a delay, like closing related popup windows.
-For that we should use another event -- `onbeforeunload`.
+This event is a good place to send out analytics.
+
+E.g. we have a script that gathers some data about mouse clicks, scrolls, viewed page areas -- the statistics that can help us to see what users want.
+
+Then `onunload` is the best time to send it out. Regular networking methods such as [fetch](info:fetch-basics) or [XMLHttpRequest](info:xmlhttprequest) don't work well, because we're in the process of leaving the page.
+
+So, there exist `navigator.sendBeacon(url, data)` method for such needs, described in the specification .
+
+It sends the data in background. The transition to another page is not delayed: the browser leaves the page and performs `sendBeacon` in background.
+
+Here's how to use it:
+```js
+let analyticsData = { /* object with gathered data */ };
+
+window.addEventListener("unload", function() {
+ navigator.sendBeacon("/analytics", JSON.stringify(analyticsData));
+};
+```
+
+- The request is sent as POST.
+- We can send not only a string, but also forms and other formats, as described in the chapter , but usually it's a stringified object.
+- The data is limited by 64kb.
+
+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).
+
+If we want to cancel the transition to another page, we can't do it here. But we can use another event -- `onbeforeunload`.
## window.onbeforeunload [#window.onbeforeunload]
diff --git a/2-ui/5-data-storage/01-cookie/article.md b/2-ui/5-data-storage/01-cookie/article.md
index ae14d0c2..047e03cd 100644
--- a/2-ui/5-data-storage/01-cookie/article.md
+++ b/2-ui/5-data-storage/01-cookie/article.md
@@ -1,17 +1,16 @@
# Cookies, document.cookie
-Cookies are small strings of data that are stored directly in the browser. They are not a part of Javascript, but rather a part of HTTP protocol, defined by [RFC 6265](https://tools.ietf.org/html/rfc6265) specification.
+Cookies are small strings of data that are stored directly in the browser. They are a part of HTTP protocol, defined by [RFC 6265](https://tools.ietf.org/html/rfc6265) specification.
-Most of the time, cookies are set by a web server.
+Most of the time, cookies are set by a web server. Then they are automatically added to every request to the same domain.
-One of the most widespread use of cookies is authentication:
+One of the most widespread use cases is authentication:
-1. Upon sign in, the server uses `Set-Cookie` HTTP-header in a response to set a cookie with "session identifier".
-2. The browser stores the cookie.
-3. Next time when the request is set to the same domain, the browser sends the over the net using `Cookie` HTTP-header.
-4. So the server knows who made the request.
+1. Upon sign in, the server uses `Set-Cookie` HTTP-header in the response to set a cookie with "session identifier".
+2. Next time when the request is set to the same domain, the browser sends the over the net using `Cookie` HTTP-header.
+3. So the server knows who made the request.
-The browser provides a special accessor `document.cookie` for cookies.
+We can also access cookies from the browser, using `document.cookie` property.
There are many tricky things about cookies and their options. In this chapter we'll cover them in detail.
@@ -32,11 +31,11 @@ alert( document.cookie ); // cookie1=value1; cookie2=value2;...
```
-The string consist of `name=value` pairs, delimited by `; `. Each one is a separate cookie.
+The value of `document.cookie` consists of `name=value` pairs, delimited by `; `. Each one is a separate cookie.
To find a particular cookie, we can split `document.cookie` by `; `, and then find the right name. We can use either a regular expression or array functions to do that.
-To make things simple, at the end of the chapter you'll find a few functions to manipulate cookies.
+We leave it as an excercise for the reader. Also, at the end of the chapter you'll find helper functions to manipulate cookies.
## Writing to document.cookie
@@ -51,26 +50,26 @@ document.cookie = "user=John"; // update only cookie named 'user'
alert(document.cookie); // show all cookies
```
-If you run it, then probably you'll see multiple cookies. That's because `document.cookie=` operation does not overwrite all cookies, but only `user`.
+If you run it, then probably you'll see multiple cookies. That's because `document.cookie=` operation does not overwrite all cookies. It only sets the mentioned cookie `user`.
Technically, name and value can have any characters, but to keep the formatting valid they should be escaped using a built-in `encodeURIComponent` function:
```js run
// special values, need encoding
-let name = "<>";
-let value = "="
+let name = "my name";
+let value = "John Smith"
-// encodes the cookie as %3C%3E=%3D
+// encodes the cookie as my%20name=John%20Smith
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);
-alert(document.cookie); // ...; %3C%3E=%3D
+alert(document.cookie); // ...; my%20name=John%20Smith
```
```warn header="Limitations"
There are few limitations:
- The `name=value` pair, after `encodeURIComponent`, should not exceed 4kb. So we can't store anything huge in a cookie.
-- The total number of cookies per domain is limited to 20+, depending on a browser.
+- The total number of cookies per domain is limited to around 20+, the exact limit depends on a browser.
```
Cookies have several options, many of them are important and should be set.
@@ -87,7 +86,7 @@ document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT"
The url path prefix, where the cookie is accessible. Must be absolute. By default, it's the current path.
-If a cookie is set with `path=/mypath`, it's visible at `/mypath` and `/mypath/*`, but not at `/page` or `/mypathpage`.
+If a cookie is set with `path=/admin`, it's visible at pages `/admin` and `/admin/something`, but not at `/home` or `/adminpage`.
Usually, we set `path=/` to make the cookie accessible from all website pages.
@@ -95,11 +94,11 @@ Usually, we set `path=/` to make the cookie accessible from all website pages.
- **`domain=site.com`**
-Domain where the cookie is accessible.
+A domain where the cookie is accessible. In practice though, there are limitations. We can't set any domain.
By default, a cookie is accessible only at the domain that set it. So, if the cookie was set by `site.com`, we won't get it `other.com`.
-...But what's more tricky, we also won't get the cookie at a subdomain `forum.site.com`:
+...But what's more tricky, we also won't get the cookie at a subdomain `forum.site.com`!
```js
// at site.com
@@ -125,6 +124,8 @@ alert(document.cookie); // with user
For historical reasons, `domain=.site.com` (a dot at the start) also works this way, it might better to add the dot to support very old browsers.
+So, `domain` option allows to make a cookie accessible at subdomains.
+
## expires, max-age
By default, if a cookie doesn't have one of these options, it disappears when the browser is closed. Such cookies are called "session cookies"
@@ -144,13 +145,13 @@ date = date.toUTCString();
document.cookie = "user=John; expires=" + date;
```
-If we set `expires` to the past, the cookie will be deleted.
+If we set `expires` to a date in the past, the cookie is deleted.
- **`max-age=3600`**
-An alternative to `expires`, specifies the cookie expiration in seconds.
+An alternative to `expires`, specifies the cookie expiration in seconds from the current moment.
-Can be either a number of seconds from the current moment, or zero/negative for immediate expiration (to remove the cookie):
+If zero or negative, then the cookie is deleted:
```js
// cookie will die +1 hour from now
@@ -168,11 +169,12 @@ The cookie should be transferred only over HTTPS.
**By default if we set a cookie at `http://site.com`, then it also appears at `https://site.com` and vise versa.**
-That is, cookies only check the domain, they do not distinguish between the protocols.
+That is, cookies are domain-based, they do not distinguish between the protocols.
-With this option, if a cookie is set by `https://site.com`, then it doesn't appear when the same site is accessed by HTTP, as `http://site.com`. So if a cookie has sensitive content that should never be sent over unencrypted HTTP, then the flag can prevent this.
+With this option, if a cookie is set by `https://site.com`, then it doesn't appear when the same site is accessed by HTTP, as `http://site.com`. So if a cookie has sensitive content that should never be sent over unencrypted HTTP, then the flag is the right thing.
```js
+// assuming we're on https:// now
// set the cookie secure (only accessible if over HTTPS)
document.cookie = "user=John; secure";
```
@@ -185,9 +187,9 @@ To understand when it's useful, let's introduce the following attack scenario.
### XSRF attack
-Imagine, you are logged into the site `bank.com`. That is: you have an authentication cookie from that site. Your browser sends it to `bank.com` on every request, so that it recognizes you and performs all sensitive financial operations.
+Imagine, you are logged into the site `bank.com`. That is: you have an authentication cookie from that site. Your browser sends it to `bank.com` with every request, so that it recognizes you and performs all sensitive financial operations.
-Now, while browsing the web in another window, you occasionally come to another site `evil.com`, that has a `