` elements that are last children:
This method is indeed powerful, because any CSS selector can be used.
```smart header="Can use pseudo-classes as well"
-Pseudo-classes in the CSS selector like `:hover` and `:active` are also supported. For instance, `document.querySelectorAll(':hover')` will return the collection with elements that the pointer is over now (in nesting order: from the outermost `` to the most nested one).
+Pseudo-classes in the CSS selector like `:hover` and `:active` are also supported. For instance, `document.querySelectorAll(':hover')` will return the collection with elements that the pointer is over now (in nesting order: from the outermost `` to the most nested one).
```
## querySelector [#querySelector]
@@ -116,7 +116,7 @@ In other words, the result is the same as `elem.querySelectorAll(css)[0]`, but t
Previous methods were searching the DOM.
-The [elem.matches(css)](https://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`.
+The [elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`.
The method comes in handy when we are iterating over elements (like in an array or something) and trying to filter out those that interest us.
@@ -142,7 +142,7 @@ For instance:
*Ancestors* of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top.
-The method `elem.closest(css)` looks for the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search.
+The method `elem.closest(css)` looks the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search.
In other words, the method `closest` goes up from the element and checks each of parents. If it matches the selector, then the search stops, and the ancestor is returned.
@@ -154,7 +154,7 @@ For instance:
Chapter 1
-
Chapter 2
+
Chapter 1
@@ -178,7 +178,7 @@ So here we cover them mainly for completeness, while you can still find them in
- `elem.getElementsByTagName(tag)` looks for elements with the given tag and returns the collection of them. The `tag` parameter can also be a star `"*"` for "any tags".
- `elem.getElementsByClassName(className)` returns elements that have the given CSS class.
-- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. Very rarely used.
+- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. very rarely used.
For instance:
```js
@@ -363,7 +363,7 @@ There are 6 main methods to search for nodes in DOM:
-By far the most used are `querySelector` and `querySelectorAll`, but `getElement(s)By*` can be sporadically helpful or found in the old scripts.
+By far the most used are `querySelector` and `querySelectorAll`, but `getElementBy*` can be sporadically helpful or found in the old scripts.
Besides that:
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 99dde5bc..78bc3fd8 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
@@ -10,7 +10,7 @@ Different DOM nodes may have different properties. For instance, an element node
Each DOM node belongs to the corresponding built-in class.
-The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](https://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:
@@ -18,39 +18,16 @@ Here's the picture, explanations to follow:
The classes are:
-- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class for everything.
-
- 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](https://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 other classes that inherit from it (and so inherit the `Node` functionality).
-
-- [Document](https://dom.spec.whatwg.org/#interface-document), for historical reasons often inherited by `HTMLDocument` (though the latest spec doesn't dictate it) -- is a document as a whole.
-
- The `document` global object belongs exactly to this class. It serves as an entry point to the DOM.
-
-- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- an "abstract" class, inherited by:
- - [Text](https://dom.spec.whatwg.org/#interface-text) -- the class corresponding to a text inside elements, e.g. `Hello` in `
diff --git a/2-ui/2-events/03-event-delegation/bagua-bubble.svg b/2-ui/2-events/03-event-delegation/bagua-bubble.svg
index 4ae67102..bb9989b4 100644
--- a/2-ui/2-events/03-event-delegation/bagua-bubble.svg
+++ b/2-ui/2-events/03-event-delegation/bagua-bubble.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css
index 8d6472ee..4522006a 100644
--- a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css
+++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css
@@ -4,6 +4,16 @@ body {
font: 75%/120% sans-serif;
}
+h2 {
+ font: bold 190%/100% sans-serif;
+ margin: 0 0 .2em;
+}
+
+h2 em {
+ font: normal 80%/100% sans-serif;
+ color: #999999;
+}
+
#largeImg {
border: solid 1px #ccc;
width: 550px;
diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css
index 8d6472ee..b6e52301 100644
--- a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css
+++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css
@@ -4,6 +4,16 @@ body {
font: 75%/120% sans-serif;
}
+h2 {
+ font: bold 190%/100% sans-serif;
+ margin: 0 0 .2em;
+}
+
+h2 em {
+ font: normal 80%/100% sans-serif;
+ color: #999999;
+}
+
#largeImg {
border: solid 1px #ccc;
width: 550px;
@@ -22,13 +32,4 @@ body {
#thumbs a:hover {
border-color: #FF9900;
-}
-
-#thumbs li {
- list-style: none;
-}
-
-#thumbs {
- margin: 0;
- padding: 0;
}
\ No newline at end of file
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 cd815654..8bb2ffc6 100644
--- a/2-ui/2-events/04-default-browser-action/article.md
+++ b/2-ui/2-events/04-default-browser-action/article.md
@@ -17,7 +17,7 @@ There are two ways to tell the browser we don't want it to act:
- The main way is to use the `event` object. There's a method `event.preventDefault()`.
- If the handler is assigned using `on` (not by `addEventListener`), then returning `false` also works the same.
-In this HTML, a click on a link doesn't lead to navigation; the browser doesn't do anything:
+In this HTML a click on a link doesn't lead to navigation, browser doesn't do anything:
```html autorun height=60 no-beautify
Click here
@@ -96,7 +96,7 @@ That's because the browser action is canceled on `mousedown`. The focusing is st
The optional `passive: true` option of `addEventListener` signals the browser that the handler is not going to call `preventDefault()`.
-Why might that be needed?
+Why that may be needed?
There are some events like `touchmove` on mobile devices (when the user moves their finger across the screen), that cause scrolling by default, but that scrolling can be prevented using `preventDefault()` in the handler.
@@ -113,7 +113,7 @@ The property `event.defaultPrevented` is `true` if the default action was preven
There's an interesting use case for it.
-You remember in the chapter we talked about `event.stopPropagation()` and why stopping bubbling is bad?
+You remember in the chapter we talked about `event.stopPropagation()` and why stopping bubbling is bad?
Sometimes we can use `event.defaultPrevented` instead, to signal other event handlers that the event was handled.
diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md
index 77a49490..ad9092fd 100644
--- a/2-ui/2-events/05-dispatch-events/article.md
+++ b/2-ui/2-events/05-dispatch-events/article.md
@@ -8,7 +8,7 @@ We can generate not only completely new events, that we invent for our own purpo
## Event constructor
-Built-in event classes form a hierarchy, similar to DOM element classes. The root is the built-in [Event](http://www.w3.org/TR/dom/#event) class.
+Build-in event classes form a hierarchy, similar to DOM element classes. The root is the built-in [Event](http://www.w3.org/TR/dom/#event) class.
We can create `Event` objects like this:
@@ -162,7 +162,7 @@ Besides, the event class describes "what kind of event" it is, and if the event
## event.preventDefault()
-Many browser events have a "default action", such as navigating to a link, starting a selection, and so on.
+Many browser events have a "default action", such as nagivating to a link, starting a selection, and so on.
For new, custom events, there are definitely no default browser actions, but a code that dispatches such event may have its own plans what to do after triggering the event.
@@ -187,6 +187,7 @@ Any handler can listen for that event with `rabbit.addEventListener('hide',...)`
-```
+```
The output order is: 1 -> nested -> 2.
-Please note that the nested event `menu-open` is caught on the `document`. The propagation and handling of the nested event is finished before the processing gets back to the outer code (`onclick`).
+Please note that the nested event `menu-open` fully bubbles up and is handled on the `document`. The propagation and handling of the nested event must be fully finished before the processing gets back to the outer code (`onclick`).
-That's not only about `dispatchEvent`, there are other cases. If an event handler calls methods that trigger other events -- they are processed synchronously too, in a nested fashion.
+That's not only about `dispatchEvent`, there are other cases. JavaScript in an event handler can call methods that lead to other events -- they are too processed synchronously.
-Let's say we don't like it. We'd want `onclick` to be fully processed first, independently from `menu-open` or any other nested events.
-
-Then we can either put the `dispatchEvent` (or another event-triggering call) at the end of `onclick` or, maybe better, wrap it in the zero-delay `setTimeout`:
+If we don't like it, we can either put the `dispatchEvent` (or other event-triggering call) at the end of `onclick` or, maybe better, wrap it in zero-delay `setTimeout`:
```html run
@@ -255,6 +253,7 @@ Then we can either put the `dispatchEvent` (or another event-triggering call) at
menu.onclick = function() {
alert(1);
+ // alert(2)
setTimeout(() => menu.dispatchEvent(new CustomEvent("menu-open", {
bubbles: true
})));
@@ -264,9 +263,9 @@ Then we can either put the `dispatchEvent` (or another event-triggering call) at
document.addEventListener('menu-open', () => alert('nested'));
-```
+```
-Now `dispatchEvent` runs asynchronously after the current code execution is finished, including `menu.onclick`, so event handlers are totally separate.
+Now `dispatchEvent` runs asynchronously after the current code execution is finished, including `mouse.onclick`, so event handlers are totally separate.
The output order becomes: 1 -> 2 -> nested.
@@ -282,9 +281,9 @@ Other constructors of native events like `MouseEvent`, `KeyboardEvent` and so on
For custom events we should use `CustomEvent` constructor. It has an additional option named `detail`, we should assign the event-specific data to it. Then all handlers can access it as `event.detail`.
-Despite the technical possibility of generating browser events like `click` or `keydown`, we should use them with great care.
+Despite the technical possibility to generate browser events like `click` or `keydown`, we should use with the great care.
-We shouldn't generate browser events as it's a hacky way to run handlers. That's bad architecture most of the time.
+We shouldn't generate browser events as it's a hacky way to run handlers. That's a bad architecture most of the time.
Native events might be generated:
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 9574b0c8..42f4b6f5 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,5 +1,4 @@
-
-# Mouse events
+# Mouse events basics
In this chapter we'll get into more details about mouse events and their properties.
@@ -7,7 +6,11 @@ Please note: such events may come not only from "mouse devices", but are also fr
## Mouse event types
-We've already seen some of these events:
+We can split mouse events into two categories: "simple" and "complex"
+
+### Simple events
+
+The most used simple events are:
`mousedown/mouseup`
: Mouse button is clicked/released over an element.
@@ -18,66 +21,54 @@ We've already seen some of these events:
`mousemove`
: Every mouse move over an element triggers that event.
+`contextmenu`
+: Triggers when opening a context menu is attempted. In the most common case, that happens when the right mouse button is pressed. Although, there are other ways to open a context menu, e.g. using a special keyboard key, so it's not exactly the mouse event.
+
+...There are several other event types too, we'll cover them later.
+
+### Complex events
+
`click`
: Triggers after `mousedown` and then `mouseup` over the same element if the left mouse button was used.
`dblclick`
-: Triggers after two clicks on the same element within a short timeframe. Rarely used nowadays.
+: Triggers after a double click over an element.
-`contextmenu`
-: Triggers when the right mouse button is pressed. There are other ways to open a context menu, e.g. using a special keyboard key, it triggers in that case also, so it's not exactly the mouse event.
+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.
-...There are several other events too, we'll cover them later.
+### Events order
-## Events order
+An action may trigger multiple events.
-As you can see from the list above, a user action may trigger multiple events.
-
-For instance, a left-button click first triggers `mousedown`, when the button is pressed, then `mouseup` and `click` when it's released.
+For instance, a click first triggers `mousedown`, when the button is pressed, then `mouseup` and `click` when it's released.
In cases when a single action initiates multiple events, their order is fixed. That is, the handlers are called in the order `mousedown` -> `mouseup` -> `click`.
```online
Click the button below and you'll see the events. Try double-click too.
-On the teststand below, all mouse events are logged, and if there is more than a 1 second delay between them, they are separated by a horizontal rule.
+On the teststand below all mouse events are logged, and if there is more than a 1 second delay between them they are separated by a horizontal ruler.
-Also, we can see the `button` property that allows us to detect the mouse button; it's explained below.
+Also we can see the `which` property that allows to detect the mouse button.
```
-## Mouse button
+## Getting the button: which
-Click-related events always have the `button` property, which allows to get the exact mouse button.
+Click-related events always have the `which` property, which allows to get the exact mouse button.
-We usually don't use it for `click` and `contextmenu` events, because the former happens only on left-click, and the latter -- only on right-click.
+It is not used for `click` and `contextmenu` events, because the former happens only on left-click, and the latter -- only on right-click.
-On the other hand, `mousedown` and `mouseup` handlers may need `event.button`, because these events trigger on any button, so `button` allows to distinguish between "right-mousedown" and "left-mousedown".
+But if we track `mousedown` and `mouseup`, then we need it, because these events trigger on any button, so `which` allows to distinguish between "right-mousedown" and "left-mousedown".
-The possible values of `event.button` are:
+There are the three possible values:
-| Button state | `event.button` |
-|--------------|----------------|
-| Left button (primary) | 0 |
-| Middle button (auxiliary) | 1 |
-| Right button (secondary) | 2 |
-| X1 button (back) | 3 |
-| X2 button (forward) | 4 |
+- `event.which == 1` -- the left button
+- `event.which == 2` - the middle button
+- `event.which == 3` - the right button
-Most mouse devices only have the left and right buttons, so possible values are `0` or `2`. Touch devices also generate similar events when one taps on them.
-
-Also there's `event.buttons` property that has all currently pressed buttons as an integer, one bit per button. In practice this property is very rarely used, you can find details at [MDN](mdn:/api/MouseEvent/buttons) if you ever need it.
-
-```warn header="The outdated `event.which`"
-Old code may use `event.which` property that's an old non-standard way of getting a button, with possible values:
-
-- `event.which == 1` – left button,
-- `event.which == 2` – middle button,
-- `event.which == 3` – right button.
-
-As of now, `event.which` is deprecated, we shouldn't use it.
-```
+The middle button is somewhat exotic right now and is very rarely used.
## Modifiers: shift, alt, ctrl and meta
@@ -125,25 +116,18 @@ For JS-code it means that we should check `if (event.ctrlKey || event.metaKey)`.
```
```warn header="There are also mobile devices"
-Keyboard combinations are good as an addition to the workflow. So that if the visitor uses a keyboard -- they work.
-
-But if their device doesn't have it -- then there should be a way to live without modifier keys.
+Keyboard combinations are good as an addition to the workflow. So that if the visitor has a
+ keyboard -- it works. And if their device doesn't have it -- then there should be another way to do the same.
```
## Coordinates: clientX/Y, pageX/Y
-All mouse events provide coordinates in two flavours:
+All mouse events have coordinates in two flavours:
1. Window-relative: `clientX` and `clientY`.
2. Document-relative: `pageX` and `pageY`.
-We already covered the difference between them in the chapter .
-
-In short, document-relative coordinates `pageX/Y` are counted from the left-upper corner of the document, and do not change when the page is scrolled, while `clientX/Y` are counted from the current window left-upper corner. When the page is scrolled, they change.
-
-For instance, if we have a window of the size 500x500, and the mouse is in the left-upper corner, then `clientX` and `clientY` are `0`, no matter how the page is scrolled.
-
-And if the mouse is in the center, then `clientX` and `clientY` are `250`, no matter what place in the document it is. They are similar to `position:fixed` in that aspect.
+For instance, if we have a window of the size 500x500, and the mouse is in the left-upper corner, then `clientX` and `clientY` are `0`. And if the mouse is in the center, then `clientX` and `clientY` are `250`, no matter what place in the document it is, how far the document was scrolled. They are similar to `position:fixed`.
````online
Move the mouse over the input field to see `clientX/clientY` (the example is in the `iframe`, so coordinates are relative to that `iframe`):
@@ -153,11 +137,13 @@ Move the mouse over the input field to see `clientX/clientY` (the example is in
```
````
-## Preventing selection on mousedown
+Document-relative coordinates `pageX`, `pageY` are counted from the left-upper corner of the document, not the window. You can read more about coordinates in the chapter .
-Double mouse click has a side effect that may be disturbing in some interfaces: it selects text.
+## Disabling selection
-For instance, double-clicking on the text below selects it in addition to our handler:
+Double mouse click has a side-effect that may be disturbing in some interfaces: it selects the text.
+
+For instance, a double-click on the text below selects it in addition to our handler:
```html autorun height=50
Double-click me
@@ -200,7 +186,7 @@ Surely the user has access to HTML-source of the page, and can take the content
Mouse events have the following properties:
-- Button: `button`.
+- 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 usually use `key:Cmd`, so it's better to check `if (e.metaKey || e.ctrlKey)`.
diff --git a/2-ui/3-event-details/1-mouse-events-basics/head.html b/2-ui/3-event-details/1-mouse-events-basics/head.html
index 1b9a73fc..f578fb7d 100644
--- a/2-ui/3-event-details/1-mouse-events-basics/head.html
+++ b/2-ui/3-event-details/1-mouse-events-basics/head.html
@@ -25,7 +25,7 @@
function logMouse(e) {
let evt = e.type;
while (evt.length < 11) evt += ' ';
- showmesg(evt + " button=" + e.button, 'test')
+ showmesg(evt + " which=" + e.which, 'test')
return false;
}
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html
index 84d52b18..e998165f 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html
@@ -54,7 +54,7 @@
Once upon a time there was a mother pig who had three little pigs.
-
The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."
+
The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."
The three little pigs set off. "We will take care that the wolf does not catch us," they said.
Once upon a time there was a mother pig who had three little pigs.
-
The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."
+
The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."
The three little pigs set off. "We will take care that the wolf does not catch us," they said.
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js
index 7503ca9c..4e6e2a3e 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js
@@ -88,7 +88,7 @@ class HoverIntent {
if (speed < this.sensitivity) {
clearInterval(this.checkSpeedInterval);
this.isHover = true;
- this.over.call(this.elem);
+ this.over.call(this.elem, event);
} else {
// speed fast, remember new coordinates as the previous ones
this.prevX = this.lastX;
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html
index 09823217..df0f1365 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html
@@ -20,17 +20,20 @@
Tooltip
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js
index f5d4aaff..7c7f6d23 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js
@@ -49,18 +49,18 @@ describe("hoverIntent", function() {
}
})
- it("mouseover -> when the pointer just arrived, no tooltip", function() {
+ it("mouseover -> immediately no tooltip", function() {
mouse('mouseover', 10, 10);
assert.isFalse(isOver);
});
- it("mouseover -> after a delay, the tooltip shows up", function() {
+ it("mouseover -> pause shows tooltip", function() {
mouse('mouseover', 10, 10);
this.clock.tick(100);
assert.isTrue(isOver);
});
- it("mouseover -> followed by fast mouseout leads doesn't show tooltip", function() {
+ it("mouseover -> fast mouseout no tooltip", function() {
mouse('mouseover', 10, 10);
setTimeout(
() => mouse('mouseout', 300, 300, { relatedTarget: document.body}),
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js
index a38b42bc..caca940b 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js
@@ -45,7 +45,6 @@ class HoverIntent {
destroy() {
/* your code to "disable" the functionality, remove all handlers */
- /* it's needed for the tests to work */
}
}
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html
index 09823217..df0f1365 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html
@@ -20,17 +20,20 @@
Tooltip
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js
index f5d4aaff..44369d8c 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js
@@ -3,7 +3,7 @@
describe("hoverIntent", function() {
function mouse(eventType, x, y, options) {
- let eventOptions = Object.assign({
+ let eventOptions = Object.assign({
bubbles: true,
clientX: x,
clientY: y,
@@ -11,15 +11,15 @@ describe("hoverIntent", function() {
pageY: y,
target: elem
}, options || {});
-
+
elem.dispatchEvent(new MouseEvent(eventType, eventOptions));
}
let isOver;
let hoverIntent;
-
-
+
+
before(function() {
this.clock = sinon.useFakeTimers();
});
@@ -27,11 +27,11 @@ describe("hoverIntent", function() {
after(function() {
this.clock.restore();
});
-
-
+
+
beforeEach(function() {
isOver = false;
-
+
hoverIntent = new HoverIntent({
elem: elem,
over: function() {
@@ -49,18 +49,18 @@ describe("hoverIntent", function() {
}
})
- it("mouseover -> when the pointer just arrived, no tooltip", function() {
+ it("mouseover -> immediately no tooltip", function() {
mouse('mouseover', 10, 10);
assert.isFalse(isOver);
});
-
- it("mouseover -> after a delay, the tooltip shows up", function() {
+
+ it("mouseover -> pause shows tooltip", function() {
mouse('mouseover', 10, 10);
this.clock.tick(100);
assert.isTrue(isOver);
});
- it("mouseover -> followed by fast mouseout leads doesn't show tooltip", function() {
+ it("mouseover -> fast mouseout no tooltip", function() {
mouse('mouseover', 10, 10);
setTimeout(
() => mouse('mouseout', 300, 300, { relatedTarget: document.body}),
@@ -94,5 +94,5 @@ describe("hoverIntent", function() {
this.clock.tick(200);
assert.isFalse(isOver);
});
-
+
});
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 d409c3f1..cbabd2f4 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
@@ -80,7 +80,7 @@ An important feature of `mouseout` -- it triggers, when the pointer moves from a
```
-If we're on `#parent` and then move the pointer deeper into `#child`, we get `mouseout` on `#parent`!
+If we're on `#parent` and then move the pointer deeper into `#child`, but we get `mouseout` on `#parent`!

@@ -125,7 +125,7 @@ If there are some actions upon leaving the parent element, e.g. an animation run
To avoid it, we can check `relatedTarget` in the handler and, if the mouse is still inside the element, then ignore such event.
-Alternatively we can use other events: `mouseenter` and `mouseleave`, that we'll be covering now, as they don't have such problems.
+Alternatively we can use other events: `mouseenter` и `mouseleave`, that we'll be covering now, as they don't have such problems.
## Events mouseenter and mouseleave
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-bubble-nested.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-bubble-nested.svg
index 6044eff1..07830295 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-bubble-nested.svg
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-bubble-nested.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg
index 22335b52..07176ba2 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg
index 437f03b1..262ddf59 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg
index 1277ddff..784f435d 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg
index 78210845..b38d76fb 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js
index 5752e83a..6d87199c 100755
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js
@@ -3,7 +3,7 @@ parent.onmouseover = parent.onmouseout = parent.onmousemove = handler;
function handler(event) {
let type = event.type;
- while (type.length < 11) type += ' ';
+ while (type < 11) type += ' ';
log(type + " target=" + event.target.id)
return false;
diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg
index f5bd9f4f..ca8bbc3b 100644
--- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg
+++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js
index 10ae2eee..1c1443a7 100644
--- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js
+++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js
@@ -7,7 +7,7 @@ document.addEventListener('mousedown', function(event) {
if (!dragElement) return;
event.preventDefault();
-
+
dragElement.ondragstart = function() {
return false;
};
@@ -19,7 +19,7 @@ document.addEventListener('mousedown', function(event) {
function onMouseUp(event) {
finishDrag();
};
-
+
function onMouseMove(event) {
moveAt(event.clientX, event.clientY);
}
@@ -31,9 +31,9 @@ document.addEventListener('mousedown', function(event) {
if(isDragging) {
return;
}
-
+
isDragging = true;
-
+
document.addEventListener('mousemove', onMouseMove);
element.addEventListener('mouseup', onMouseUp);
@@ -50,10 +50,10 @@ document.addEventListener('mousedown', function(event) {
if(!isDragging) {
return;
}
-
+
isDragging = false;
- dragElement.style.top = parseInt(dragElement.style.top) + window.pageYOffset + 'px';
+ dragElement.style.top = parseInt(dragElement.style.top) + pageYOffset + 'px';
dragElement.style.position = 'absolute';
document.removeEventListener('mousemove', onMouseMove);
@@ -113,4 +113,4 @@ document.addEventListener('mousedown', function(event) {
dragElement.style.top = newY + 'px';
}
-});
+});
\ No newline at end of file
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 4c928eef..ae77f1b6 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
@@ -1,12 +1,12 @@
# Drag'n'Drop with mouse events
-Drag'n'Drop is a great interface solution. Taking something and dragging and dropping it is a clear and simple way to do many things, from copying and moving documents (as in file managers) to ordering (dropping items into a cart).
+Drag'n'Drop is a great interface solution. Taking something and dragging and dropping it is a clear and simple way to do many things, from copying and moving documents (as in file managers) to ordering (dropping into a cart).
In the modern HTML standard there's a [section about Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd) with special events such as `dragstart`, `dragend`, and so on.
-These events allow us to support special kinds of drag'n'drop, such as handling dragging a file from OS file-manager and dropping it into the browser window. Then JavaScript can access the contents of such files.
+These events are useful in that they allow us to solve simple tasks easily. For instance, they allow us to handle the drag'n'drop of "external" files into the browser, so we can take a file in the OS file-manager and drop it into the browser window, thereby giving JavaScript access to its contents.
-But native Drag Events also have limitations. For instance, we can't prevent dragging from a certain area. Also we can't make the dragging "horizontal" or "vertical" only. And there are many other drag'n'drop tasks that can't be done using them. Also, mobile device support for such events is very weak.
+But native Drag Events also have limitations. For instance, we can't limit dragging by a certain area. Also we can't make it "horizontal" or "vertical" only. And there are other drag'n'drop tasks that can't be done using that API. Also, mobile devices support for such events is almost non-existant.
So here we'll see how to implement Drag'n'Drop using mouse events.
@@ -14,23 +14,26 @@ So here we'll see how to implement Drag'n'Drop using mouse events.
The basic Drag'n'Drop algorithm looks like this:
-1. On `mousedown` - prepare the element for moving, if needed (maybe create a clone of it, add a class to it or whatever).
-2. Then on `mousemove` move it by changing `left/top` with `position:absolute`.
-3. On `mouseup` - perform all actions related to finishing the drag'n'drop.
+1. On `mousedown` - prepare the element for moving, if needed (maybe create a copy of it).
+2. Then on `mousemove` move it by changing `left/top` and `position:absolute`.
+3. On `mouseup` - perform all actions related to a finished Drag'n'Drop.
-These are the basics. Later we'll see how to add other features, such as highlighting current underlying elements while we drag over them.
+These are the basics. Later we can extend it, for instance, by highlighting droppable (available for the drop) elements when hovering over them.
-Here's the implementation of dragging a ball:
+Here's the algorithm for drag'n'drop of a ball:
```js
-ball.onmousedown = function(event) {
- // (1) prepare to moving: make absolute and on top by z-index
+ball.onmousedown = function(event) { // (1) start the process
+
+ // (2) prepare to moving: make absolute and on top by z-index
ball.style.position = 'absolute';
ball.style.zIndex = 1000;
-
// move it out of any current parents directly into body
// to make it positioned relative to the body
- document.body.append(ball);
+ document.body.append(ball);
+ // ...and put that absolutely positioned ball under the pointer
+
+ moveAt(event.pageX, event.pageY);
// centers the ball at (pageX, pageY) coordinates
function moveAt(pageX, pageY) {
@@ -38,17 +41,14 @@ ball.onmousedown = function(event) {
ball.style.top = pageY - ball.offsetHeight / 2 + 'px';
}
- // move our absolutely positioned ball under the pointer
- moveAt(event.pageX, event.pageY);
-
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
}
- // (2) move the ball on mousemove
+ // (3) move the ball on mousemove
document.addEventListener('mousemove', onMouseMove);
- // (3) drop the ball, remove unneeded handlers
+ // (4) drop the ball, remove unneeded handlers
ball.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
ball.onmouseup = null;
@@ -64,10 +64,10 @@ Here's an example in action:
[iframe src="ball" height=230]
-Try to drag'n'drop with the mouse and you'll see such behavior.
+Try to drag'n'drop the mouse and you'll see such behavior.
```
-That's because the browser has its own drag'n'drop support for images and some other elements. It runs automatically and conflicts with ours.
+That's because the browser has its own Drag'n'Drop for images and some other elements that runs automatically and conflicts with ours.
To disable it:
@@ -93,14 +93,14 @@ So we should listen on `document` to catch it.
## Correct positioning
-In the examples above the ball is always moved so that its center is under the pointer:
+In the examples above the ball is always moved so, that it's center is under the pointer:
```js
ball.style.left = pageX - ball.offsetWidth / 2 + 'px';
ball.style.top = pageY - ball.offsetHeight / 2 + 'px';
```
-Not bad, but there's a side effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if "take" it from its edge, then the ball suddenly "jumps" to become centered under the mouse pointer.
+Not bad, but there's a side-effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if "take" it from its edge, then the ball suddenly "jumps" to become centered under the mouse pointer.
It would be better if we keep the initial shift of the element relative to the pointer.
@@ -124,7 +124,7 @@ Let's update our algorithm:
```js
// onmousemove
- // ball has position:absolute
+ // ball has position:absoute
ball.style.left = event.pageX - *!*shiftX*/!* + 'px';
ball.style.top = event.pageY - *!*shiftY*/!* + 'px';
```
@@ -219,7 +219,7 @@ That's why the initial idea to put handlers on potential droppables doesn't work
So, what to do?
-There's a method called `document.elementFromPoint(clientX, clientY)`. It returns the most nested element on given window-relative coordinates (or `null` if given coordinates are out of the window). If there are multiple overlapping elements on the same coordinates, then the topmost one is returned.
+There's a method called `document.elementFromPoint(clientX, clientY)`. It returns the most nested element on given window-relative coordinates (or `null` if given coordinates are out of the window).
We can use it in any of our mouse event handlers to detect the potential droppable under the pointer, like this:
@@ -276,7 +276,7 @@ function onMouseMove(event) {
}
```
-In the example below when the ball is dragged over the soccer goal, the goal is highlighted.
+In the example below when the ball is dragged over the soccer gate, the gate is highlighted.
[codetabs height=250 src="ball4"]
@@ -300,4 +300,4 @@ We can lay a lot on this foundation.
- We can use event delegation for `mousedown/up`. A large-area event handler that checks `event.target` can manage Drag'n'Drop for hundreds of elements.
- And so on.
-There are frameworks that build architecture over it: `DragZone`, `Droppable`, `Draggable` and other classes. Most of them do the similar stuff to what's described above, so it should be easy to understand them now. Or roll your own, as you can see that that's easy enough to do, sometimes easier than adapting a third-party solution.
+There are frameworks that build architecture over it: `DragZone`, `Droppable`, `Draggable` and other classes. Most of them do the similar stuff to described above, so it should be easy to understand them now. Or roll our own, as you can see that's easy enough to do, sometimes easier than adapting a third-part solution.
diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html b/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html
index 8751c70a..3fdd7fe7 100644
--- a/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html
+++ b/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html
@@ -13,13 +13,16 @@
```
-The `onkeydown` handler here uses `checkPhoneKey` to check for the key pressed. If it's valid (from `0..9` or one of `+-()`), then it returns `true`, otherwise `false`.
+Please note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, `key:Ctrl+V`, do not work in the input. That's a side-effect of the strict filter `checkPhoneKey`.
-As we know, the `false` value returned from the event handler, assigned using a DOM property or an attribute, such as above, prevents the default action, so nothing appears in the `` for keys that don't pass the test. (The `true` value returned doesn't affect anything, only returning `false` matters)
+Let's relax it a little bit:
-Please note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, do not work in the input. That's a side effect of the strict filter `checkPhoneKey`. These keys make it return `false`.
-
-Let's relax the filter a little bit by allowing arrow keys `key:Left`, `key:Right` and `key:Delete`, `key:Backspace`:
```html autorun height=60 run
@@ -165,9 +162,7 @@ function checkPhoneKey(key) {
Now arrows and deletion works well.
-Even though we have the key filter, one still can enter anything using a mouse and right-click + Paste. Mobile devices provide other means to enter values. So the filter is not 100% reliable.
-
-The alternative approach would be to track the `oninput` event -- it triggers *after* any modification. There we can check the new `input.value` and modify it/highlight the `` when it's invalid. Or we can use both event handlers together.
+...But we still can enter anything by using a mouse and right-click + Paste. So the filter is not 100% reliable. We can just let it be like that, because most of time it works. Or an alternative approach would be to track the `input` event -- it triggers after any modification. There we can check the new value and highlight/modify it when it's invalid.
## Legacy
@@ -175,12 +170,6 @@ In the past, there was a `keypress` event, and also `keyCode`, `charCode`, `whic
There were so many browser incompatibilities while working with them, that developers of the specification had no way, other than deprecating all of them and creating new, modern events (described above in this chapter). The old code still works, as browsers keep supporting them, but there's totally no need to use those any more.
-## Mobile Keyboards
-
-When using virtual/mobile keyboards, formally known as IME (Input-Method Editor), the W3C standard states that a KeyboardEvent's [`e.keyCode` should be `229`](https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode) and [`e.key` should be `"Unidentified"`](https://www.w3.org/TR/uievents-key/#key-attr-values).
-
-While some of these keyboards might still use the right values for `e.key`, `e.code`, `e.keyCode`... when pressing certain keys such as arrows or backspace, there's no guarantee, so your keyboard logic might not always work on mobile devices.
-
## Summary
Pressing a key always generates a keyboard event, be it symbol keys or special keys like `key:Shift` or `key:Ctrl` and so on. The only exception is `key:Fn` key that sometimes presents on a laptop keyboard. There's no keyboard event for it, because it's often implemented on lower level than OS.
diff --git a/2-ui/3-event-details/5-keyboard-events/german-layout.svg b/2-ui/3-event-details/5-keyboard-events/german-layout.svg
new file mode 100644
index 00000000..8a880e8e
--- /dev/null
+++ b/2-ui/3-event-details/5-keyboard-events/german-layout.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html b/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html
similarity index 95%
rename from 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html
rename to 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html
index a0d5a4f4..40106283 100644
--- a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html
+++ b/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html
@@ -28,7 +28,7 @@
-
+
diff --git a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js b/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js
similarity index 96%
rename from 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js
rename to 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js
index d97f7a7b..5eba24c7 100644
--- a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js
+++ b/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js
@@ -5,8 +5,6 @@ let lastTime = Date.now();
function handle(e) {
if (form.elements[e.type + 'Ignore'].checked) return;
- area.scrollTop = 1e6;
-
let text = e.type +
' key=' + e.key +
' code=' + e.code +
diff --git a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css b/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/style.css
similarity index 100%
rename from 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css
rename to 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/style.css
diff --git a/2-ui/3-event-details/5-keyboard-events/us-layout.svg b/2-ui/3-event-details/5-keyboard-events/us-layout.svg
new file mode 100644
index 00000000..699277e0
--- /dev/null
+++ b/2-ui/3-event-details/5-keyboard-events/us-layout.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/6-pointer-events/article.md b/2-ui/3-event-details/6-pointer-events/article.md
deleted file mode 100644
index b8873e9d..00000000
--- a/2-ui/3-event-details/6-pointer-events/article.md
+++ /dev/null
@@ -1,282 +0,0 @@
-# Pointer events
-
-Pointer events are a modern way to handle input from a variety of pointing devices, such as a mouse, a pen/stylus, a touchscreen, and so on.
-
-## The brief history
-
-Let's make a small overview, so that you understand the general picture and the place of Pointer Events among other event types.
-
-- Long ago, in the past, there were only mouse events.
-
- Then touch devices became widespread, phones and tablets in particular. For the existing scripts to work, they generated (and still generate) mouse events. For instance, tapping a touchscreen generates `mousedown`. So touch devices worked well with web pages.
-
- But touch devices have more capabilities than a mouse. For example, it's possible to touch multiple points at once ("multi-touch"). Although, mouse events don't have necessary properties to handle such multi-touches.
-
-- So touch events were introduced, such as `touchstart`, `touchend`, `touchmove`, that have touch-specific properties (we don't cover them in detail here, because pointer events are even better).
-
- Still, it wasn't enough, as there are many other devices, such as pens, that have their own features. Also, writing code that listens for both touch and mouse events was cumbersome.
-
-- To solve these issues, the new standard Pointer Events was introduced. It provides a single set of events for all kinds of pointing devices.
-
-As of now, [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) specification is supported in all major browsers, while the newer [Pointer Events Level 3](https://w3c.github.io/pointerevents/) is in the works and is mostly compatible with Pointer Events level 2.
-
-Unless you develop for old browsers, such as Internet Explorer 10, or for Safari 12 or below, there's no point in using mouse or touch events any more -- we can switch to pointer events.
-
-Then your code will work well with both touch and mouse devices.
-
-That said, there are some important peculiarities that one should know in order to use Pointer Events correctly and avoid surprises. We'll make note of them in this article.
-
-## Pointer event types
-
-Pointer events are named similarly to mouse events:
-
-| Pointer event | Similar mouse event |
-|---------------|-------------|
-| `pointerdown` | `mousedown` |
-| `pointerup` | `mouseup` |
-| `pointermove` | `mousemove` |
-| `pointerover` | `mouseover` |
-| `pointerout` | `mouseout` |
-| `pointerenter` | `mouseenter` |
-| `pointerleave` | `mouseleave` |
-| `pointercancel` | - |
-| `gotpointercapture` | - |
-| `lostpointercapture` | - |
-
-As we can see, for every `mouse`, there's a `pointer` that plays a similar role. Also there are 3 additional pointer events that don't have a corresponding `mouse...` counterpart, we'll explain them soon.
-
-```smart header="Replacing `mouse` with `pointer` in our code"
-We can replace `mouse` events with `pointer` in our code and expect things to continue working fine with mouse.
-
-The support for touch devices will also "magically" improve. Although, we may need to add `touch-action: none` in some places in CSS. We'll cover it below in the section about `pointercancel`.
-```
-
-## Pointer event properties
-
-Pointer events have the same properties as mouse events, such as `clientX/Y`, `target`, etc., plus some others:
-
-- `pointerId` - the unique identifier of the pointer causing the event.
-
- Browser-generated. Allows us to handle multiple pointers, such as a touchscreen with stylus and multi-touch (examples will follow).
-- `pointerType` - the pointing device type. Must be a string, one of: "mouse", "pen" or "touch".
-
- We can use this property to react differently on various pointer types.
-- `isPrimary` - is `true` for the primary pointer (the first finger in multi-touch).
-
-Some pointer devices measure contact area and pressure, e.g. for a finger on the touchscreen, there are additional properties for that:
-
-- `width` - the width of the area where the pointer (e.g. a finger) touches the device. Where unsupported, e.g. for a mouse, it's always `1`.
-- `height` - the height of the area where the pointer touches the device. Where unsupported, it's always `1`.
-- `pressure` - the pressure of the pointer tip, in range from 0 to 1. For devices that don't support pressure must be either `0.5` (pressed) or `0`.
-- `tangentialPressure` - the normalized tangential pressure.
-- `tiltX`, `tiltY`, `twist` - pen-specific properties that describe how the pen is positioned relative the surface.
-
-These properties aren't supported by most devices, so they are rarely used. You can find the details about them in the [specification](https://w3c.github.io/pointerevents/#pointerevent-interface) if needed.
-
-## Multi-touch
-
-One of the things that mouse events totally don't support is multi-touch: a user can touch in several places at once on their phone or tablet, or perform special gestures.
-
-Pointer Events allow handling multi-touch with the help of the `pointerId` and `isPrimary` properties.
-
-Here's what happens when a user touches a touchscreen in one place, then puts another finger somewhere else on it:
-
-1. At the first finger touch:
- - `pointerdown` with `isPrimary=true` and some `pointerId`.
-2. For the second finger and more fingers (assuming the first one is still touching):
- - `pointerdown` with `isPrimary=false` and a different `pointerId` for every finger.
-
-Please note: the `pointerId` is assigned not to the whole device, but for each touching finger. If we use 5 fingers to simultaneously touch the screen, we have 5 `pointerdown` events, each with their respective coordinates and a different `pointerId`.
-
-The events associated with the first finger always have `isPrimary=true`.
-
-We can track multiple touching fingers using their `pointerId`. When the user moves and then removes a finger, we get `pointermove` and `pointerup` events with the same `pointerId` as we had in `pointerdown`.
-
-```online
-Here's the demo that logs `pointerdown` and `pointerup` events:
-
-[iframe src="multitouch" edit height=200]
-
-Please note: you must be using a touchscreen device, such as a phone or a tablet, to actually see the difference in `pointerId/isPrimary`. For single-touch devices, such as a mouse, there'll be always same `pointerId` with `isPrimary=true`, for all pointer events.
-```
-
-## Event: pointercancel
-
-The `pointercancel` event fires when there's an ongoing pointer interaction, and then something happens that causes it to be aborted, so that no more pointer events are generated.
-
-Such causes are:
-- The pointer device hardware was physically disabled.
-- The device orientation changed (tablet rotated).
-- The browser decided to handle the interaction on its own, considering it a mouse gesture or zoom-and-pan action or something else.
-
-We'll demonstrate `pointercancel` on a practical example to see how it affects us.
-
-Let's say we're implementing drag'n'drop for a ball, just as in the beginning of the article .
-
-Here is the flow of user actions and the corresponding events:
-
-1) The user presses on an image, to start dragging
- - `pointerdown` event fires
-2) Then they start moving the pointer (thus dragging the image)
- - `pointermove` fires, maybe several times
-3) And then the surprise happens! The browser has native drag'n'drop support for images, that kicks in and takes over the drag'n'drop process, thus generating `pointercancel` event.
- - The browser now handles drag'n'drop of the image on its own. The user may even drag the ball image out of the browser, into their Mail program or a File Manager.
- - No more `pointermove` events for us.
-
-So the issue is that the browser "hijacks" the interaction: `pointercancel` fires in the beginning of the "drag-and-drop" process, and no more `pointermove` events are generated.
-
-```online
-Here's the drag'n'drop demo with loggin of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`:
-
-[iframe src="ball" height=240 edit]
-```
-
-We'd like to implement the drag'n'drop on our own, so let's tell the browser not to take it over.
-
-**Prevent the default browser action to avoid `pointercancel`.**
-
-We need to do two things:
-
-1. Prevent native drag'n'drop from happening:
- - We can do this by setting `ball.ondragstart = () => false`, just as described in the article .
- - That works well for mouse events.
-2. For touch devices, there are other touch-related browser actions (besides drag'n'drop). To avoid problems with them too:
- - Prevent them by setting `#ball { touch-action: none }` in CSS.
- - Then our code will start working on touch devices.
-
-After we do that, the events will work as intended, the browser won't hijack the process and doesn't emit `pointercancel`.
-
-```online
-This demo adds these lines:
-
-[iframe src="ball-2" height=240 edit]
-
-As you can see, there's no `pointercancel` any more.
-```
-
-Now we can add the code to actually move the ball, and our drag'n'drop will work for mouse devices and touch devices.
-
-## Pointer capturing
-
-Pointer capturing is a special feature of pointer events.
-
-The idea is very simple, but may seem quite odd at first, as nothing like that exists for any other event type.
-
-The main method is:
-- `elem.setPointerCapture(pointerId)` -- binds events with the given `pointerId` to `elem`. After the call all pointer events with the same `pointerId` will have `elem` as the target (as if happened on `elem`), no matter where in document they really happened.
-
-In other words, `elem.setPointerCapture(pointerId)` retargets all subsequent events with the given `pointerId` to `elem`.
-
-The binding is removed:
-- automatically when `pointerup` or `pointercancel` events occur,
-- automatically when `elem` is removed from the document,
-- when `elem.releasePointerCapture(pointerId)` is called.
-
-Now what is it good for? It's time to see a real-life example.
-
-**Pointer capturing can be used to simplify drag'n'drop kind of interactions.**
-
-Let's recall how one can implement a custom slider, described in the .
-
-We can make a `slider` element to represent the strip and the "runner" (`thumb`) inside it:
-
-```html
-
-
-
-```
-
-With styles, it looks like this:
-
-[iframe src="slider-html" height=40 edit]
-
-
-
-And here's the working logic, as it was described, after replacing mouse events with similar pointer events:
-
-1. The user presses on the slider `thumb` -- `pointerdown` triggers.
-2. Then they move the pointer -- `pointermove` triggers, and our code moves the `thumb` element along.
- - ...As the pointer moves, it may leave the slider `thumb` element, go above or below it. The `thumb` should move strictly horizontally, remaining aligned with the pointer.
-
-In the mouse event based solution, to track all pointer movements, including when it goes above/below the `thumb`, we had to assign `mousemove` event handler on the whole `document`.
-
-That's not a cleanest solution, though. One of the problems is that when a user moves the pointer around the document, it may trigger event handlers (such as `mouseover`) on some other elements, invoke totally unrelated UI functionality, and we don't want that.
-
-This is the place where `setPointerCapture` comes into play.
-
-- We can call `thumb.setPointerCapture(event.pointerId)` in `pointerdown` handler,
-- Then future pointer events until `pointerup/cancel` will be retargeted to `thumb`.
-- When `pointerup` happens (dragging complete), the binding is removed automatically, we don't need to care about it.
-
-So, even if the user moves the pointer around the whole document, events handlers will be called on `thumb`. Nevertheless, coordinate properties of the event objects, such as `clientX/clientY` will still be correct - the capturing only affects `target/currentTarget`.
-
-Here's the essential code:
-
-```js
-thumb.onpointerdown = function(event) {
- // retarget all pointer events (until pointerup) to thumb
- thumb.setPointerCapture(event.pointerId);
-
- // start tracking pointer moves
- thumb.onpointermove = function(event) {
- // moving the slider: listen on the thumb, as all pointer events are retargeted to it
- let newLeft = event.clientX - slider.getBoundingClientRect().left;
- thumb.style.left = newLeft + 'px';
- };
-
- // on pointer up finish tracking pointer moves
- thumb.onpointerup = function(event) {
- thumb.onpointermove = null;
- thumb.onpointerup = null;
- // ...also process the "drag end" if needed
- };
-};
-
-// note: no need to call thumb.releasePointerCapture,
-// it happens on pointerup automatically
-```
-
-```online
-The full demo:
-
-[iframe src="slider" height=100 edit]
-
-
-
-In the demo, there's also an additional element with `onmouseover` handler showing the current date.
-
-Please note: while you're dragging the thumb, you may hover over this element, and its handler *does not* trigger.
-
-So the dragging is now free of side effects, thanks to `setPointerCapture`.
-```
-
-
-
-At the end, pointer capturing gives us two benefits:
-1. The code becomes cleaner as we don't need to add/remove handlers on the whole `document` any more. The binding is released automatically.
-2. If there are other pointer event handlers in the document, they won't be accidentally triggered by the pointer while the user is dragging the slider.
-
-### Pointer capturing events
-
-There's one more thing to mention here, for the sake of completeness.
-
-There are two events associated with pointer capturing:
-
-- `gotpointercapture` fires when an element uses `setPointerCapture` to enable capturing.
-- `lostpointercapture` fires when the capture is released: either explicitly with `releasePointerCapture` call, or automatically on `pointerup`/`pointercancel`.
-
-## Summary
-
-Pointer events allow handling mouse, touch and pen events simultaneously, with a single piece of code.
-
-Pointer events extend mouse events. We can replace `mouse` with `pointer` in event names and expect our code to continue working for mouse, with better support for other device types.
-
-For drag'n'drops and complex touch interactions that the browser may decide to hijack and handle on its own - remember to cancel the default action on events and set `touch-action: none` in CSS for elements that we engage.
-
-Additional abilities of pointer events are:
-
-- Multi-touch support using `pointerId` and `isPrimary`.
-- Device-specific properties, such as `pressure`, `width/height`, and others.
-- Pointer capturing: we can retarget all pointer events to a specific element until `pointerup`/`pointercancel`.
-
-As of now, pointer events are supported in all major browsers, so we can safely switch to them, especially if IE10- and Safari 12- are not needed. And even with those browsers, there are polyfills that enable the support of pointer events.
diff --git a/2-ui/3-event-details/6-pointer-events/ball-2.view/index.html b/2-ui/3-event-details/6-pointer-events/ball-2.view/index.html
deleted file mode 100644
index 5f3abbcb..00000000
--- a/2-ui/3-event-details/6-pointer-events/ball-2.view/index.html
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
diff --git a/2-ui/3-event-details/6-pointer-events/slider.view/style.css b/2-ui/3-event-details/6-pointer-events/slider.view/style.css
deleted file mode 100644
index a84cd5e7..00000000
--- a/2-ui/3-event-details/6-pointer-events/slider.view/style.css
+++ /dev/null
@@ -1,20 +0,0 @@
-.slider {
- border-radius: 5px;
- background: #E0E0E0;
- background: linear-gradient(left top, #E0E0E0, #EEEEEE);
- width: 310px;
- height: 15px;
- margin: 5px;
-}
-
-.thumb {
- touch-action: none;
- width: 10px;
- height: 25px;
- border-radius: 3px;
- position: relative;
- left: 10px;
- top: -5px;
- background: blue;
- cursor: pointer;
-}
diff --git a/2-ui/3-event-details/7-keyboard-events/german-layout.svg b/2-ui/3-event-details/7-keyboard-events/german-layout.svg
deleted file mode 100644
index 7ac9a400..00000000
--- a/2-ui/3-event-details/7-keyboard-events/german-layout.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/2-ui/3-event-details/7-keyboard-events/us-layout.svg b/2-ui/3-event-details/7-keyboard-events/us-layout.svg
deleted file mode 100644
index 353f225f..00000000
--- a/2-ui/3-event-details/7-keyboard-events/us-layout.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
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 54c10119..10945ccd 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
@@ -55,11 +55,11 @@ function populate() {
// document bottom
let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;
- // if the user hasn't scrolled far enough (>100px to the end)
- if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;
-
- // let's add more data
- document.body.insertAdjacentHTML("beforeend", `
Date: ${new Date()}
`);
+ // if the user scrolled far enough (<100px to the end)
+ if (windowRelativeBottom < document.documentElement.clientHeight + 100) {
+ // let's add more data
+ document.body.insertAdjacentHTML("beforeend", `
Date: ${new Date()}
`);
+ }
}
}
```
diff --git a/2-ui/3-event-details/8-onscroll/article.md b/2-ui/3-event-details/8-onscroll/article.md
index 734bd84c..22ff8d85 100644
--- a/2-ui/3-event-details/8-onscroll/article.md
+++ b/2-ui/3-event-details/8-onscroll/article.md
@@ -1,6 +1,6 @@
# Scrolling
-The `scroll` event allows reacting to a page or element scrolling. There are quite a few good things we can do here.
+The `scroll` event allows to react on a page or element scrolling. There are quite a few good things we can do here.
For instance:
- Show/hide additional controls or information depending on where in the document the user is.
@@ -10,7 +10,7 @@ Here's a small function to show the current scroll:
```js autorun
window.addEventListener('scroll', function() {
- document.getElementById('showScroll').innerHTML = window.pageYOffset + 'px';
+ document.getElementById('showScroll').innerHTML = pageYOffset + 'px';
});
```
@@ -34,4 +34,4 @@ If we add an event handler to these events and `event.preventDefault()` in it, t
There are many ways to initiate a scroll, so it's more reliable to use CSS, `overflow` property.
-Here are few tasks that you can solve or look through to see applications of `onscroll`.
+Here are few tasks that you can solve or look through to see the applications on `onscroll`.
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 f22518d9..d989a5fb 100644
--- a/2-ui/4-forms-controls/1-form-elements/article.md
+++ b/2-ui/4-forms-controls/1-form-elements/article.md
@@ -8,11 +8,11 @@ Working with forms will be much more convenient when we learn them.
Document forms are members of the special collection `document.forms`.
-That's a so-called *"named collection"*: it's both named and ordered. We can use both the name or the number in the document to get the form.
+That's a so-called "named collection": it's both named and ordered. We can use both the name or the number in the document to get the form.
```js no-beautify
-document.forms.my; // the form with name="my"
-document.forms[0]; // the first form in the document
+document.forms.my - the form with name="my"
+document.forms[0] - the first form in the document
```
When we have a form, then any element is available in the named collection `form.elements`.
@@ -36,9 +36,9 @@ For instance:
```
-There may be multiple elements with the same name. This is typical with radio buttons and checkboxes.
+There may be multiple elements with the same name, that's often the case with radio buttons.
-In that case, `form.elements[name]` is a *collection*. For instance:
+In that case `form.elements[name]` is a collection, for instance:
```html run height=40