Skip to content

Commit d94b292

Browse files
committed
fixes
1 parent 7fd3eb1 commit d94b292

File tree

16 files changed

+217
-179
lines changed

16 files changed

+217
-179
lines changed

1-js/05-data-types/05-array-methods/9-shuffle/solution.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ There are other good ways to do the task. For instance, there's a great algorith
6868
function shuffle(array) {
6969
for (let i = array.length - 1; i > 0; i--) {
7070
let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
71-
[array[i], array[j]] = [array[j], array[i]]; // swap elements
71+
72+
// swap elements array[i] and array[j]
73+
// we use "destructuring assignment" syntax to achieve that
74+
// you'll find more details about that syntax in later chapters
75+
// same can be written as:
76+
// let t = array[i]; array[i] = array[j]; array[j] = t
77+
[array[i], array[j]] = [array[j], array[i]];
7278
}
7379
}
7480
```

2-ui/3-event-details/1-mouse-events-basics/head.html

+11-8
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,28 @@
44

55
function showmesg(t, form) {
66

7-
if (timer==0) timer = new Date()
7+
if (timer == 0) {
8+
timer = new Date();
9+
}
10+
11+
let tm = new Date();
812

9-
let tm = new Date()
10-
if (tm-timer > 300) {
11-
t = '------------------------------\n'+t
13+
if (tm - timer > 300) {
14+
t = '------------------------------\n' + t;
1215
}
1316

14-
let area = document.forms[form+'form'].getElementsByTagName('textarea')[0]
17+
let area = document.forms[form + 'form'].getElementsByTagName('textarea')[0];
1518

1619
area.value += t + '\n';
17-
area.scrollTop = area.scrollHeight
20+
area.scrollTop = area.scrollHeight;
1821

19-
timer = tm
22+
timer = tm;
2023
}
2124

2225
function logMouse(e) {
2326
let evt = e.type;
2427
while (evt.length < 11) evt += ' ';
25-
showmesg(evt+" which="+e.which, 'test')
28+
showmesg(evt + " which=" + e.which, 'test')
2629
return false;
2730
}
2831

Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
# Moving: mouseover/out, mouseenter/leave
1+
# Moving the mouse: mouseover/out, mouseenter/leave
22

3-
Let's dive into more details about events that happen when mouse moves between elements.
3+
Let's dive into more details about events that happen when the mouse moves between elements.
44

5-
## Mouseover/mouseout, relatedTarget
5+
## Events mouseover/mouseout, relatedTarget
66

77
The `mouseover` event occurs when a mouse pointer comes over an element, and `mouseout` -- when it leaves.
88

99
![](mouseover-mouseout.svg)
1010

11-
These events are special, because they have a `relatedTarget`.
12-
13-
This property complements `target`. When a mouse leaves one element for another, one of them becomes `target`, and the other one `relatedTarget`.
11+
These events are special, because they have property `relatedTarget`. This property complements `target`. When a mouse leaves one element for another, one of them becomes `target`, and the other one - `relatedTarget`.
1412

1513
For `mouseover`:
1614

@@ -19,13 +17,13 @@ For `mouseover`:
1917

2018
For `mouseout` the reverse:
2119

22-
- `event.target` -- is the element that mouse left.
20+
- `event.target` -- is the element that the mouse left.
2321
- `event.relatedTarget` -- is the new under-the-pointer element, that mouse left for (`target` -> `relatedTarget`).
2422

2523
```online
26-
In the example below each face feature is an element. When you move the mouse, you can see mouse events in the text area.
24+
In the example below each face and its features are separate elements. When you move the mouse, you can see mouse events in the text area.
2725
28-
Each event has the information about where the element came and where it came from.
26+
Each event has the information about both `target` and `relatedTarget`:
2927
3028
[codetabs src="mouseoverout" height=280]
3129
```
@@ -38,103 +36,128 @@ That's normal and just means that the mouse came not from another element, but f
3836
We should keep that possibility in mind when using `event.relatedTarget` in our code. If we access `event.relatedTarget.tagName`, then there will be an error.
3937
```
4038
41-
## Events frequency
39+
## Skipping elements
4240
4341
The `mousemove` event triggers when the mouse moves. But that doesn't mean that every pixel leads to an event.
4442
4543
The browser checks the mouse position from time to time. And if it notices changes then triggers the events.
4644
47-
That means that if the visitor is moving the mouse very fast then DOM-elements may be skipped:
45+
That means that if the visitor is moving the mouse very fast then some DOM-elements may be skipped:
4846
4947
![](mouseover-mouseout-over-elems.svg)
5048
5149
If the mouse moves very fast from `#FROM` to `#TO` elements as painted above, then intermediate `<div>` (or some of them) may be skipped. The `mouseout` event may trigger on `#FROM` and then immediately `mouseover` on `#TO`.
5250
53-
In practice that's helpful, because if there may be many intermediate elements. We don't really want to process in and out of each one.
51+
That's good for performance, because if there may be many intermediate elements. We don't really want to process in and out of each one.
5452
55-
On the other hand, we should keep in mind that we can't assume that the mouse slowly moves from one event to another. No, it can "jump".
53+
On the other hand, we should keep in mind that the mouse pointer doesn't "visit" all elements along the way. It can "jump".
5654
57-
In particular it's possible that the cursor jumps right inside the middle of the page from out of the window. And `relatedTarget=null`, because it came from "nowhere":
55+
In particular, it's possible that the pointer jumps right inside the middle of the page from out of the window. In that case `relatedTarget` is `null`, because it came from "nowhere":
5856
5957
![](mouseover-mouseout-from-outside.svg)
6058
61-
<div style="display:none">
62-
In case of a fast move, intermediate elements may trigger no events. But if the mouse enters the element (`mouseover`), when we're guaranteed to have `mouseout` when it leaves it.
63-
</div>
64-
6559
```online
66-
Check it out "live" on a teststand below.
60+
You can check it out "live" on a teststand below.
6761
68-
The HTML is two nested `<div>` elements. If you move the mouse fast over them, then there may be no events at all, or maybe only the red div triggers events, or maybe the green one.
62+
Its HTML has two nested elements: the `<div id="child">` is inside the `<div id="parent">`. If you move the mouse fast over them, then maybe only the child div triggers events, or maybe the parent one, or maybe there will be no events at all.
6963
70-
Also try to move the pointer over the red `div`, and then move it out quickly down through the green one. If the movement is fast enough then the parent element is ignored.
64+
Also move the pointer into the child `div`, and then move it out quickly down through the parent one. If the movement is fast enough, then the parent element is ignored. The mouse will cross the parent element without noticing it.
7165
7266
[codetabs height=360 src="mouseoverout-fast"]
7367
```
7468

75-
## "Extra" mouseout when leaving for a child
69+
```smart header="If `mouseover` triggered, there must be `mouseout`"
70+
In case of fast mouse movements, intermediate elements may be ignores, but one thing we know for sure: elements can be only skipped as a whole.
71+
72+
If the pointer "officially" entered an element with `mouseover`, then upon leaving it we always get `mouseout`.
73+
```
7674
77-
Imagine -- a mouse pointer entered an element. The `mouseover` triggered. Then the cursor goes into a child element. The interesting fact is that `mouseout` triggers in that case. The cursor is still in the element, but we have a `mouseout` from it!
75+
## Mouseout when leaving for a child
76+
77+
An important feature of `mouseout` -- it triggers, when the pointer moves from an element to its descendant.
78+
79+
Visually, the pointer is still on the element, but we get `mouseout`!
7880
7981
![](mouseover-to-child.svg)
8082
81-
That seems strange, but can be easily explained.
83+
That looks strange, but can be easily explained.
8284
83-
**According to the browser logic, the mouse cursor may be only over a *single* element at any time -- the most nested one (and top by z-index).**
85+
**According to the browser logic, the mouse cursor may be only over a *single* element at any time -- the most nested one and top by z-index.**
8486
85-
So if it goes to another element (even a descendant), then it leaves the previous one. That simple.
87+
So if it goes to another element (even a descendant), then it leaves the previous one.
8688
87-
There's a funny consequence that we can see on the example below.
89+
Please note an important detail.
8890
89-
The red `<div>` is nested inside the blue one. The blue `<div>` has `mouseover/out` handlers that log all events in the textarea below.
91+
The `mouseover` event on a descendant bubbles up. So, if the parent element has such handler, it triggers.
9092
91-
Try entering the blue element and then moving the mouse on the red one -- and watch the events:
93+
![](mouseover-bubble-nested.svg)
9294
93-
[codetabs height=360 src="mouseoverout-child"]
95+
So, when we move from a parent element to a child, then two handlers trigger on the parent element: `mouseout` and `mouseover`:
96+
97+
```js
98+
parent.onmouseout = function(event) {
99+
/* event.target: parent element */
100+
};
101+
parent.onmouseover = function(event) {
102+
/* event.target: child element */
103+
};
104+
```
105+
106+
If the code inside the handlers doesn't look at `target`, then it might think that the mouse left the `parent` element, and then came back over it. But it's not the case! The mouse never left, it just moved to the child element.
94107

95-
1. On entering the blue one -- we get `mouseover [target: blue]`.
96-
2. Then after moving from the blue to the red one -- we get `mouseout [target: blue]` (left the parent).
97-
3. ...And immediately `mouseover [target: red]`.
108+
```online
109+
In the example below the `<div id="child">` is inside the `<div id="parent">`. There are handlers on the parent that listen for `mouseover/out` events and output their details.
110+
111+
If you move the mouse from the parent to the child, you see two events: `mouseout [target: parent]` (left the parent) and `mouseover [target: child]` (came to the child, bubbled).
98112
99-
So, for a handler that does not take `target` into account, it looks like we left the parent in `mouseout` in `(2)` and returned back to it by `mouseover` in `(3)`.
113+
[codetabs height=360 src="mouseoverout-child"]
114+
```
100115

101-
If we perform some actions on entering/leaving the element, then we'll get a lot of extra "false" runs. For simple stuff that may be unnoticeable. For complex things that may bring unwanted side-effects.
116+
If there's some action upon leaving the element, e.g. animation runs, then such interpretation may bring unwanted side effects.
102117

103-
We can fix it by using `mouseenter/mouseleave` events instead.
118+
To avoid it, we can check `relatedTarget` and, if the mouse is still inside the element, then ignore such event.
119+
120+
Alternatively we can use other events: `mouseenter` и `mouseleave`, that we'll be covering now, as they don't have such problems.
104121

105122
## Events mouseenter and mouseleave
106123

107-
Events `mouseenter/mouseleave` are like `mouseover/mouseout`. They also trigger when the mouse pointer enters/leaves the element.
124+
Events `mouseenter/mouseleave` are like `mouseover/mouseout`. They trigger when the mouse pointer enters/leaves the element.
108125

109-
But there are two differences:
126+
But there are two important differences:
110127

111128
1. Transitions inside the element are not counted.
112129
2. Events `mouseenter/mouseleave` do not bubble.
113130

114-
These events are intuitively very clear.
131+
These events are extremely simple.
132+
133+
When the pointer enters an element -- `mouseenter` triggers. The exact location of the pointer inside the element or its descendants doesn't matter.
115134

116-
When the pointer enters an element -- the `mouseenter` triggers, and then doesn't matter where it goes while inside the element. The `mouseleave` event only triggers when the cursor leaves it.
135+
When the pointer leaves an element -- `mouseleave` triggers.
117136

118-
If we make the same example, but put `mouseenter/mouseleave` on the blue `<div>`, and do the same -- we can see that events trigger only on entering and leaving the blue `<div>`. No extra events when going to the red one and back. Children are ignored.
137+
```online
138+
This example is similar to the one above, but now the top element has `mouseenter/mouseleave` instead of `mouseover/mouseout`.
139+
140+
As you can see, the only generated events are the ones related to moving the pointer in and out of the top element. Nothing happens when the pointer goes to the child and back. Transitions between descendants are ignores
119141
120142
[codetabs height=340 src="mouseleave"]
143+
```
121144

122145
## Event delegation
123146

124147
Events `mouseenter/leave` are very simple and easy to use. But they do not bubble. So we can't use event delegation with them.
125148

126149
Imagine we want to handle mouse enter/leave for table cells. And there are hundreds of cells.
127150

128-
The natural solution would be -- to set the handler on `<table>` and process events there. But `mouseenter/leave` don't bubble. So if such event happens on `<td>`, then only a handler on that `<td>` can catch it.
151+
The natural solution would be -- to set the handler on `<table>` and process events there. But `mouseenter/leave` don't bubble. So if such event happens on `<td>`, then only a handler on that `<td>` is able to catch it.
129152

130-
Handlers for `mouseenter/leave` on `<table>` only trigger on entering/leaving the whole table. It's impossible to get any information about transitions inside it.
153+
Handlers for `mouseenter/leave` on `<table>` only trigger when the pointer enters/leaves the table as a whole. It's impossible to get any information about transitions inside it.
131154

132-
Not a problem -- let's use `mouseover/mouseout`.
155+
So, let's use `mouseover/mouseout`.
133156

134-
A simple handler may look like this:
157+
Let's start with handlers that highlight the element under mouse:
135158

136159
```js
137-
// let's highlight cells under mouse
160+
// let's highlight an element under the pointer
138161
table.onmouseover = function(event) {
139162
let target = event.target;
140163
target.style.background = 'pink';
@@ -147,41 +170,38 @@ table.onmouseout = function(event) {
147170
```
148171

149172
```online
173+
Here they are in action. As the mouse travels across the elements of this table, the current one is highlighted:
174+
150175
[codetabs height=480 src="mouseenter-mouseleave-delegation"]
151176
```
152177

153-
These handlers work when going from any element to any inside the table.
178+
In our case we'd like to handle transitions between table cells `<td>`: entering a cell and leaving it. Other transitions, such as inside the cell or outside of any cells, don't interest us. Let's filter them out.
154179

155-
But we'd like to handle only transitions in and out of `<td>` as a whole. And highlight the cells as a whole. We don't want to handle transitions that happen between the children of `<td>`.
180+
Here's what we can do:
156181

157-
One of solutions:
158-
159-
- Remember the currently highlighted `<td>` in a variable.
182+
- Remember the currently highlighted `<td>` in a variable, let's call it `currentElem`.
160183
- On `mouseover` -- ignore the event if we're still inside the current `<td>`.
161184
- On `mouseout` -- ignore if we didn't leave the current `<td>`.
162185

163-
That filters out "extra" events when we are moving between the children of `<td>`.
186+
Here's an example of code that accounts for all possible situations:
164187

165-
```offline
166-
The details are in the [full example](sandbox:mouseenter-mouseleave-delegation-2).
167-
```
188+
[js src="mouseenter-mouseleave-delegation-2/script.js"]
168189

169190
```online
170191
Here's the full example with all details:
171192
172193
[codetabs height=380 src="mouseenter-mouseleave-delegation-2"]
173194
174-
Try to move the cursor in and out of table cells and inside them. Fast or slow -- doesn't matter. Only `<td>` as a whole is highlighted unlike the example before.
195+
Try to move the cursor in and out of table cells and inside them. Fast or slow -- doesn't matter. Only `<td>` as a whole is highlighted, unlike the example before.
175196
```
176197

177-
178198
## Summary
179199

180200
We covered events `mouseover`, `mouseout`, `mousemove`, `mouseenter` and `mouseleave`.
181201

182-
Things that are good to note:
202+
These things are good to note:
183203

184-
- A fast mouse move can make `mouseover, mousemove, mouseout` to skip intermediate elements.
185-
- Events `mouseover/out` and `mouseenter/leave` have an additional target: `relatedTarget`. That's the element that we are coming from/to, complementary to `target`.
186-
- Events `mouseover/out` trigger even when we go from the parent element to a child element. They assume that the mouse can be only over one element at one time -- the deepest one.
187-
- Events `mouseenter/leave` do not bubble and do not trigger when the mouse goes to a child element. They only track whether the mouse comes inside and outside the element as a whole.
204+
- A fast mouse move may skip intermediate elements.
205+
- Events `mouseover/out` and `mouseenter/leave` have an additional property: `relatedTarget`. That's the element that we are coming from/to, complementary to `target`.
206+
- Events `mouseover/out` trigger even when we go from the parent element to a child element. The browser assumes that the mouse can be only over one element at one time -- the deepest one.
207+
- Events `mouseenter/leave` do not bubble and do not trigger when the mouse goes to a child element. They only track whether the mouse comes inside and outside the element as a whole. So, they are simpler than `mouseover/out`, but we can't implement delegation using them.

2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/script.js

+18-9
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,46 @@
22
let currentElem = null;
33

44
table.onmouseover = function(event) {
5-
if (currentElem) {
6-
// before entering a new element, the mouse always leaves the previous one
7-
// if we didn't leave <td> yet, then we're still inside it, so can ignore the event
8-
return;
9-
}
5+
// before entering a new element, the mouse always leaves the previous one
6+
// if currentElem is set, we didn't leave the previous <td>,
7+
// that's a mouseover inside it, ignore the event
8+
if (currentElem) return;
109

1110
let target = event.target.closest('td');
12-
if (!target || !table.contains(target)) return;
1311

14-
// yeah we're inside <td> now
12+
// we moved not into a <td> - ignore
13+
if (!target) return;
14+
15+
// moved into <td>, but outside of our table (possible in case of nested tables)
16+
// ignore
17+
if (!table.contains(target)) return;
18+
19+
// hooray! we entered a new <td>
1520
currentElem = target;
1621
target.style.background = 'pink';
1722
};
1823

1924

2025
table.onmouseout = function(event) {
2126
// if we're outside of any <td> now, then ignore the event
27+
// that's probably a move inside the table, but out of <td>,
28+
// e.g. from <tr> to another <tr>
2229
if (!currentElem) return;
2330

24-
// we're leaving the element -- where to? Maybe to a child element?
31+
// we're leaving the element -- where to? Maybe to a descendant?
2532
let relatedTarget = event.relatedTarget;
33+
2634
if (relatedTarget) { // possible: relatedTarget = null
2735
while (relatedTarget) {
2836
// go up the parent chain and check -- if we're still inside currentElem
2937
// then that's an internal transition -- ignore it
3038
if (relatedTarget == currentElem) return;
39+
3140
relatedTarget = relatedTarget.parentNode;
3241
}
3342
}
3443

35-
// we left the element. really.
44+
// we left the <td>. really.
3645
currentElem.style.background = '';
3746
currentElem = null;
3847
};

2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave.view/index.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
<!DOCTYPE HTML>
1+
<!doctype html>
22
<html>
33

44
<head>
5-
<meta charset="utf-8">
5+
<meta charset="UTF-8">
66
<link rel="stylesheet" href="style.css">
77
</head>
88

99
<body>
1010

11-
<div id="blue" onmouseenter="log(event)" onmouseleave="log(event)">
12-
<div id="red"></div>
11+
<div id="parent" onmouseenter="mouselog(event)" onmouseleave="mouselog(event)">parent
12+
<div id="child">child</div>
1313
</div>
1414

1515
<textarea id="text"></textarea>
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
function log(event) {
2-
text.value += event.type + ' [target: ' + event.target.id + ']\n';
1+
function mouselog(event) {
2+
let d = new Date();
3+
text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\n`.replace(/(:|^)(\d\D)/, '$10$2');
34
text.scrollTop = text.scrollHeight;
4-
}
5+
}

0 commit comments

Comments
 (0)