Merge remote-tracking branch 'refs/remotes/iliakan/master'

This commit is contained in:
reigningmetal 2017-06-12 11:32:17 -04:00
commit 2aebe4bce8
17 changed files with 154 additions and 128 deletions

View file

@ -28,9 +28,9 @@ Different engines have different "codenames", for example:
- [Gecko](https://en.wikipedia.org/wiki/Gecko_(software)) -- in Firefox.
- ...There are other codenames like "Trident", "Chakra" for different versions of IE, "ChakraCore" for Microsoft Edge, "Nitro" and "SquirrelFish" for Safari etc.
These terms above are good to remember, because they are used in developer articles in 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.
The terms above are good to remember, because they are used in developer articles in 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 the engines work?"
```smart header="How engines work?"
Engines are complicated. But the basics are easy.
@ -38,7 +38,7 @@ Engines are complicated. But the basics are easy.
2. The engine (embedded if it's a browser) reads the script ("parses") and converts ("compiles") it to the machine language.
3. And then it runs, pretty fast.
The engine applies optimizations on every stage of the process. It even watches the script as it runs, analyzes the data which flows through it and applies optimizations to the machine-code basing on that knowledge.
The engine applies optimizations on every stage of the process. It even watches the script as it runs, analyzes the data which flows through it and applies optimizations to the machine-code basing on that knowledge. That's why the code runs fast.
```
## What can in-browser JavaScript do?
@ -57,7 +57,7 @@ For instance, in-browser JavaScript is able to:
- Get and set cookies, ask questions to the visitor, show messages.
- Remember the data on the browser side ("local storage").
## What can in-browser JavaScript NOT do?
## What in-browser JavaScript can NOT do?
JavaScript abilities in the browser are limited for the sake of the user's safety. The aim is to prevent an evil webpage from accessing private information or harming the user's data.
@ -89,9 +89,9 @@ There are at least *three* great things about JavaScript:
+ Supported by all major browsers and enabled by default.
```
Combined, these 3 things only exist in JavaScript and no other browser technology.
Combined, these 3 things exist only in JavaScript and no other browser technology.
That's what makes JavaScript unique. That's why it is the most widespread way of creating browser interfaces.
That's what makes JavaScript unique. That's why it's the most widespread tool to create browser interfaces.
While planning to learn a new technology, it's beneficial to check its perspectives. So let's move on to the modern trends that include new languages and browser abilities.

View file

@ -10,14 +10,14 @@ There are two archetypes: IDE and lightweight editors. Many people feel comforta
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".
An IDE loads the project (can be many files), and then allows to navigate between files, provides autocompletion based on the whole project, integrates with version management system (like [git](https://git-scm.com/)), with testing environment and other "project-level" stuff.
An IDE loads the project (can be many files), and then allows navigation between files, provides autocompletion based on the whole project, integrates with version management system (like [git](https://git-scm.com/)), with testing environment and other "project-level" stuff.
If you haven't considered selecting an IDE, 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 other 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 it's lightweight free version [Komodo Edit](http://www.activestate.com/komodo-edit).
- [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.

View file

@ -37,7 +37,7 @@ For instance:
```
```online
You can run the example clicking on a "Play" button in it's right-top corner.
You can run the example by clicking on the "Play" button in its right-top corner.
```
The `<script>` tag contains JavaScript code which is automatically executed when the browser meets the tag.
@ -49,7 +49,7 @@ The `<script>` tag has a few attributes that are rarely used nowadays, but we ca
The `type` attribute: <code>&lt;script <u>type</u>=...&gt;</code>
: The old standard HTML4 required a script to have the type. Usually it was `type="text/javascript"`. The modern HTML standard assumes this `type` by default, no attribute is required.
: The old standard HTML4 required a script to have a type. Usually it was `type="text/javascript"`. The modern HTML standard assumes this `type` by default, no attribute is required.
The `language` attribute: <code>&lt;script <u>language</u>=...&gt;</code>
: This attribute was meant to show the language of the script. As of now, this attribute makes no sense, the language is JavaScript by default. No need to use it.
@ -95,7 +95,7 @@ To attach several scripts, use multiple tags:
```
```smart
As a rule, only simplest scripts are put into HTML. More complex ones reside in separate files.
As a rule, only the simplest scripts are put into HTML. More complex ones reside in separate files.
The benefit of a separate file is that the browser will download it and then store in its [cache](https://en.wikipedia.org/wiki/Web_cache).

View file

@ -114,7 +114,7 @@ alert('Hello');
alert('World'); // This comment follows the statement
```
**Multiline comments start with a forward slash and an asterisk <code>"/&#42;"</code> and end with an asterisk and a forward slash <code>"&#42;/"</code>.**
**Multiline comments start with a forward slash and an asterisk <code>/&#42;</code> and end with an asterisk and a forward slash <code>&#42;/</code>.**
Like this:

View file

@ -45,7 +45,6 @@ alert("some code");
Only comments may appear above `"use strict"`.
````
```smart header="`use strict` for functions"
We will learn functions (a way to group commands) soon.
@ -59,6 +58,7 @@ It is recommended to always start a script with `"use strict"`, for the followin
1. First, all modern browsers support it. Only outdated ones like Internet Explorer 9 and below do not.
2. Second, the modern JavaScript actually forces us into the strict mode. There are several modern language features like "classes" and "modules" that enable strict mode automatically. So, it's hard to evade it.
3. The last, but not the least: strict mode is the modern mode. Makes the language a little bit better in few aspects. We'll see that as we study more language features.
Here in the tutorial, all code (where not explicitly noted otherwise) works in `"use strict"`. We concentrate on modern JavaScript. But there will be notes about what happens without `"use strict"`, so that you can understand what's going on if you forget it or if you're working with an outdated script that doesn't have it.
@ -67,4 +67,4 @@ Here in the tutorial, all code (where not explicitly noted otherwise) works in `
- The `"use strict"` directive switches the engine to the "modern" mode, changing the behavior of some built-in features.
- Several modern features of the language enable `"use strict"` implicitly, so it's quite hard to evade it.
It's always recommended to start scripts with `"use strict"`. All examples in this book assume so, unless (very rarely) specified otherwise.
It's always recommended to start scripts with `"use strict"`. All examples in this tutorial assume so, unless (very rarely) specified otherwise.

View file

@ -176,9 +176,9 @@ alert( null >= 0 ); // (3) *!*true*/!*
Yeah, mathematically that's strange. The last result states that "`null` is equal or greater than zero". Then one of the comparisons above must be correct, but they are both falsy.
The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, hence treat it as `0`. That's why (1) `null >= 0` is true and (3) `null > 0` is false.
The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, hence treat it as `0`. That's why (3) `null >= 0` is true and (1) `null > 0` is false.
From the other hand, the equality check `==` for `undefined` and `null` works by the rule, without any conversions. They equal each other and don't equal anything else. That's why (2) `null == 0` is false.
On the other hand, the equality check `==` for `undefined` and `null` works by the rule, without any conversions. They equal each other and don't equal anything else. That's why (2) `null == 0` is false.
### An incomparable undefined
@ -192,7 +192,7 @@ alert( undefined == 0 ); // false (3)
Why does it dislike a zero so much? Always false!
We've got such result, because:
We've got these results because:
- Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN`. And `NaN` is a special numeric value which returns `false` for all comparisons.
- The equality check `(3)` returns `false`, because `undefined` only equals `null` and no other value.
@ -201,7 +201,7 @@ We've got such result, because:
Why did we observe these examples? Should we remember these pecularities all the time? Well, not really. Actually, these tricky things will gradually become familiar over the time, but there's a solid way to evade any problems with them.
Just treat any comparison with `undefined/null` except the strict equality `===` with an exceptional care.
Just treat any comparison with `undefined/null` except the strict equality `===` with exceptional care.
Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you are really sure what you're doing. If a variable can have such values, then check for them separately.

View file

@ -37,10 +37,10 @@ It is recommended to use figure brackets every time with `if`, even if there's o
The `if (…)` operator evaluates the expression in parentheses and converts it to the boolean type.
Let's recall the conversion rules:
Let's recall the conversion rules from the chapter <info:type-conversions>:
- A number `0`, an empty string `""`, `null`, `undefined` and `NaN` are `false`,
- Other values -- `true`.
- A number `0`, an empty string `""`, `null`, `undefined` and `NaN` become `false`. Because of that they are called "falsy" values.
- Other values become `true`, so they are called "truthy".
So, the code under this condition would never execute:

View file

@ -189,13 +189,11 @@ JavaScript engines apply many optimizations to make it run faster and not affect
Some of the optimizations:
- **Generational collection** -- objects are split into two sets: "new ones" and "old ones". Many objects appear, then do their job and die fast, so they can be cleaned up aggressively. Those that survive for long enough, become "old".
- **Incremental collection** -- if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine tries to split the job into pieces. Then pieces are executed one at a time. That requires some extra bookkeeping between them to track changes.
- **Generational collection** -- objects are split into two sets: "new ones" and "old ones". Many objects appear, do their job and die fast, they can be cleaned up aggressively. Those that survive for long enough, become "old" and are examined less often.
- **Incremental collection** -- if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine tries to split the garbage collection into pieces. Then the pieces are executed one by one, separately. That requires some extra bookkeeping between them to track changes, but we have many tiny delays instead of a big one.
- **Idle-time collection** -- the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution.
There are other optimizations and flavours of garbage collection algorithms. As much as I'd like to describe them here, I have to hold off, because different engines implement different tweaks and techniques.
And -- what's even more important, things change as engines develop, so going really deep "in advance", without a real need is probably not worth that. Unless, of course, it is a matter of pure interest, then there will be some links for you below.
There are other optimizations and flavours of garbage collection algorithms. As much as I'd like to describe them here, I have to hold off, because different engines implement different tweaks and techniques. And -- what's even more important, things change as engines develop, so going deeper "in advance", without a real need is probably not worth that. Unless, of course, it is a matter of pure interest, then there will be some links for you below.
## Summary
@ -209,8 +207,8 @@ Modern engines implement advanced algorithms of garbage collection.
A general book "The Garbage Collection Handbook: The Art of Automatic Memory Management" (R. Jones at al) covers some of them.
If you are familiar with low-level programming, the more detailed information about V8 garbage collector is in the article [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection).
If you are familiar with low-level programming, the more detailed information about V8 garbage collector is in the article [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection).
[V8 blog](http://v8project.blogspot.com/) also publishes articles about changes in memory management from time to time. Naturally, to learn the garbage collection, you'd better prepare by learning about V8 internals in general and read the blog of [Vyacheslav Egorov](http://mrale.ph) who worked as one of V8 engineers. I'm saying: "V8", because it is best covered with articles in the internet. For other engines, many approaches are similar, but garbage collection differs in many aspects.
[V8 blog](http://v8project.blogspot.com/) also publishes articles about changes in memory management from time to time. Naturally, to learn the garbage collection, you'd better prepare by learning about V8 internals in general and read the blog of [Vyacheslav Egorov](http://mrale.ph) who worked as one of V8 engineers. I'm saying: "V8", because it is best covered with articles in the internet. For other engines, many approaches are similar, but garbage collection differs in many aspects.
In-depth knowledge of engines is good when you need low-level optimizations. It would be wise to plan that as the next step after you're familiar with the language.

View file

@ -159,14 +159,14 @@ alert( clone[id] ); // 123
There's no paradox here. That's by design. The idea is that when we clone an object or merge objects, we usually want *all* properties to be copied (including symbols like `id`).
````smart header="Property keys of other types are coerced to strings"
We can only use strings or symbols as keys in objects. Other types are coerced to strings.
We can only use strings or symbols as keys in objects. Other types are converted to strings.
For instance:
For instance, a number `0` becomes a string `"0"` when used as a property key:
```js run
let obj = {
0: "test" // same as "0": "test"
}
};
// both alerts access the same property (the number 0 is converted to string "0")
alert( obj["0"] ); // test
@ -176,11 +176,11 @@ alert( obj[0] ); // test (same property)
## Global symbols
Normally, all symbols are different. But sometimes we want same-named symbols to be the same.
As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be same entities.
For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property.
To achieve that, there exists a *global symbol registry*. We can create symbols in it and and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol.
To achieve that, there exists a *global symbol registry*. We can create symbols in it and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol.
In order to create or read a symbol in the registry, use `Symbol.for(name)`.
@ -230,7 +230,9 @@ alert( Symbol.keyFor(Symbol.for("name")) ); // name, global symbol
alert( Symbol.keyFor(Symbol("name2")) ); // undefined, non-global symbol
```
For non-global symbols, the name is only used for debugging purposes.
So, for global symbols the name may be indeed helpful, as we can get a symbol by id.
And for non-global symbols the name is only used for debugging purposes, like printing out a symbol.
## System symbols

View file

@ -1,7 +1,7 @@
# Object.keys, values, entries
Let's step away from the indivitual data structures and talk about the iterations over them.
Let's step away from the individual data structures and talk about the iterations over them.
In the previous chapter we saw methods `map.keys()`, `map.values()`, `map.entries()`.
@ -63,8 +63,9 @@ for(let value of Object.values(user)) {
}
```
```smart header="`Object.keys/values/entries` ignore symbolic properties"
Just like `for..in` loop, these methods ignore properties that use `Symbol(...)` as keys.
## Object.keys/values/entries ignore symbolic properties
Just like a `for..in` loop, these methods ignore properties that use `Symbol(...)` as keys.
Usually that's convenient. But if we want symbolic keys too, then there's a separate method [Object.getOwnPropertySymbols](mdn:js/Object/getOwnPropertySymbols) that returns an array of only symbolic keys. Also, the method [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) returns *all* keys.
```

View file

@ -143,7 +143,7 @@ user.name = "Alice"; // Error
## Non-enumerable
Now let's a custom `toString` to `user`.
Now let's add a custom `toString` to `user`.
Normally, a built-in `toString` for objects is non-enumerable, it does not show up in `for..in`. But if we add `toString` of our own, then by default it shows up in `for..in`.

View file

@ -8,13 +8,11 @@ In this chapter we'll see more into what they are and their most used properties
## DOM node classes
DOM nodes have different properties depending on their class. For instance, element nodes corresponding to tag `<a>` have link-related properties, and those corresponding to `<input>` have input-related and so on.
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.
Text nodes are not the same as element nodes: they have properties of their own.
Each DOM node belongs to the corresponding built-in class.
But there are also common properties and methods between all of them, because of the inheritance. Each DOM node belongs to the corresponding built-in class. These classes form a hierarchy.
The root is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](http://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it.
The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](http://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it.
Here's the picture, explanations to follow:
@ -22,10 +20,10 @@ Here's the picture, explanations to follow:
The classes are:
- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) is the root "abstract" class. Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later.
- [Node](http://dom.spec.whatwg.org/#interface-node) is also an "abstract" class, serving as a base for DOM nodes. It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are concrete node classes that inherit from it, namely: `Text` for text nodes, `Element` for element nodes and more exotic ones like `Comment` for comment nodes.
- [Element](http://dom.spec.whatwg.org/#interface-element) is a base class for DOM elements. It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`. In the browser there may be not only HTML, but also XML and SVG documents. The `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` and `HTMLElement`.
- [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) is finally the basic class for all HTML elements. It is inherited by various HTML elements:
- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class. Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later.
- [Node](http://dom.spec.whatwg.org/#interface-node) -- is also an "abstract" class, serving as a base for DOM nodes. It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are concrete node classes that inherit from it, namely: `Text` for text nodes, `Element` for element nodes and more exotic ones like `Comment` for comment nodes.
- [Element](http://dom.spec.whatwg.org/#interface-element) -- is a base class for DOM elements. It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`. In the browser there may be not only HTML, but also XML and SVG documents. The `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` and `HTMLElement`.
- [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) -- is finally the basic class for all HTML elements. It is inherited by various HTML elements:
- [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- the class for `<input>` elements,
- [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- the class for `<body>` elements,
- [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- the class for `<a>` elements
@ -33,15 +31,16 @@ The classes are:
So, the full set of properties and methods of a given node comes as the result of the inheritance.
For instance, `<input>` element is of the [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) class:
- `HTMLInputElement` itself provides input-specific properties.
- It inherits the common HTML element methods (and getters/setters) from `HTMLElement` class.
- Then it supports common element methods, because `HTMLElement` inherits from `Element`.
- Then it supports common DOM node properties, because it inherits from `Node`.
- Finally, it supports events (to be covered), because it inherits from `EventTarget`.
- (and for completeness: `EventTarget` inherits from `Object`)
For example, let's consider the DOM object for an `<input>` element. It belongs to [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) class. It gets properties and methods as a superposition of:
To see the DOM node class name, we can remember that an object usually has the `constructor` property. It references to the class constructor, so the `constructor.name` is what we need:
- `HTMLInputElement` -- this class provides input-specific properties, and inherits from...
- `HTMLElement` -- it provides common HTML element methods (and getters/setters) and inherits from...
- `Element` -- provides generic element methods and inherits from...
- `Node` -- provides common DOM node properties and inherits from...
- `EventTarget` -- gives the support for events (to be covered),
- ...and finally it inherits from `Object`, so "pure object" methods like `hasOwnProperty` are also available.
To see the DOM node class name, we can recall that an object usually has the `constructor` property. It references to the class constructor, and `constructor.name` is its name:
```js run
alert( document.body.constructor.name ); // HTMLBodyElement
@ -53,7 +52,7 @@ alert( document.body.constructor.name ); // HTMLBodyElement
alert( document.body ); // [object HTMLBodyElement]
```
We also can use `instanceof` to check the inheritance chain:
We also can use `instanceof` to check the inheritance:
```js run
alert( document.body instanceof HTMLBodyElement ); // true
@ -63,7 +62,9 @@ alert( document.body instanceof Node ); // true
alert( document.body instanceof EventTarget ); // true
```
As we can see, DOM nodes are regular JavaScript objects. They use prototype-based classes for inheritance. That's easy to see by outputting an element with `console.dir(elem)`. There you can see `HTMLElement.prototype`, `Element.prototype` and so on.
As we can see, DOM nodes are regular JavaScript objects. They use prototype-based classes for inheritance.
That's also easy to see by outputting an element with `console.dir(elem)` in a browser. There in the console you can see `HTMLElement.prototype`, `Element.prototype` and so on.
```smart header="`console.dir(elem)` versus `console.log(elem)`"
Most browsers support two commands in their developer tools: `console.log` and `console.dir`. They output their arguments to the console. For JavaScript objects these commands usually do the same.
@ -79,19 +80,20 @@ 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.
The most important difference is that all properties are given with their types. For instance, `DOMString`, `boolean` and so on.
In IDL all properties are prepended with their types. For instance, `DOMString`, `boolean` and so on.
Here's an excerpt from it, with comments:
```js
// Define HTMLInputElement
*!*
// The colon means that it inherits from HTMLElement
// The colon ":" means that HTMLInputElement inherits from HTMLElement
*/!*
interface HTMLInputElement: HTMLElement {
// here go properties and methods of <input> elements
*!*
// "DOMString" means that the property is a string
// "DOMString" means that these properties are strings
*/!*
attribute DOMString accept;
attribute DOMString alt;
@ -158,14 +160,14 @@ alert( document.body.tagName ); // BODY
Is there any difference between tagName and nodeName?
Actually, yes, the difference is reflected in their names, but is indeed a bit subtle.
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.
So `tagName` can only be used for elements, while `nodeName` can say something about other node types.
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:
@ -193,7 +195,7 @@ The browser has two modes of processing documents: HTML and XML. Usually the HTM
In HTML mode `tagName/nodeName` is always uppercased: it's `BODY` either for `<body>` or `<BoDy>`.
In XML mode the case is kept "as is", but it's rarely used.
In XML mode the case is kept "as is". Nowadays XML mode is rarely used.
```
@ -287,7 +289,9 @@ Here's an example:
</script>
```
Unlike `innerHTML`, writing to `outerHTML` does not change the element. Instead, it replaces it as a whole in the outer context. Yeah, sounds strange, and strange it is. Take a look.
**Beware: unlike `innerHTML`, writing to `outerHTML` does not change the element. Instead, it replaces it as a whole in the outer context.**
Yeah, sounds strange, and strange it is, that's why we make a separate note about it here. Take a look.
Consider the example:

View file

@ -108,11 +108,11 @@ Sometimes `event.stopPropagation()` creates hidden pitfalls that later may becom
For instance:
1. We create a nested menu. Each submenu handles clicks on its elements and calls `stopPropagation` so that outer parts don't trigger.
2. Later we decide to catch clicks inside the whole window, to track users' behavior (where people click). Some counters do that. Usually a counter code does that by `document.addEventListener('click'…)`.
3. Our counter won't work over the area where clicks are stopped by `stopPropagation`. We've got a "dead zone".
1. We create a nested menu. Each submenu handles clicks on its elements and calls `stopPropagation` so that outer menu don't trigger.
2. Later we decide to catch clicks on the whole window, to track users' behavior (where people click). Some analytic systems do that. Usually the code uses `document.addEventListener('click'…)` to catch all clicks.
3. Our analytic won't work over the area where clicks are stopped by `stopPropagation`. We've got a "dead zone".
There's usually no real need to prevent the bubbling. One of them is to use custom events, we'll cover them later. Also we can write our data into the `event` object in one handler and read it in another one, so we can pass to handlers on parents information about the processing below.
There's usually no real need to prevent the bubbling. A task that seemingly requires that may be solved by other means. One of them is to use custom events, we'll cover them later. Also we can write our data into the `event` object in one handler and read it in another one, so we can pass to handlers on parents information about the processing below.
```
@ -138,14 +138,14 @@ Handlers added using `on<event>`-property or using HTML attributes or using `add
To catch an event on the capturing phase, we need to set the 3rd argument of `addEventListener` to `true`.
Actually, there are two possible values for that optional last argument:
There are two possible values for that optional last argument:
- If it's `false` (default), then the handler is set on the bubbling phase.
- If it's `true`, then the handler is set on the capturing phase.
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.
Handlers on the target element trigger last on the capturing state, and then trigger first on the bubbling stage.
If one puts a handler on the target element -- it triggers last on the capturing state, and first on the bubbling stage.
Let's see it in action:

View file

@ -238,7 +238,7 @@ Page lifecycle events:
- All scripts are executed except those that are external with `async` or `defer`
- Images and other resources may still continue loading.
- `load` event on `window` triggers when the page and all resources are loaded. We rarely use it, because there's usually no need to wait for so long.
- `beforeload` event on `window` triggers when the user wants to leave the page. If it returns a string, the browser shows a question whether the user really wants to leave or not.
- `beforeunload` event on `window` triggers when the user wants to leave the page. If it returns a string, the browser shows a question whether the user really wants to leave or not.
- `unload` event on `window` triggers when the user is finally leaving, in the handler we can only do simple things that do not involve delays or asking a user. Because of that limitation, it's rarely used.
- `document.readyState` is the current state of the document, changes can be tracked in the `readystatechange` event:
- `loading` -- the document is loading.

View file

@ -15,11 +15,11 @@ And if we increase it in `setInterval`, by making 50 small changes per second, t
The pseudo-code can look like this:
```js
let fps = 50; // 50 frames per second
let delay = 1000 / 50; // in 1 second 50 frames
let timer = setInterval(function() {
if (animation complete) clearInterval(timer);
else increase style.left
}, 1000 / fps)
}, delay)
```
More complete example of the animation:
@ -54,13 +54,13 @@ Click for the demo:
## requestAnimationFrame
Let's imagine we have several simultaneous animations.
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`.
Each `setInterval` triggers once per `20ms`, but they are independent, so we have several independent runs within `20ms`.
These several independent actions should be grouped together, because it's easier for the browser to redraw things once per `20ms`.
These several independent redraws should be grouped together, to make it easier for the browser.
In other words, this:
@ -80,11 +80,11 @@ setInterval(animate2, 20);
setInterval(animate3, 20);
```
There's one more thing to keep in mind. Sometimes when CPU is overloaded or for other reasons it may be better to trigger redraws less often. Not 20, but maybe 200ms.
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.
There's a standard [Animation timing](http://www.w3.org/TR/animation-timing/) that provides the function `requestAnimationFrame`.
It addresses all those issues and even more.
It addresses all these issues and even more.
The syntax:
```js

View file

@ -10,28 +10,30 @@ The idea is that if we have two windows open: one from `john-smith.com`, and ano
Two URLs are said to have the "same origin" if they have the same protocol, domain and port.
These URLs have the same origin:
These URLs all share the same origin:
- `http://site.com`
- `http://site.com/`
- `http://site.com/my/page.html`
These ones are not:
These ones do not:
- `http://www.site.com` (another domain: `www.` matters)
- `http://site.org` (another domain: `.org` matters)
- `https://site.com` (another protocol: `https`)
- `http://site.com:8080` (another port: `8080`)
- <code>http://<b>www.</b>site.com</code> (another domain: `www.` matters)
- <code>http://<b>site.org</b></code> (another domain: `.org` matters)
- <code><b>https://</b>site.com</code> (another protocol: `https`)
- <code>http://site.com:<b>8080</b></code> (another port: `8080`)
If we have a reference to another window (a popup or iframe), and that window comes from the same origin, then we can do everything with it.
Otherwise, we can only change its location. Please note: not *read*, but modify it, redirect it to another place. That's possible, because such action does not reveal any data. Also such windows windows may exchange messages. Soon about that later.
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.
````warn header="Exclusion: subdomains may be same-origin"
There's an important exclusion in the same-origin policy.
If windows share the same second-level domain, for instance `john.site.com`, `peter.site.com` and `site.com`, and assign to `document.domain` their common second-level domain `site.com`, then limitations are removed.
If windows share the same second-level domain, for instance `john.site.com`, `peter.site.com` and `site.com`, we can use JavaScript to assign to `document.domain` their common second-level domain `site.com`. Then these windows are treated as having the same origin.
In other words, all such documents (including the one from `site.com`) should have the code:
@ -40,49 +42,51 @@ document.domain = 'site.com';
```
Then they can interact without limitations.
That's only possible for pages with the same second-level domain.
````
## Managing iframes
## Accessing an iframe contents
An `<iframe>` is a two-faced beast. From one side it's a tag, just like `<script>` or `<img>`. From another side it's a window-in-window.
An `<iframe>` is a two-faced beast. From one side it's a tag, just like `<script>` or `<img>`. From the other side it's a window-in-window.
The embedded window has a separate `window` and `document` objects, scripts and so on.
The embedded window has a separate `document` and `window` objects.
We can access them:
We can access them like using the properties:
- `iframe.contentWindow` is a reference to the window inside the `<iframe>`.
- `iframe.contentDocument` is a reference to the document inside it.
- `iframe.contentDocument` is a reference to the document inside the `<iframe>`.
When we access an embedded window, the browser checks if the iframe has the same origin. If that's not so then the access to almost everything is denied.
When we access an embedded window, the browser checks if the iframe has the same origin. If that's not so then the access is denied (with exclusions noted above).
For instance:
For instance, here's an `<iframe>` from another origin:
```html run
<iframe src="https://example.com" id="iframe"></iframe>
<script>
iframe.onload = function() {
// can get the reference to the inner window
// we can get the reference to the inner window
let iframeWindow = iframe.contentWindow;
try {
// ...but not to the document inside it
let doc = iframe.contentDocument;
} catch(e) {
alert(e); // Security Error
alert(e); // Security Error (another origin)
}
// also can't read iframe.contentWindow.location:
// also we can't read the URL of the page in it
try {
alert(iframe.contentWindow.location);
} catch(e) {
alert(e); // Security Error
}
// ...but can modify it!
// ...but we can change it (and thus load something else into the iframe)!
iframe.contentWindow.location = '/'; // works
iframe.onload = null; // run this code only once
iframe.onload = null; // clear the handler, to run this code only once
};
</script>
```
@ -98,7 +102,7 @@ The `iframe.onload` event is actually the same as `iframe.contentWindow.onload`.
...But `iframe.onload` is always available, while `iframe.contentWindow.onload` needs the same origin.
```
And here's an example with the same origin:
And now an example with the same origin. We can do anything with the embedded window:
```html run
<iframe src="/" id="iframe"></iframe>
@ -111,7 +115,7 @@ And here's an example with the same origin:
</script>
```
### Wait until the iframe loads
### Please wait until the iframe loads
When an iframe is created, it immediately has a document. But that document is different from the one that finally loads into it!
@ -126,6 +130,7 @@ Here, look:
iframe.onload = function() {
let newDoc = iframe.contentDocument;
*!*
// the loaded document is not the same as initial!
alert(oldDoc == newDoc); // false
*/!*
};
@ -134,9 +139,9 @@ Here, look:
That's actually a well-known pitfall for novice developers. We shouldn't work with the document immediately, because that's the *wrong document*. If we set any event handlers on it, they will be ignored.
...But the `onload` event triggers when the whole iframe with all resources is loaded. What if we want to act sooner, on `DOMContentLoaded`?
...But the `onload` event triggers when the whole iframe with all resources is loaded. What if we want to act sooner, on `DOMContentLoaded` of the embedded document?
We can try to catch the moment when a new document appears, and then setup necessary handlers, like this:
That's not possible if the iframe comes from another origin. But for the same origin we can try to catch the moment when a new document appears, and then setup necessary handlers, like this:
```html run
<iframe src="/" id="iframe"></iframe>
@ -148,19 +153,19 @@ We can try to catch the moment when a new document appears, and then setup neces
let timer = setInterval(() => {
if (iframe.contentDocument == oldDoc) return;
// yeah, now set handlers and do whatever we want
clearInterval(timer);
// new document, let's set handlers
iframe.contentDocument.addEventListener('DOMContentLoaded', () => {
iframe.contentDocument.body.prepend('Hello, world!');
});
clearInterval(timer); // cancel setInterval, don't need it any more
}, 100);
</script>
```
Let me know in comments if you know a better solution here.
### window.frames
## window.frames
An alternative way to get a window object for `<iframe>` -- is to get it from the named collection `window.frames`:
@ -202,16 +207,18 @@ if (window == top) { // current window == window.top?
}
```
### The sandbox attribute
## The sandbox attribute
The `sandbox` attribute allows to run untrusted content inside an `<iframe>`. It "sandboxes" the iframe by treating it as coming from another origin and forbidding certain actions.
The `sandbox` attribute allows to forbid certain actions inside an `<iframe>`, to run an untrusted code. It "sandboxes" the iframe by treating it as coming from another origin and/or applying other limitations.
There are many restrictions. By default, for `<iframe sandbox src="...">` all of them are applied. But if we specify them in a value of the attribute, like this: `<iframe sandbox="allow-forms allow-popups">`, then they are lifted.
By default, for `<iframe sandbox src="...">` the "default set" of restrictions is applied to the iframe. But we can provide a space-separated list of "excluded" limitations as a value of the attribute, like this: `<iframe sandbox="allow-forms allow-popups">`. The listed limitations are not applied.
In other words, an empty `"sandbox"` attribute puts the strictest limitations possible, but we can put a space-delimited list of those that we want to lift:
In other words, an empty `"sandbox"` attribute puts the strictest limitations possible, but we can put a space-delimited list of those that we want to lift.
Here's a list of limitations:
`allow-same-origin`
: By default `"sandbox"` forces the browser to treat the `iframe` as coming from another origin, even if it's `src` points to the same site. This option removes that feature.
: By default `"sandbox"` forces the "different origin" policy for the iframe. In other words, it makes the browser to treat the `iframe` as coming from another origin, even if its `src` points to the same site. With all implied restrictions for scripts. This option removes that feature.
`allow-top-navigation`
: Allows the `iframe` to change `parent.location`.
@ -227,13 +234,15 @@ In other words, an empty `"sandbox"` attribute puts the strictest limitations po
See [the manual](mdn:/HTML/Element/iframe) for more.
The example below demonstrates a sandboxed iframe with some JavaScript and a form. Neither one works:
The example below demonstrates a sandboxed iframe with the default set of restrictions: `<iframe sandbox src="...">`. It has some JavaScript and a form.
Please note that nothing works. So the default set is really harsh:
[codetabs src="sandbox" height=140]
```smart
The purpose of the `"sandbox"` attribute is to add restrictions. It cannot remove them. In particular, it can't relax same-origin restrictions if the iframe comes from another origin.
The purpose of the `"sandbox"` attribute is only to *add more* restrictions. It cannot remove them. In particular, it can't relax same-origin restrictions if the iframe comes from another origin.
```
## Cross-window messaging
@ -249,18 +258,16 @@ The window that wants to send a message calls [postMessage](mdn:api/Window.postM
Arguments:
`data`
: The data to send. Can be any object, the data is cloned using the "structured cloning algorithm". IE supports only strings, so we can `JSON.stringify` complex objects.
: The data to send. Can be any object, the data is cloned using the "structured cloning algorithm". IE supports only strings, so we should `JSON.stringify` complex objects to support that browser.
`targetOrigin`
: Allow only a window from the given origin to get the message.
: Specifies the origin for the target window, so that only a window from the given origin will get the message.
The `targetOrigin` is a safety measure. If the target window comes from another origin, we can't read it's `location`. We can't be sure which site is open in it right now, the user could navigate away.
The `targetOrigin` is a safety measure. Remember, if the target window comes from another origin, we can't read it's `location`. So we can't be sure which site is open in the intended window right now: the user could navigate away.
Specifying `targetOrigin` ensures that the window only receives the data if it's still at that site. Good when the data is secure.
Specifying `targetOrigin` ensures that the window only receives the data if it's still at that site. Good when the data is sensitive.
If we don't want that check, we can set `targetOrigin` to `*`.
For instance:
For instance, here `win` will only receive the message if it has a document from the origin `http://example.com`:
```html no-beautify
<iframe src="http://example.com" name="example">
@ -269,18 +276,32 @@ For instance:
let win = window.frames.example;
win.postMessage("message", "http://example.com");
// win.postMessage("message", "*");
</script>
```
If we don't want that check, we can set `targetOrigin` to `*`.
```html no-beautify
<iframe src="http://example.com" name="example">
<script>
let win = window.frames.example;
*!*
win.postMessage("message", "*");
*/!*
</script>
```
### onmessage
To receive a message, the window should have a handler on the `message` event.
To receive a message, the target window should have a handler on the `message` event. It triggers when `postMessage` is called (and `targetOrigin` check is successful).
The event object has special properties:
`data`
: The data from `postMessage`
: The data from `postMessage`.
`origin`
: The origin of the sender, for instance `http://javascript.info`.
@ -315,23 +336,23 @@ There's totally no delay between `postMessage` and the `message` event. That hap
To call methods and access the content of another window, we should first have a reference to it.
For popups:
For popups we have two properties:
- `window.open` -- opens a new window and returns a reference to it,
- `window.opener` -- a reference to the opener window from a popup
For iframes:
- `window.frames` is a collection of nested window objects,
For iframes, we can access parent/children windows using:
- `window.frames` -- a collection of nested window objects,
- `window.parent`, `window.top` are the references to parent and top windows,
- `iframe.contentWindow` is the window inside an `<iframe>` tag.
Then if they come from the same origin (host, port, protocol), then windows can do whatever they want with each other.
If windows share the same origin (host, port, protocol), then windows can do whatever they want with each other.
Otherwise, only possible actions are:
- Change the location of another window (write-only access).
- Post a message to it.
Exclusions are:
- Windows share the same main domain: `a.site.com` and `b.site.com`. Then setting `document.domain='site.com'` in both of them puts them into "same origin".
- Windows that share the same second-level domain: `a.site.com` and `b.site.com`. Then setting `document.domain='site.com'` in both of them puts them into the "same origin" state.
- If an iframe has a `sandbox` attribute, it is forcefully put into the "different origin" state, unless the `allow-same-origin` is specified in the attribute value. That can be used to run untrusted code in iframes from the same site.
The `postMessage` interface allows two windows to talk with security checks:

View file

@ -223,7 +223,7 @@ That's sometimes called "callback hell" or "pyramid of doom".
![](callback-hell.png)
The "pyramid" of nested calls grows to the right with every asynchronous action. Soon it spirales out of control.
The "pyramid" of nested calls grows to the right with every asynchronous action. Soon it spirals out of control.
So this way of coding isn't very good.