handleEvent

This commit is contained in:
Ilya Kantor 2017-06-17 18:01:43 +03:00
parent ae1a1a0941
commit 749e6e164b
2 changed files with 107 additions and 22 deletions

View file

@ -1,6 +1,6 @@
# Constructor, operator "new" # Constructor, operator "new"
The regular `{...}` syntax allows to create one object. But often we need to create many similar objects. The regular `{...}` syntax allows to create one object. But often we need to create many similar objects, like multiple users or menu items and so on.
That can be done using constructor functions and the `"new"` operator. That can be done using constructor functions and the `"new"` operator.
@ -32,7 +32,7 @@ alert(user.isAdmin); // false
When a function is executed as `new User(...)`, it does the following steps: When a function is executed as `new User(...)`, it does the following steps:
1. A new empty object is created and assigned to `this`. 1. A new empty object is created and assigned to `this`.
2. The function executes. Usually it modifies `this`, adds new properties to it. 2. The function body executes. Usually it modifies `this`, adds new properties to it.
3. The value of `this` is returned. 3. The value of `this` is returned.
In other words, `new User(...)` does something like: In other words, `new User(...)` does something like:
@ -43,7 +43,7 @@ function User(name) {
// this = {}; (implicitly) // this = {}; (implicitly)
*/!* */!*
// we add properties to this // add properties to this
this.name = name; this.name = name;
this.isAdmin = false; this.isAdmin = false;
@ -62,7 +62,7 @@ let user = {
}; };
``` ```
Now if we want to create other users, we can call `new User("Ann")`, `new User("Alice")` and so on. Much shorter than using literals every time, and also reads well. Now if we want to create other users, we can call `new User("Ann")`, `new User("Alice")` and so on. Much shorter than using literals every time, and also easy to read.
That's the main purpose of constructors -- to implement reusable object creation code. That's the main purpose of constructors -- to implement reusable object creation code.
@ -82,26 +82,28 @@ let user = new function() {
}; };
``` ```
The constructor can't be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code for a single complex object only. The constructor can't be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code that constructs the single object, without future reuse.
```` ````
## Dual-use constructors: new.target ## Dual-syntax constructors: new.target
Inside a function, we can check whether it was called with `new` or without it, using a special `new.target` property. Inside a function, we can check whether it was called with `new` or without it, using a special `new.target` property.
It is empty for ordinary runs and equals the function if called with `new`: It is empty for regulsar calls and equals the function if called with `new`:
```js run ```js run
function User() { function User() {
alert(new.target); alert(new.target);
} }
// without new:
User(); // undefined User(); // undefined
// with new:
new User(); // function User { ... } new User(); // function User { ... }
``` ```
That can be used to allow both `new` and ordinary syntax work the same: That can be used to allow both `new` and regular syntax work the same:
```js run ```js run
function User(name) { function User(name) {
@ -116,7 +118,7 @@ let john = User("John"); // redirects call to new User
alert(john.name); // John alert(john.name); // John
``` ```
This approach is sometimes used in libraries to make the syntax more flexible. Probably not a good thing to use everywhere though, because it makes a bit less obvious what's going on for a person who's familiar with the internals of `User`. This approach is sometimes used in libraries to make the syntax more flexible. Probably not a good thing to use everywhere though, because omitting `new` makes a bit less obvious what's going on. With `new` we all know that the new object is being created, that's a good thing.
## Return from constructors ## Return from constructors
@ -127,7 +129,7 @@ But if there is a `return` statement, then the rule is simple:
- If `return` is called with object, then it is returned instead of `this`. - If `return` is called with object, then it is returned instead of `this`.
- If `return` is called with a primitive, it's ignored. - If `return` is called with a primitive, it's ignored.
In other words, `return` with an object returns that object, otherwise `this` is returned. In other words, `return` with an object returns that object, in all other cases `this` is returned.
For instance, here `return` overrides `this` by returning an object: For instance, here `return` overrides `this` by returning an object:
@ -139,7 +141,7 @@ function BigUser() {
return { name: "Godzilla" }; // <-- returns an object return { name: "Godzilla" }; // <-- returns an object
} }
alert( new BigUser().name ); // Godzilla, got that object alert( new BigUser().name ); // Godzilla, got that object ^^
``` ```
And here's an example with an empty `return` (or we could place a primitive after it, doesn't matter): And here's an example with an empty `return` (or we could place a primitive after it, doesn't matter):
@ -158,7 +160,7 @@ function SmallUser() {
alert( new SmallUser().name ); // John alert( new SmallUser().name ); // John
``` ```
Most of the time constructors return nothing. Here we mention the special behavior with returning objects mainly for the sake of completeness. Usually constructors don't have a `return` statement. Here we mention the special behavior with returning objects mainly for the sake of completeness.
````smart header="Omitting brackets" ````smart header="Omitting brackets"
By the way, we can omit brackets after `new`, if it has no arguments: By the way, we can omit brackets after `new`, if it has no arguments:
@ -208,12 +210,12 @@ john = {
- Constructor functions or, shortly, constructors, are regular functions, but there's a common agreement to name them with capital letter first. - Constructor functions or, shortly, constructors, are regular functions, but there's a common agreement to name them with capital letter first.
- Constructor functions should only be called using `new`. Such call implies a creation of empty `this` at the start and returning the populated one at the end. - Constructor functions should only be called using `new`. Such call implies a creation of empty `this` at the start and returning the populated one at the end.
We can use constructor functions to make multiple similar objects. We can use constructor functions to make multiple similar objects.
JavaScript provides constructor functions for many built-in language objects: like `Date` for dates, `Set` for sets and others that we plan to study. JavaScript provides constructor functions for many built-in language objects: like `Date` for dates, `Set` for sets and others that we plan to study.
```smart header="Objects, we'll be back!" ```smart header="Objects, we'll be back!"
In this chapter we only cover the basics about objects. They are essential for learning more about data types and functions in the next chapters. In this chapter we only cover the basics about objects and constructors. They are essential for learning more about data types and functions in the next chapters.
After we learn that, in the chapter <info:object-oriented-programming> we return to objects and cover them in-depth, including inheritance and classes. After we learn that, in the chapter <info:object-oriented-programming> we return to objects and cover them in-depth, including inheritance and classes.
``` ```

View file

@ -9,8 +9,8 @@ Here's a list of the most useful DOM events, just to take a look at:
**Mouse events:** **Mouse events:**
- `click` -- when the mouse clicks on an element (touchscreen devices generate it on a tap). - `click` -- when the mouse clicks on an element (touchscreen devices generate it on a tap).
- `contextmenu` -- when the mouse right-clicks on an element. - `contextmenu` -- when the mouse right-clicks on an element.
- `mouseover` -- when the mouse cursor comes over an element. - `mouseover` / `mouseout` -- when the mouse cursor comes over / leaves an element.
- `mousedown` and `mouseup` -- when the mouse button is pressed and released over an element. - `mousedown` / `mouseup` -- when the mouse button is pressed / released over an element.
- `mousemove` -- when the mouse is moved. - `mousemove` -- when the mouse is moved.
**Form element events:** **Form element events:**
@ -26,7 +26,7 @@ Here's a list of the most useful DOM events, just to take a look at:
**CSS events:** **CSS events:**
- `transitionend` -- when a CSS-animation finishes. - `transitionend` -- when a CSS-animation finishes.
There are many other events. There are many other events. We'll get into more details of particular events in next chapters.
## Event handlers ## Event handlers
@ -325,7 +325,6 @@ Try the code below. In most browsers only the second handler works, not the firs
``` ```
```` ````
## Event object ## Event object
To properly handle an event we'd want to know more about what's happened. Not just a "click" or a "keypress", but what were the pointer coordinates? Which key was pressed? And so on. To properly handle an event we'd want to know more about what's happened. Not just a "click" or a "keypress", but what were the pointer coordinates? Which key was pressed? And so on.
@ -341,7 +340,7 @@ Here's an example of getting mouse coordinates from the event object:
elem.onclick = function(*!*event*/!*) { elem.onclick = function(*!*event*/!*) {
// show event type, element and coordinates of the click // show event type, element and coordinates of the click
alert(event.type + " at " + event.currentTarget); alert(event.type + " at " + event.currentTarget);
alert(event.clientX + ":" + event.clientY); alert("Coordinates: " + event.clientX + ":" + event.clientY);
}; };
</script> </script>
``` ```
@ -369,6 +368,88 @@ If we assign a handler in HTML, we can also use the `event` object, like this:
That's possible because when the browser reads the attribute, it creates a handler like this: `function(event) { alert(event.type) }`. That is: its first argument is called `"event"`, and the body is taken from the attribute. That's possible because when the browser reads the attribute, it creates a handler like this: `function(event) { alert(event.type) }`. That is: its first argument is called `"event"`, and the body is taken from the attribute.
```` ````
## Object handlers: handleEvent
We can assign an object as an event handler using `addEventListener`. When an event occurs, its `handleEvent` method is called with it.
For instance:
```html run
<button id="elem">Click me</button>
<script>
elem.addEventListener('click', {
handleEvent(event) {
alert(event.type + " at " + event.currentTarget);
}
});
</script>
```
In other words, when `addEventListener` receives and object as the handler, it calls `object.handleEvent(event)` in case of an event.
We could also use a class for that:
```html run
<button id="elem">Click me</button>
<script>
class Menu {
handleEvent(event) {
switch(event.type) {
case 'mousedown':
elem.innerHTML = "Mouse button pressed";
break;
case 'mouseup':
elem.innerHTML += "...and released.";
break;
}
}
}
*!*
let menu = new Menu();
elem.addEventListener('mousedown', menu);
elem.addEventListener('mouseup', menu);
*/!*
</script>
```
Here the same object handles both events. Please note that we need to explicitly setup the listeners. The `menu` object only gets `mousedown` and `mouseup` here, not any other types of events.
The method `handleEvent` does not have to handle everything by itself. It can call the corresponding event-specific instead, like this:
```html run
<button id="elem">Click me</button>
<script>
class Menu {
handleEvent(event) {
// mousedown -> onMousedown
let method = 'on' + event.type[0].toUpperCase() + event.type.slice(1);
this[method](event);
}
onMousedown() {
elem.innerHTML = "Mouse button pressed";
}
onMouseup() {
elem.innerHTML += "...and released.";
}
}
let menu = new Menu();
elem.addEventListener('mousedown', menu);
elem.addEventListener('mouseup', menu);
</script>
```
Now event handlers are clearly separated, that may be easier to support.
## Summary ## Summary
There are 3 ways to assign event handlers: There are 3 ways to assign event handlers:
@ -383,6 +464,8 @@ DOM properties are ok to use, but we can't assign more than one handler of the p
The last way is the most flexible, but it is also the longest to write. There are few events that only work with it, for instance `transtionend` and `DOMContentLoaded` (to be covered). The last way is the most flexible, but it is also the longest to write. There are few events that only work with it, for instance `transtionend` and `DOMContentLoaded` (to be covered).
When a handler is called, it gets an event objects as the first argument. It contains details about what's happened. We'll see more of them later. Also `addEventListener` supports objects as event handlers. In that case the method `handleEvent` is called in case of the event.
As of now we're just starting to work with events. More details in the next chapters. When a handler is called, it gets an event object as the first argument that contains the details about what's happened.
We'll learn more about events in general and about different types of events in the next chapters.