You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/04-object-basics/03-symbol/article.md
+11-7Lines changed: 11 additions & 7 deletions
Original file line number
Diff line number
Diff line change
@@ -74,15 +74,19 @@ alert(id.description); // id
74
74
75
75
Symbols allow us to create "hidden" properties of an object, that no other part of code can occasionally access or overwrite.
76
76
77
-
For instance, if we're working with `user` objects, that belong to a third-party code and don't have any `id` field. We'd like to add identifiers to them.
77
+
For instance, if we're working with `user` objects, that belong to a third-party code. We'd like to add identifiers to them.
78
78
79
79
Let's use a symbol key for it:
80
80
81
81
```js run
82
-
let user = { name:"John" };
82
+
let user = { // belongs to another code
83
+
name:"John"
84
+
};
85
+
83
86
let id =Symbol("id");
84
87
85
-
user[id] ="ID Value";
88
+
user[id] =1;
89
+
86
90
alert( user[id] ); // we can access the data using the symbol as the key
87
91
```
88
92
@@ -108,13 +112,13 @@ There will be no conflict between our and their identifiers, because symbols are
108
112
```js run
109
113
let user = { name:"John" };
110
114
111
-
//our script uses "id" property
112
-
user.id="ID Value";
115
+
//Our script uses "id" property
116
+
user.id="Our id value";
113
117
114
-
// ...if later another script the uses "id" for its purposes...
118
+
// ...Another script also wants "id" for its purposes...
115
119
116
120
user.id="Their id value"
117
-
//boom! overwritten! it did not mean to harm the colleague, but did it!
Copy file name to clipboardExpand all lines: 2-ui/3-event-details/4-mouse-drag-and-drop/article.md
+31-25Lines changed: 31 additions & 25 deletions
Original file line number
Diff line number
Diff line change
@@ -6,20 +6,19 @@ In the modern HTML standard there's a [section about Drag and Drop](https://html
6
6
7
7
They are interesting because they allow to solve simple tasks easily, and also allow to handle 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. Then JavaScript gains access to its contents.
8
8
9
-
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. There are other drag'n'drop tasks that can't be implemented using that API.
9
+
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. There are other drag'n'drop tasks that can't be done using that API.
10
10
11
-
So here we'll see how to implement Drag'n'Drop using mouse events. Not that hard either.
11
+
Here we'll see how to implement Drag'n'Drop using mouse events.
12
12
13
13
## Drag'n'Drop algorithm
14
14
15
15
The basic Drag'n'Drop algorithm looks like this:
16
16
17
-
1. Catch `mousedown` on a draggable element.
18
-
2. Prepare the element for moving (maybe create a copy of it or whatever).
19
-
3. Then on `mousemove` move it by changing `left/top` and `position:absolute`.
20
-
4. On `mouseup` (button release) -- perform all actions related to a finished Drag'n'Drop.
17
+
1. On `mousedown` - prepare the element for moving, if needed (maybe create a copy of it).
18
+
2. Then on `mousemove` move it by changing `left/top` and `position:absolute`.
19
+
3. On `mouseup` - perform all actions related to a finished Drag'n'Drop.
21
20
22
-
These are the basics. We can extend it, for instance, by highlighting droppable (available for the drop) elements when hovering over them.
21
+
These are the basics. Later we can extend it, for instance, by highlighting droppable (available for the drop) elements when hovering over them.
23
22
24
23
Here's the algorithm for drag'n'drop of a ball:
25
24
@@ -32,7 +31,7 @@ ball.onmousedown = function(event) { // (1) start the process
32
31
// move it out of any current parents directly into body
33
32
// to make it positioned relative to the body
34
33
document.body.append(ball);
35
-
// ...and put that absolutely positioned ball under the cursor
34
+
// ...and put that absolutely positioned ball under the pointer
36
35
37
36
moveAt(event.pageX, event.pageY);
38
37
@@ -65,7 +64,7 @@ Here's an example in action:
65
64
66
65
[iframe src="ball" height=230]
67
66
68
-
Try to drag'n'drop the mouse and you'll see the strange behavior.
67
+
Try to drag'n'drop the mouse and you'll see such behavior.
69
68
```
70
69
71
70
That's because the browser has its own Drag'n'Drop for images and some other elements that runs automatically and conflicts with ours.
@@ -88,7 +87,7 @@ In action:
88
87
89
88
Another important aspect -- we track `mousemove` on `document`, not on `ball`. From the first sight it may seem that the mouse is always over the ball, and we can put `mousemove` on it.
90
89
91
-
But as we remember, `mousemove` triggers often, but not for every pixel. So after swift move the cursor can jump from the ball somewhere in the middle of document (or even outside of the window).
90
+
But as we remember, `mousemove` triggers often, but not for every pixel. So after swift move the pointer can jump from the ball somewhere in the middle of document (or even outside of the window).
Not bad, but there's a side-effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if do it at the edge, then the ball suddenly "jumps" to become centered.
103
+
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.
105
104
106
105
It would be better if we keep the initial shift of the element relative to the pointer.
107
106
108
-
For instance, if we start dragging by the edge of the ball, then the cursor should remain over the edge while dragging.
107
+
For instance, if we start dragging by the edge of the ball, then the pointer should remain over the edge while dragging.
109
108
110
109

111
110
112
-
1. When a visitor presses the button (`mousedown`) -- we can remember the distance from the cursor to the left-upper corner of the ball in variables `shiftX/shiftY`. We should keep that distance while dragging.
111
+
Let's update our algorithm:
112
+
113
+
1. When a visitor presses the button (`mousedown`) - remember the distance from the pointer to the left-upper corner of the ball in variables `shiftX/shiftY`. We'll keep that distance while dragging.
113
114
114
115
To get these shifts we can substract the coordinates:
115
116
@@ -123,7 +124,7 @@ For instance, if we start dragging by the edge of the ball, then the cursor shou
@@ -177,25 +178,27 @@ In action (inside `<iframe>`):
177
178
[iframe src="ball3" height=230]
178
179
```
179
180
180
-
The difference is especially noticeable if we drag the ball by its right-bottom corner. In the previous example the ball "jumps" under the pointer. Now it fluently follows the cursor from the current position.
181
+
The difference is especially noticeable if we drag the ball by its right-bottom corner. In the previous example the ball "jumps" under the pointer. Now it fluently follows the pointer from the current position.
181
182
182
183
## Potential drop targets (droppables)
183
184
184
-
In previous examples the ball could be dropped just "anywhere" to stay. In real-life we usually take one element and drop it onto another. For instance, a file into a folder, or a user into a trash can or whatever.
185
+
In previous examples the ball could be dropped just "anywhere" to stay. In real-life we usually take one element and drop it onto another. For instance, a "file" into a "folder" or something else.
185
186
186
-
In other words, we take a "draggable" element and drop it onto "droppable" element.
187
+
Speaking abstract, we take a "draggable" element and drop it onto "droppable" element.
187
188
188
-
We need to know where the element was dropped at the end of Drag'n'Drop -- to do the corresponding action, and, preferably, know the droppable we're dragging over, to highlight it.
189
+
We need to know:
190
+
- where the element was dropped at the end of Drag'n'Drop -- to do the corresponding action,
191
+
- and, preferably, know the droppable we're dragging over, to highlight it.
189
192
190
193
The solution is kind-of interesting and just a little bit tricky, so let's cover it here.
191
194
192
-
What may be the first idea? Probably to set `mouseover/mouseup` handlers on potential droppables and detect when the mouse pointer appears over them. And then we know that we are dragging over/dropping on that element.
195
+
What may be the first idea? Probably to set `mouseover/mouseup` handlers on potential droppables?
193
196
194
197
But that doesn't work.
195
198
196
199
The problem is that, while we're dragging, the draggable element is always above other elements. And mouse events only happen on the top element, not on those below it.
197
200
198
-
For instance, below are two `<div>` elements, red on top ofblue. There's no way to catch an event on the blue one, because the red is on top:
201
+
For instance, below are two `<div>` elements, red one on top ofthe blueone (fully covers). There's no way to catch an event on the blue one, because the red is on top:
199
202
200
203
```html run autorun height=60
201
204
<style>
@@ -218,24 +221,27 @@ So, what to do?
218
221
219
222
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).
220
223
221
-
So in any of our mouse event handlers we can detect the potential droppable under the pointer like this:
224
+
We can use it in any of our mouse event handlers to detect the potential droppable under the pointer, like this:
222
225
223
226
```js
224
227
// in a mouse event handler
225
-
ball.hidden = true; // (*)
228
+
ball.hidden = true; // (*) hide the element that we drag
229
+
226
230
let elemBelow = document.elementFromPoint(event.clientX, event.clientY);
227
-
ball.hidden = false;
228
231
// elemBelow is the element below the ball, may be droppable
232
+
233
+
ball.hidden = false;
229
234
```
230
235
231
-
Please note: we need to hide the ball before the call `(*)`. Otherwise we'll usually have a ball on these coordinates, as it's the top element under the pointer:`elemBelow=ball`.
236
+
Please note: we need to hide the ball before the call `(*)`. Otherwise we'll usually have a ball on these coordinates, as it's the top element under the pointer:`elemBelow=ball`.So we hide it and immediately show again.
232
237
233
238
We can use that code to check what element we're "flying over" at any time. And handle the drop when it happens.
234
239
235
240
An extended code of `onMouseMove` to find "droppable" elements:
236
241
237
242
```js
238
-
let currentDroppable = null; // potential droppable that we're flying over right now
243
+
// potential droppable that we're flying over right now
0 commit comments