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.
|
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]
|
[demo]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Eval: run a code string
|
# 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:
|
The syntax is:
|
||||||
|
|
||||||
|
@ -15,7 +15,9 @@ let code = 'alert("Hello")';
|
||||||
eval(code); // 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:
|
For example:
|
||||||
```js run
|
```js run
|
||||||
|
@ -23,7 +25,12 @@ let value = eval('1+1');
|
||||||
alert(value); // 2
|
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
|
```js run no-beautify
|
||||||
let a = 1;
|
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).
|
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.
|
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(...)`:**
|
**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
|
```js run
|
||||||
let f = new Function('a', 'alert(a)');
|
let f = new Function('a', 'alert(a)');
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
We can not only assign handlers, but also generate events from JavaScript.
|
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
|
## Event constructor
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ Arguments:
|
||||||
|
|
||||||
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:
|
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.
|
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
|
## 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.
|
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 `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()
|
## 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">
|
<pre id="rabbit">
|
||||||
|\ /|
|
|\ /|
|
||||||
\|_|/
|
\|_|/
|
||||||
|
@ -182,6 +184,7 @@ A handler set by `rabbit.addEventListener('hide',...)` will learn about that and
|
||||||
=\_Y_/=
|
=\_Y_/=
|
||||||
{>o<}
|
{>o<}
|
||||||
</pre>
|
</pre>
|
||||||
|
<button onclick="hide()">Hide()</button>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// hide() will be called automatically in 2 seconds
|
// 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
|
cancelable: true // without that flag preventDefault doesn't work
|
||||||
});
|
});
|
||||||
if (!rabbit.dispatchEvent(event)) {
|
if (!rabbit.dispatchEvent(event)) {
|
||||||
alert('the action was prevented by a handler');
|
alert('The action was prevented by a handler');
|
||||||
} else {
|
} else {
|
||||||
rabbit.hidden = true;
|
rabbit.hidden = true;
|
||||||
}
|
}
|
||||||
|
@ -201,13 +204,10 @@ A handler set by `rabbit.addEventListener('hide',...)` will learn about that and
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// hide in 2 seconds
|
|
||||||
setTimeout(hide, 2000);
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Обратите внимание: событие должно иметь флаг `cancelable: true`, иначе вызов `event.preventDefault()` будет проигнорирован.
|
||||||
|
|
||||||
## Events-in-events are synchronous
|
## 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`:
|
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>
|
<button id="menu">Menu (click me)</button>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// 1 -> nested -> 2
|
|
||||||
menu.onclick = function() {
|
menu.onclick = function() {
|
||||||
alert(1);
|
alert(1);
|
||||||
|
|
||||||
|
@ -239,17 +238,18 @@ For instance, here the nested `menu-open` event is processed synchronously, duri
|
||||||
</script>
|
</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.
|
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
|
```html run
|
||||||
<button id="menu">Menu (click me)</button>
|
<button id="menu">Menu (click me)</button>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Now the result is: 1 -> 2 -> nested
|
|
||||||
menu.onclick = function() {
|
menu.onclick = function() {
|
||||||
alert(1);
|
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.
|
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
|
## Summary
|
||||||
|
|
||||||
To generate an event from code, we first need to create an event object.
|
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:
|
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.
|
- `bubbles: true` if the event should bubble.
|
||||||
- `cancelable: true` if the `event.preventDefault()` should work.
|
- `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.
|
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