up
This commit is contained in:
parent
4ae129054e
commit
ab9ab64bd5
476 changed files with 3370 additions and 532 deletions
388
2-ui/2-events/01-introduction-browser-events/article.md
Normal file
388
2-ui/2-events/01-introduction-browser-events/article.md
Normal file
|
@ -0,0 +1,388 @@
|
|||
# Introduction to browser events
|
||||
|
||||
*An event* is a signal that something has happened. All DOM nodes generate such signals (but events are not limited to DOM).
|
||||
|
||||
[cut]
|
||||
|
||||
Here's a list of the most useful DOM events, just to take a look at:
|
||||
|
||||
**Mouse events:**
|
||||
- `click` -- when the mouse clicks on an element (touchscreen devices generate it on a tap).
|
||||
- `contextmenu` -- when the mouse right-clicks on an element.
|
||||
- `mouseover` -- when the mouse cursor comes over an element.
|
||||
- `mousedown` and `mouseup` -- when the mouse button is pressed and released over an element.
|
||||
- `mousemove` -- when the mouse is moved.
|
||||
|
||||
**Form element events:**
|
||||
- `submit` -- when the visitor submits a `<form>`.
|
||||
- `focus` -- when the visitor focuses on an element, e.g. on an `<input>`.
|
||||
|
||||
**Keyboard events:**
|
||||
- `keydown` and `keyup` -- when the visitor presses and then releases the button.
|
||||
|
||||
**Document events**
|
||||
- `DOMContentLoaded` -- when the HTML is loaded and processed, DOM is fully built.
|
||||
|
||||
**CSS events:**
|
||||
- `transitionend` -- when a CSS-animation finishes.
|
||||
|
||||
There are many other events.
|
||||
|
||||
## Event handlers
|
||||
|
||||
To react on events we can assign a *handler* -- a function that runs in case of an event.
|
||||
|
||||
Handlers is a way to run JavaScript code in case of user actions.
|
||||
|
||||
There are several ways to assign a handler. Let's see them, starting from the simplest one.
|
||||
|
||||
### HTML-attribute
|
||||
|
||||
A handler can be set in HTML with an attribute named `on<event>`.
|
||||
|
||||
For instance, to assign a `click` handler for an `input`, we can use `onclick`, like here:
|
||||
|
||||
```html run
|
||||
<input value="Click me" *!*onclick="alert('Click!')"*/!* type="button">
|
||||
```
|
||||
|
||||
On mouse click, the code inside `onclick` runs.
|
||||
|
||||
Please note that inside `onclick` we use single quotes, because the attribute itself is in double quotes. If we fforget that the code is inside the attribute and use double quotes inside, like this: `onclick="alert("Click!")"`, then it won't work right.
|
||||
|
||||
An HTML-attribute is not a convenient place to write a lot of code, so we'd better create a JavaScript function and call it there.
|
||||
|
||||
Here a click runs the function `countRabbits()`:
|
||||
|
||||
```html autorun height=50
|
||||
<script>
|
||||
function countRabbits() {
|
||||
for(let i=1; i<=3; i++) {
|
||||
alert("Rabbit number " + i);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<input type="button" *!*onclick="countRabbits()"*/!* value="Count rabbits!">
|
||||
```
|
||||
|
||||
As we know, HTML attribute names are not case-sensitive, so `ONCLICK` works as well as `onClick` and `onCLICK`... But usually attributes are lowercased: `onclick`.
|
||||
|
||||
### DOM property
|
||||
|
||||
We can assign a handler using a DOM property `on<event>`.
|
||||
|
||||
For instance, `elem.onclick`:
|
||||
|
||||
```html autorun
|
||||
<input id="elem" type="button" value="Click me">
|
||||
<script>
|
||||
*!*
|
||||
elem.onclick = function() {
|
||||
alert('Thank you');
|
||||
};
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
If the handler is assigned using an HTML-attribute then the browser reads it, creates a new function from the attribute content and writes it to the DOM property.
|
||||
|
||||
So this way is actually the same as the previous one.
|
||||
|
||||
**The handler is always in the DOM property: the HTML-attribute is just one of the ways to initialize it.**
|
||||
|
||||
These two code pieces work the same:
|
||||
|
||||
1. Only HTML:
|
||||
|
||||
```html autorun height=50
|
||||
<input type="button" *!*onclick="alert('Click!')"*/!* value="Button">
|
||||
```
|
||||
2. HTML + JS:
|
||||
|
||||
```html autorun height=50
|
||||
<input type="button" id="button" value="Button">
|
||||
<script>
|
||||
*!*
|
||||
button.onclick = function() {
|
||||
alert('Click!');
|
||||
};
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
**As there's only one `onclick` property, we can't assign more than one event handler.**
|
||||
|
||||
In the example below adding a handler with JavaScript overwrites the existing handler:
|
||||
|
||||
```html run height=50 autorun
|
||||
<input type="button" id="elem" onclick="alert('Before')" value="Click me">
|
||||
<script>
|
||||
*!*
|
||||
elem.onclick = function() { // overwrites the existing handler
|
||||
alert('After'); // only this will be shown
|
||||
};
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
By the way, we can assign an existing function as a handler directly:
|
||||
|
||||
```js
|
||||
function sayThanks() {
|
||||
alert('Thanks!');
|
||||
}
|
||||
|
||||
elem.onclick = sayThanks;
|
||||
```
|
||||
|
||||
To remove a handler -- assign `elem.onclick = null`.
|
||||
|
||||
## Accessing the element: this
|
||||
|
||||
The value of `this` inside a handler is the element. The one which has the handler on it.
|
||||
|
||||
In the code below `button` shows its contents using `this.innerHTML`:
|
||||
|
||||
```html height=50 autorun
|
||||
<button onclick="alert(this.innerHTML)">Click me</button>
|
||||
```
|
||||
|
||||
## Possible mistakes
|
||||
|
||||
If you're starting to work with event -- please note some subtleties.
|
||||
|
||||
**The function should be assigned as `sayThanks`, not `sayThanks()`.**
|
||||
|
||||
```js
|
||||
// right
|
||||
button.onclick = sayThanks;
|
||||
|
||||
// wrong
|
||||
button.onclick = sayThanks();
|
||||
```
|
||||
|
||||
If we add brackets, then `sayThanks()` -- will be the *result* of the function execution, so `onclick` in the last code becomes `undefined` (the function returns nothing). That won't work.
|
||||
|
||||
...But in the markup we do need the brackets:
|
||||
|
||||
```html
|
||||
<input type="button" id="button" onclick="sayThanks()">
|
||||
```
|
||||
|
||||
The difference is easy to explain. When the browser reads the attribute, it creates a handler function with the body from its content.
|
||||
|
||||
So the last example is the same as:
|
||||
```js
|
||||
button.onclick = function() {
|
||||
*!*
|
||||
sayThanks(); // the attribute content
|
||||
*/!*
|
||||
};
|
||||
```
|
||||
|
||||
**Use functions, not strings.**
|
||||
|
||||
The assignment `elem.onclick = "alert(1)"` would work too. It works for compatibility reasons, but strongly not recommended.
|
||||
|
||||
**Don't use `setAttribute` for handlers.**
|
||||
|
||||
Such a call won't work:
|
||||
|
||||
```js run no-beautify
|
||||
// a click on <body> will generate errors,
|
||||
// because attributes are always strings, function becomes a string
|
||||
document.body.setAttribute('onclick', function() { alert(1) });
|
||||
```
|
||||
|
||||
**DOM-property case matters.**
|
||||
|
||||
Assign a handler to `elem.onclick`, not `elem.ONCLICK`, because DOM properties are case-sensitive.
|
||||
|
||||
## addEventListener
|
||||
|
||||
The fundamental problem of the aforementioned ways to assign handlers -- we can't assign multiple handlers to one event.
|
||||
|
||||
For instance, one part of our code wants to highlight a button on click, and another one wants to show a message.
|
||||
|
||||
We'd like to assign two event handlers for that. But a new DOM property will overwrite the existing one:
|
||||
|
||||
```js no-beautify
|
||||
input.onclick = function() { alert(1); }
|
||||
// ...
|
||||
input.onclick = function() { alert(2); } // replaces the previous handler
|
||||
```
|
||||
|
||||
Web-standard developers understood that long ago and suggested an alternative way of managing handlers using special methods `addEventListener` and `removeEventListener`. They are free of such a problem.
|
||||
|
||||
The syntax to add a handler:
|
||||
|
||||
```js
|
||||
element.addEventListener(event, handler[, phase]);
|
||||
```
|
||||
|
||||
`event`
|
||||
: Event name, e.g. `"click"`.
|
||||
|
||||
`handler`
|
||||
: The handler function.
|
||||
|
||||
`phase`
|
||||
: An optional argument, the "phase" for the handler to work. To be covered later. Usually we don't use it.
|
||||
|
||||
To remove the handler, use `removeEventListener`:
|
||||
|
||||
|
||||
```js
|
||||
// exactly the same arguments as addEventListener
|
||||
element.removeEventListener(event, handler[, phase]);
|
||||
```
|
||||
|
||||
````warn header="Removal requires the same function"
|
||||
To remove a handler we should pass exactly the same function as was assigned.
|
||||
|
||||
That doesn't work:
|
||||
|
||||
```js no-beautify
|
||||
elem.addEventListener( "click" , () => alert('Thanks!'));
|
||||
// ....
|
||||
elem.removeEventListener( "click", () => alert('Thanks!'));
|
||||
```
|
||||
|
||||
The handler won't be removed, because `removeEventListener` gets another function -- with the same code, but that doesn't matter.
|
||||
|
||||
Here's the right way:
|
||||
|
||||
```js
|
||||
function handler() {
|
||||
alert( 'Thanks!' );
|
||||
}
|
||||
|
||||
input.addEventListener("click", handler);
|
||||
// ....
|
||||
input.removeEventListener("click", handler);
|
||||
```
|
||||
|
||||
Please note -- if we don't store the function in a variable, then we can't remove it. There's no way to "read back" handlers assigned by `addEventListener`.
|
||||
````
|
||||
|
||||
Multiple calls to `addEventListener` allow to add multiple handlers, like this:
|
||||
|
||||
```html run no-beautify
|
||||
<input id="elem" type="button" value="Click me"/>
|
||||
|
||||
<script>
|
||||
function handler1() {
|
||||
alert('Thanks!');
|
||||
};
|
||||
|
||||
function handler2() {
|
||||
alert('Thanks again!');
|
||||
}
|
||||
|
||||
*!*
|
||||
elem.onclick = () => alert("Hello");
|
||||
elem.addEventListener("click", handler1); // Thanks!
|
||||
elem.addEventListener("click", handler2); // Thanks again!
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
As we can see in the example above, we can set handlers *both* using a DOM-property and `addEventListener`. But generally we use only one of these ways.
|
||||
|
||||
````warn header="For some events handlers only work with `addEventListener`"
|
||||
There exist events that can't be assigned via a DOM-property. Must use `addEventListener`.
|
||||
|
||||
For instance, the event `transitionend` (CSS animation finished) is like that.
|
||||
|
||||
Try the code below. In most browsers only the second handler works, not the first one.
|
||||
|
||||
```html run
|
||||
<style>
|
||||
input {
|
||||
transition: width 1s;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.wide {
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<input type="button" id="elem" onclick="this.classList.toggle('wide')" value="Click me">
|
||||
|
||||
<script>
|
||||
elem.ontransitionend = function() {
|
||||
alert("DOM property"); // doesn't work
|
||||
};
|
||||
|
||||
*!*
|
||||
elem.addEventListener("transitionend", function() {
|
||||
alert("addEventListener"); // shows up when the animation finishes
|
||||
});
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
````
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
When an event happens, the browser creates an *event object*, puts details into it and passes it as an argument to the handler.
|
||||
|
||||
Here's an example of getting mouse coordinates from the event object:
|
||||
|
||||
```html run
|
||||
<input type="button" value="Click me" id="elem">
|
||||
|
||||
<script>
|
||||
elem.onclick = function(*!*event*/!*) {
|
||||
// show event type, element and coordinates of the click
|
||||
alert(event.type + " at " + event.currentTarget);
|
||||
alert(event.clientX + ":" + event.clientY);
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
Some properties of `event` object:
|
||||
|
||||
`event.type`
|
||||
: Event type, here it's `"click"`.
|
||||
|
||||
`event.currentTarget`
|
||||
: Element that handled the event. That's exactly the same as `this`, unless you bind `this` to something else, and then `event.currentTarget` becomes useful.
|
||||
|
||||
`event.clientX / event.clientY`
|
||||
: Window-relative coordinates of the cursor, for mouse events.
|
||||
|
||||
There are more properties. They depend on the event type, so we'll study them later when come to different events in details.
|
||||
|
||||
````smart header="The event object is also accessible from HTML"
|
||||
If we assign a handler in HTML, we can also use the `event` object, like this:
|
||||
|
||||
```html autorun height=60
|
||||
<input type="button" onclick="*!*alert(event.type)*/!*" value="Event type">
|
||||
```
|
||||
|
||||
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.
|
||||
````
|
||||
|
||||
## Summary
|
||||
|
||||
There are 3 ways to assign event handlers:
|
||||
|
||||
1. HTML attribute: `onclick="..."`.
|
||||
2. DOM property: `elem.onclick = function`.
|
||||
3. Methods `elem.addEventListener(event, handler[, phase])`, to remove: `removeEventListener`.
|
||||
|
||||
HTML attributes are used sparingly, because JavaScript in the middle of an HTML tag looks a little bit odd and alien. Also can't write lots of code in there.
|
||||
|
||||
DOM properties are ok to use, but we can't assign more than one handler of the particular event. In many cases that limitation is not pressing.
|
||||
|
||||
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.
|
||||
|
||||
As of now we're just starting to work with events. More details in the next chapters.
|
Loading…
Add table
Add a link
Reference in a new issue