minor
This commit is contained in:
parent
a2daa0191f
commit
4bc42e1360
3 changed files with 43 additions and 34 deletions
|
@ -6,6 +6,6 @@ importance: 4
|
|||
|
||||
Create a calculator that prompts for an arithmetic expression and returns its result.
|
||||
|
||||
There's no need to check the expression for correctness in this task.
|
||||
There's no need to check the expression for correctness in this task. Just evaluate and return the result.
|
||||
|
||||
[demo]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Eval: run a code string
|
||||
|
||||
The built-in `eval` function allows to execute a string of `code`.;
|
||||
The built-in `eval` function allows to execute a string of code.
|
||||
|
||||
The syntax is:
|
||||
|
||||
|
@ -15,7 +15,9 @@ let code = 'alert("Hello")';
|
|||
eval(code); // Hello
|
||||
```
|
||||
|
||||
A call to `eval` returns the result of the last statement.
|
||||
A string of code may be long, contain line breaks, function declarations, variables and so on.
|
||||
|
||||
The result of `eval` is the result of the last statement.
|
||||
|
||||
For example:
|
||||
```js run
|
||||
|
@ -23,7 +25,12 @@ let value = eval('1+1');
|
|||
alert(value); // 2
|
||||
```
|
||||
|
||||
The code is executed in the current lexical environment, so it can see outer variables:
|
||||
```js run
|
||||
let value = eval('let i = 0; ++i');
|
||||
alert(value); // 1
|
||||
```
|
||||
|
||||
The eval'ed code is executed in the current lexical environment, so it can see outer variables:
|
||||
|
||||
```js run no-beautify
|
||||
let a = 1;
|
||||
|
@ -68,13 +75,13 @@ The reason is simple: long, long time ago JavaScript was a much weaker language,
|
|||
|
||||
Right now, there's almost no reason to use `eval`. If someone is using it, there's a good chance they can replace it with a modern language construct or a [JavaScript Module](info:modules).
|
||||
|
||||
Still, if you're sure you need to dynamically `eval` a string of code, please note that its ability to access outer variables has side-effects.
|
||||
Please note that its ability to access outer variables has side-effects.
|
||||
|
||||
Code minifiers (tools used before JS gets to production, to compress it) replace local variables with shorter ones for brewity. That's usually safe, but not if `eval` is used, as it may reference them. So minifiers don't replace all local variables that might be visible from `eval`. That negatively affects code compression ratio.
|
||||
Code minifiers (tools used before JS gets to production, to compress it) replace local variables with shorter ones for optimization. That's usually safe, but not if `eval` is used, as it may reference them. So minifiers don't replace all local variables that might be visible from `eval`. That negatively affects code compression ratio.
|
||||
|
||||
Using outer local variables inside `eval` is a bad programming practice, as it makes maintaining the code more difficult.
|
||||
|
||||
There are two ways how to evade any eval-related problems.
|
||||
There are two ways how to be totally safe from such problems.
|
||||
|
||||
**If eval'ed code doesn't use outer variables, please call `eval` as `window.eval(...)`:**
|
||||
|
||||
|
@ -88,7 +95,7 @@ let x = 1;
|
|||
}
|
||||
```
|
||||
|
||||
**If your code needs local variables, execute it with `new Function` and pass them as arguments:**
|
||||
**If eval'ed code needs local variables, change `eval` to `new Function` and pass them as arguments:**
|
||||
|
||||
```js run
|
||||
let f = new Function('a', 'alert(a)');
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
We can not only assign handlers, but also generate events from JavaScript.
|
||||
|
||||
Custom events can be used to create "graphical components". For instance, a root element of the menu may trigger events telling what happens with the menu: `open` (menu open), `select` (an item is selected) and so on.
|
||||
Custom events can be used to create "graphical components". For instance, a root element of our own JS-based menu may trigger events telling what happens with the menu: `open` (menu open), `select` (an item is selected) and so on. Another code may listen to the events and observe what's happening with the menu.
|
||||
|
||||
Also we can generate built-in events like `click`, `mousedown` etc, that may be good for testing.
|
||||
We can generate not only completely new events, that we invent for our own purposes, but also built-in ones, such as `click`, `mousedown` etc. That may be helpful for automated testing.
|
||||
|
||||
## Event constructor
|
||||
|
||||
|
@ -27,9 +27,9 @@ Arguments:
|
|||
|
||||
## dispatchEvent
|
||||
|
||||
After an event object is created, we should "run" it on an element using the call `elem.dispatchEvent(event)`.
|
||||
After an event object is created, we should "run" it on an element using the call `elem.dispatchEvent(event)`.
|
||||
|
||||
Then handlers react on it as if it were a regular built-in event. If the event was created with the `bubbles` flag, then it bubbles.
|
||||
Then handlers react on it as if it were a regular browser event. If the event was created with the `bubbles` flag, then it bubbles.
|
||||
|
||||
In the example below the `click` event is initiated in JavaScript. The handler works same way as if the button was clicked:
|
||||
|
||||
|
@ -129,11 +129,11 @@ alert(event.clientX); // undefined, the unknown property is ignored!
|
|||
|
||||
Technically, we can work around that by assigning directly `event.clientX=100` after creation. So that's a matter of convenience and following the rules. Browser-generated events always have the right type.
|
||||
|
||||
The full list of properties for different UI events is in the specification, for instance [MouseEvent](https://www.w3.org/TR/uievents/#mouseevent).
|
||||
The full list of properties for different UI events is in the specification, for instance, [MouseEvent](https://www.w3.org/TR/uievents/#mouseevent).
|
||||
|
||||
## Custom events
|
||||
|
||||
For our own, custom events like `"hello"` we should use `new CustomEvent`. Technically [CustomEvent](https://dom.spec.whatwg.org/#customevent) is the same as `Event`, with one exception.
|
||||
For our own, completely new events types like `"hello"` we should use `new CustomEvent`. Technically [CustomEvent](https://dom.spec.whatwg.org/#customevent) is the same as `Event`, with one exception.
|
||||
|
||||
In the second argument (object) we can add an additional property `detail` for any custom information that we want to pass with the event.
|
||||
|
||||
|
@ -158,23 +158,25 @@ For instance:
|
|||
|
||||
The `detail` property can have any data. Technically we could live without, because we can assign any properties into a regular `new Event` object after its creation. But `CustomEvent` provides the special `detail` field for it to evade conflicts with other event properties.
|
||||
|
||||
The event class tells something about "what kind of event" it is, and if the event is custom, then we should use `CustomEvent` just to be clear about what it is.
|
||||
Besides, the event class describes "what kind of event" it is, and if the event is custom, then we should use `CustomEvent` just to be clear about what it is.
|
||||
|
||||
## event.preventDefault()
|
||||
|
||||
We can call `event.preventDefault()` on a script-generated event if `cancelable:true` flag is specified.
|
||||
Many browser events have a "default action", such as nagivating to a link, starting a selection, and so on.
|
||||
|
||||
Of course, for custom events, with names unknown for the browser, there are no "default browser actions". But our code may plan its own actions after `dispatchEvent`.
|
||||
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.
|
||||
|
||||
The call of `event.preventDefault()` is a way for the handler to send a signal that those actions should be canceled.
|
||||
By calling `event.preventDefault()`, an event handler may send a signal that those actions should be canceled.
|
||||
|
||||
In that case the call to `elem.dispatchEvent(event)` returns `false`. And the event-generating code knows that the processing shouldn't continue.
|
||||
In that case the call to `elem.dispatchEvent(event)` returns `false`. And the code that dispatched it knows that it shouldn't continue.
|
||||
|
||||
For instance, in the example below there's a `hide()` function. It generates the `"hide"` event on the element `#rabbit`, notifying all interested parties that the rabbit is going to hide.
|
||||
Let's see a practical example - a hiding rabbit (could be a closing menu or something else).
|
||||
|
||||
A handler set by `rabbit.addEventListener('hide',...)` will learn about that and, if it wants, can prevent that action by calling `event.preventDefault()`. Then the rabbit won't hide:
|
||||
Below you can see a `#rabbit` and `hide()` function that dispatches `"hide"` event on it, to let all interested parties know that the rabbit is going to hide.
|
||||
|
||||
```html run refresh
|
||||
Any handler can listen to that event with `rabbit.addEventListener('hide',...)` and, if needed, cancel the action using `event.preventDefault()`. Then the rabbit won't disappear:
|
||||
|
||||
```html run refresh autorun
|
||||
<pre id="rabbit">
|
||||
|\ /|
|
||||
\|_|/
|
||||
|
@ -182,6 +184,7 @@ A handler set by `rabbit.addEventListener('hide',...)` will learn about that and
|
|||
=\_Y_/=
|
||||
{>o<}
|
||||
</pre>
|
||||
<button onclick="hide()">Hide()</button>
|
||||
|
||||
<script>
|
||||
// hide() will be called automatically in 2 seconds
|
||||
|
@ -190,7 +193,7 @@ A handler set by `rabbit.addEventListener('hide',...)` will learn about that and
|
|||
cancelable: true // without that flag preventDefault doesn't work
|
||||
});
|
||||
if (!rabbit.dispatchEvent(event)) {
|
||||
alert('the action was prevented by a handler');
|
||||
alert('The action was prevented by a handler');
|
||||
} else {
|
||||
rabbit.hidden = true;
|
||||
}
|
||||
|
@ -201,13 +204,10 @@ A handler set by `rabbit.addEventListener('hide',...)` will learn about that and
|
|||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// hide in 2 seconds
|
||||
setTimeout(hide, 2000);
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
Обратите внимание: событие должно иметь флаг `cancelable: true`, иначе вызов `event.preventDefault()` будет проигнорирован.
|
||||
|
||||
## Events-in-events are synchronous
|
||||
|
||||
|
@ -219,11 +219,10 @@ Then the control jumps to the nested event handler, and after it goes back.
|
|||
|
||||
For instance, here the nested `menu-open` event is processed synchronously, during the `onclick`:
|
||||
|
||||
```html run
|
||||
```html run autorun
|
||||
<button id="menu">Menu (click me)</button>
|
||||
|
||||
<script>
|
||||
// 1 -> nested -> 2
|
||||
menu.onclick = function() {
|
||||
alert(1);
|
||||
|
||||
|
@ -239,17 +238,18 @@ For instance, here the nested `menu-open` event is processed synchronously, duri
|
|||
</script>
|
||||
```
|
||||
|
||||
Please note that the nested event `menu-open` bubbles up and is handled on the `document`. The propagation of the nested event is fully finished before the processing gets back to the outer code (`onclick`).
|
||||
The output order is: 1 -> nested -> 2.
|
||||
|
||||
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. JavaScript in an event handler can call methods that lead to other events -- they are too processed synchronously.
|
||||
|
||||
If we don't like it, we can either put the `dispatchEvent` (or other event-triggering call) at the end of `onclick` or wrap it in 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
|
||||
<button id="menu">Menu (click me)</button>
|
||||
|
||||
<script>
|
||||
// Now the result is: 1 -> 2 -> nested
|
||||
menu.onclick = function() {
|
||||
alert(1);
|
||||
|
||||
|
@ -267,13 +267,15 @@ If we don't like it, we can either put the `dispatchEvent` (or other event-trigg
|
|||
|
||||
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.
|
||||
|
||||
## Summary
|
||||
|
||||
To generate an event from code, we first need to create an event object.
|
||||
|
||||
The generic `Event(name, options)` constructor accepts an arbitrary event name and the `options` object with two properties:
|
||||
- `bubbles: true` if the event should bubble.
|
||||
- `cancelable: true` if the `event.preventDefault()` should work.
|
||||
- `bubbles: true` if the event should bubble.
|
||||
- `cancelable: true` if the `event.preventDefault()` should work.
|
||||
|
||||
Other constructors of native events like `MouseEvent`, `KeyboardEvent` and so on accept properties specific to that event type. For instance, `clientX` for mouse events.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue