diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index 74521076..69bb189a 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -25,12 +25,12 @@ The browser has an embedded engine, sometimes it's also called a "JavaScript vir Different engines have different "codenames", for example: - [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome and Opera. -- [Gecko](https://en.wikipedia.org/wiki/Gecko_(software)) -- in Firefox. +- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox. - ...There are other codenames like "Trident", "Chakra" for different versions of IE, "ChakraCore" for Microsoft Edge, "Nitro" and "SquirrelFish" for Safari etc. The terms above are good to remember, because they are used in developer articles on the internet. We'll use them too. For instance, if "a feature X is supported by V8", then it probably works in Chrome and Opera. -```smart header="How engines work?" +```smart header="How do engines work?" Engines are complicated. But the basics are easy. @@ -108,7 +108,7 @@ Modern tools make the transpilation very fast and transparent, actually allowing Examples of such languages: -- [CoffeeScript](http://coffeescript.org/) is a "syntax sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby devs like it. +- [CoffeeScript](http://coffeescript.org/) is a "syntactic sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby devs like it. - [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing", to simplify development and support of complex systems. It is developed by Microsoft. - [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps). It was initially offered by Google as a replacement for JavaScript, but as of now, browsers require it to be transpiled to JavaScript just like the ones above. diff --git a/1-js/01-getting-started/2-code-editors/article.md b/1-js/01-getting-started/2-code-editors/article.md index 65ef7476..0d714cdb 100644 --- a/1-js/01-getting-started/2-code-editors/article.md +++ b/1-js/01-getting-started/2-code-editors/article.md @@ -1,26 +1,22 @@ # Code editors -A code editor is the place where a programmer spends most of his time. +A code editor is the place where programmers spend most of their time. There are two archetypes: IDE and lightweight editors. Many people feel comfortable choosing one tool of each type. -[cut] - ## IDE -The term [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (Integrated Development Environment) means a powerful editor with many features that usually operates on a "whole project". As said, that's not just an editor, but a full-scale "development environment". +The term [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (Integrated Development Environment) means a powerful editor with many features that usually operates on a "whole project". As the name suggests, that's not just an editor, but a full-scale "development environment". -An IDE loads the project (can be many files), and then allows navigation between files, provides autocompletion based on the whole project, integrates with a version management system (like [git](https://git-scm.com/)), a testing environment and other "project-level" stuff. +An IDE loads the project (can be many files), allows navigation between files, provides autocompletion based on the whole project (not just the open file), integrates with a version management system (like [git](https://git-scm.com/)), a testing environment and other "project-level" stuff. If you haven't considered selecting an IDE yet, look at the following variants: -- IntelliJ editors: [WebStorm](http://www.jetbrains.com/webstorm/) for frontend development and [PHPStorm (PHP)](http://www.jetbrains.com/phpstorm/), [IDEA (Java)](http://www.jetbrains.com/idea/), [RubyMine (Ruby)](http://www.jetbrains.com/ruby/) and others if you need additional languages. +- [WebStorm](http://www.jetbrains.com/webstorm/) for frontend development and other editors of the same company if you need additional languages. - Visual Studio is fine if you're a .NET developer, and a free version is available ([Visual Studio Community](https://www.visualstudio.com/vs/community/)) -- Eclipse-based products, like [Aptana](http://www.aptana.com/) and Zend Studio. -- [Komodo IDE](http://www.activestate.com/komodo-ide) and its lightweight free version [Komodo Edit](http://www.activestate.com/komodo-edit). - [Netbeans](http://netbeans.org/). -All of the IDEs listed above are available on both Windows and Mac, and the IDEs other than Visual Studio are also available on Linux. +All of the IDEs except Visual Studio are available on Windows, MacOs and Linux. Visual Studio doesn't work on Linux. Most IDEs are paid, but have a trial period. Their cost is usually negligible compared to a qualified developer's salary, so just choose the best one for you. @@ -40,7 +36,7 @@ The following options deserve your attention: - [Atom](https://atom.io/) (cross-platform, free). - [Sublime Text](http://www.sublimetext.com) (cross-platform, shareware). - [Notepad++](https://notepad-plus-plus.org/) (Windows, free). -- Vim and Emacs are also cool, if you know how to use them. +- [Vim](http://www.vim.org/) and [Emacs](https://www.gnu.org/software/emacs/) are also cool, if you know how to use them. ## My favorites @@ -48,11 +44,9 @@ The personal preference of the author is to have both an IDE for projects and a I'm using: -- [WebStorm](http://www.jetbrains.com/webstorm/) for JS, and if there is one more language in the project, then I switch to other Jetbrains editors like [PHPStorm](http://www.jetbrains.com/phpstorm/) (PHP), [IDEA](http://www.jetbrains.com/idea/) (Java), [RubyMine](http://www.jetbrains.com/ruby/) (Ruby). There are editors for other languages too, but I haven't used them. +- [WebStorm](http://www.jetbrains.com/webstorm/) for JS, and if there is one more language in the project, then I switch to one of the other Jetbrains offerings listed above. - As a lightweight editor -- [Sublime Text](http://www.sublimetext.com) or [Atom](https://atom.io/). -If you don't know what to choose, you can consider these ones. - ## Let's not argue The editors in the lists above are those that either I or my friends who I consider good developers have been using for a long time and are happy with. diff --git a/1-js/01-getting-started/3-devtools/article.md b/1-js/01-getting-started/3-devtools/article.md index 34e5c9a7..f250502d 100644 --- a/1-js/01-getting-started/3-devtools/article.md +++ b/1-js/01-getting-started/3-devtools/article.md @@ -10,8 +10,6 @@ Most often developers lean towards Chrome or Firefox for development, because th Developer tools are really powerful, there are many features. To start, we'll learn how to open them, look at errors and run JavaScript commands. -[cut] - ## Google Chrome Open the page [bug.html](bug.html). diff --git a/1-js/02-first-steps/01-hello-world/article.md b/1-js/02-first-steps/01-hello-world/article.md index e69a25f0..69d2ba97 100644 --- a/1-js/02-first-steps/01-hello-world/article.md +++ b/1-js/02-first-steps/01-hello-world/article.md @@ -7,8 +7,6 @@ But, we need a working environment to run our scripts, and, just because this bo So first, let's see how to attach a script to a webpage. For server-side environments, you can just execute it with a command like `"node my.js"` for Node.JS. -[cut] - ## The "script" tag JavaScript programs can be inserted in any part of an HTML document with the help of the ` ```online -On the picture above element nodes you can click on element nodes. Their children will open/collapse. +On the picture above, you can click on element nodes and their children will open/collapse. ``` -Tags are called *element nodes* (or just elements). Nested tags become children of the enclosing ones. As a result we have a tree of elements: `` is at the root, then `` and `` are its children etc. +Tags are called *element nodes* (or just elements). Nested tags become children of the enclosing ones. As a result we have a tree of elements: `` is at the root, then `` and `` are its children, etc. The text inside elements forms *text nodes*, labelled as `#text`. A text node contains only a string. It may not have children and is always a leaf of the tree. @@ -55,13 +55,13 @@ Please note the special characters in text nodes: - a newline: `↵` (in JavaScript known as `\n`) - a space: `␣` -Spaces and newlines -- are totally valid characters, they form text nodes and become a part of the DOM. So, for instance, in the example above the `` tag contains come spaces before ``, and that text becomes a `#text` node (it contains a newline and some spaces only). +Spaces and newlines -- are totally valid characters, they form text nodes and become a part of the DOM. So, for instance, in the example above the `<head>` tag contains some spaces before `<title>`, and that text becomes a `#text` node (it contains a newline and some spaces only). There are only two top-level exclusions: 1. Spaces and newlines before `<head>` are ignored for historical reasons, -2. If we put something after `</body>`, then that is automatically moved inside the `body`, at the end, as HTML spec requires that all content must be inside `<body>`. So there may be no spaces after `</body>`. +2. If we put something after `</body>`, then that is automatically moved inside the `body`, at the end, as the HTML spec requires that all content must be inside `<body>`. So there may be no spaces after `</body>`. -In other cases everything's honest -- if there are spaces (just like any character) in the document, then they text nodes in DOM, and if we remove them, then there won't be any. +In other cases everything's straightforward -- if there are spaces (just like any character) in the document, then they become text nodes in DOM, and if we remove them, then there won't be any. Here are no space-only text nodes: @@ -79,9 +79,9 @@ drawHtmlTree(node2, 'div.domtree', 690, 210); </script> ```smart header="Edge spaces and in-between empty text are usually hidden in tools" -Browser tools (to be covered soon) that work with DOM usually do not show spaces at start/end of the text and empty text nodes (line-breaks) between tags. . +Browser tools (to be covered soon) that work with DOM usually do not show spaces at the start/end of the text and empty text nodes (line-breaks) between tags. -That's because they are mainly used to decorate HTML, and do not affect (in most cases) how it is shown. +That's because they are mainly used to decorate HTML, and do not affect how it is shown (in most cases). On further DOM pictures we'll sometimes omit them where they are irrelevant, to keep things short. ``` @@ -91,7 +91,7 @@ On further DOM pictures we'll sometimes omit them where they are irrelevant, to If the browser encounters malformed HTML, it automatically corrects it when making DOM. -For instance, the top tag is always `<html>`. Even if it doesn't exist in the document -- it will be in DOM, the browser will create it. The same about `<body>`. +For instance, the top tag is always `<html>`. Even if it doesn't exist in the document -- it will exist in the DOM, the browser will create it. The same goes for `<body>`. As an example, if the HTML file is a single word `"Hello"`, the browser will wrap it into `<html>` and `<body>`, add the required `<head>`, and the DOM will be: @@ -104,7 +104,7 @@ let node3 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node3, 'div.domtree', 690, 150); </script> -While generating DOM, browser automatically processes errors in the document, closes tags and so on. +While generating the DOM, browsers automatically process errors in the document, close tags and so on. Such an "invalid" document: @@ -115,7 +115,7 @@ Such an "invalid" document: <li>Dad ``` -...Will become a normal DOM, as the browser read tags and restores the missing parts: +...Will become a normal DOM, as the browser reads tags and restores the missing parts: <div class="domtree"></div> @@ -143,7 +143,7 @@ let node5 = {"name":"TABLE","nodeType":1,"children":[{"name":"TBODY","nodeType": drawHtmlTree(node5, 'div.domtree', 600, 200); </script> -You see? The `<tbody>` has appeared out of nowhere. Should keep in mind while working with tables to evade surprises. +You see? The `<tbody>` appeared out of nowhere. You should keep this in mind while working with tables to avoid surprises. ```` ## Other node types @@ -178,7 +178,7 @@ Here we see a new tree node type -- *comment node*, labeled as `#comment`. We may think -- why a comment is added to the DOM? It doesn't affect the visual representation in any way. But there's a rule -- if something's in HTML, then it also must be in the DOM tree. -**Everything in HTML, even comments, becomes a part of DOM.** +**Everything in HTML, even comments, becomes a part of the DOM.** Even the `<!DOCTYPE...>` directive at the very beginning of HTML is also a DOM node. It's in the DOM tree right before `<html>`. We are not going to touch that node, we even don't draw it on diagrams for that reason, but it's there. @@ -189,39 +189,39 @@ There are [12 node types](https://dom.spec.whatwg.org/#node). In practice we usu 1. `document` -- the "entry point" into DOM. 2. element nodes -- HTML-tags, the tree building blocks. 3. text nodes -- contain text. -4. comments -- sometimes we can put the information there, it won't be shown, but JS can read it from DOM. +4. comments -- sometimes we can put the information there, it won't be shown, but JS can read it from the DOM. -## See it yourself +## See it for yourself -To see the DOM structure in real-time, try [Live DOM Viewer](http://software.hixie.ch/utilities/js/live-dom-viewer/). Just type in the document, and it will show up DOM at instant. +To see the DOM structure in real-time, try [Live DOM Viewer](http://software.hixie.ch/utilities/js/live-dom-viewer/). Just type in the document, and it will show up DOM at an instant. ## In the browser inspector -Another way to explore DOM is to use browser developer tools. Actually, that's what we use when developing. +Another way to explore the DOM is to use the browser developer tools. Actually, that's what we use when developing. -To do so, open the web-page [elks.html](elks.html), turn on browser developer tools and switch to Elements tab. +To do so, open the web-page [elks.html](elks.html), turn on the browser developer tools and switch to the Elements tab. -Should look like this: +It should look like this: ![](elks.png) You can see the DOM, click on elements, see their details and so on. -Please note that the DOM structure in developer tools is simplified. Text nodes are shown just as text. And there are no "blank" (space only) text nodes at all. That's fine, because most of time we are interested in element nodes. +Please note that the DOM structure in developer tools is simplified. Text nodes are shown just as text. And there are no "blank" (space only) text nodes at all. That's fine, because most of the time we are interested in element nodes. -Clicking the <span class="devtools" style="background-position:-328px -124px"></span> button in the left-upper corner allows to choose a node from the webpage using a mouse (or other pointer device) and "inspect" it (scroll to it in the elements tab). Works great when we have a huge HTML page and would like to see the DOM of a particular place in it. +Clicking the <span class="devtools" style="background-position:-328px -124px"></span> button in the left-upper corner allows to choose a node from the webpage using a mouse (or other pointer devices) and "inspect" it (scroll to it in the Elements tab). This works great when we have a huge HTML page (and corresponding huge DOM) and would like to see the place of a particual element in it. Another way to do it would be just right-clicking on a webpage and selecting "Inspect" in the context menu. ![](inspect.png) -At the right part of the tools there are following subtabs: -- Styles -- we can see CSS applied to the current element rule by rule, including built-in rules (gray). Almost everything can be edited at-place including the dimensions/margins/paddings of the box below. -- Computed -- to see CSS applied to the element by property: for each property we can see a rule that gives it (including CSS inheritance and such). -- Event Listeners -- to see event listeners attached to DOM elements (we'll cover them in the next part of the tutorial). +At the right part of the tools there are the following subtabs: +- **Styles** -- we can see CSS applied to the current element rule by rule, including built-in rules (gray). Almost everything can be edited in-place, including the dimensions/margins/paddings of the box below. +- **Computed** -- to see CSS applied to the element by property: for each property we can see a rule that gives it (including CSS inheritance and such). +- **Event Listeners** -- to see event listeners attached to DOM elements (we'll cover them in the next part of the tutorial). - ...and so on. -The best way to study them is to click around. Most values are in-place editable. +The best way to study them is to click around. Most values are editable in-place. ## Interaction with console @@ -244,7 +244,7 @@ Or we can just output it in the console and explore "at-place", like `document.b That's for debugging purposes of course. From the next chapter on we'll access and modify DOM using JavaScript. -The browser developer tools are a great help in development: we can explore DOM, try things and see what goes wrong. +The browser developer tools are a great help in development: we can explore the DOM, try things and see what goes wrong. ## Summary @@ -256,6 +256,6 @@ An HTML/XML document is represented inside the browser as the DOM tree. We can use developer tools to inspect DOM and modify it manually. -Here we covered the basics, most used and important actions to start with. There's an extensive documentation about Chrome developer tools at <https://developers.google.com/web/tools/chrome-devtools>. The best way to learn the tools is to click here and there, read menus: most options are obvious. Later, when you know them in general, read the docs and pick up the rest. +Here we covered the basics, the most used and important actions to start with. There's an extensive documentation about Chrome Developer Tools at <https://developers.google.com/web/tools/chrome-devtools>. The best way to learn the tools is to click here and there, read menus: most options are obvious. Later, when you know them in general, read the docs and pick up the rest. DOM nodes have properties and methods that allow to travel between them, modify, move around the page and more. We'll get down to them in the next chapters. diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index ab96e5f7..976e595a 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -7,11 +7,9 @@ libs: # Walking the DOM -DOM allows to do anything with elements and their contents, but first we need to reach the corresponding DOM object, get it into a variable, and then we are able to modify it. +The DOM allows to do anything with elements and their contents, but first we need to reach the corresponding DOM object, get it into a variable, and then we are able to modify it. -All operations on DOM start with the `document` object. From it we can access any node. - -[cut] +All operations on the DOM start with the `document` object. From it we can access any node. Here's a picture of links that allow to travel between DOM nodes: @@ -88,7 +86,7 @@ For instance, here `<body>` has children `<div>` and `<ul>` (and few blank text </html> ``` -...And if we ask for all descendants of `<body>`, then we get direct children `<div>`, `<ul>` and also more nested elements like `<li>` (being a child of `<ul>`) and `<b>` (being a child of `<li>`) -- the whole subtree. +...And if we ask for all descendants of `<body>`, then we get direct children `<div>`, `<ul>` and also more nested elements like `<li>` (being a child of `<ul>`) and `<b>` (being a child of `<li>`) -- the entire subtree. **The `childNodes` collection provides access to all child nodes, including text nodes.** @@ -157,7 +155,7 @@ The first thing is nice. The second is tolerable, because we can use `Array.from ```warn header="DOM collections are read-only" DOM collections, and even more -- *all* navigation properties listed in this chapter are read-only. -We can't replace an child by something else assigning `childNodes[i] = ...`. +We can't replace a child by something else assigning `childNodes[i] = ...`. Changing DOM needs other methods, we'll see them in the next chapter. ``` @@ -177,7 +175,7 @@ Please, don't. The `for..in` loop iterates over all enumerable properties. And c <body> <script> // shows 0, 1, length, item, values and more. - for(let prop in document.body.childNodes) alert(prop); + for (let prop in document.body.childNodes) alert(prop); </script> </body> ```` @@ -242,7 +240,7 @@ In other words, the `documentElement` (`<html>`) is the root node. Formally, it Sometimes that matters when we're walking over the chain of parents and call a method on each of them, but `document` doesn't have it, so we exclude it. ```` -Let's modify one of examples above: replace `childNodes` with `children`. Now it shows only elements: +Let's modify one of the examples above: replace `childNodes` with `children`. Now it shows only elements: ```html run <html> @@ -275,7 +273,7 @@ Certain types of DOM elements may provide additional properties, specific to the Tables are a great example and important particular case of that. -**`<table>`** element supports (in addition to the given above) these properties: +**The `<table>`** element supports (in addition to the given above) these properties: - `table.rows` -- the collection of `<tr>` elements of the table. - `table.caption/tHead/tFoot` -- references to elements `<caption>`, `<thead>`, `<tfoot>`. - `table.tBodies` -- the collection of `<tbody>` elements (can be many according to the standard). @@ -285,8 +283,8 @@ Tables are a great example and important particular case of that. **`<tr>`:** - `tr.cells` -- the collection of `<td>` and `<th>` cells inside the given `<tr>`. -- `tr.sectionRowIndex` -- the number of the given `<tr>` inside the enclosing `<thead>/<tbody>`. -- `tr.rowIndex` -- the number of the `<tr>` in the table. +- `tr.sectionRowIndex` -- the position (index) of the given `<tr>` inside the enclosing `<thead>/<tbody>/<tfoot>`. +- `tr.rowIndex` -- the number of the `<tr>` in the table as a whole (including all table rows). **`<td>` and `<th>`:** - `td.cellIndex` -- the number of the cell inside the enclosing `<tr>`. diff --git a/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md b/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md index 0ada42ac..c7080388 100644 --- a/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md +++ b/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md @@ -25,7 +25,7 @@ let form = document.getElementsByName('search')[0] document.querySelector('form[name="search"]') // 5. The first input in that form. -form.getElementsByTagName('input') +form.getElementsByTagName('input')[0] // or form.querySelector('input') diff --git a/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.md b/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.md index 5bb2effa..781b7a92 100644 --- a/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.md +++ b/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.md @@ -1,7 +1,7 @@ Let's make a loop over `<li>`: ```js -for (let li of document.querySelector('li')) { +for (let li of document.querySelectorAll('li')) { ... } ``` @@ -9,7 +9,7 @@ for (let li of document.querySelector('li')) { In the loop we need to get the text inside every `li`. We can read it directly from the first child node, that is the text node: ```js -for (let li of document.querySelector('li')) { +for (let li of document.querySelectorAll('li')) { let title = li.firstChild.data; // title is the text in <li> before any other nodes diff --git a/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.view/index.html b/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.view/index.html index d712bb9d..5947ec09 100644 --- a/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.view/index.html +++ b/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.view/index.html @@ -40,7 +40,7 @@ </ul> <script> - for (let li of document.querySelector('li')) { + for (let li of document.querySelectorAll('li')) { // get the title from the text node let title = li.firstChild.data; diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md index 50674218..3a7a9aee 100644 --- a/2-ui/1-document/04-searching-elements-dom/article.md +++ b/2-ui/1-document/04-searching-elements-dom/article.md @@ -3,8 +3,6 @@ DOM navigation properties are great when elements are close to each other. What if they are not? How to get an arbitrary element of the page? There are additional searching methods for that. -[cut] - ## document.getElementById or just id If an element has the `id` attribute, then there's a global variable by the name from that `id`. @@ -84,7 +82,7 @@ let divs = document.getElementsByTagName('div'); This method is callable in the context of any DOM element. -Let's find all `input` inside the table: +Let's find all `input` tags inside the table: ```html run height=50 <table id="table"> @@ -123,16 +121,16 @@ The `"s"` letter is absent in `getElementById`, because it returns a single elem ``` ````warn header="It returns a collection, not an element!" -Another widespread novice mistake is to write like: +Another widespread novice mistake is to write: ```js // doesn't work document.getElementsByTagName('input').value = 5; ``` -That won't work, because it takes a *collection* of inputs and assigns the value to it, rather to elements inside it. +That won't work, because it takes a *collection* of inputs and assigns the value to it rather than to elements inside it. -We should either iterate over the collection or get an element by the number, and then assign, like this: +We should either iterate over the collection or get an element by its index, and then assign, like this: ```js // should work (if there's an input) @@ -374,7 +372,7 @@ Other methods can be called on elements too. For instance `elem.querySelectorAll Besides that: - There is `elem.matches(css)` to check if `elem` matches the given CSS selector. -- There is `elem.closest(css)` to look for a nearest ancestor that matches the given CSS-selector. The `elem` itself is also checked. +- There is `elem.closest(css)` to look for the nearest ancestor that matches the given CSS-selector. The `elem` itself is also checked. And let's mention one more method here to check for the child-parent relationship: - `elemA.contains(elemB)` returns true if `elemB` is inside `elemA` (a descendant of `elemA`) or when `elemA==elemB`. diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md index 596192a1..0ed407ca 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md @@ -4,7 +4,7 @@ importance: 5 # What's in the nodeType? -What the script shows? +What does the script show? ```html <html> diff --git a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md index 89b76cfd..32900a78 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md +++ b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md @@ -12,6 +12,6 @@ The answer: **`BODY`**. What's going on step by step: -1. The content of `<body>` is replaced with the comment. The comment is <code><!--BODY--></code>, because `body.tagName == "BODY"`. As we remember, `tagName` is always uppercase in HTML. +1. The content of `<body>` is replaced with the comment. The comment is `<!--BODY-->`, because `body.tagName == "BODY"`. As we remember, `tagName` is always uppercase in HTML. 2. The comment is now the only child node, so we get it in `body.firstChild`. 3. The `data` property of the comment is its contents (inside `<!--...-->`): `"BODY"`. diff --git a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md index 40bea8a0..efe50b48 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md @@ -4,7 +4,7 @@ importance: 3 # Tag in comment -What this code shows? +What does this code show? ```html <script> diff --git a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md index d83602dc..de266c6a 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md @@ -4,7 +4,7 @@ importance: 4 # Where's the "document" in the hierarchy? -Which class the `document` belongs to? +Which class does the `document` belong to? What's its place in the DOM hierarchy? diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md index 2783c3a8..85bca24b 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/article.md +++ b/2-ui/1-document/05-basic-dom-node-properties/article.md @@ -4,8 +4,6 @@ Let's get a more in-depth look at DOM nodes. In this chapter we'll see more into what they are and their most used properties. -[cut] - ## DOM node classes DOM nodes have different properties depending on their class. For instance, an element node corresponding to tag `<a>` has link-related properties, and the one corresponding to `<input>` has input-related properties and so on. Text nodes are not the same as element nodes. But there are also common properties and methods between all of them, because all classes of DOM nodes form a single hierarchy. @@ -78,7 +76,7 @@ Try it on `document.body`. ``` ````smart header="IDL in the spec" -In the specification classes are described using not JavaScript, but a special [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL), that is usually easy to understand. +In the specification, classes are described not using JavaScript, but a special [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL), that is usually easy to understand. In IDL all properties are prepended with their types. For instance, `DOMString`, `boolean` and so on. @@ -165,11 +163,11 @@ Sure, the difference is reflected in their names, but is indeed a bit subtle. - The `tagName` property exists only for `Element` nodes. - The `nodeName` is defined for any `Node`: - for elements it means the same as `tagName`. - - for other node types (text, comment etc) it has a string with the node type. + - for other node types (text, comment, etc.) it has a string with the node type. In other words, `tagName` is only supported by element nodes (as it originates from `Element` class), while `nodeName` can say something about other node types. -For instance let's compare `tagName` and `nodeName` for the `document` and a comment node: +For instance, let's compare `tagName` and `nodeName` for the `document` and a comment node: ```html run @@ -177,7 +175,7 @@ For instance let's compare `tagName` and `nodeName` for the `document` and a com <script> // for comment - alert( document.body.firstChild.tagName ); // undefined (not element) + alert( document.body.firstChild.tagName ); // undefined (no element) alert( document.body.firstChild.nodeName ); // #comment // for document @@ -220,7 +218,7 @@ The example shows the contents of `document.body` and then replaces it completel </body> ``` -We can try to insert an invalid HTML, the browser will fix our errors: +We can try to insert invalid HTML, the browser will fix our errors: ```html run <body> @@ -463,28 +461,28 @@ Most standard HTML attributes have the corresponding DOM property, and we can ac If we want to know the full list of supported properties for a given class, we can find them in the specification. For instance, HTMLInputElement is documented at <https://html.spec.whatwg.org/#htmlinputelement>. -Or if we'd like to get them fast or interested in the concrete browser -- we can always output the element using `console.dir(elem)` and read the properties. Or explore "DOM properties" in Elements tab of the browser developer tools. +Or if we'd like to get them fast or are interested in a concrete browser specification -- we can always output the element using `console.dir(elem)` and read the properties. Or explore "DOM properties" in the Elements tab of the browser developer tools. ## Summary -Each DOM node belongs to a certain class. The classes form a hierarchy. The full set of properties and methods comes as the result of inheritance. +Each DOM node belongs to a certain class. The classes form a hierarchy. The full set of properties and methods come as the result of inheritance. Main DOM node properties are: `nodeType` -: Node type. We can get it from the DOM object class, but often we need just to see if it is a text or element node. The `nodeType` property is good for that. It has numeric values, most important are: `1` -- for elements,`3` -- for text nodes. Read-only. +: We can get `nodeType` from the DOM object class, but often we need just to see if it is a text or element node. The `nodeType` property is good for that. It has numeric values, most important are: `1` -- for elements,`3` -- for text nodes. Read-only. `nodeName/tagName` -: For elements, tag name (uppercased unless XML-mode). For non-element nodes `nodeName` describes what is it. Read-only. +: For elements, tag name (uppercased unless XML-mode). For non-element nodes `nodeName` describes what it is. Read-only. `innerHTML` -: The HTML content of the element. Can modify. +: The HTML content of the element. Can be modified. `outerHTML` : The full HTML of the element. A write operation into `elem.outerHTML` does not touch `elem` itself. Instead it gets replaced with the new HTML in the outer context. `nodeValue/data` -: The content of a non-element node (text, comment). These two are almost the same, usually we use `data`. Can modify. +: The content of a non-element node (text, comment). These two are almost the same, usually we use `data`. Can be modified. `textContent` : The text inside the element, basically HTML minus all `<tags>`. Writing into it puts the text inside the element, with all special characters and tags treated exactly as text. Can safely insert user-generated text and protect from unwanted HTML insertions. @@ -492,6 +490,6 @@ Main DOM node properties are: `hidden` : When set to `true`, does the same as CSS `display:none`. -DOM nodes also have other properties depending on their class. For instance, `<input>` elements (`HTMLInputElement`) support `value`, `type`, while `<a>` elements (`HTMLAnchorElement`) support `href` etc. Most standard HTML attributes have the corresponding DOM property. +DOM nodes also have other properties depending on their class. For instance, `<input>` elements (`HTMLInputElement`) support `value`, `type`, while `<a>` elements (`HTMLAnchorElement`) support `href` etc. Most standard HTML attributes have a corresponding DOM property. But HTML attributes and DOM properties are not always the same, as we'll see in the next chapter. diff --git a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md index 5d3fe241..0507832f 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md @@ -1,6 +1,6 @@ ```html run height=100 -<!DOCTYLE HTML> +<!DOCTYPE html> <html> <body> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md index 0d11ff97..4cdf231b 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md @@ -7,7 +7,7 @@ importance: 5 Write the code to select the element with `data-widget-name` attribute from the document and to read its value. ```html run -<!DOCTYLE HTML> +<!DOCTYPE html> <html> <body> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/article.md b/2-ui/1-document/06-dom-attributes-and-properties/article.md index bdfb51f0..8c082cb6 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/article.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/article.md @@ -4,9 +4,7 @@ When the browser loads the page, it "reads" (another word: "parses") HTML text a For instance, if the tag is `<body id="page">`, then the DOM object has `body.id="page"`. -But the attribute-property mapping is not one-to-one! In this chapter we'll pay attention to separate these two notions, to see how to work with them, when they are same, and when they are different. - -[cut] +But the attribute-property mapping is not one-to-one! In this chapter we'll pay attention to separate these two notions, to see how to work with them, when they are the same, and when they are different. ## DOM properties @@ -28,11 +26,11 @@ alert(document.body.myData.title); // Imperator We can add a method as well: ```js run -document.body.sayHi = function() { +document.body.sayTagName = function() { alert(this.tagName); }; -document.body.sayHi(); // BODY (the value of "this" in the method is document.body) +document.body.sayTagName(); // BODY (the value of "this" in the method is document.body) ``` We can also modify built-in prototypes like `Element.prototype` and add new methods to all elements: @@ -110,10 +108,10 @@ Here's a demo of reading a non-standard property: </body> ``` -HTML attributes have following features: +HTML attributes have the following features: -- Their name is case-insensitive (that's HTML: `id` is same as `ID`). -- They are always strings. +- Their name is case-insensitive (`id` is same as `ID`). +- Their values are always strings. Here's an extended demo of working with attributes: @@ -129,7 +127,7 @@ Here's an extended demo of working with attributes: alert( elem.outerHTML ); // (3), see it's there for (let attr of elem.attributes) { // (4) list all - alert( attr.name + " = " + attr.value ); + alert( `${attr.name} = ${attr.value}` ); } </script> </body> @@ -138,15 +136,15 @@ Here's an extended demo of working with attributes: Please note: 1. `getAttribute('About')` -- the first letter is uppercase here, and in HTML it's all lowercase. But that doesn't matter: attribute names are case-insensitive. -2. We can assign anything to an attribute, but that becomes a string. So here we have `"123"` as the value. +2. We can assign anything to an attribute, but it becomes a string. So here we have `"123"` as the value. 3. All attributes including ones that we set are visible in `outerHTML`. 4. The `attributes` collection is iterable and has all attributes with `name` and `value`. ## Property-attribute synchronization -When a standard attribute changes, the corresponding property is auto-updated, and (with some exceptions) vise-versa. +When a standard attribute changes, the corresponding property is auto-updated, and (with some exceptions) vice versa. -In the example below `id` is modified as an attribute, and we can see the property change too. And then the same backwards: +In the example below `id` is modified as an attribute, and we can see the property changed too. And then the same backwards: ```html run <input> @@ -188,11 +186,11 @@ In the example above: - Changing the attribute `value` updates the property. - But the property change does not affect the attribute. -That "feature" may actually can come in handy, because the user may modify `value`, and then after it, if we want to recover the "original" value from HTML, it's in the attribute. +That "feature" may actually come in handy, because the user may modify `value`, and then after it, if we want to recover the "original" value from HTML, it's in the attribute. ## DOM properties are typed -DOM properties are not always strings. For instance, `input.checked` property (for checkboxes) is boolean: +DOM properties are not always strings. For instance, the `input.checked` property (for checkboxes) is a boolean: ```html run <input id="input" type="checkbox" checked> checkbox @@ -203,7 +201,7 @@ DOM properties are not always strings. For instance, `input.checked` property (f </script> ``` -There are other examples. The `style` attribute is a string, but `style` property is an object: +There are other examples. The `style` attribute is a string, but the `style` property is an object: ```html run <div id="div" style="color:red;font-size:120%">Hello</div> @@ -311,9 +309,9 @@ div.setAttribute('order-state', 'canceled'); But there may be a possible problem with custom attributes. What if we use a non-standard attribute for our purposes and later the standard introduces it and makes it do something? The HTML language is alive, it grows, more attributes appear to suit the needs of developers. There may be unexpected effects in such case. -To evade conflicts, there exist [data-*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes) attributes. +To avoid conflicts, there exist [data-*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes) attributes. -**All attributes starting with "data-" are reserved for programmers' use. They are available in `dataset` property.** +**All attributes starting with "data-" are reserved for programmers' use. They are available in the `dataset` property.** For instance, if an `elem` has an attribute named `"data-about"`, it's available as `elem.dataset.about`. @@ -382,7 +380,7 @@ Methods to work with attributes are: - `elem.removeAttribute(name)` -- to remove the attribute. - `elem.attributes` is a collection of all attributes. -For most needs DOM properties can serve us well. We should refer to attributes only when DOM properties do not suit us, when we need exactly attributes, for instance: +For most needs, DOM properties can serve us well. We should refer to attributes only when DOM properties do not suit us, when we need exactly attributes, for instance: - We need a non-standard attribute. But if it starts with `data-`, then we should use `dataset`. -- We want to read the value "as written" in HTML. The value of the DOM property may be different, for instance `href` property is always a full URL, and we may want to get the "original" value. +- We want to read the value "as written" in HTML. The value of the DOM property may be different, for instance the `href` property is always a full URL, and we may want to get the "original" value. diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md index 25c1e819..6b85168b 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md @@ -4,6 +4,6 @@ The browser has to fix it automatically. But there may be no text inside the `<t Now it's obvious that when we remove the table, it remains. -The question can be easily answered by exploring DOM using the browser tools. They show `"aaa"` before the `<table>`. +The question can be easily answered by exploring the DOM using the browser tools. It shows `"aaa"` before the `<table>`. The HTML standard specifies in detail how to process bad HTML, and such behavior of the browser is correct. diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/task.md b/2-ui/1-document/07-modifying-document/5-why-aaa/task.md index aedf8697..03064ed2 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/task.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/task.md @@ -2,9 +2,9 @@ importance: 1 --- -# Why "aaa" remains? +# Why does "aaa" remain? -Run the example. Why `table.remove()` does not delete the text `"aaa"`? +Run the example. Why does `table.remove()` not delete the text `"aaa"`? ```html height=100 run <table id="table"> diff --git a/2-ui/1-document/07-modifying-document/article.md b/2-ui/1-document/07-modifying-document/article.md index adef6f9f..38e1199e 100644 --- a/2-ui/1-document/07-modifying-document/article.md +++ b/2-ui/1-document/07-modifying-document/article.md @@ -6,11 +6,9 @@ Here we'll see how to create new elements "on the fly" and modify the existing p First we'll see a simple example and then explain the methods. -[cut] - ## Example: show a message -For the start, let's see how to add a message on the page that looks nicer than `alert`. +For a start, let's see how to add a message on the page that looks nicer than `alert`. Here's how it will look: @@ -32,7 +30,7 @@ Here's how it will look: */!* ``` -That was an HTML example. Now let's create the same `div` with JavaScript (assuming that the styles are still in the HTML or an external CSS). +That was an HTML example. Now let's create the same `div` with JavaScript (assuming that the styles are still in the HTML or an external CSS file). ## Creating an element @@ -63,7 +61,7 @@ div.className = "alert alert-success"; div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; ``` -After that, we have a ready DOM element. Right now it's in the variable, but can not be seen, because not inserted into the page yet. +After that, we have a ready DOM element. Right now it's in the variable, but can not be seen, because it's not been inserted into the page yet. ## Insertion methods @@ -137,9 +135,8 @@ Here's a brief list of methods to insert a node into a parent element (`parentEl */!* </script> ``` - - To insert as the first element, we can do like this: - + To insert `newLi` as the first element, we can do it like this: + ```js list.insertBefore(newLi, list.firstChild); ``` @@ -153,7 +150,7 @@ These methods are "old school": they exist from the ancient times and we can mee For instance, how to insert *html* if we have it as a string? Or, given a node, how to insert another node *before* it? Of course, all that is doable, but not in an elegant way. -So there exists two other sets of insertion methods to handle all cases easily. +So there exist two other sets of insertion methods to handle all cases easily. ### prepend/append/before/after @@ -206,7 +203,7 @@ before after ``` -These methods can insert multiple list of nodes and text pieces in a single call. +These methods can insert multiple lists of nodes and text pieces in a single call. For instance, here a string and an element are inserted: @@ -279,7 +276,7 @@ The method has two brothers: - `elem.insertAdjacentText(where, text)` -- the same syntax, but a string of `text` in inserted "as text" instead of HTML, - `elem.insertAdjacentElement(where, elem)` -- the same syntax, but inserts an element. -They exist mainly to make the syntax "uniform". In practice, most of time only `insertAdjacentHTML` is used, because for elements and text we have methods `append/prepend/before/after` -- they are shorter to write and can insert nodes/text pieces. +They exist mainly to make the syntax "uniform". In practice, most of the time only `insertAdjacentHTML` is used, because for elements and text we have methods `append/prepend/before/after` -- they are shorter to write and can insert nodes/text pieces. So here's an alternative variant of showing a message: @@ -305,7 +302,7 @@ So here's an alternative variant of showing a message: How to insert one more similar message? -We could do a function and put the code there. But the alternative way would be to *clone* the existing `div` and modify the text inside it (if needed). +We could make a function and put the code there. But the alternative way would be to *clone* the existing `div` and modify the text inside it (if needed). Sometimes when we have a big element, that may be faster and simpler. @@ -340,7 +337,7 @@ An example of copying the message: ## Removal methods -To remove nodes, there are following methods: +To remove nodes, there are the following methods: `parentElem.removeChild(node)` @@ -414,7 +411,7 @@ The call to `document.write(html)` writes the `html` into page "right here and n The method comes from times when there were no DOM, no standards... Really old times. It still lives, because there are scripts using it. -In modern scripts we can rarely see it, because of the important limitation. +In modern scripts we can rarely see it, because of the following important limitation: **The call to `document.write` only works while the page is loading.** @@ -482,4 +479,4 @@ Insertion and removal of nodes: - To append HTML to the page before it has finished loading: - `document.write(html)` - After the page is loaded such call erases the document. Mostly seen in old scripts. + After the page is loaded such a call erases the document. Mostly seen in old scripts. diff --git a/2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html b/2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html index 5b8a6336..dc4eeec9 100755 --- a/2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html +++ b/2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html @@ -26,7 +26,7 @@ notification.style.top = top + 'px'; notification.style.right = right + 'px'; - notification.innerHTML = options.html; + notification.innerHTML = html; document.body.append(notification); setTimeout(() => notification.remove(), 1500); diff --git a/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md b/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md index 7df1986c..60930cb6 100644 --- a/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md +++ b/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md @@ -4,7 +4,7 @@ importance: 5 # Create a notification -Write a function `showNotification(options)` that a notification: `<div class="notification">` with the given content. The notification should automatically disappear after 1.5 seconds. +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. The options are: diff --git a/2-ui/1-document/08-styles-and-classes/article.md b/2-ui/1-document/08-styles-and-classes/article.md index 1c5f0948..3227da15 100644 --- a/2-ui/1-document/08-styles-and-classes/article.md +++ b/2-ui/1-document/08-styles-and-classes/article.md @@ -7,8 +7,6 @@ There are generally two ways to style an element: 1. Create a class in CSS and add it: `<div class="...">` 2. Write properties directly into `style`: `<div style="...">`. -[cut] - CSS is always the preferred way -- not only for HTML, but in JavaScript as well. We should only manipulate the `style` property if classes "can't handle it". @@ -26,7 +24,7 @@ For other cases, like making the text red, adding a background icon -- describe ## className and classList -Changing a class is one of the most often actions in scripts. +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`. @@ -76,7 +74,7 @@ Besides that, `classList` is iterable, so we can list all classes like this: ```html run <body class="main page"> <script> - for(let name of document.body.classList) { + for (let name of document.body.classList) { alert(name); // main, and then page } </script> @@ -209,7 +207,7 @@ For instance, here `style` doesn't see the margin: </body> ``` -...But what if we need, say, increase the margin by 20px? We want the current value for the start. +...But what if we need, say, to increase the margin by 20px? We want the current value for the start. There's another method for that: `getComputedStyle`. @@ -223,7 +221,7 @@ element : Element to read the value for. pseudo -: A pseudo-element if required, for instance `::before`. An empty string or no argument mean the element itself. +: A pseudo-element if required, for instance `::before`. An empty string or no argument means the element itself. The result is an object with style properties, like `elem.style`, but now with respect to all CSS classes. @@ -250,10 +248,10 @@ For instance: ```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. If can look like `height:1em` or `font-size:125%`. +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`. -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. +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. ``` @@ -283,7 +281,7 @@ 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 we 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 side way for an evil page to test if a link was visited and hence to break the privacy. +JavaScript we 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 diff --git a/2-ui/1-document/09-size-and-scroll/article.md b/2-ui/1-document/09-size-and-scroll/article.md index 375cb4d9..4fadba81 100644 --- a/2-ui/1-document/09-size-and-scroll/article.md +++ b/2-ui/1-document/09-size-and-scroll/article.md @@ -1,11 +1,9 @@ # Element size and scrolling -There are many JavaScript properties that allow to read information about element width, height and other geometry features. +There are many JavaScript properties that allow us to read information about element width, height and other geometry features. We often need them when moving or positioning elements in JavaScript, to correctly calculate coordinates. -[cut] - ## Sample element @@ -37,7 +35,7 @@ You can [open the document in the sandbox](sandbox:metric). ```smart header="Mind the scrollbar" The picture above demonstrates the most complex case when the element has a scrollbar. Some browsers (not all) reserve the space for it by taking it from the content. -So, without scrollbar the content width would be `300px`, but if the scrollbar is `16px` wide (the width may vary between devices and browsers) then only `300-16 = 284px` remains, and we should take it into account. That's why examples from this chapter assume that there's a scrollbar. If there's no scrollbar, then things are just a bit simpler. +So, without scrollbar the content width would be `300px`, but if the scrollbar is `16px` wide (the width may vary between devices and browsers) then only `300 - 16 = 284px` remains, and we should take it into account. That's why examples from this chapter assume that there's a scrollbar. If there's no scrollbar, then things are just a bit simpler. ``` ```smart header="The `padding-bottom` may be filled with text" @@ -68,7 +66,7 @@ The `offsetParent` is the nearest ancestor that is: In most practical cases we can use `offsetParent` to get the nearest CSS-positioned ancestor. And `offsetLeft/offsetTop` provide x/y coordinates relative to it's left-upper corner. -In the example below the inner `<div>` has `<main>` as `offsetParent` and `offsetLeft/offsetTop` are shifts from its left-upper corner (`180`): +In the example below the inner `<div>` has `<main>` as `offsetParent` and `offsetLeft/offsetTop` shifts from its left-upper corner (`180`): ```html run height=10 <main style="position: relative" id="main"> @@ -90,11 +88,11 @@ There are several occasions when `offsetParent` is `null`: 1. For not shown elements (`display:none` or not in the document). 2. For `<body>` and `<html>`. -3. For elements with `position:fixed` on them. +3. For elements with `position:fixed`. ## offsetWidth/Height -Now let's move to the element itself. +Now let's move on to the element itself. These two properties are the simplest ones. They provide the "outer" width/height of the element. Or, in other words, its full size including borders. @@ -102,7 +100,7 @@ These two properties are the simplest ones. They provide the "outer" width/heigh For our sample element: -- `offsetWidth = 390` -- the outer width, can be calculated as inner CSS-width (`300px`) plus paddings (`2*20px`) and borders (`2*25px`). +- `offsetWidth = 390` -- the outer width, can be calculated as inner CSS-width (`300px`) plus paddings (`2 * 20px`) and borders (`2 * 25px`). - `offsetHeight = 290` -- the outer height. ````smart header="Geometry properties for not shown elements are zero/null" @@ -140,9 +138,9 @@ In our example: What's the difference? -It becomes visible when the document is right-to-left (the operation system is in arabic or hebrew languages). The scrollbar is then not on the right, but on the left, and then `clientLeft` also includes the scrollbar width. +It becomes visible when the document is right-to-left (the operating system is in Arabic or Hebrew languages). The scrollbar is then not on the right, but on the left, and then `clientLeft` also includes the scrollbar width. -In that case `clientLeft` in our example would be not `25`, but with the scrollbar width `25+16=41`: +In that case, `clientLeft` would be not `25`, but with the scrollbar width `25 + 16 = 41`: ![](metric-client-left-top-rtl.png) @@ -154,7 +152,7 @@ They include the content width together with paddings, but without the scrollbar ![](metric-client-width-height.png) -On the picture above let's first consider `clientHeight`: it's easier to evaluate. There's no horizontal scrollbar, so it's exactly the sum of what's inside the borders: CSS-height `200px` plus top and bottom paddings (`2*20px`) total `240px`. +On the picture above let's first consider `clientHeight`: it's easier to evaluate. There's no horizontal scrollbar, so it's exactly the sum of what's inside the borders: CSS-height `200px` plus top and bottom paddings (`2 * 20px`) total `240px`. Now `clientWidth` -- here the content width is not `300px`, but `284px`, because `16px` are occupied by the scrollbar. So the sum is `284px` plus left and right paddings, total `324px`. @@ -167,13 +165,13 @@ So when there's no padding we can use `clientWidth/clientHeight` to get the cont ## scrollWidth/Height - Properties `clientWidth/clientHeight` only account for the visible part of the element. -- Properties `scrollWidth/scrollHeight` also include the scrolled out (hidden) part: +- Properties `scrollWidth/scrollHeight` also include the scrolled out (hidden) parts: ![](metric-scroll-width-height.png) On the picture above: -- `scrollHeight = 723` -- is the full inner height of the content area including the scrolled out part. +- `scrollHeight = 723` -- is the full inner height of the content area including the scrolled out parts. - `scrollWidth = 324` -- is the full inner width, here we have no horizontal scroll, so it equals `clientWidth`. We can use these properties to expand the element wide to its full width/height. @@ -182,7 +180,7 @@ Like this: ```js // expand the element to the full content height -element.style.height = element.scrollHeight + 'px'; +element.style.height = `${element.scrollHeight}px`; ``` ```online @@ -190,7 +188,7 @@ Click the button to expand the element: <div id="element" style="width:300px;height:200px; padding: 0;overflow: auto; border:1px solid black;">text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text</div> -<button style="padding:0" onclick="element.style.height = element.scrollHeight + 'px'">element.style.height = element.scrollHeight + 'px'</button> +<button style="padding:0" onclick="element.style.height = `${element.scrollHeight}px`">element.style.height = `${element.scrollHeight}px`</button> ``` ## scrollLeft/scrollTop @@ -207,7 +205,7 @@ In other words, `scrollTop` is "how much is scrolled up". Most geometry properties that are read-only, but `scrollLeft/scrollTop` can be changed, and the browser will scroll the element. ```online -If you click the element below, the code `elem.scrollTop+=10` executes. That makes the element content scroll `10px` below. +If you click the element below, the code `elem.scrollTop += 10` executes. That makes the element content scroll `10px` down. <div onclick="this.scrollTop+=10" style="cursor:pointer;border:1px solid black;width:100px;height:80px;overflow:auto">Click<br>Me<br>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9</div> ``` @@ -229,7 +227,7 @@ let elem = document.body; alert( getComputedStyle(elem).width ); // show CSS width for elem ``` -Why we should use geometry properties instead? There are two reasons: +Why should we use geometry properties instead? There are two reasons: 1. First, CSS width/height depend on another property: `box-sizing` that defines "what is" CSS width and height. A change in `box-sizing` for CSS purposes may break such JavaScript. 2. Second, CSS `width/height` may be `auto`, for instance for an inline element: @@ -260,7 +258,7 @@ The element with text has CSS `width:300px`. On a Desktop Windows OS, Firefox, Chrome, Edge all reserve the space for the scrollbar. But Firefox shows `300px`, while Chrome and Edge show less. That's because Firefox returns the CSS width and other browsers return the "real" width. ``` -Please note that the described difference are only about reading `getComputedStyle(...).width` from JavaScript, visually everything is correct. +Please note that the described difference is only about reading `getComputedStyle(...).width` from JavaScript, visually everything is correct. ## Summary @@ -271,7 +269,7 @@ Elements have the following geometry properties: - `offsetWidth/offsetHeight` -- "outer" width/height of an element including borders. - `clientLeft/clientTop` -- the distance from the left-upper outer corner to its left-upper inner corner. For left-to-right OS they are always the widths of left/top borders. For right-to-left OS the vertical scrollbar is on the left so `clientLeft` includes its width too. - `clientWidth/clientHeight` -- the width/height of the content including paddings, but without the scrollbar. -- `scrollWidth/scrollHeight` -- the width/height of the content including the scrolled out part. Also includes paddings, but not the scrollbar. +- `scrollWidth/scrollHeight` -- the width/height of the content including the scrolled out parts. Also includes paddings, but not the scrollbar. - `scrollLeft/scrollTop` -- width/height of the scrolled out part of the element, starting from its left-upper corner. All properties are read-only except `scrollLeft/scrollTop`. They make the browser scroll the element if changed. diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png index 5f1e58f9..01d2d000 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png index af62ebdc..125a91e1 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png differ diff --git a/2-ui/1-document/10-size-and-scroll-window/article.md b/2-ui/1-document/10-size-and-scroll-window/article.md index 0825b752..4a62f56c 100644 --- a/2-ui/1-document/10-size-and-scroll-window/article.md +++ b/2-ui/1-document/10-size-and-scroll-window/article.md @@ -4,8 +4,6 @@ How to find out the width of the browser window? How to get the full height of t From the DOM point of view, the root document element is `document.documentElement`. That element corresponds to `<html>` and has geometry properties described in the [previous chapter](info:size-and-scroll). For some cases we can use it, but there are additional methods and peculiarities important enough to consider. -[cut] - ## Width/height of the window Properties `clientWidth/clientHeight` of `document.documentElement` is exactly what we want here: @@ -55,7 +53,7 @@ let scrollHeight = Math.max( document.body.clientHeight, document.documentElement.clientHeight ); -alert('Full document width, with scrolled out part: ' + scrollHeight); +alert('Full document height, with scrolled out part: ' + scrollHeight); ``` Why so? Better don't ask. These inconsistencies come from ancient times, not a "smart" logic. diff --git a/2-ui/1-document/11-coordinates/article.md b/2-ui/1-document/11-coordinates/article.md index 98758249..35ba15e1 100644 --- a/2-ui/1-document/11-coordinates/article.md +++ b/2-ui/1-document/11-coordinates/article.md @@ -9,8 +9,6 @@ Most JavaScript methods deal with one of two coordinate systems: It's important to understand the difference and which type is where. -[cut] - ## Window coordinates: getBoundingClientRect Window coordinates start at the left-upper corner of the window. @@ -57,7 +55,7 @@ If we compare window coordinates versus CSS positioning, then there are obvious But in CSS the `right` property means the distance from the right edge, and the `bottom` -- from the bottom edge. -If we just look at the picture below, we can see that in JavaScript it is not so. All window coordinates are counted from the upper-left corner, including these ones. +If we just look at the picture above, we can see that in JavaScript it is not so. All window coordinates are counted from the upper-left corner, including these ones. ``` ## elementFromPoint(x, y) [#elementFromPoint] diff --git a/2-ui/1-document/11-coordinates/head.html b/2-ui/1-document/11-coordinates/head.html index efc6cd1c..0914f7f8 100644 --- a/2-ui/1-document/11-coordinates/head.html +++ b/2-ui/1-document/11-coordinates/head.html @@ -1,7 +1,11 @@ <script> document.addEventListener('DOMContentLoaded', function() { - document.getElementById('coords-show-mark').onclick = function() { +let elem = document.getElementById('coords-show-mark'); + +// no elem in ebook mode +if (elem) { + elem.onclick = function() { let elem = document.getElementById("coords-show-mark"); function createMessageUnder(elem, text) { @@ -21,6 +25,7 @@ document.addEventListener('DOMContentLoaded', function() { document.body.append(message); setTimeout(() => message.remove(), 5000); } +} }); diff --git a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md index 2355e535..d569f0e4 100644 --- a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md @@ -13,4 +13,4 @@ button.addEventListener("click", handler); button.removeEventListener("click", handler); ``` -The handler `button.onclick` works independantly and in addition to `addEventListener`. +The handler `button.onclick` works independently and in addition to `addEventListener`. diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png index 871acdc7..ca2f49ed 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png and b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png index 923bfc82..b4c4d3ef 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png and b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png index 22a8ab6b..b58ac0a6 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png and b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png index 9d2ff7e5..fe6a0bad 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png and b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png index bb2c6219..351ead9c 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png and b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png index 37aa58cc..05dbc1ad 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png and b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/article.md b/2-ui/2-events/01-introduction-browser-events/article.md index e5a0915a..423c41c6 100644 --- a/2-ui/2-events/01-introduction-browser-events/article.md +++ b/2-ui/2-events/01-introduction-browser-events/article.md @@ -2,8 +2,6 @@ *An event* is a signal that something has happened. All DOM nodes generate such signals (but events are not limited to DOM). -[cut] - Here's a list of the most useful DOM events, just to take a look at: **Mouse events:** diff --git a/2-ui/2-events/02-bubbling-and-capturing/article.md b/2-ui/2-events/02-bubbling-and-capturing/article.md index 6e39eae2..bc5d70dc 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/article.md +++ b/2-ui/2-events/02-bubbling-and-capturing/article.md @@ -5,7 +5,7 @@ Let's start with an example. This handler is assigned to `<div>`, but also runs if you click any nested tag like `<em>` or `<code>`: ```html autorun height=60 -<div onclick="alert('The handler !')"> +<div onclick="alert('The handler!')"> <em>If you click on <code>EM</code>, the handler on <code>DIV</code> runs.</em> </div> ``` @@ -50,7 +50,7 @@ The process is called "bubbling", because events "bubble" from the inner element ```warn header="*Almost* all events bubble." The key word in this phrase is "almost". -For instance, a `focus` event does not bubble. There are other examples too, we'll meet them. But still it's an exception, rather then a rule, most events do bubble. +For instance, a `focus` event does not bubble. There are other examples too, we'll meet them. But still it's an exception, rather than a rule, most events do bubble. ``` ## event.target @@ -145,7 +145,7 @@ There are two possible values for that optional last argument: Note that while formally there are 3 phases, the 2nd phase ("target phase": the event reached the element) is not handled separately: handlers on both capturing and bubbling phases trigger at that phase. -If one puts a handler on the target element -- it triggers last on the capturing state, and first on the bubbling stage. +If one puts capturing and bubbling handlers on the target element, the capture handler triggers last in the capturing phase and the bubble handler triggers first in the bubbling phase. Let's see it in action: diff --git a/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js b/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js index d3959a68..b1353712 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js +++ b/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js @@ -2,7 +2,9 @@ form.onclick = function(event) { event.target.style.backgroundColor = 'yellow'; - alert("target = " + event.target.tagName + ", this=" + this.tagName); - - event.target.style.backgroundColor = ''; + // chrome needs some time to paint yellow + setTimeout(() => { + alert("target = " + event.target.tagName + ", this=" + this.tagName); + event.target.style.backgroundColor = '' + }, 0); }; diff --git a/2-ui/2-events/03-event-delegation/article.md b/2-ui/2-events/03-event-delegation/article.md index 16d9dc71..83b4b6ce 100644 --- a/2-ui/2-events/03-event-delegation/article.md +++ b/2-ui/2-events/03-event-delegation/article.md @@ -1,7 +1,7 @@ # Event delegation -Capturing and bubbling allow to implement one of most powerful event handling patterns called *event delegation*. +Capturing and bubbling allow us to implement one of most powerful event handling patterns called *event delegation*. The idea is that if we have a lot of elements handled in a similar way, then instead of assigning a handler to each of them -- we put a single handler on their common ancestor. diff --git a/2-ui/2-events/04-default-browser-action/article.md b/2-ui/2-events/04-default-browser-action/article.md index 652253e1..2e338dc5 100644 --- a/2-ui/2-events/04-default-browser-action/article.md +++ b/2-ui/2-events/04-default-browser-action/article.md @@ -10,8 +10,6 @@ For instance: If we handle an event in JavaScript, often we don't want browser actions. Fortunately, it can be prevented. -[cut] - ## Preventing browser actions There are two ways to tell the browser we don't want it to act: diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md index 28fd78ab..f6ac9f22 100644 --- a/2-ui/2-events/05-dispatch-events/article.md +++ b/2-ui/2-events/05-dispatch-events/article.md @@ -6,8 +6,6 @@ Custom events can be used to create "graphical components". For instance, a root Also we can generate built-in events like `click`, `mousedown` etc, that may be good for testing. -[cut] - ## Event constructor Events form a hierarchy, just like DOM element classes. The root is the built-in [Event](http://www.w3.org/TR/dom/#event) class. diff --git a/2-ui/3-event-details/1-mouse-events-basics/article.md b/2-ui/3-event-details/1-mouse-events-basics/article.md index fd1c6322..668b7ad9 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/article.md +++ b/2-ui/3-event-details/1-mouse-events-basics/article.md @@ -1,8 +1,8 @@ # Mouse events basics -In this chapter we'll get into more details about mouse events and their properties. +Mouse events come not only from "mouse manipulators", but are also emulated on touch devices, to make them compatible. -[cut] +In this chapter we'll get into more details about mouse events and their properties. ## Mouse event types @@ -26,7 +26,7 @@ The most used simple events are: ### Complex events `click` -: Triggers after `mousedown` and then `mouseup` over the same element. +: Triggers after `mousedown` and then `mouseup` over the same element if the left mouse button was used. `contextmenu` : Triggers after `mousedown` if the right mouse button was used. @@ -36,8 +36,6 @@ The most used simple events are: Complex events are made of simple ones, so in theory we could live without them. But they exist, and that's good, because they are convenient. -For touchscreen and touchpad devices mouse events also happen, they are emulated. - ### Events order An action may trigger multiple events. @@ -58,7 +56,7 @@ Also we can see the `which` property that allows to detect the mouse button. ## Getting the button: which -Click-related events always have the `which` property that allows to get the button. +Click-related events always have the `which` property, which allows to get the exact mouse button. It is not used for `click` and `contextmenu` events, because the former happens only on left-click, and the latter -- only on right-click. @@ -177,7 +175,7 @@ Now if you double-click on "Unselectable", it doesn't get selected. Seems to wor ...But there is a potential problem! The text became truly unselectable. Even if a user starts the selection from "Before" and ends with "After", the selection skips "Unselectable" part. Do we really want to make our text unselectable? -Most of time, we don't. A user may have valid reasons to select the text, for copying or other needs. That may be disturbing if we don't allow him to do it. So the solution is not that good. +Most of time, we don't. A user may have valid reasons to select the text, for copying or other needs. That may be inconvenient if we don't allow him to do it. So this solution is not that good. What we want is to prevent the selection on double-click, that's it. @@ -193,7 +191,7 @@ Before... Now the bold element is not selected on double clicks. -From the other hand, the text inside it is still selectable. The selection should start not on the text itself, but before or after it. Usually that's fine. +The text inside it is still selectable. However, the selection should start not on the text itself, but before or after it. Usually that's fine though. ````smart header="Canceling the selection" Instead of *preventing* the selection, we can cancel it "post-factum" in the event handler. @@ -228,18 +226,18 @@ Surely that can't stop the user from opening HTML-source, but not everyone knows ## Summary -Mouse events have following properties: +Mouse events have the following properties: - Button: `which`. - Modifier keys (`true` if pressed): `altKey`, `ctrlKey`, `shiftKey` and `metaKey` (Mac). - If you want to handle `key:Ctrl`, then don't forget Mac users, they use `key:Cmd`, so it's better to check `if (e.metaKey || e.ctrlKey)`. - Window-relative coordinates: `clientX/clientY`. -- Document-relative coordinates: `pageX/clientX`. +- Document-relative coordinates: `pageX/pageY`. -In the tasks below it's also important to deal with the selection as an unwanted side-effect of clicks. +It's also important to deal with text selection as an unwanted side-effect of clicks. -There are several ways, for instance: -1. CSS-property `user-select:none` (with browser prefixes) completely disables it. +There are several ways to do this, for instance: +1. The CSS-property `user-select:none` (with browser prefixes) completely disables text-selection. 2. Cancel the selection post-factum using `getSelection().removeAllRanges()`. 3. Handle `mousedown` and prevent the default action (usually the best). 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 277b70ef..4bf6eaa7 100644 --- a/2-ui/3-event-details/10-onload-ondomcontentloaded/article.md +++ b/2-ui/3-event-details/10-onload-ondomcontentloaded/article.md @@ -14,8 +14,6 @@ Each event may be useful: Let's explore the details of these events. -[cut] - ## DOMContentLoaded The `DOMContentLoaded` event happens on the `document` object. @@ -128,28 +126,24 @@ For that we should use another event -- `onbeforeunload`. ## window.onbeforeunload [#window.onbeforeunload] -If a visitor initiated leaving the page or tries to close the window, the `beforeunload` handler can ask for additional confirmation. +If a visitor initiated navigation away from the page or tries to close the window, the `beforeunload` handler asks for additional confirmation. -It needs to return the string with the question. The browser will show it. +It may return a string with the question. Historically browsers used to show it, but as of now only some of them do. That's because certain webmasters abused this event handler by showing misleading and hackish messages. -For instance: +You can try it by running this code and then reloading the page. -```js +```js run window.onbeforeunload = function() { return "There are unsaved changes. Leave now?"; }; ``` ```online -Click on the button in `<iframe>` below to set the handler, and then click the link to see it in action: +Or you can click on the button in `<iframe>` below to set the handler, and then click the link: [iframe src="window-onbeforeunload" border="1" height="80" link edit] ``` -```warn header="Some browsers ignore the text and show their own message instead" -Some browsers like Chrome and Firefox ignore the string and shows its own message instead. That's for sheer safety, to protect the user from potentially misleading and hackish messages. -``` - ## readyState What happens if we set the `DOMContentLoaded` handler after the document is loaded? diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md index 5c1846a9..5eedf0e6 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md @@ -44,4 +44,4 @@ The demo: If you move the mouse over the "clock" fast then nothing happens, and if you do it slow or stop on them, then there will be a tooltip. -Please note: the tooltip doesn't "blink" when the cursor between the clock subelements. +Please note: the tooltip doesn't "blink" when the cursor moves between the clock subelements. diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md index 73ac58da..25b92181 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md @@ -2,8 +2,6 @@ Let's dive into more details about events that happen when mouse moves between elements. -[cut] - ## Mouseover/mouseout, relatedTarget The `mouseover` event occurs when a mouse pointer comes over an element, and `mouseout` -- when it leaves. diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png index 628aa330..cf9fa448 100644 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png differ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png index c320e2cc..a2749d68 100644 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png differ diff --git a/2-ui/3-event-details/5-keyboard-events/article.md b/2-ui/3-event-details/5-keyboard-events/article.md index 445379b0..5430606e 100644 --- a/2-ui/3-event-details/5-keyboard-events/article.md +++ b/2-ui/3-event-details/5-keyboard-events/article.md @@ -6,8 +6,6 @@ So if we want to track any input into an `<input>` field, then keyboard events a Keyboard events should be used when we want to handle keyboard actions (virtual keyboard also counts). For instance, to react on arrow keys `key:Up` and `key:Down` or hotkeys (including combinations of keys). -[cut] - ## Teststand [#keyboard-test-stand] diff --git a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md index a3f21b57..f7412bd4 100644 --- a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md +++ b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md @@ -18,7 +18,7 @@ For instance, if the height of the whole HTML document is 2000px, then: document.documentElement.getBoundingClientRect().top = 0 // window-relative bottom = 2000 -// the document is long, so that is probably far beyound the window bottom +// the document is long, so that is probably far beyond the window bottom document.documentElement.getBoundingClientRect().bottom = 2000 ``` diff --git a/2-ui/3-event-details/8-onscroll/1-endless-page/task.md b/2-ui/3-event-details/8-onscroll/1-endless-page/task.md index 4b49070b..7c8d14fc 100644 --- a/2-ui/3-event-details/8-onscroll/1-endless-page/task.md +++ b/2-ui/3-event-details/8-onscroll/1-endless-page/task.md @@ -12,7 +12,7 @@ Like this: Please note two important features of the scroll: -1. **The scroll is "elastic".** We can scroll a little beyound the document start or end in some browsers/devices (empty space below is shown, and then the document will automatically "bounces back" to normal). +1. **The scroll is "elastic".** We can scroll a little beyond the document start or end in some browsers/devices (empty space below is shown, and then the document will automatically "bounces back" to normal). 2. **The scroll is imprecise.** When we scroll to page end, then we may be in fact like 0-50px away from the real document bottom. So, "scrolling to the end" should mean that the visitor is no more than 100px away from the document end. diff --git a/2-ui/3-event-details/8-onscroll/article.md b/2-ui/3-event-details/8-onscroll/article.md index 4eefb793..1eb134f9 100644 --- a/2-ui/3-event-details/8-onscroll/article.md +++ b/2-ui/3-event-details/8-onscroll/article.md @@ -6,8 +6,6 @@ For instance: - Show/hide additional controls or information depending on where in the document the user is. - Load more data when the user scrolls down till the end of the page. -[cut] - Here's a small function to show the current scroll: ```js autorun diff --git a/2-ui/4-forms-controls/1-form-elements/article.md b/2-ui/4-forms-controls/1-form-elements/article.md index 2c20ddfd..1a4009bc 100644 --- a/2-ui/4-forms-controls/1-form-elements/article.md +++ b/2-ui/4-forms-controls/1-form-elements/article.md @@ -4,8 +4,6 @@ Forms and control elements, such as `<input>` have a lot of special properties a Working with forms can be much more convenient if we know them. -[cut] - ## Navigation: form and elements Document forms are members of the special collection `document.forms`. diff --git a/2-ui/4-forms-controls/2-focus-blur/article.md b/2-ui/4-forms-controls/2-focus-blur/article.md index d829fb36..3177ee39 100644 --- a/2-ui/4-forms-controls/2-focus-blur/article.md +++ b/2-ui/4-forms-controls/2-focus-blur/article.md @@ -4,17 +4,15 @@ An element receives a focus when the user either clicks on it or uses the `key:T Focusing generally means: "prepare to accept the data here", so that's the moment when we can run the code to initialize or load something. -The moment of loosing the focus ("blur") can be even more important. That's when a user clicks somewhere else or presses `key:Tab` to go to the next form field, or there are other means as well. +The moment of losing the focus ("blur") can be even more important. That's when a user clicks somewhere else or presses `key:Tab` to go to the next form field, or there are other means as well. -Loosing the focus generally means: "the data has been entered", so we can run the code to check it or even to save it to the server and so on. +Losing the focus generally means: "the data has been entered", so we can run the code to check it or even to save it to the server and so on. There are important peculiarities when working with focus events. We'll do the best to cover them here. -[cut] - ## Events focus/blur -The `focus` event is called on focusing, and `blur` -- when the element looses the focus. +The `focus` event is called on focusing, and `blur` -- when the element loses the focus. Let's use them for validation of an input field. @@ -68,6 +66,7 @@ For instance, let's make the visitor unable to leave the input if the value is i </style> Your email please: <input type="email" id="input"> +<input type="text" style="width:220px" placeholder="make email invalid and try to focus here"> <script> input.onblur = function() { @@ -89,7 +88,7 @@ It works in all browsers except Firefox ([bug](https://bugzilla.mozilla.org/show If we enter something into the input and then try to use `key:Tab` or click away from the `<input>`, then `onblur` returns the focus back. -Please note that we can't "prevent loosing focus" by calling `event.preventDefault()` in `onblur`, because `onblur` works *after* the element lost the focus. +Please note that we can't "prevent losing focus" by calling `event.preventDefault()` in `onblur`, because `onblur` works *after* the element lost the focus. ```warn header="JavaScript-initiated focus loss" A focus loss can occur for many reasons. @@ -213,7 +212,7 @@ So here's another working variant: ## Summary -Events `focus` and `blur` trigger on focusing/loosing focus on the element. +Events `focus` and `blur` trigger on focusing/losing focus on the element. Their specials are: - They do not bubble. Can use capturing state instead or `focusin/focusout`. diff --git a/2-ui/4-forms-controls/3-events-change-input/article.md b/2-ui/4-forms-controls/3-events-change-input/article.md index 81407dc7..2b46b083 100644 --- a/2-ui/4-forms-controls/3-events-change-input/article.md +++ b/2-ui/4-forms-controls/3-events-change-input/article.md @@ -76,4 +76,4 @@ Data change events: |---------|----------|-------------| | `change`| A value was changed. | For text inputs triggers on focus loss. | | `input` | For text inputs on every change. | Triggers immediately unlike `change`. | -| `cut/copy/paste` | Cut/copy/paste actions. | The action can be prevented. The `event.clipbordData` property gives read/write access to the clipboard. | +| `cut/copy/paste` | Cut/copy/paste actions. | The action can be prevented. The `event.clipboardData` property gives read/write access to the clipboard. | diff --git a/2-ui/4-forms-controls/4-forms-submit/article.md b/2-ui/4-forms-controls/4-forms-submit/article.md index 15647dfc..69d5a3dc 100644 --- a/2-ui/4-forms-controls/4-forms-submit/article.md +++ b/2-ui/4-forms-controls/4-forms-submit/article.md @@ -6,8 +6,6 @@ The method `form.submit()` allows to initiate form sending from JavaScript. We c Let's see more details of them. -[cut] - ## Event: submit There are two main ways to submit a form: diff --git a/2-ui/index.md b/2-ui/index.md index 5c4e1484..d94378cf 100644 --- a/2-ui/index.md +++ b/2-ui/index.md @@ -1,3 +1,3 @@ -# Browser: Document, DOM, Interfaces +# Browser: Document, Events, Interfaces Learning how to manage the browser page: add elements, manipulate their size and position, dynamically create interfaces and interact with the visitor. diff --git a/3-animation/1-bezier-curve/article.md b/3-animation/1-bezier-curve/article.md index 4af01bb5..6166f25c 100644 --- a/3-animation/1-bezier-curve/article.md +++ b/3-animation/1-bezier-curve/article.md @@ -4,8 +4,6 @@ Bezier curves are used in computer graphics to draw shapes, for CSS animation an They are actually a very simple thing, worth to study once and then feel comfortable in the world of vector graphics and advanced animations. -[cut] - ## Control points A [bezier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) is defined by control points. @@ -28,14 +26,14 @@ If you look closely at these curves, you can immediately notice: 1. **Points are not always on curve.** That's perfectly normal, later we'll see how the curve is built. 2. **The curve order equals the number of points minus one**. -For two points we have a linear curve (that's a straight line), for three points -- quadratic curve (parabolic), for three points -- cubic curve. +For two points we have a linear curve (that's a straight line), for three points -- quadratic curve (parabolic), for four points -- cubic curve. 3. **A curve is always inside the [convex hull](https://en.wikipedia.org/wiki/Convex_hull) of control points:** ![](bezier4-e.png) ![](bezier3-e.png) Because of that last property, in computer graphics it's possible to optimize intersection tests. If convex hulls do not intersect, then curves do not either. So checking for the convex hulls intersection first can give a very fast "no intersection" result. Checking the intersection or convex hulls is much easier, because they are rectangles, triangles and so on (see the picture above), much simpler figures than the curve. -The main value of Bezier curves for drawing -- by moving the points the curve is changing *in intuitively obvious way*. +**The main value of Bezier curves for drawing -- by moving the points the curve is changing *in intuitively obvious way*.** Try to move control points using a mouse in the example below: @@ -49,53 +47,16 @@ Here are some examples: ![](bezier-car.png) ![](bezier-letter.png) ![](bezier-vase.png) -## Maths - -A Bezier curve can be described using a mathematical formula. - -As we'll see soon -- there's no need to know it. But for completeness -- here it is. - -Given the coordinates of control points <code>P<sub>i</sub></code>: the first control point has coordinates <code>P<sub>1</sub> = (x<sub>1</sub>, y<sub>1</sub>)</code>, the second: <code>P<sub>2</sub> = (x<sub>2</sub>, y<sub>2</sub>)</code>, and so on, the curve coordinates are described by the equation that depends on the parameter `t` from the segment `[0,1]`. - -- The formula for a 2-points curve: - - <code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code> -- For three points: - - <code>P = (1−t)<sup>2</sup>P<sub>1</sub> + 2(1−t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code> -- For four points: - - <code>P = (1−t)<sup>3</sup>P<sub>1</sub> + 3(1−t)<sup>2</sup>tP<sub>2</sub> +3(1−t)t<sup>2</sup>P<sub>3</sub> + t<sup>3</sup>P<sub>4</sub></code> - -These are vector equations. - -We can rewrite them coordinate-by-coordinate, for instance the 3-point curve: - -- <code>x = (1−t)<sup>2</sup>x<sub>1</sub> + 2(1−t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code> -- <code>y = (1−t)<sup>2</sup>y<sub>1</sub> + 2(1−t)ty<sub>2</sub> + t<sup>2</sup>y<sub>3</sub></code> - -Instead of <code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> we should put coordinates of 3 control points. - -For instance, if control points are `(0,0)`, `(0.5, 1)` and `(1, 0)`, the equations are: - -- <code>x = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 0.5 + t<sup>2</sup> * 1 = (1-t)t + t<sup>2</sup> = t</code> -- <code>y = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 1 + t<sup>2</sup> * 0 = 2(1-t)t = –t<sup>2</sup> + 2t</code> - -Now as `t` runs from `0` to `1`, the set of values `(x,y)` for each `t` forms the curve. - -That's probably too scientific, not very obvious why curves look like that, and how they depend on control points. - -So here's the drawing algorithm that may be easier to understand. - ## De Casteljau's algorithm -[De Casteljau's algorithm](https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm) is identical to the mathematical definition of the curve, but visually shows how it is built. +There's a mathematical formula for Bezier curves, but let's cover it a bit later, because +[De Casteljau's algorithm](https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm) it is identical to the mathematical definition and visually shows how it is constructed. -Let's see it on the 3-points example. +First let's see the 3-points example. Here's the demo, and the explanation follow. -Points can be moved by the mouse. Press the "play" button to run it. +Control points (1,2 and 3) can be moved by the mouse. Press the "play" button to run it. [iframe src="demo.svg?p=0,0,0.5,1,1,0&animate=1" height=370] @@ -118,19 +79,19 @@ Points can be moved by the mouse. Press the "play" button to run it. | ------------------------ | ---------------------- | | ![](bezier3-draw1.png) | ![](bezier3-draw2.png) | -4. Now the <span style="color:#167490">blue</span> segment take a point on the distance proportional to the same value of `t`. That is, for `t=0.25` (the left picture) we have a point at the end of the left quarter of the segment, and for `t=0.5` (the right picture) -- in the middle of the segment. On pictures above that point is <span style="color:red">red</span>. +4. Now in the <span style="color:#167490">blue</span> segment take a point on the distance proportional to the same value of `t`. That is, for `t=0.25` (the left picture) we have a point at the end of the left quarter of the segment, and for `t=0.5` (the right picture) -- in the middle of the segment. On pictures above that point is <span style="color:red">red</span>. 5. As `t` runs from `0` to `1`, every value of `t` adds a point to the curve. The set of such points forms the Bezier curve. It's red and parabolic on the pictures above. That was a process for 3 points. But the same is for 4 points. -The demo for 4 points (points can be moved by mouse): +The demo for 4 points (points can be moved by a mouse): [iframe src="demo.svg?p=0,0,0.5,0,0.5,1,1,1&animate=1" height=370] -The algorithm: +The algorithm for 4 points: -- Control points are connected by segments: 1 -> 2, 2 -> 3, 3 -> 4. We have 3 <span style="color:#825E28">brown</span> segments. +- Connect control points by segments: 1 -> 2, 2 -> 3, 3 -> 4. There will be 3 <span style="color:#825E28">brown</span> segments. - For each `t` in the interval from `0` to `1`: - We take points on these segments on the distance proportional to `t` from the beginning. These points are connected, so that we have two <span style="color:#0A0">green segments</span>. - On these segments we take points proportional to `t`. We get one <span style="color:#167490">blue segment</span>. @@ -139,41 +100,86 @@ The algorithm: The algorithm is recursive and can be generalized for any number of control points. -Given N of control points, we connect them to get initially N-1 segments. +Given N of control points: -Then for each `t` from `0` to `1`: -- Take a point on each of segment on the distance proportional to `t` and connect them -- there will be N-2 segments. -- Take a point on each of these segments on the distance proportional to `t` and connect -- there will be N-3 segments, and so on... -- Till we have one point. These points make the curve. +1. We connect them to get initially N-1 segments. +2. Then for each `t` from `0` to `1`, we take a point on each segment on the distance proportional to `t` and connect them. There will be N-2 segments. +3. Repeat step 2 until there is only one point. -Move examples of curves: +These points make the curve. + +```online +**Run and pause examples to clearly see the segments and how the curve is built.** +``` + + +A curve that looks like `y=1/t`: [iframe src="demo.svg?p=0,0,0,0.75,0.25,1,1,1&animate=1" height=370] -With other points: +Zig-zag control points also work fine: [iframe src="demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1" height=370] -Loop form: +Making a loop is possible: [iframe src="demo.svg?p=0,0,1,0.5,0,1,0.5,0&animate=1" height=370] -Not smooth Bezier curve: +A non-smooth Bezier curve (yeah, that's possible too): [iframe src="demo.svg?p=0,0,1,1,0,1,1,0&animate=1" height=370] -As the algorithm is recursive, we can build Bezier curves of any order: using 5, 6 or more control points. But in practice they are less useful. Usually we take 2-3 points, and for complex lines glue several curves together. That's simpler to develop and calculate. +```online +If there's anything unclear in the algorithm description, then live examples above show how +the curve is built. +``` + +As the algorithm is recursive, we can build Bezier curves of any order, that is: using 5, 6 or more control points. But in practice many points are less useful. Usually we take 2-3 points, and for complex lines glue several curves together. That's simpler to develop and calculate. ```smart header="How to draw a curve *through* given points?" -We use control points for a Bezier curve. As we can see, they are not on the curve. Or, to be precise, the first and the last ones do belong to curve, but others don't. +We use control points for a Bezier curve. As we can see, they are not on the curve, except the first and the last ones. Sometimes we have another task: to draw a curve *through several points*, so that all of them are on a single smooth curve. That task is called [interpolation](https://en.wikipedia.org/wiki/Interpolation), and here we don't cover it. -There are mathematical formulas for such curves, for instance [Lagrange polynomial](https://en.wikipedia.org/wiki/Lagrange_polynomial). - -In computer graphics [spline interpolation](https://en.wikipedia.org/wiki/Spline_interpolation) is often used to build smooth curves that connect many points. +There are mathematical formulas for such curves, for instance [Lagrange polynomial](https://en.wikipedia.org/wiki/Lagrange_polynomial). In computer graphics [spline interpolation](https://en.wikipedia.org/wiki/Spline_interpolation) is often used to build smooth curves that connect many points. ``` + +## Maths + +A Bezier curve can be described using a mathematical formula. + +As we saw -- there's actually no need to know it, most people just draw the curve by moving points with a mouse. But if you're into maths -- here it is. + +Given the coordinates of control points <code>P<sub>i</sub></code>: the first control point has coordinates <code>P<sub>1</sub> = (x<sub>1</sub>, y<sub>1</sub>)</code>, the second: <code>P<sub>2</sub> = (x<sub>2</sub>, y<sub>2</sub>)</code>, and so on, the curve coordinates are described by the equation that depends on the parameter `t` from the segment `[0,1]`. + +- The formula for a 2-points curve: + + <code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code> +- For 3 control points: + + <code>P = (1−t)<sup>2</sup>P<sub>1</sub> + 2(1−t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code> +- For 4 control points: + + <code>P = (1−t)<sup>3</sup>P<sub>1</sub> + 3(1−t)<sup>2</sup>tP<sub>2</sub> +3(1−t)t<sup>2</sup>P<sub>3</sub> + t<sup>3</sup>P<sub>4</sub></code> + + +These are vector equations. In other words, we can put `x` and `y` instead of `P` to get corresponding coordinates. + +For instance, the 3-point curve is formed by points `(x,y)` calculated as: + +- <code>x = (1−t)<sup>2</sup>x<sub>1</sub> + 2(1−t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code> +- <code>y = (1−t)<sup>2</sup>y<sub>1</sub> + 2(1−t)ty<sub>2</sub> + t<sup>2</sup>y<sub>3</sub></code> + +Instead of <code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> we should put coordinates of 3 control points, and then as `t` moves from `0` to `1`, for each value of `t` we'll have `(x,y)` of the curve. + +For instance, if control points are `(0,0)`, `(0.5, 1)` and `(1, 0)`, the equations become: + +- <code>x = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 0.5 + t<sup>2</sup> * 1 = (1-t)t + t<sup>2</sup> = t</code> +- <code>y = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 1 + t<sup>2</sup> * 0 = 2(1-t)t = –t<sup>2</sup> + 2t</code> + +Now as `t` runs from `0` to `1`, the set of values `(x,y)` for each `t` forms the curve for such control points. + ## Summary Bezier curves are defined by their control points. diff --git a/3-animation/2-css-animations/article.md b/3-animation/2-css-animations/article.md index 998a8c22..1189ab95 100644 --- a/3-animation/2-css-animations/article.md +++ b/3-animation/2-css-animations/article.md @@ -4,8 +4,6 @@ CSS animations allow to do simple animations without JavaScript at all. JavaScript can be used to control CSS animation and make them even better with a little of code. -[cut] - ## CSS transitions [#css-transition] The idea of CSS transitions is simple. We describe a property and how its changes should be animated. When the property changes, the browser paints the animation. @@ -42,7 +40,7 @@ Click the button below to animate the background: </script> ``` -There are 5 properties to describe CSS transitions: +There are 4 properties to describe CSS transitions: - `transition-property` - `transition-duration` @@ -262,9 +260,15 @@ But how to make the Bezier curve for a specific task? There are many tools. For Timing function `steps(number of steps[, start/end])` allows to split animation into steps. -Let's see that in an example with digits. We'll make the digits change not in a smooth, but in a discrete way. +Let's see that in an example with digits. -For that we split the animation into 9 steps: +Here's a list of digits, without any animations, just as a source: + +[codetabs src="step-list"] + +We'll make the digits appear in a discrete way by making the part of the list outside of the red "window" invisible and shifting the list to the left with each step. + +There will be 9 steps, a step-move for each digit: ```css #stripe.animate { @@ -273,11 +277,11 @@ For that we split the animation into 9 steps: } ``` -In action `step(9, start)`: +In action: [codetabs src="step"] -The first argument of `steps` is the number of steps. The transform will be split into 9 parts (10% each). The time interval is divided as well: 9 seconds split into 1 second intervals. +The first argument of `steps(9, start)` is the number of steps. The transform will be split into 9 parts (10% each). The time interval is automatically divided into 9 parts as well, so `transition: 9s` gives us 9 seconds for the whole animation – 1 second per digit. The second argument is one of two words: `start` or `end`. @@ -303,7 +307,7 @@ So the process would go like this: - ... - `9s` -- `-90%` -In action `step(9, end)`: +Here's `step(9, end)` in action (note the pause between the first digit change): [codetabs src="step-end"] diff --git a/3-animation/2-css-animations/step-list.view/index.html b/3-animation/2-css-animations/step-list.view/index.html new file mode 100644 index 00000000..d7931f5e --- /dev/null +++ b/3-animation/2-css-animations/step-list.view/index.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <link rel="stylesheet" href="style.css"> +</head> + +<body> + + <div id="digit"><div id="stripe">0123456789</div></div> + +</body> +</html> diff --git a/3-animation/2-css-animations/step-list.view/style.css b/3-animation/2-css-animations/step-list.view/style.css new file mode 100644 index 00000000..a934e834 --- /dev/null +++ b/3-animation/2-css-animations/step-list.view/style.css @@ -0,0 +1,9 @@ +#digit { + border: 1px solid red; + width: 1.2em; +} + +#stripe { + display: inline-block; + font: 32px monospace; +} diff --git a/3-animation/3-js-animation/article.md b/3-animation/3-js-animation/article.md index 99775934..f0f258c3 100644 --- a/3-animation/3-js-animation/article.md +++ b/3-animation/3-js-animation/article.md @@ -4,22 +4,19 @@ JavaScript animations can handle things that CSS can't. For instance, moving along a complex path, with a timing function different from Bezier curves, or an animation on a canvas. -[cut] +## Using setInterval -## setInterval +An animation can be implemented as a sequence of frames -- usually small changes to HTML/CSS properties. -From the HTML/CSS point of view, an animation is a gradual change of the style property. For instance, changing `style.left` from `0px` to `100px` moves the element. - -And if we increase it in `setInterval`, by making 50 small changes per second, then it looks smooth. That's the same principle as in the cinema: 24 or more frames per second is enough to make it look smooth. +For instance, changing `style.left` from `0px` to `100px` moves the element. And if we increase it in `setInterval`, changing by `2px` with a tiny delay, like 50 times per second, then it looks smooth. That's the same principle as in the cinema: 24 or more frames per second is enough to make it look smooth. The pseudo-code can look like this: ```js -let delay = 1000 / 50; // in 1 second 50 frames let timer = setInterval(function() { if (animation complete) clearInterval(timer); - else increase style.left -}, delay) + else increase style.left by 2px +}, 20); // change by 2px every 20ms, about 50 frames per second ``` More complete example of the animation: @@ -52,15 +49,13 @@ Click for the demo: [codetabs height=200 src="move"] -## requestAnimationFrame +## Using requestAnimationFrame Let's imagine we have several animations running simultaneously. -If we run them separately, each one with its own `setInterval(..., 20)`, then the browser would have to repaint much more often than every `20ms`. +If we run them separately, then even though each one has `setInterval(..., 20)`, then the browser would have to repaint much more often than every `20ms`. -Each `setInterval` triggers once per `20ms`, but they are independent, so we have several independent runs within `20ms`. - -These several independent redraws should be grouped together, to make it easier for the browser. +That's because they have different starting time, so "every 20ms" differs between different animations. The intervals are not alignned. So we'll have several independent runs within `20ms`. In other words, this: @@ -72,19 +67,19 @@ setInterval(function() { }, 20) ``` -...Is lighter than this: +...Is lighter than three independent calls: ```js -setInterval(animate1, 20); -setInterval(animate2, 20); +setInterval(animate1, 20); // independent animations +setInterval(animate2, 20); // in different places of the script setInterval(animate3, 20); ``` -There's one more thing to keep in mind. Sometimes when CPU is overloaded, or there are other reasons to redraw less often. For instance, if the browser tab is hidden, then there's totally no point in drawing. +These several independent redraws should be grouped together, to make the redraw easier for the browser (and hence smoother for people). -There's a standard [Animation timing](http://www.w3.org/TR/animation-timing/) that provides the function `requestAnimationFrame`. +There's one more thing to keep in mind. Sometimes when CPU is overloaded, or there are other reasons to redraw less often (like when the browser tab is hidden), so we really shouldn't run it every `20ms`. -It addresses all these issues and even more. +But how do we know about that in JavaScript? There's a specification [Animation timing](http://www.w3.org/TR/animation-timing/) that provides the function `requestAnimationFrame`. It addresses all these issues and even more. The syntax: ```js @@ -418,7 +413,7 @@ Here's the animated "bouncing" text typing: ## Summary -JavaScript animation should be implemented via `requestAnimationFrame`. That built-in method allows to setup a callback function to run when the browser will be preparing a repaint. Usually that's very soon, but the exact time depends on the browser. +For animations that CSS can't handle well, or those that need tight control, JavaScript can help. JavaScript animations should be implemented via `requestAnimationFrame`. That built-in method allows to setup a callback function to run when the browser will be preparing a repaint. Usually that's very soon, but the exact time depends on the browser. When a page is in the background, there are no repaints at all, so the callback won't run: the animation will be suspended and won't consume resources. That's great. diff --git a/4-frames-and-windows/01-popup-windows/article.md b/4-frames-and-windows/01-popup-windows/article.md index a1839d6a..fa144f88 100644 --- a/4-frames-and-windows/01-popup-windows/article.md +++ b/4-frames-and-windows/01-popup-windows/article.md @@ -9,8 +9,6 @@ window.open('http://javascript.info/') ... And it will open a new window with given URL. Most modern browsers are configured to open new tabs instead of separate windows. -[cut] - ## Popup blocking Popups exist from really ancient times. The initial idea was to show another content without closing the main window. As of now, there are other ways to do that: JavaScript is able to send requests for server, so popups are rarely used. But sometimes they are still handy. diff --git a/4-frames-and-windows/03-cross-window-communication/article.md b/4-frames-and-windows/03-cross-window-communication/article.md index 15394969..d9b9247c 100644 --- a/4-frames-and-windows/03-cross-window-communication/article.md +++ b/4-frames-and-windows/03-cross-window-communication/article.md @@ -4,8 +4,6 @@ The "Same Origin" (same site) policy limits access of windows and frame to each The idea is that if we have two windows open: one from `john-smith.com`, and another one is `gmail.com`, then we wouldn't want a script from `john-smith.com` to read our mail. -[cut] - ## Same Origin [#same-origin] Two URLs are said to have the "same origin" if they have the same protocol, domain and port. @@ -27,7 +25,7 @@ If we have a reference to another window (a popup or iframe), and that window co If it comes from another origin, then we can only change its location. Please note: not *read* the location, but *modify* it, redirect it to another place. That's safe, because the URL may contain sensitive parameters, so reading it from another origin is prohibited, but changing is not. -Also such windows windows may exchange messages. Soon about that later. +Also such windows may exchange messages. Soon about that later. ````warn header="Exclusion: subdomains may be same-origin" diff --git a/4-frames-and-windows/06-clickjacking/article.md b/4-frames-and-windows/06-clickjacking/article.md index 66b918e1..bcffc3fd 100644 --- a/4-frames-and-windows/06-clickjacking/article.md +++ b/4-frames-and-windows/06-clickjacking/article.md @@ -4,22 +4,20 @@ The "clickjacking" attack allows an evil page to click on a "victim site" *on be Many sites were hacked this way, including Twitter, Facebook, Paypal and other sites. They are all fixed, of course. -[cut] - ## The idea The idea is very simple. Here's how clickjacking was done with Facebook: -1. A visitor is lured to the evil page. No matter how. -2. The page has a harmlessly-looking link on it (like "get rich now" or "click here, very funny" and so on). +1. A visitor is lured to the evil page. It doesn't matter how. +2. The page has a harmless-looking link on it (like "get rich now" or "click here, very funny"). 3. Over that link the evil page positions a transparent `<iframe>` with `src` from facebook.com, in such a way that the "Like" button is right above that link. Usually that's done with `z-index`. -4. Clicking on that link, the visitor in fact presses that button. +4. In attempting to click the link, the visitor in fact clicks the button. ## The demo -Here's how the evil page looks like. To make things clear, the `<iframe>` is half-transparent (in real evil pages it's fully transparent): +Here's how the evil page looks. To make things clear, the `<iframe>` is half-transparent (in real evil pages it's fully transparent): ```html run height=120 no-beautify <style> @@ -39,10 +37,10 @@ iframe { /* iframe from the victim site */ <!-- The url from the victim site --> *!* -<iframe src="facebook.html"></iframe> +<iframe src="/clickjacking/facebook.html"></iframe> <button>Click here!</button> - */!* +*/!* <div>...And you're cool (I'm a cool hacker actually)!</div> ``` @@ -53,7 +51,7 @@ The full demo of the attack: Here we have a half-transparent `<iframe src="facebook.html">`, and in the example we can see it hovering over the button. A click on the button actually clicks on the iframe, but that's not visible to the user, because the iframe is transparent. -As a result if the visitor is authorized on facebook ("remember me" is usually turned on), then it adds a "Like". On Twitter that would be a "Follow" button. +As a result, if the visitor is authorized on Facebook ("remember me" is usually turned on), then it adds a "Like". On Twitter that would be a "Follow" button. Here's the same example, but closer to reality, with `opacity:0` for `<iframe>`: @@ -68,14 +66,14 @@ Technically, if we have a text field to hack, then we can position an iframe in But then there's a problem. Everything that the visitor types will be hidden, because the iframe is not visible. -So that would look really odd to the user, and he will stop. +People will usually stop typing when they can't see their new characters printing on the screen. ``` ## Old-school defences (weak) -The oldest defence method is the piece of JavaScript that forbids to open the page in a frame (so-called "framebusting"). +The oldest defence is a bit of JavaScript which forbids opening the page in a frame (so-called "framebusting"). -Like this: +That looks like this: ```js if (top != window) { @@ -83,15 +81,15 @@ if (top != window) { } ``` -That is: if the window finds out that it's not on the top, then it automatically makes itself the top. +That is: if the window finds out that it's not on top, then it automatically makes itself the top. -As of now, that's not reliable, because there are many ways to hack around it. Let's cover a few. +This not a reliable defence, because there are many ways to hack around it. Let's cover a few. ### Blocking top-navigation We can block the transition caused by changing `top.location` in the [beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload) event. -The top page (that belongs to the hacker) sets a handler to it, and when the `iframe` tries to change `top.location` the visitor gets a message asking him whether he wants to leave. +The top page (belonging to the hacker) sets a handler to it, and when the `iframe` tries to change `top.location` the visitor gets a message asking him whether he wants to leave. Like this: ```js @@ -101,7 +99,7 @@ window.onbeforeunload = function() { }; ``` -In most cases the visitor would answer negatively, because he doesn't know about the iframe, all he can see is the top page, there's no reason to leave. And so the `top.location` won't change! +In most cases the visitor would answer negatively, because he doesn't know about the iframe, all he can see is the top page, leading him to think there is no reason to leave. So `top.location` won't change! In action: @@ -111,7 +109,7 @@ In action: One of the things restricted by the `sandbox` attribute is navigation. A sandboxed iframe may not change `top.location`. -So we can add the iframe with `sandbox="allow-scripts allow-forms"`. That would relax the restrictions allowing scripts and forms. But we don't put `allow-top-navigation` in the value so that the navigation is still forbidden. And the change of `top.location` won't work. +So we can add the iframe with `sandbox="allow-scripts allow-forms"`. That would relax the restrictions, permitting scripts and forms. But we omit `allow-top-navigation` so that changing `top.location` is forbidden. Here's the code: @@ -123,21 +121,21 @@ There are other ways to work around that simple protection too. ## X-Frame-Options -Server-side header `X-Frame-Options` can allow or forbid showing the page inside a frame. +The server-side header `X-Frame-Options` can permit or forbid displaying the page inside a frame. -It must be sent by the server: browser ignore it if found in `<meta>` tags. So `<meta http-equiv="X-Frame-Options"...>` won't do anything. +It must be sent *by the server*: the browser will ignore it if found in a `<meta>` tag. So, `<meta http-equiv="X-Frame-Options"...>` won't do anything. The header may have 3 values: `DENY` -: Never ever show the page inside an iframe. +: Never ever show the page inside a frame. `SAMEORIGIN` -: Allow to show inside an iframe if the parent document comes from the same origin. +: Allow inside a frame if the parent document comes from the same origin. `ALLOW-FROM domain` -: Allows to show inside an iframe if the parent document is from the given domain. +: Allow inside a frame if the parent document is from the given domain. For instance, Twitter uses `X-Frame-Options: SAMEORIGIN`. Here's the result: @@ -147,15 +145,15 @@ For instance, Twitter uses `X-Frame-Options: SAMEORIGIN`. Here's the result: <iframe src="https://twitter.com"></iframe> -Depending on the browser, `iframe` above is either empty or it has a message telling that "the browser can't show it". +Depending on your browser, the `iframe` above is either empty or alerting you that the browser won't permit that page to be navigating in this way. ## Showing with disabled functionality -The protecting `X-Frame-Options` header has a side-effect. Other sites can't show our page in an `iframe`, even if they have "legal" reasons to do so. +The `X-Frame-Options` header has a side-effect. Other sites won't be able to show our page in a frame, even if they have good reasons to do so. -So there are other solutions. For instance, we can "cover" the page with a `<div>` with `height:100%;width:100%`, so that it handles all clicks. That `<div>` should disappear if `window == top` or we figure out that we don't need protection. +So there are other solutions... For instance, we can "cover" the page with a `<div>` with `height: 100%; width: 100%;`, so that it intercepts all clicks. That `<div>` should disappear if `window == top` or if we figure out that we don't need the protection. -Like this: +Something like this: ```html <style> @@ -188,13 +186,13 @@ The demo: ## Summary -Clickjacking is a way to "trick" users into a clicking on a victim site without even knowing what happens. That's dangerous if there are important click-activated actions. +Clickjacking is a way to "trick" users into clicking on a malicious site without even knowing what's happening. That's dangerous if there are important click-activated actions. -A hacker can post a link to his evil page in a message or lure visitors to his page by other means. There are many variants. +A hacker can post a link to his evil page in a message, or lure visitors to his page by some other means. There are many variations. -From one side -- the attack is "not deep": all a hacker can do is one click. But from another side, if the hacker knows that after the click another control appears, then it may use cunning messages to make the user to click on it as well. +From one perspective -- the attack is "not deep": all a hacker is doing is intercepting a single click. But from another perspective, if the hacker knows that after the click another control will appear, then he may use cunning messages to coerce the user into clicking on them as well. -The attack is quite dangerous, because when we engineer the UI we usually don't think that a hacker can click on behalf of the visitor. So vulnerabilities can be found in totally unexpected places. +The attack is quite dangerous, because when we engineer the UI we usually don't anticipate that a hacker may click on behalf of the visitor. So vulnerabilities can be found in totally unexpected places. -- It's recommended to use `X-Frame-Options: SAMEORIGIN` on pages that are totally not meant to be shown inside iframes (or just for the whole site). -- Use a covering `<div>` if we want to allow our pages to be shown in iframes, and still stay safe. +- It is recommended to use `X-Frame-Options: SAMEORIGIN` on pages (or whole websites) which are not intended to be viewed inside frames. +- Use a covering `<div>` if we want to allow our pages to be shown in iframes, but still stay safe. diff --git a/5-regular-expressions/01-regexp-introduction/article.md b/5-regular-expressions/01-regexp-introduction/article.md index 7cdb55e0..5b6c9473 100644 --- a/5-regular-expressions/01-regexp-introduction/article.md +++ b/5-regular-expressions/01-regexp-introduction/article.md @@ -6,8 +6,6 @@ In JavaScript regular expressions are implemented using objects of a built-in `R Please note that regular expressions vary between programming languages. In this tutorial we concentrate on JavaScript. Of course there's a lot in common, but they are a somewhat different in Perl, Ruby, PHP etc. -[cut] - ## Regular expressions A regular expression (also "regexp", or just "reg") consists of a *pattern* and optional *flags*. diff --git a/5-regular-expressions/02-regexp-methods/article.md b/5-regular-expressions/02-regexp-methods/article.md index 1d808bf3..c5a4cbdc 100644 --- a/5-regular-expressions/02-regexp-methods/article.md +++ b/5-regular-expressions/02-regexp-methods/article.md @@ -7,8 +7,6 @@ There are two sets of methods to deal with regular expressions. The structure is a bit messed up, so we'll first consider methods separately, and then -- practical recipes for common tasks. -[cut] - ## str.search(reg) We've seen this method already. It returns the position of the first match or `-1` if none found: @@ -153,7 +151,7 @@ We can use special characters in it: |`$&`|the whole match| |<code>$`</code>|a part of the string before the match| |`$'`|a part of the string after the match| -|`$n`|if `n` is a 1-2 digit number, then it means the contents of n-th parentheses counting fro left to right| +|`$n`|if `n` is a 1-2 digit number, then it means the contents of n-th parentheses counting from left to right| For instance let's use `$&` to replace all entries of `"John"` by `"Mr.John"`: diff --git a/5-regular-expressions/03-regexp-character-classes/article.md b/5-regular-expressions/03-regexp-character-classes/article.md index 41372bd0..2b41d57e 100644 --- a/5-regular-expressions/03-regexp-character-classes/article.md +++ b/5-regular-expressions/03-regexp-character-classes/article.md @@ -4,8 +4,6 @@ Consider a practical task -- we have a phone number `"+7(903)-123-45-67"`, and w A character class is a special notation that matches any symbol from the set. -[cut] - For instance, there's a "digit" class. It's written as `\d`. We put it in the pattern, and during the search any digit matches it. For instance, the regexp `pattern:/\d/` looks for a single digit: @@ -101,9 +99,9 @@ So it matches `pattern:\bHello\b` and `pattern:\bJava\b`, but not `pattern:\bHel ```js run alert( "Hello, Java!".match(/\bHello\b/) ); // Hello -alert( "Hello, Java!".match(/\Java\b/) ); // Java -alert( "Hello, Java!".match(/\Hell\b/) ); // null -alert( "Hello, Java!".match(/\Java!\b/) ); // null +alert( "Hello, Java!".match(/\bJava\b/) ); // Java +alert( "Hello, Java!".match(/\bHell\b/) ); // null +alert( "Hello, Java!".match(/\bJava!\b/) ); // null ``` Once again let's note that `pattern:\b` makes the searching engine to test for the boundary, so that `pattern:Java\b` finds `match:Java` only when followed by a word boundary, but it does not add a letter to the result. diff --git a/5-regular-expressions/04-regexp-escaping/article.md b/5-regular-expressions/04-regexp-escaping/article.md index cb7a3eb1..11767c3d 100644 --- a/5-regular-expressions/04-regexp-escaping/article.md +++ b/5-regular-expressions/04-regexp-escaping/article.md @@ -30,7 +30,7 @@ alert( "function g()".match(/g\(\)/) ); // "g()" If we're looking for a backslash `\`, then we should double it: ```js run -alert( "1\2".match(/\\/) ); // '\' +alert( "1\\2".match(/\\/) ); // '\' ``` ## A slash diff --git a/5-regular-expressions/05-regexp-character-sets-and-ranges/article.md b/5-regular-expressions/05-regexp-character-sets-and-ranges/article.md index 90c890c7..5c8a8bab 100644 --- a/5-regular-expressions/05-regexp-character-sets-and-ranges/article.md +++ b/5-regular-expressions/05-regexp-character-sets-and-ranges/article.md @@ -2,8 +2,6 @@ Several characters or character classes inside square brackets `[…]` mean to "search for any character among given". -[cut] - ## Sets For instance, `pattern:[eao]` means any of the 3 characters: `'a'`, `'e'`, or `'o'`. diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/article.md b/5-regular-expressions/08-regexp-greedy-and-lazy/article.md index 43650b36..44a32062 100644 --- a/5-regular-expressions/08-regexp-greedy-and-lazy/article.md +++ b/5-regular-expressions/08-regexp-greedy-and-lazy/article.md @@ -4,8 +4,6 @@ Quantifiers are very simple from the first sight, but in fact they can be tricky We should understand how the search works very well if we plan to look for something more complex than `pattern:/\d+/`. -[cut] - Let's take the following task as an example. We have a text and need to replace all quotes `"..."` with guillemet marks: `«...»`. They are preferred for typography in many countries. diff --git a/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md b/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md index 2747933e..63c1da7b 100644 --- a/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md +++ b/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md @@ -12,5 +12,5 @@ let reg = /\d+(\.\d+)?/g; let str = "1.5 0 12. 123.4."; -alert( str.match(re) ); // 1.5, 0, 12, 123.4 +alert( str.match(reg) ); // 1.5, 0, 12, 123.4 ``` diff --git a/5-regular-expressions/09-regexp-groups/article.md b/5-regular-expressions/09-regexp-groups/article.md index 6bd14e7c..ab0d301a 100644 --- a/5-regular-expressions/09-regexp-groups/article.md +++ b/5-regular-expressions/09-regexp-groups/article.md @@ -7,8 +7,6 @@ That has two effects: 1. It allows to place a part of the match into a separate array item when using [String#match](mdn:js/String/match) or [RegExp#exec](mdn:/RegExp/exec) methods. 2. If we put a quantifier after the parentheses, it applies to the parentheses as a whole, not the last character. -[cut] - ## Example In the example below the pattern `pattern:(go)+` finds one or more `match:'go'`: diff --git a/5-regular-expressions/10-regexp-backreferences/article.md b/5-regular-expressions/10-regexp-backreferences/article.md index f47515d7..a5d25107 100644 --- a/5-regular-expressions/10-regexp-backreferences/article.md +++ b/5-regular-expressions/10-regexp-backreferences/article.md @@ -2,8 +2,6 @@ Capturing groups may be accessed not only in the result, but in the replacement string, and in the pattern too. -[cut] - ## Group in replacement: $n When we are using `replace` method, we can access n-th group in the replacement string using `$n`. diff --git a/5-regular-expressions/11-regexp-alternation/article.md b/5-regular-expressions/11-regexp-alternation/article.md index c071be70..a01eac0f 100644 --- a/5-regular-expressions/11-regexp-alternation/article.md +++ b/5-regular-expressions/11-regexp-alternation/article.md @@ -4,8 +4,6 @@ Alternation is the term in regular expression that is actually a simple "OR". In a regular expression it is denoted with a vertical line character `pattern:|`. -[cut] - For instance, we need to find programming languages: HTML, PHP, Java or JavaScript. The corresponding regexp: `pattern:html|php|java(script)?`. diff --git a/5-regular-expressions/12-regexp-anchors/article.md b/5-regular-expressions/12-regexp-anchors/article.md index b4ae67cf..b4981e09 100644 --- a/5-regular-expressions/12-regexp-anchors/article.md +++ b/5-regular-expressions/12-regexp-anchors/article.md @@ -2,14 +2,12 @@ The caret `pattern:'^'` and dollar `pattern:'$'` characters have special meaning in a regexp. They are called "anchors". -[cut] - The caret `pattern:^` matches at the beginning of the text, and the dollar `pattern:$` -- in the end. For instance, let's test if the text starts with `Mary`: ```js run -let str1 = 'Mary had a little lamb, it's fleece was white as snow'; +let str1 = "Mary had a little lamb, it's fleece was white as snow"; let str2 = 'Everywhere Mary went, the lamp was sure to go'; alert( /^Mary/.test(str1) ); // true diff --git a/5-regular-expressions/13-regexp-multiline-mode/article.md b/5-regular-expressions/13-regexp-multiline-mode/article.md index fe95ca50..1b5623dc 100644 --- a/5-regular-expressions/13-regexp-multiline-mode/article.md +++ b/5-regular-expressions/13-regexp-multiline-mode/article.md @@ -2,8 +2,6 @@ The multiline mode is enabled by the flag `pattern:/.../m`. -[cut] - It only affects the behavior of `pattern:^` and `pattern:$`. In the multiline mode they match not only at the beginning and end of the string, but also at start/end of line. diff --git a/5-regular-expressions/15-regexp-infinite-backtracking-problem/article.md b/5-regular-expressions/15-regexp-infinite-backtracking-problem/article.md index bb4c7d6f..cdb5f40e 100644 --- a/5-regular-expressions/15-regexp-infinite-backtracking-problem/article.md +++ b/5-regular-expressions/15-regexp-infinite-backtracking-problem/article.md @@ -2,16 +2,14 @@ Some regular expressions are looking simple, but can execute veeeeeery long time, and even "hang" the JavaScript engine. -Sooner or later all developers occasionally meets this behavior. +Sooner or later most developers occasionally face such behavior. -The typical situation -- a regular expression works fine for some time, and then starts to "hang" the script and make it consume 100% of CPU. +The typical situation -- a regular expression works fine sometimes, but for certain strings it "hangs" consuming 100% of CPU. -That may even be a vulnerability. For instance, if JavaScript is on the server and uses regular expressions on user data. There were many vulnerabilities of that kind even in widely distributed systems. +That may even be a vulnerability. For instance, if JavaScript is on the server, and it uses regular expressions to process user data, then such an input may cause denial of service. The author personally saw and reported such vulnerabilities even for well-known and widely used programs. So the problem is definitely worth to deal with. -[cut] - ## Example The plan will be like this: @@ -42,7 +40,7 @@ In the regexp language that is: `pattern:<\w+(\s*\w+=(\w+|"[^"]*")\s*)*>`: 1. `pattern:<\w+` -- is the tag start, 2. `pattern:(\s*\w+=(\w+|"[^"]*")\s*)*` -- is an arbitrary number of pairs `word=value`, where the value can be either a word `pattern:\w+` or a quoted string `pattern:"[^"]*"`. -That doesn't yet support the details of HTML grammer, for instance strings can be in 'single' quotes, but these can be added later, so that's somewhat close to real life. For now we want the regexp to be simple. +That doesn't yet support few details of HTML grammar, for instance strings in 'single' quotes, but they can be added later, so that's somewhat close to real life. For now we want the regexp to be simple. Let's try it in action: @@ -58,7 +56,7 @@ Great, it works! It found both the long tag `match:<a test="<>" href="#">` and t Now let's see the problem. -If you run the example below, it may hang the browser (or another JavaScript engine): +If you run the example below, it may hang the browser (or whatever JavaScript engine runs): ```js run let reg = /<\w+(\s*\w+=(\w+|"[^"]*")\s*)*>/g; @@ -76,7 +74,9 @@ Some regexp engines can handle that search, but most of them don't. What's the matter? Why a simple regular expression on such a small string "hangs"? -Let's simplify the situation by removing the tag and quoted strings, we'll look only for attributes: +Let's simplify the situation by removing the tag and quoted strings. + +Here we look only for attributes: ```js run // only search for space-delimited attributes @@ -91,15 +91,15 @@ alert( str.match(reg) ); */!* ``` -The same. +The same problem persists. -Here we end the demo of the problem and start looking into what's going on. +Here we end the demo of the problem and start looking into what's going on and why it hangs. ## Backtracking To make an example even simpler, let's consider `pattern:(\d+)*$`. -In most regexp engines that search takes a very long time (careful -- can hang): +This regular expression also has the same problem. In most regexp engines that search takes a very long time (careful -- can hang): ```js run alert( '12345678901234567890123456789123456789z'.match(/(\d+)*$/) ); @@ -107,9 +107,9 @@ alert( '12345678901234567890123456789123456789z'.match(/(\d+)*$/) ); So what's wrong with the regexp? -Actually, it looks a little bit strange. The quantifier `pattern:*` looks extraneous. If we want a number, we can use `pattern:\d+$`. +First, one may notice that the regexp is a little bit strange. The quantifier `pattern:*` looks extraneous. If we want a number, we can use `pattern:\d+$`. -Yes, the regexp is artificial, but the reason why it is slow is the same as those we saw above. So let's understand it. +Indeed, the regexp is artificial. But the reason why it is slow is the same as those we saw above. So let's understand it, and then return to the real-life examples. What happen during the search of `pattern:(\d+)*$` in the line `subject:123456789z`? @@ -119,7 +119,7 @@ What happen during the search of `pattern:(\d+)*$` in the line `subject:12345678 \d+....... (123456789)z ``` -2. Then it tries to apply the start around the parentheses `pattern:(\d+)*`, but there are no more digits, so it the star doesn't give anything. +2. Then it tries to apply the star around the parentheses `pattern:(\d+)*`, but there are no more digits, so it the star doesn't give anything. Then the pattern has the string end anchor `pattern:$`, and in the text we have `subject:z`. diff --git a/6-async/01-callbacks/article.md b/6-async/01-callbacks/article.md index 60ed5e83..0cc13215 100644 --- a/6-async/01-callbacks/article.md +++ b/6-async/01-callbacks/article.md @@ -2,7 +2,7 @@ # Introduction: callbacks -Many actions in Javascript are *asynchronous*. +Many actions in JavaScript are *asynchronous*. For instance, take a look at the function `loadScript(src)`: @@ -23,7 +23,7 @@ We can use it like this: loadScript('/my/script.js'); ``` -The function is called "asynchronous", because the action (script loading) finishes not now, but later. +The function is called "asynchronously", because the action (script loading) finishes not now, but later. The call initiates the script loading, then the execution continues. While the script is loading, the code below may finish executing, and if the loading takes time, other scripts may run meanwhile too. @@ -35,7 +35,7 @@ loadScript('/my/script.js'); Now let's say we want to use the new script when it loads. It probably declares new functions, so we'd like to run them. -...But if we do that immediately after the `loadScript(…)` call, that wouldn't work: +But if we do that immediately after the `loadScript(…)` call, that wouldn't work: ```js loadScript('/my/script.js'); // the script has "function newFunction() {…}" @@ -45,7 +45,7 @@ newFunction(); // no such function! */!* ``` -Naturally, the browser probably didn't have time to load the script. So the immediate call to the new function fails. As of now, `loadScript` function doesn't provide a way to track the load completion. The script loads and eventually runs, that's all. But we'd like to know when happens, to use new functions and variables from that script. +Naturally, the browser probably didn't have time to load the script. So the immediate call to the new function fails. As of now, `loadScript` function doesn't provide a way to track the load completion. The script loads and eventually runs, that's all. But we'd like to know when it happens, to use new functions and variables from that script. Let's add a `callback` function as a second argument to `loadScript` that should execute when the script loads: @@ -118,7 +118,7 @@ loadScript('/my/script.js', function(script) { After the outer `loadScript` is complete, the callback initiates the inner one. -...What if we want one more script? +What if we want one more script...? ```js loadScript('/my/script.js', function(script) { @@ -183,7 +183,7 @@ So the single `callback` function is used both for reporting errors and passing From the first look it's a viable way of asynchronous coding. And indeed it is. For one or maybe two nested calls it looks fine. -But for multiple asynchronous actions that follow one after another we'll have a code like this: +But for multiple asynchronous actions that follow one after another we'll have code like this: ```js loadScript('1.js', function(error, script) { @@ -264,8 +264,8 @@ See? It does the same, and there's no deep nesting now, because we made every ac It works, but the code looks like a torn apart spreadsheet. It's difficult to read, you probably noticed that. One needs to eye-jump between pieces while reading it. That's inconvenient, especially the reader is not familiar with the code and doesn't know where to eye-jump. -Also the functions named `step*` are all of a single use, they are created only to evade the "pyramid of doom". No one is going to reuse them outside of the action chain. So there's a bit of a namespace cluttering here. +Also the functions named `step*` are all of a single use, they are created only to avoid the "pyramid of doom". No one is going to reuse them outside of the action chain. So there's a bit of a namespace cluttering here. We'd like to have a something better. -Luckily, there are other ways to evade such pyramids. One of the best ways is to use "promises", described in the next chapter. +Luckily, there are other ways to avoid such pyramids. One of the best ways is to use "promises", described in the next chapter. diff --git a/6-async/02-promise-basics/article.md b/6-async/02-promise-basics/article.md index bae64b37..d7bdb036 100644 --- a/6-async/02-promise-basics/article.md +++ b/6-async/02-promise-basics/article.md @@ -1,18 +1,18 @@ # Promise -Imagine that you're a top singer, and fans ask for your next upcoming single day and night. +Imagine that you're a top singer, and fans ask day and night for your upcoming single. -To get a relief, you promise to send it to them when it's published. You give your fans a list. They can fill in their coordinates, so that when the song becomes available, all subscribed parties instantly get it. And if something goes very wrong, so that the song won't be published ever, then they are also to be notified. +To get some relief, you promise to send it to them when it's published. You give your fans a list to which they can subscribe for updates. They can fill in their email addresses, so that when the song becomes available, all subscribed parties instantly receive it. And even if something goes very wrong, say, if plans to publish the song are cancelled, they will still be notified. Everyone is happy: you, because the people don't crowd you any more, and fans, because they won't miss the single. -That was a real-life analogy for things we often have in programming: +This is a real-life analogy for things we often have in programming: -1. A "producing code" that does something and needs time. For instance, it loads a remote script. That's a "singer". -2. A "consuming code" wants the result when it's ready. Many functions may need that result. These are "fans". -3. A *promise* is a special JavaScript object that links them together. That's a "list". The producing code creates it and gives to everyone, so that they can subscribe for the result. +1. A "producing code" that does something and takes time. For instance, the code loads a remote script. That's a "singer". +2. A "consuming code" that wants the result of the "producing code" once it's ready. Many functions may need that result. These are the "fans". +3. A *promise* is a special JavaScript object that links the "producing code" and the "consuming code" together. In terms of our analogy: this is the "subscription list". The "producing code" takes whatever time it needs to produce the promised result, and the "promise" makes that result available to all of the subscribed code when it's ready. -The analogy isn't very accurate, because JavaScript promises are more complex than a simple list: they have additional features and limitations. But still they are alike. +The analogy isn't terribly accurate, because JavaScript promises are more complex than a simple subscription list: they have additional features and limitations. But it's fine to begin with. The constructor syntax for a promise object is: @@ -22,33 +22,32 @@ let promise = new Promise(function(resolve, reject) { }); ``` -The function passed to `new Promise` is called *executor*. When the promise is created, it's called automatically. It contains the producing code, that should eventually finish with a result. In terms of the analogy above, the executor is a "singer". +The function passed to `new Promise` is called the *executor*. When the promise is created, this executor function runs automatically. It contains the producing code, that should eventually produce a result. In terms of the analogy above: the executor is the "singer". The resulting `promise` object has internal properties: -- `state` -- initially is "pending", then changes to "fulfilled" or "rejected", -- `result` -- an arbitrary value, initially `undefined`. +- `state` — initially "pending", then changes to either "fulfilled" or "rejected", +- `result` — an arbitrary value of your choosing, initially `undefined`. -When the executor finishes the job, it should call one of: +When the executor finishes the job, it should call one of the functions that it gets as arguments: -- `resolve(value)` -- to indicate that the job finished successfully: +- `resolve(value)` — to indicate that the job finished successfully: - sets `state` to `"fulfilled"`, - sets `result` to `value`. -- `reject(error)` -- to indicate that an error occurred: +- `reject(error)` — to indicate that an error occurred: - sets `state` to `"rejected"`, - sets `result` to `error`. ![](promise-resolve-reject.png) -Here's a simple executor, to gather that all together: +Later we'll see how these changes become known to "fans". + +Here's an example of a Promise constructor and a simple executor function with its "producing code" (the `setTimeout`): ```js run let promise = new Promise(function(resolve, reject) { // the function is executed automatically when the promise is constructed - alert(resolve); // function () { [native code] } - alert(reject); // function () { [native code] } - // after 1 second signal that the job is done with the result "done!" setTimeout(() => *!*resolve("done!")*/!*, 1000); }); @@ -56,16 +55,16 @@ let promise = new Promise(function(resolve, reject) { We can see two things by running the code above: -1. The executor is called automatically and immediately (by `new Promise`). -2. The executor receives two arguments: `resolve` and `reject` -- these functions come from JavaScript engine. We don't need to create them. Instead the executor should call them when ready. +1. The executor is called automatically and immediately (by the `new Promise`). +2. The executor receives two arguments: `resolve` and `reject` — these functions are pre-defined by the JavaScript engine. So we don't need to create them. Instead, we should write the executor to call them when ready. -After one second of thinking the executor calls `resolve("done")` to produce the result: +After one second of "processing" the executor calls `resolve("done")` to produce the result: ![](promise-resolve-1.png) -That was an example of the "successful job completion". +That was an example of a successful job completion, a "fulfilled promise". -And now an example where the executor rejects promise with an error: +And now an example of the executor rejecting the promise with an error: ```js let promise = new Promise(function(resolve, reject) { @@ -76,12 +75,12 @@ let promise = new Promise(function(resolve, reject) { ![](promise-reject-1.png) -To summarize, the executor should do a job (something that takes time usually) and then call `resolve` or `reject` to change the state of the corresponding promise object. +To summarize, the executor should do a job (something that takes time usually) and then call `resolve` or `reject` to change the state of the corresponding Promise object. -The promise that is either resolved or rejected is called "settled", as opposed to a "pending" promise. +The Promise that is either resolved or rejected is called "settled", as opposed to a "pending" Promise. -````smart header="There can be only one result or an error" -The executor should call only one `resolve` or `reject`. The promise state change is final. +````smart header="There can be only a single result or an error" +The executor should call only one `resolve` or `reject`. The promise's state change is final. All further calls of `resolve` and `reject` are ignored: @@ -94,48 +93,58 @@ let promise = new Promise(function(resolve, reject) { }); ``` -The idea is that a job done by the executor may have only one result or an error. In programming, there exist other data structures that allow many "flowing" results, for instance streams and queues. They have their own advantages and disadvantages versus promises. They are not supported by JavaScript core and lack certain language features that promises provide, we don't cover them here to concentrate on promises. +The idea is that a job done by the executor may have only one result or an error. -Also if we call `resolve/reject` with more then one argument -- only the first argument is used, the next ones are ignored. +Further, `resolve`/`reject` expect only one argument and will ignore additional arguments. ```` ```smart header="Reject with `Error` objects" -Technically we can call `reject` (just like `resolve`) with any type of argument. But it's recommended to use `Error` objects in `reject` (or inherit from them). The reasoning for that will become obvious soon. +In case if something goes wrong, we can call `reject` with any type of argument (just like `resolve`). But it is recommended to use `Error` objects (or objects that inherit from `Error`). The reasoning for that will soon become apparent. ``` -````smart header="Resolve/reject can be immediate" -In practice an executor usually does something asynchronously and calls `resolve/reject` after some time, but it doesn't have to. We can call `resolve` or `reject` immediately, like this: +````smart header="Immediately calling `resolve`/`reject`" +In practice, an executor usually does something asynchronously and calls `resolve`/`reject` after some time, but it doesn't have to. We also can call `resolve` or `reject` immediately, like this: ```js let promise = new Promise(function(resolve, reject) { + // not taking our time to do the job resolve(123); // immediately give the result: 123 }); ``` -For instance, it happens when we start to do a job and then see that everything has already been done. Technically that's fine: we have a resolved promise right now. +For instance, this might happen when we start to do a job but then see that everything has already been completed. + +That's fine. We immediately have a resolved Promise, nothing wrong with that. ```` ```smart header="The `state` and `result` are internal" -Properties `state` and `result` of a promise object are internal. We can't directly access them from our code, but we can use methods `.then/catch` for that, they are described below. +The properties `state` and `result` of the Promise object are internal. We can't directly access them from our "consuming code". We can use the methods `.then`/`.catch` for that. They are described below. ``` -## Consumers: ".then" and ".catch" - -A promise object serves as a link between the producing code (executor) and the consuming functions -- those that want to receive the result/error. Consuming functions can be registered using methods `promise.then` and `promise.catch`. +## Consumers: "then" and "catch" +A Promise object serves as a link between the executor (the "producing code" or "singer) and the consuming functions (the "fans"), which will receive the result or error. Consuming functions can be registered (subscribed) using the methods `.then` and `.catch`. The syntax of `.then` is: ```js promise.then( - function(result) { /* handle a successful result */ }, - function(error) { /* handle an error */ } + function(result) { *!*/* handle a successful result */*/!* }, + function(error) { *!*/* handle an error */*/!* } ); ``` -The first function argument runs when the promise is resolved and gets the result, and the second one -- when it's rejected and gets the error. +The first argument of `.then` is a function that: -For instance: +1. runs when the Promise is resolved, and +2. receives the result. + +The second argument of `.then` is a function that: + +1. runs when the Promise is rejected, and +2. receives the error. + +For instance, here's the reaction to a successfuly resolved promise: ```js run let promise = new Promise(function(resolve, reject) { @@ -151,7 +160,9 @@ promise.then( ); ``` -In case of a rejection: +The first function was executed. + +And in the case of a rejection -- the second one: ```js run let promise = new Promise(function(resolve, reject) { @@ -167,7 +178,7 @@ promise.then( ); ``` -If we're interested only in successful completions, then we can provide only one argument to `.then`: +If we're interested only in successful completions, then we can provide only one function argument to `.then`: ```js run let promise = new Promise(resolve => { @@ -179,7 +190,7 @@ promise.then(alert); // shows "done!" after 1 second */!* ``` -If we're interested only in errors, then we can use `.then(null, function)` or an "alias" to it: `.catch(function)` +If we're interested only in errors, then we can use `null` as the first argument: `.then(null, errorHandlingFunction)`. Or we can use `.catch(errorHandlingFunction)`, which is exactly the same: ```js run @@ -205,35 +216,44 @@ let promise = new Promise(resolve => resolve("done!")); promise.then(alert); // done! (shows up right now) ``` -That's handy for jobs that may sometimes require time and sometimes finish immediately. The handler is guaranteed to run in both cases. +Some tasks may sometimes require time and sometimes finish immediately. The good thing is: the `.then` handler is guaranteed to run in both cases. ```` -````smart header="Handlers of `.then/catch` are always asynchronous" -To be even more precise, when `.then/catch` handler should execute, it first gets into an internal queue. The JavaScript engine takes handlers from the queue and executes when the current code finishes, similar to `setTimeout(..., 0)`. +````smart header="Handlers of `.then`/`.catch` are always asynchronous" +Even when the Promise is immediately resolved, code which occurs on lines *below* your `.then`/`.catch` may still execute first. -In other words, when `.then(handler)` is going to trigger, it does something like `setTimeout(handler, 0)` instead. +The JavaScript engine has an internal execution queue which gets all `.then/catch` handlers. -In the example below the promise is immediately resolved, so `.then(alert)` triggers right now: the `alert` call is queued and runs immediately after the code finishes. +But it only looks into that queue when the current execution is finished. + +In other words, `.then/catch` handlers are pending execution until the engine is done with the current code. + +For instance, here: ```js run -// an immediately resolved promise -let promise = new Promise(resolve => resolve("done!")); +// an "immediately" resolved Promise +const executor = resolve => resolve("done!"); +const promise = new Promise(executor); -promise.then(alert); // done! (right after the current code finishes) +promise.then(alert); // this alert shows last (*) alert("code finished"); // this alert shows first ``` -So the code after `.then` always executes before the handler (even in the case of a pre-resolved promise). Usually that's unimportant, in some scenarios may matter. +The promise becomes settled immediately, but the engine first finishes the current code, calls `alert`, and only *afterwards* looks into the queue to run `.then` handler. + +So the code *after* `.then` ends up always running *before* the Promise's subscribers, even in the case of an immediately-resolved Promise. + +Usually that's unimportant, but in some scenarios the order may matter a great deal. ```` -Now let's see more practical examples how promises can help us in writing asynchronous code. +Next, let's see more practical examples of how promises can help us to write asynchronous code. ## Example: loadScript We've got the `loadScript` function for loading a script from the previous chapter. -Here's the callback-based variant, just to remind it: +Here's the callback-based variant, just to remind us of it: ```js function loadScript(src, callback) { @@ -247,9 +267,9 @@ function loadScript(src, callback) { } ``` -Let's rewrite it using promises. +Let's rewrite it using Promises. -The new function `loadScript` will not require a callback. Instead it will create and return a promise object that settles when the loading is complete. The outer code can add handlers to it using `.then`: +The new function `loadScript` will not require a callback. Instead, it will create and return a Promise object that resolves when the loading is complete. The outer code can add handlers (subscribing functions) to it using `.then`: ```js run function loadScript(src) { @@ -278,13 +298,13 @@ promise.then( promise.then(script => alert('One more handler to do something else!')); ``` -We can immediately see few benefits over the callback-based syntax: +We can immediately see a few benefits over the callback-based pattern: ```compare minus="Callbacks" plus="Promises" - We must have a ready `callback` function when calling `loadScript`. In other words, we must know what to do with the result *before* `loadScript` is called. - There can be only one callback. -+ Promises allow us to code things in the natural order. First we run `loadScript`, and `.then` write what to do with the result. -+ We can call `.then` on a promise as many times as we want, at any time later. ++ Promises allow us to do things in the natural order. First, we run `loadScript`, and `.then` we write what to do with the result. ++ We can call `.then` on a Promise as many times as we want. Each time, we're adding a new "fan", a new subscribing function, to the "subscription list". More about this in the next section: [Promise Chaining](/promise-chaining). ``` -So promises already give us better code flow and flexibility. But there's more. We'll see that in the next chapters. +So Promises already give us better code flow and flexibility. But there's more. We'll see that in the next chapters. diff --git a/6-async/03-promise-chaining/02-error-async/task.md b/6-async/03-promise-chaining/02-error-async/task.md index f3fdf62b..bafc47ce 100644 --- a/6-async/03-promise-chaining/02-error-async/task.md +++ b/6-async/03-promise-chaining/02-error-async/task.md @@ -1,6 +1,6 @@ # Error in setTimeout -How do you think, does the `.catch` trigger? Explain your answer? +What do you think? Will the `.catch` trigger? Explain your answer. ```js new Promise(function(resolve, reject) { diff --git a/6-async/03-promise-chaining/article.md b/6-async/03-promise-chaining/article.md index 41acc387..3df3b0da 100644 --- a/6-async/03-promise-chaining/article.md +++ b/6-async/03-promise-chaining/article.md @@ -1,15 +1,13 @@ # Promises chaining -Let's return to the problem mentioned in the chapter <info:callbacks>: +Let's return to the problem mentioned in the chapter <info:callbacks>. - We have a sequence of asynchronous tasks to be done one after another. For instance, loading scripts. - How to code it well? Promises provide a couple of recipes to do that. -[cut] - In this chapter we cover promise chaining. It looks like this: @@ -49,7 +47,7 @@ As the result is passed along the chain of handlers, we can see a sequence of `a ![](promise-then-chain.png) -The whole thing works, because a call to `promise.then` returns a promise, so that we can call next `.then` on it. +The whole thing works, because a call to `promise.then` returns a promise, so that we can call the next `.then` on it. When a handler returns a value, it becomes the result of that promise, so the next `.then` is called with it. @@ -142,7 +140,7 @@ new Promise(function(resolve, reject) { Here the first `.then` shows `1` returns `new Promise(…)` in the line `(*)`. After one second it resolves, and the result (the argument of `resolve`, here it's `result*2`) is passed on to handler of the second `.then` in the line `(**)`. It shows `2` and does the same thing. -So the output is again 1 -> 2 > 4, but now with 1 second delay between `alert` calls. +So the output is again 1 -> 2 -> 4, but now with 1 second delay between `alert` calls. Returning promises allows us to build chains of asynchronous actions. @@ -365,7 +363,7 @@ loadJson('/article/promise-chaining/user.json') ## Error handling -Asynchronous actions may sometimes fail: in case of an error the corresponding promises becomes rejected. For instance, `fetch` fails if the remote server is not available. We can use `.catch` to handle errors (rejections). +Asynchronous actions may sometimes fail: in case of an error the corresponding promise becomes rejected. For instance, `fetch` fails if the remote server is not available. We can use `.catch` to handle errors (rejections). Promise chaining is great at that aspect. When a promise rejects, the control jumps to the closest rejection handler down the chain. That's very convenient in practice. @@ -617,12 +615,13 @@ What happens when an error is not handled? For instance, after the rethrow as in ```js untrusted run refresh new Promise(function() { noSuchFunction(); // Error here (no such function) -}); +}); // no .catch attached ``` Or here: ```js untrusted run refresh +// a chain of promises without .catch at the end new Promise(function() { throw new Error("Whoops!"); }).then(function() { @@ -634,9 +633,9 @@ new Promise(function() { }); ``` -In theory, nothing should happen. In case of an error happens, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is no such handler in the examples above. So the error gets "stuck". +In case of an error, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is no such handler in the examples above. So the error gets "stuck". -In practice, that means that the code is bad. Indeed, how come that there's no error handling? +In practice, that's usually because of the bad code. Indeed, how come that there's no error handling? Most JavaScript engines track such situations and generate a global error in that case. We can see it in the console. diff --git a/6-async/04-promise-api/01-promise-errors-as-results/source.view/index.html b/6-async/04-promise-api/01-promise-errors-as-results/source.view/index.html index 241b09ad..c760ad8c 100644 --- a/6-async/04-promise-api/01-promise-errors-as-results/source.view/index.html +++ b/6-async/04-promise-api/01-promise-errors-as-results/source.view/index.html @@ -21,7 +21,7 @@ alert(responses[0].status); // 200 alert(responses[1].status); // 200 alert(responses[2]); // TypeError: failed to fetch (text may vary) - )); + }); </script> </body> diff --git a/6-async/04-promise-api/01-promise-errors-as-results/task.md b/6-async/04-promise-api/01-promise-errors-as-results/task.md index 4d53cbc1..d6904f1b 100644 --- a/6-async/04-promise-api/01-promise-errors-as-results/task.md +++ b/6-async/04-promise-api/01-promise-errors-as-results/task.md @@ -17,7 +17,7 @@ Promise.all(urls.map(url => fetch(url))) for(let response of responses) { alert(`${response.url}: ${response.status}`); } - )); + }); ``` The problem is that if any of requests fails, then `Promise.all` rejects with the error, and we loose results of all the other requests. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..60f06924 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,14 @@ + +# Attribution-NonCommercial-ShareAlike 4.0 + +The full license text is at <https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode>. + +You are free to: +- **Share** – copy and redistribute the tutorial in any medium or material. +- **Adapt** – remix, transform, and build upon the material. + +Under the following terms: + +- **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. +- **NonCommercial** — You may not use the material for commercial purposes. +- **ShareAlike** — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. diff --git a/README.md b/README.md index 3f0565f5..c6f4a062 100755 --- a/README.md +++ b/README.md @@ -1,13 +1,25 @@ - # The JavaScript Tutorial This repository hosts the content of the Modern JavaScript Tutorial, published at [https://javascript.info](https://javascript.info). -Other languages: -- Russian: [https://github.com/iliakan/javascript-tutorial-ru](https://github.com/iliakan/javascript-tutorial-ru). -- Chinese: the ongoing translation at [https://github.com/iliakan/javascript-tutorial-cn](https://github.com/iliakan/javascript-tutorial-cn), go ahead and join if you know Chinese. +## Translations -Please use this repository to file issues and suggest PRs for the text. +(In alphabetical order): + +| Language | Github | Translation leads | Translated (%) | Published | +|----------|--------|-------------------|-----------------|-----------| +| Chinese | https://github.com/xitu/javascript-tutorial-zh | @leviding | ![](http://translate-hook.javascript.info/stats/zh.svg) | - | +| Danish | https://github.com/ockley/javascript-tutorial-da | @ockey | ![](http://translate-hook.javascript.info/stats/da.svg) | - | +| French | https://github.com/SugoiNelson/javascript-tutorial-fr | @SugoiNelson | ![](http://translate-hook.javascript.info/stats/fr.svg) | - | +| German | https://github.com/MartinEls/javascript-tutorial-de | @MartilEls | ![](http://translate-hook.javascript.info/stats/de.svg) | - | +| Japanese | https://github.com/KenjiI/javascript-tutorial-ja | @KenjiI | ![](http://translate-hook.javascript.info/stats/ja.svg) | - | +| Russian | https://github.com/iliakan/javascript-tutorial-ru | @iliakan | | https://learn.javascript.ru | +| Turkish | https://github.com/sahinyanlik/javascript-tutorial-tr | @sahinyanlik | ![](http://translate-hook.javascript.info/stats/tr.svg) | - | +| Uzbek | https://github.com/aruzikulov/javascript-tutorial-uz | @aruzikulov | ![](http://translate-hook.javascript.info/stats/uz.svg) | - | + +If you'd like to translate it into your language, please clone the repository, change its name to `javascript-tutorial-...` (by the language) and [create an issue](https://github.com/iliakan/javascript-tutoria-en/issues/new) for me to add you to the list. + +You can edit the text in any editor (markdown-like syntax). The server to run the tutorial locally and see how it looks is at <https://github.com/iliakan/javascript-tutorial-server>. ## Structure @@ -22,5 +34,3 @@ The type of the material is defined by the file inside the folder: - `task.md` stands for a task (solution must be provided in `solution.md` file aswell) Each of these files starts from the `# Main header`. - -Assets required for the material reside in the same folder. diff --git a/TRANSLATION.md b/TRANSLATION.md new file mode 100644 index 00000000..bde4be18 --- /dev/null +++ b/TRANSLATION.md @@ -0,0 +1,13 @@ + +# The JavaScript Tutorial + +This repository hosts the translation of the [Modern JavaScript Tutorial](https://javascript.info). + +The full list of translations and leads is at <https://github.com/iliakan/javascript-tutorial-en>. + +Please contact the translation lead for any questions. +Contact @iliakan there's no answer or the translation appears to be stalled. + +If there are other translators, notify them (create an issue?) when you're taking a chapter. + + diff --git a/archive/01-hello-world/article.md b/archive/01-hello-world/article.md index bfab9428..b08909eb 100644 --- a/archive/01-hello-world/article.md +++ b/archive/01-hello-world/article.md @@ -6,8 +6,6 @@ But we need something like a "base environment" to run our scripts, and browser So we'll start with attaching a script to the webpage. For other environments like Node.JS there are other ways to run it. -[cut] - [todo remove defer/async from here and move to 2nd part?] ## The "script" tag diff --git a/archive/2-events-and-timing-depth/article.md b/archive/2-events-and-timing-depth/article.md index b56243f0..3254326f 100644 --- a/archive/2-events-and-timing-depth/article.md +++ b/archive/2-events-and-timing-depth/article.md @@ -4,8 +4,6 @@ Здесь мы разберём, как браузер обычно работает с одновременно возникающими событиями и какие есть исключения из общего правила. -[cut] - ## Главный поток В каждом окне выполняется только один *главный* поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM. diff --git a/archive/5-drag-and-drop-objects/article.md b/archive/5-drag-and-drop-objects/article.md index 74c58d1d..3b8f3313 100644 --- a/archive/5-drag-and-drop-objects/article.md +++ b/archive/5-drag-and-drop-objects/article.md @@ -8,8 +8,6 @@ Этот материал не строго обязателен для изучения, он специфичен именно для Drag'n'Drop. -[cut] - ## Документ Как пример задачи -- возьмём документ с иконками браузера ("объекты переноса"), которые можно переносить в компьютер ("цель переноса"): diff --git a/archive/recursion.md b/archive/recursion.md index 0af749b5..4b142bd4 100644 --- a/archive/recursion.md +++ b/archive/recursion.md @@ -8,8 +8,6 @@ In particular, a function can make a sub-call *itself*. That's called *a recursi We'll fiddle around that advanced case to better learn how functions work. -[cut] - [smart header="A topic you already know?"] Recursion is a general programming term. If you are already familiar with it, then you may list the page to the next chapter. Please read on if you are new to functions or just prefer not to skip parts. [/smart] diff --git a/contributors.md b/contributors.md deleted file mode 100644 index c6b26c99..00000000 --- a/contributors.md +++ /dev/null @@ -1,13 +0,0 @@ - -The file lists people who made significant contributions to the project: - -<ul> -<li>Alexey Maximov @amaxcz (admin)</li> -<li>Alexey Shisterov (tutorial)</li> -<li>Anton Vernogor @smmurf (markup)</li> -<li>Artem Beztsenny @bezart (design)</li> -<li>Ilya Kantor @iliakan (tutorial, code)</li> -<li>Yuri Tkachenko @tyv (markup)</li> -</ul> - -The project exists for a long time, I might have missed someone. If you expect to find yourself in the list, but you're not -- please mail me at mk@javascript.ru. diff --git a/figures.sketch b/figures.sketch index ac92dd46..b6abb538 100644 Binary files a/figures.sketch and b/figures.sketch differ