diff --git a/1-js/02-first-steps/08-operators/article.md b/1-js/02-first-steps/08-operators/article.md index a4958b878..498870e42 100644 --- a/1-js/02-first-steps/08-operators/article.md +++ b/1-js/02-first-steps/08-operators/article.md @@ -56,17 +56,21 @@ alert( 8 % 3 ); // 2, a remainder of 8 divided by 3 ### Exponentiation ** -The exponentiation operator `a ** b` multiplies `a` by itself `b` times. +The exponentiation operator `a ** b` raises `a` to the power of `b`. + +In school maths, we write that as ab. For instance: ```js run -alert( 2 ** 2 ); // 4 (2 multiplied by itself 2 times) -alert( 2 ** 3 ); // 8 (2 * 2 * 2, 3 times) -alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2, 4 times) +alert( 2 ** 2 ); // 2² = 4 +alert( 2 ** 3 ); // 2³ = 8 +alert( 2 ** 4 ); // 2⁴ = 16 ``` -Mathematically, the exponentiation is defined for non-integer numbers as well. For example, a square root is an exponentiation by `1/2`: +Just like in maths, the exponentiation operator is defined for non-integer numbers as well. + +For example, a square root is an exponentiation by ½: ```js run alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root) diff --git a/1-js/02-first-steps/13-while-for/article.md b/1-js/02-first-steps/13-while-for/article.md index 56a59850a..2579b647d 100644 --- a/1-js/02-first-steps/13-while-for/article.md +++ b/1-js/02-first-steps/13-while-for/article.md @@ -368,9 +368,18 @@ break label; // jump to the label below (doesn't work) label: for (...) ``` -A call to `continue` is only possible from inside the loop. +A `break` directive must be inside a code block. Technically, any labelled code block will do, e.g.: +```js +label: { + // ... + break label; // works + // ... +} +``` + +...Although, 99.9% of the time `break` used is inside loops, as we've seen in the examples above. -The `break` directive may be placed before code blocks too, as `label: { ... }`, but it's almost never used like that. And it also works only inside-out. +A `continue` is only possible from inside a loop. ```` ## Summary diff --git a/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md b/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md index c8ee9618f..e48502642 100644 --- a/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md +++ b/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md @@ -14,4 +14,4 @@ function checkAge(age) { } ``` -Note that the parentheses around `age > 18` are not required here. They exist for better readabilty. +Note that the parentheses around `age > 18` are not required here. They exist for better readability. diff --git a/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md index 3ea112473..041db18bc 100644 --- a/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md +++ b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md @@ -1,7 +1,7 @@ ```js run function ask(question, yes, no) { - if (confirm(question)) yes() + if (confirm(question)) yes(); else no(); } diff --git a/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md index 2f44db27e..e18c08a83 100644 --- a/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md +++ b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md @@ -5,7 +5,7 @@ Replace Function Expressions with arrow functions in the code below: ```js run function ask(question, yes, no) { - if (confirm(question)) yes() + if (confirm(question)) yes(); else no(); } diff --git a/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md b/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md index 6a4c20baf..e2147ccfa 100644 --- a/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md +++ b/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md @@ -25,7 +25,7 @@ messages.shift(); // now readMessages has 1 element (technically memory may be cleaned later) ``` -The `WeakSet` allows to store a set of messages and easily check for the existance of a message in it. +The `WeakSet` allows to store a set of messages and easily check for the existence of a message in it. It cleans up itself automatically. The tradeoff is that we can't iterate over it, can't get "all read messages" from it directly. But we can do it by iterating over all messages and filtering those that are in the set. diff --git a/1-js/05-data-types/11-date/article.md b/1-js/05-data-types/11-date/article.md index a3117806e..ed4e21359 100644 --- a/1-js/05-data-types/11-date/article.md +++ b/1-js/05-data-types/11-date/article.md @@ -348,7 +348,7 @@ let time1 = 0; let time2 = 0; *!* -// run bench(upperSlice) and bench(upperLoop) each 10 times alternating +// run bench(diffSubtract) and bench(diffGetTime) each 10 times alternating for (let i = 0; i < 10; i++) { time1 += bench(diffSubtract); time2 += bench(diffGetTime); diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md index 3dbefb521..9d99aa717 100644 --- a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md +++ b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md @@ -88,11 +88,11 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco Here `let j = i` declares an "iteration-local" variable `j` and copies `i` into it. Primitives are copied "by value", so we actually get an independent copy of `i`, belonging to the current loop iteration. - The shooters work correctly, because the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds the current loop iteration: + The shooters work correctly, because the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds to the current loop iteration: ![](lexenv-makearmy-while-fixed.svg) - Such problem could also be avoided if we used `for` in the beginning, like this: + Such a problem could also be avoided if we used `for` in the beginning, like this: ```js run demo function makeArmy() { diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md index 346e4060a..b16b35290 100644 --- a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md @@ -27,7 +27,7 @@ The code above demonstrates it. function func() { *!* // the local variable x is known to the engine from the beginning of the function, - // but "unitialized" (unusable) until let ("dead zone") + // but "uninitialized" (unusable) until let ("dead zone") // hence the error */!* diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index 3a6a7e4e5..199887063 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -146,7 +146,7 @@ Despite being simple, slightly modified variants of that code have practical use How does this work? If we create multiple counters, will they be independent? What's going on with the variables here? -Undestanding such things is great for the overall knowledge of JavaScript and beneficial for more complex scenarios. So let's go a bit in-depth. +Understanding such things is great for the overall knowledge of JavaScript and beneficial for more complex scenarios. So let's go a bit in-depth. ## Lexical Environment diff --git a/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md index 981da23cd..e97039f72 100644 --- a/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md +++ b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md @@ -52,4 +52,4 @@ function f(b) { } ``` -This `f` will be used in the next call, again return itself, so many times as needed. Then, when used as a number or a string -- the `toString` returns the `currentSum`. We could also use `Symbol.toPrimitive` or `valueOf` here for the conversion. +This `f` will be used in the next call, again return itself, as many times as needed. Then, when used as a number or a string -- the `toString` returns the `currentSum`. We could also use `Symbol.toPrimitive` or `valueOf` here for the conversion. diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md index cf851f771..6950664be 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md @@ -12,11 +12,10 @@ function throttle(func, ms) { savedThis = this; return; } + isThrottled = true; func.apply(this, arguments); // (1) - isThrottled = true; - setTimeout(function() { isThrottled = false; // (3) if (savedArgs) { diff --git a/1-js/09-classes/01-class/article.md b/1-js/09-classes/01-class/article.md index cf428ac11..8d4731e1b 100644 --- a/1-js/09-classes/01-class/article.md +++ b/1-js/09-classes/01-class/article.md @@ -25,7 +25,7 @@ class MyClass { } ``` -Then `new MyClass()` creates a new object with all the listed methods. +Then use `new MyClass()` to create a new object with all the listed methods. The `constructor()` method is called automatically by `new`, so we can initialize the object there. @@ -53,7 +53,7 @@ When `new User("John")` is called: 1. A new object is created. 2. The `constructor` runs with the given argument and assigns it to `this.name`. -...Then we can call methods, such as `user.sayHi`. +...Then we can call object methods, such as `user.sayHi()`. ```warn header="No comma between class methods" @@ -64,7 +64,7 @@ The notation here is not to be confused with object literals. Within the class, ## What is a class? -So, what exactly is a `class`? That's not an entirely new language-level entity, as one might think. +So, what exactly is a `class`? That's not an entirely new language-level entity, as one might think. Let's unveil any magic and see what a class really is. That'll help in understanding many complex aspects. @@ -85,11 +85,9 @@ alert(typeof User); // function ``` What `class User {...}` construct really does is: -1. Creates a function named `User`, that becomes the result of the class declaration. - - The function code is taken from the `constructor` method (assumed empty if we don't write such method). -3. Stores all methods, such as `sayHi`, in `User.prototype`. -Afterwards, for new objects, when we call a method, it's taken from the prototype, just as described in the chapter . So `new User` object has access to class methods. +1. Creates a function named `User`, that becomes the result of the class declaration. The function code is taken from the `constructor` method (assumed empty if we don't write such method). +2. Stores class methods, such as `sayHi`, in `User.prototype`. After `new User` object is created, when we call its method, it's taken from the prototype, just as described in the chapter . So the object has access to class methods. @@ -99,7 +97,6 @@ We can illustrate the result of `class User` declaration as: Here's the code to introspect it: - ```js run class User { constructor(name) { this.name = name; } @@ -113,7 +110,7 @@ alert(typeof User); // function alert(User === User.prototype.constructor); // true // The methods are in User.prototype, e.g: -alert(User.prototype.sayHi); // alert(this.name); +alert(User.prototype.sayHi); // the code of the sayHi method // there are exactly two methods in the prototype alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi @@ -171,16 +168,15 @@ Still, there are important differences. ``` There are other differences, we'll see them soon. -2. Class methods are non-enumerable +2. Class methods are non-enumerable. A class definition sets `enumerable` flag to `false` for all methods in the `"prototype"`. That's good, because if we `for..in` over an object, we usually don't want its class methods. -3. Classes always `use strict` +3. Classes always `use strict`. All code inside the class construct is automatically in strict mode. - -Also, in addition to its basic operation, the `class` syntax brings many other features with it which we'll explore later. +Besides, `class` syntax brings many other features that we'll explore later. ## Class Expression @@ -196,21 +192,22 @@ let User = class { }; ``` -Similar to Named Function Expressions, class expressions may or may not have a name. +Similar to Named Function Expressions, class expressions may have a name. If a class expression has a name, it's visible inside the class only: ```js run -// "Named Class Expression" (alas, no such term, but that's what's going on) +// "Named Class Expression" +// (no such term in the spec, but that's similar to Named Function Expression) let User = class *!*MyClass*/!* { sayHi() { - alert(MyClass); // MyClass is visible only inside the class + alert(MyClass); // MyClass name is visible only inside the class } }; new User().sayHi(); // works, shows MyClass definition -alert(MyClass); // error, MyClass not visible outside of the class +alert(MyClass); // error, MyClass name isn't visible outside of the class ``` We can even make classes dynamically "on-demand", like this: @@ -243,7 +240,7 @@ class User { constructor(name) { // invokes the setter - this._name = name; + this.name = name; } *!* @@ -277,10 +274,11 @@ Technically, such class declaration works by creating getters and setters in `Us Here's an example with a computed method name using brackets `[...]`: ```js run -function f() { return "sayHi"; } - class User { - [f()]() { + +*!* + ['say' + 'Hi']() { +*/!* alert("Hello"); } @@ -405,29 +403,11 @@ That's especially useful in browser environment, for event listeners. ## Summary -JavaScript provides many ways to create a class. - -First, as per the general object-oriented terminology, a class is something that provides "object templates", allows to create same-structured objects. - -When we say "a class", that doesn't necessary means the `class` keyword. - -This is a class: - -```js -function User(name) { - this.sayHi = function() { - alert(name); - } -} -``` - -...But in most cases `class` keyword is used, as it provides great syntax and many additional features. - The basic class syntax looks like this: ```js class MyClass { - prop = value; // field + prop = value; // property constructor(...) { // constructor // ... @@ -438,7 +418,7 @@ class MyClass { get something(...) {} // getter method set something(...) {} // setter method - [Symbol.iterator]() {} // method with computed name/symbol name + [Symbol.iterator]() {} // method with computed name (symbol here) // ... } ``` diff --git a/1-js/11-async/08-async-await/article.md b/1-js/11-async/08-async-await/article.md index bde7c72ea..de965d48d 100644 --- a/1-js/11-async/08-async-await/article.md +++ b/1-js/11-async/08-async-await/article.md @@ -69,10 +69,10 @@ The function execution "pauses" at the line `(*)` and resumes when the promise s Let's emphasize: `await` literally suspends the function execution until the promise settles, and then resumes it with the promise result. That doesn't cost any CPU resources, because the JavaScript engine can do other jobs in the meantime: execute other scripts, handle events, etc. -It's just a more elegant syntax of getting the promise result than `promise.then`, easier to read and write. +It's just a more elegant syntax of getting the promise result than `promise.then`. And, it's easier to read and write. ````warn header="Can't use `await` in regular functions" -If we try to use `await` in non-async function, there would be a syntax error: +If we try to use `await` in a non-async function, there would be a syntax error: ```js run function f() { @@ -83,7 +83,7 @@ function f() { } ``` -We may get this error if we forget to put `async` before a function. As said, `await` only works inside an `async` function. +We may get this error if we forget to put `async` before a function. As stated earlier, `await` only works inside an `async` function. ```` Let's take the `showAvatar()` example from the chapter and rewrite it using `async/await`: @@ -186,7 +186,7 @@ class Waiter { new Waiter() .wait() - .then(alert); // 1 + .then(alert); // 1 (this is the same as (result => alert(result))) ``` The meaning is the same: it ensures that the returned value is a promise and enables `await`. 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 6b85168b9..3d1f6698f 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 @@ -1,9 +1,9 @@ The HTML in the task is incorrect. That's the reason of the odd thing. -The browser has to fix it automatically. But there may be no text inside the ``: according to the spec only table-specific tags are allowed. So the browser adds `"aaa"` *before* the `
`. +The browser has to fix it automatically. But there may be no text inside the `
`: according to the spec only table-specific tags are allowed. So the browser shows `"aaa"` *before* the `
`. Now it's obvious that when we remove the table, it remains. -The question can be easily answered by exploring the DOM using the browser tools. It shows `"aaa"` before the `
`. +The question can be easily answered by exploring the DOM using the browser tools. You'll see `"aaa"` before the `
`. The HTML standard specifies in detail how to process bad HTML, and such behavior of the browser is correct. diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md index 6cb1152c1..49ab88be2 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md @@ -124,7 +124,7 @@ Let's update our algorithm: ```js // onmousemove - // ball has position:absoute + // ball has position:absolute ball.style.left = event.pageX - *!*shiftX*/!* + 'px'; ball.style.top = event.pageY - *!*shiftY*/!* + 'px'; ``` diff --git a/2-ui/99-ui-misc/02-selection-range/article.md b/2-ui/99-ui-misc/02-selection-range/article.md index b53d41778..6284e02e3 100644 --- a/2-ui/99-ui-misc/02-selection-range/article.md +++ b/2-ui/99-ui-misc/02-selection-range/article.md @@ -26,18 +26,45 @@ let range = new Range(); Then we can set the selection boundaries using `range.setStart(node, offset)` and `range.setEnd(node, offset)`. -The first argument `node` can be either a text node or an element node. The meaning of the second argument depends on that: +As you might guess, further we'll use the `Range` objects for selection, but first let's create few such objects. -- If `node` is a text node, then `offset` must be the position in the text. -- If `node` is an element node, then `offset` must be the child number. +### Selecting the text partially -For example, let's create a range in this fragment: +The interesting thing is that the first argument `node` in both methods can be either a text node or an element node, and the meaning of the second argument depends on that. + +**If `node` is a text node, then `offset` must be the position in its text.** + +For example, given the element `

Hello

`, we can create the range containing the letters "ll" as follows: + +```html run +

Hello

+ +``` + +Here we take the first child of `

` (that's the text node) and specify the text positions inside it: + +![](range-hello-1.svg) + +### Selecting element nodes + +**Alternatively, if `node` is an element node, then `offset` must be the child number.** + +That's handy for making ranges that contain nodes as a whole, not stop somewhere inside their text. + +For example, we have a more complex document fragment: ```html autorun

Example: italic and bold

``` -Here's its DOM structure: +Here's its DOM structure with both element and text nodes:
@@ -77,14 +104,18 @@ drawHtmlTree(selectPDomtree, 'div.select-p-domtree', 690, 320); Let's make a range for `"Example: italic"`. -As we can see, this phrase consists of exactly the first and the second children of `

`: +As we can see, this phrase consists of exactly two children of `

`, with indexes `0` and `1`: ![](range-example-p-0-1.svg) - The starting point has `

` as the parent `node`, and `0` as the offset. + + So we can set it as `range.setStart(p, 0)`. - The ending point also has `

` as the parent `node`, but `2` as the offset (it specifies the range up to, but not including `offset`). -Here's the demo, if you run it, you can see that the text gets selected: + So we can set it as `range.setEnd(p, 2)`. + +Here's the demo. If you run it, you can see that the text gets selected: ```html run

Example: italic and bold

@@ -100,12 +131,12 @@ Here's the demo, if you run it, you can see that the text gets selected: // toString of a range returns its content as text, without tags console.log(range); // Example: italic - // let's apply this range for document selection (explained later) + // apply this range for document selection (explained later below) document.getSelection().addRange(range); ``` -Here's a more flexible test stand where you try more variants: +Here's a more flexible test stand where you can set range start/end numbers and explore other variants: ```html run autorun

Example: italic and bold

@@ -121,26 +152,28 @@ From – To ` first child (taking all but two first letters of "Example: ") @@ -162,7 +195,13 @@ We need to create a range, that: ``` -The range object has following properties: +As you can see, it's fairly easy to make a range of whatever we want. + +If we'd like to take nodes as a whole, we can pass elements in `setStart/setEnd`. Otherwise, we can work on the text level. + +## Range properties + +The range object that we created in the example above has following properties: ![](range-example-p-2-b-3-range.svg) @@ -175,10 +214,13 @@ The range object has following properties: - `commonAncestorContainer` -- the nearest common ancestor of all nodes within the range, - in the example above: `

` -## Range methods + +## Range selection methods There are many convenience methods to manipulate ranges. +We've already seen `setStart` and `setEnd`, here are other similar methods. + Set range start: - `setStart(node, offset)` set start at: position `offset` in `node` @@ -191,15 +233,19 @@ Set range end (similar methods): - `setEndBefore(node)` set end at: right before `node` - `setEndAfter(node)` set end at: right after `node` -**As it was demonstrated, `node` can be both a text or element node: for text nodes `offset` skips that many of characters, while for element nodes that many child nodes.** +Technically, `setStart/setEnd` can do anything, but more methods provide more convenience. -Others: +In all these methods, `node` can be both a text or element node: for text nodes `offset` skips that many of characters, while for element nodes that many child nodes. + +Even more methods to create ranges: - `selectNode(node)` set range to select the whole `node` - `selectNodeContents(node)` set range to select the whole `node` contents - `collapse(toStart)` if `toStart=true` set end=start, otherwise set start=end, thus collapsing the range - `cloneRange()` creates a new range with the same start/end -To manipulate the content within the range: +## Range editing methods + +Once the range is created, we can manipulate its content using these methods: - `deleteContents()` -- remove range content from the document - `extractContents()` -- remove range content from the document and return as [DocumentFragment](info:modifying-document#document-fragment) @@ -271,7 +317,9 @@ There also exist methods to compare ranges, but these are rarely used. When you ## Selection -`Range` is a generic object for managing selection ranges. We may create `Range` objects, pass them around -- they do not visually select anything on their own. +`Range` is a generic object for managing selection ranges. Although, creating a `Range` doesn't mean that we see a selection on screen. + +We may create `Range` objects, pass them around -- they do not visually select anything on their own. The document selection is represented by `Selection` object, that can be obtained as `window.getSelection()` or `document.getSelection()`. A selection may include zero or more ranges. At least, the [Selection API specification](https://www.w3.org/TR/selection-api/) says so. In practice though, only Firefox allows to select multiple ranges in the document by using `key:Ctrl+click` (`key:Cmd+click` for Mac). @@ -281,9 +329,19 @@ Here's a screenshot of a selection with 3 ranges, made in Firefox: Other browsers support at maximum 1 range. As we'll see, some of `Selection` methods imply that there may be many ranges, but again, in all browsers except Firefox, there's at maximum 1. +Here's a small demo that shows the current selection (select something and click) as text: + + + ## Selection properties -Similar to a range, a selection has a start, called "anchor", and the end, called "focus". +As said, a selection may in theory contain multiple ranges. We can get these range objects using the method: + +- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except Firefox, only `0` is used. + +Also, there exist properties that often provide better convenience. + +Similar to a range, a selection object has a start, called "anchor", and the end, called "focus". The main selection properties are: @@ -294,36 +352,39 @@ The main selection properties are: - `isCollapsed` -- `true` if selection selects nothing (empty range), or doesn't exist. - `rangeCount` -- count of ranges in the selection, maximum `1` in all browsers except Firefox. -````smart header="Usually, the selection end `focusNode` is after its start `anchorNode`, but it's not always the case" -There are many ways to select the content, depending on the user agent: mouse, hotkeys, taps on a mobile etc. +```smart header="Selection end/start vs Range" + +There's an important differences of a selection anchor/focus compared with a `Range` start/end. + +As we know, `Range` objects always have their start before the end. -Some of them, such as a mouse, allow the same selection can be created in two directions: "left-to-right" and "right-to-left". +For selections, that's not always the case. -If the start (anchor) of the selection goes in the document before the end (focus), this selection is said to have "forward" direction. +Selecting something with a mouse can be done in both directions: either "left-to-right" or "right-to-left". + +In other words, when the mouse button is pressed, and then it moves forward in the document, then its end (focus) will be after its start (anchor). E.g. if the user starts selecting with mouse and goes from "Example" to "italic": ![](selection-direction-forward.svg) -Otherwise, if they go from the end of "italic" to "Example", the selection is directed "backward", its focus will be before the anchor: +...But the same selection could be done backwards: starting from "italic" to "Example" (backward direction), then its end (focus) will be before the start (anchor): ![](selection-direction-backward.svg) - -That's different from `Range` objects that are always directed forward: the range start can't be after its end. -```` +``` ## Selection events There are events on to keep track of selection: -- `elem.onselectstart` -- when a selection starts on `elem`, e.g. the user starts moving mouse with pressed button. - - Preventing the default action makes the selection not start. -- `document.onselectionchange` -- whenever a selection changes. - - Please note: this handler can be set only on `document`. +- `elem.onselectstart` -- when a selection *starts* on speficially elemen `elem` (or inside it). For instance, when the user presses the mouse button on it and starts to move the pointer. + - Preventing the default action cancels the selection start. So starting a selection from this element becomes impossible, but the element is still selectable. The visitor just needs to start the selection from elsewhere. +- `document.onselectionchange` -- whenever a selection changes or starts. + - Please note: this handler can be set only on `document`, it tracks all selections in it. ### Selection tracking demo -Here's a small demo that shows selection boundaries dynamically as it changes: +Here's a small demo. It tracks the current selection on the `document` and shows its boundaries: ```html run height=80

Select me: italic and bold

@@ -331,21 +392,25 @@ Here's a small demo that shows selection boundaries dynamically as it changes: From – To ``` -### Selection getting demo +### Selection copying demo + +There are two approaches to copying the selected content: -To get the whole selection: -- As text: just call `document.getSelection().toString()`. -- As DOM nodes: get the underlying ranges and call their `cloneContents()` method (only first range if we don't support Firefox multiselection). +1. We can use `document.getSelection().toString()` to get it as text. +2. Otherwise, to copy the full DOM, e.g. if we need to keep formatting, we can get the underlying ranges with `getRangesAt(...)`. A `Range` object, in turn, has `cloneContents()` method that clones its content and returns as `DocumentFragment` object, that we can insert elsewhere. -And here's the demo of getting the selection both as text and as DOM nodes: +Here's the demo of copying the selected content both as text and as DOM nodes: ```html run height=100

Select me: italic and bold

@@ -373,15 +438,15 @@ As text: ## Selection methods -Selection methods to add/remove ranges: +We can work with the selection by addding/removing ranges: -- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except firefox, only `0` is used. +- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except Firefox, only `0` is used. - `addRange(range)` -- add `range` to selection. All browsers except Firefox ignore the call, if the selection already has an associated range. - `removeRange(range)` -- remove `range` from the selection. - `removeAllRanges()` -- remove all ranges. - `empty()` -- alias to `removeAllRanges`. -Also, there are convenience methods to manipulate the selection range directly, without `Range`: +There are also convenience methods to manipulate the selection range directly, without intermediate `Range` calls: - `collapse(node, offset)` -- replace selected range with a new one that starts and ends at the given `node`, at position `offset`. - `setPosition(node, offset)` -- alias to `collapse`. @@ -393,7 +458,7 @@ Also, there are convenience methods to manipulate the selection range directly, - `deleteFromDocument()` -- remove selected content from the document. - `containsNode(node, allowPartialContainment = false)` -- checks whether the selection contains `node` (partially if the second argument is `true`) -So, for many tasks we can call `Selection` methods, no need to access the underlying `Range` object. +For most tasks these methods are just fine, there's no need to access the underlying `Range` object. For example, selecting the whole contents of the paragraph `

`: @@ -420,10 +485,10 @@ The same thing using ranges: ``` -```smart header="To select, remove the existing selection first" -If the selection already exists, empty it first with `removeAllRanges()`. And then add ranges. Otherwise, all browsers except Firefox ignore new ranges. +```smart header="To select something, remove the existing selection first" +If a document selection already exists, empty it first with `removeAllRanges()`. And then add ranges. Otherwise, all browsers except Firefox ignore new ranges. -The exception is some selection methods, that replace the existing selection, like `setBaseAndExtent`. +The exception is some selection methods, that replace the existing selection, such as `setBaseAndExtent`. ``` ## Selection in form controls diff --git a/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg b/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg new file mode 100644 index 000000000..90054cef1 --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg @@ -0,0 +1 @@ +<p>Hello</p>p.firstChild \ No newline at end of file diff --git a/8-web-components/2-custom-elements/article.md b/8-web-components/2-custom-elements/article.md index 13af80b8c..a84ed1192 100644 --- a/8-web-components/2-custom-elements/article.md +++ b/8-web-components/2-custom-elements/article.md @@ -365,7 +365,7 @@ Our new button extends the built-in one. So it keeps the same styles and standar ## References - HTML Living Standard: . -- Compatiblity: . +- Compatiblity: . ## Summary @@ -397,4 +397,4 @@ Custom elements can be of two types: /*