> For the complete documentation index, see [llms.txt](https://andersenlab.gitbook.io/javascript/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://andersenlab.gitbook.io/javascript/js-in-browser/events.md).

# Events

## Браузерные события

Для реакции на действия посетителя и внутреннего взаимодействия скриптов существуют *события*.

*Событие*– это сигнал от браузера о том, что что-то произошло. Существует много видов событий. Посмотрим список самых часто используемых, пока просто для ознакомления:

**События мыши:**

* `click`– происходит, когда кликнули на элемент левой кнопкой мыши
* `contextmenu`– происходит, когда кликнули на элемент правой кнопкой мыши
* `mouseover`– возникает, когда на элемент наводится мышь
* `mousedown`и`mouseup`– когда кнопку мыши нажали или отжали
* `mousemove`– при движении мыши

**События на элементах управления:**

* `submit`– посетитель отправил форму`<form>`
* `focus`– посетитель фокусируется на элементе, например: нажимает на`<input>`

**Клавиатурные события:**

* `keydown`– когда посетитель нажимает клавишу
* `keyup`– когда посетитель отпускает клавишу

**События документа:**

* `DOMContentLoaded`– когда HTML загружен и обработан, DOM документа полностью построен и доступен.

**События CSS:**

* `transitionend`– когда CSS-анимация завершена.

### Назначение обработчиков событий

Событию можно назначить *обработчик*, то есть функцию, которая сработает, как только событие произошло.

Именно благодаря обработчикам JavaScript-код может реагировать на действия посетителя.

Есть несколько способов назначить событию обработчик. Сейчас мы их рассмотрим, начиная от самого простого.

#### Использование атрибута HTML

Обработчик может быть назначен прямо в разметке в атрибуте, который называется`on<событие>`.

Например, чтобы прикрепить`click`-событие к`input`кнопке, можно присвоить обработчик`onclick`таким образом:

```
<input value="Нажми меня" onclick="alert('Клик!')" type="button">
```

При клике мышкой, на кнопке выполнится код, указанный в атрибуте`onclick`.

Обратите внимание: для содержимого атрибута`onclick`используются *одинарные кавычки*, так как сам атрибут находится в двойных.

Частая ошибка новичков в том, что они забывают, что код находится внутри атрибута. Запись вида`onclick="alert("Клик!")"` с двойными кавычками внутри не будет работать. Если вам действительно нужно использовать именно двойные кавычки, то это можно сделать, заменив их на`&quot;`, то есть так:`onclick="alert(&quot;Клик!&quot;)"`.

Однако обычно этого не требуется, так как прямо в разметке пишутся только очень простые обработчики. Если нужно сделать что-то сложное, то имеет смысл описать это в функции, и в обработчике вызвать уже её.

Следующий пример по клику запускает функцию`countRabbits()`.

```javascript
<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">

  <script>
    function countRabbits() {
      for(var i=1; i<=3; i++) {
        alert("Кролик номер " + i);
      }
    }
  </script>
</head>
<body>
  <input type="button" onclick="countRabbits()" value="Считать кроликов!"/>
</body>
</html>
```

Как мы помним, атрибут HTML-тега не чувствителен к регистру, поэтому `ONCLICK` будет работать так же, как`onClick`или `onCLICK`… Но, как правило, атрибуты пишут в нижнем регистре:`onclick`.

#### Использование свойства DOM-объекта

Можно назначать обработчик, используя свойство DOM-элемента`on<событие>`.

Пример установки обработчика`click`:

```
<input id="elem" type="button" value="Нажми меня" />
<script>
  elem.onclick = function() {
    alert( 'Спасибо' );
  };
</script>
```

Если обработчик задан через атрибут, то браузер читает HTML-разметку, создаёт новую функцию из содержимого атрибута и записывает в свойство`onclick`.

**Этот способ, по сути, аналогичен предыдущему.**

Обработчик хранится именно в DOM-свойстве, а атрибут – лишь один из способов его инициализации.

Эти два примера кода работают одинаково:

```javascript
<input type="button" onclick="alert('Клик!')" value="Кнопка"/>
```

HTML + JS:

```
<input type="button" id="button" value="Кнопка" />
<script>
  button.onclick = function() {
    alert( 'Клик!' );
  };
</script>
```

**Так как DOM-свойство**`onclick`**, в итоге одно, то назначить более одного обработчика нельзя.**

В примере ниже назначение через JavaScript перезапишет обработчик из атрибута:

```
<input type="button" id="elem" onclick="alert('До')" value="Нажми меня" />
<script>
  elem.onclick = function() { // перезапишет существующий обработчик
    alert( 'После' ); // выведется только это
  };
</script>
```

Кстати, обработчиком можно назначить и уже существующую функцию:

```javascript
function sayThanks() {
  alert( 'Спасибо!' );
}

elem.onclick = sayThanks;
```

#### Доступ к элементу через this

Внутри обработчика события`this`ссылается на текущий элемент, то есть на тот, на котором он сработал.

Это можно использовать, чтобы получить свойства или изменить элемент.

В коде ниже`button`выводит свое содержимое, используя`this.innerHTML`:

```
<button onclick="alert(this.innerHTML)">Нажми меня</button>
```

#### Недостаток назначения через свойство

Фундаментальный недостаток описанных выше способов назначения обработчика – невозможность повесить *несколько* обработчиков на одно событие.

Например, одна часть кода хочет при клике на кнопку делать ее подсвеченной, а другая – выдавать сообщение. Нужно в разных местах два обработчика повесить.

При этом новый обработчик будет затирать предыдущий. Например, следующий код на самом деле назначает один обработчик – последний:

```javascript
input.onclick = function() { alert(1); }
// ...
input.onclick = function() { alert(2); } // заменит предыдущий обработчик
```

Разработчики стандартов достаточно давно это поняли и предложили альтернативный способ назначения обработчиков при помощи специальных методов, которые свободны от указанного недостатка.

#### addEventListener и removeEventListener

Методы`addEventListener`и`removeEventListener`являются современным способом назначить или удалить обработчик, и при этом позволяют использовать сколько угодно любых обработчиков.

Назначение обработчика осуществляется вызовом`addEventListener`с тремя аргументами:

```javascript
element.addEventListener(event, handler[, phase]);
```

`event`

Имя события, например`click`

`handler`

Ссылка на функцию, которую надо поставить обработчиком.

`phase`

Необязательный аргумент, «фаза», на которой обработчик должен сработать.

Удаление обработчика осуществляется вызовом`removeEventListener`:

```javascript
// передать те же аргументы, что были у addEventListener
element.removeEventListener(event, handler[, phase]);
```

Метод`addEventListener`позволяет добавлять несколько обработчиков на одно событие одного элемента, например:

```
<input id="elem" type="button" value="Нажми меня"/>

<script>
  function handler1() {
    alert('Спасибо!');
  };

  function handler2() {
    alert('Спасибо ещё раз!');
  }

  elem.onclick = function() { alert("Привет"); };
  elem.addEventListener("click", handler1); // Спасибо!
  elem.addEventListener("click", handler2); // Спасибо ещё раз!
</script>
```

Как видно из примера выше, можно одновременно назначать обработчики и через DOM-свойство, и через`addEventListener`. Однако во избежание путаницы, рекомендуется выбрать один способ.

## Порядок обработки событий

События могут возникать не только по очереди, но и «пачкой» по много сразу. Возможно и такое, что во время обработки одного события возникают другие, например пока выполнялся код для`onclick`– посетитель нажал кнопку на клавиатуре (событие`keydown`).

### Главный поток

В каждом окне выполняется только один *главный* поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM.

Он выполняет команды последовательно, может делать только одно дело одновременно и блокируется при выводе модальных окон, таких как`alert`.

### Очередь событий

Произошло одновременно несколько событий или во время работы одного случилось другое – как главному потоку обработать это?

Если главный поток прямо сейчас занят, то он не может срочно выйти из середины одной функции и прыгнуть в другую. А потом третью. Отладка при этом могла бы превратиться в кошмар, потому что пришлось бы разбираться с совместным состоянием нескольких функций сразу.

**Когда происходит событие, оно попадает в очередь.**

Внутри браузера непрерывно работает «главный внутренний цикл», который следит за состоянием очереди и обрабатывает события, запускает соответствующие обработчики и т.п.

**Иногда события добавляются в очередь сразу пачкой.**

Например, при клике на элементе генерируется несколько событий:

Сначала

1. `mousedown`– нажата кнопка мыши.
2. Затем`mouseup`– кнопка мыши отпущена, и так как это было над одним элементом, то дополнительно генерируется`click`(два события сразу).

```
<textarea rows="8" cols="40" id="area">Кликни меня
</textarea>

<script>
  area.onmousedown = function(e) { this.value += "mousedown\n"; this.scrollTop = this.scrollHeight; };
  area.onmouseup = function(e) { this.value += "mouseup\n"; this.scrollTop = this.scrollHeight; };
  area.onclick = function(e) { this.value += "click\n"; this.scrollTop = this.scrollHeight; };
</script>
```

Таким образом, при нажатии кнопки мыши в очередь попадёт событие`mousedown`, а при отпускании – сразу два события:`mouseup`и`click`. Браузер обработает их строго одно за другим:`mousedown`→`mouseup`→`click`.

При этом каждое событие из очереди обрабатывается полностью отдельно от других.

### Вложенные (синхронные) события

Обычно возникающие события «становятся в очередь».

Но в тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.

Рассмотрим в качестве примера событие`onfocus`.

#### Пример: событие onfocus

Когда посетитель фокусируется на элементе, возникает событие`onfocus`. Обычно оно происходит, когда посетитель кликает на поле ввода, например:

```
<p>При фокусе на поле оно изменит значение.</p>
<input type="text" onfocus="this.value = 'Фокус!'" value="Кликни меня">
```

При фокусе на поле, оно изменит значение.

Но ту же фокусировку можно вызвать и явно, вызовом метода`elem.focus()`:

```
<input type="text" id="elem" onfocus="this.value = 'Фокус!'">

<script>
  // сфокусируется на input и вызовет обработчик onfocus
  elem.focus();
</script>
```

При этом обработчик`onclick`вызовет метод`focus()`на текстовом поле`text`. Код обработчика`onfocus`, который при этом запустится, сработает синхронно, прямо сейчас, до завершения`onclick`.

```
<input type="button" id="button" value="Нажми меня">
<input type="text" id="text" size="60">

<script>

  button.onclick = function() {
    text.value += ' ->в onclick ';

    text.focus(); // вызов инициирует событие onfocus

    text.value += ' из onclick-> ';
  };

  text.onfocus = function() {
    text.value += ' !focus! ';
  };
</script>
```

## Объект события

Чтобы хорошо обработать событие, недостаточно знать о том, что это – «клик» или «нажатие клавиши». Могут понадобиться детали: координаты курсора, введенный символ и другие, в зависимости от события.

Детали произошедшего браузер записывает в «объект события», который передается первым аргументом в обработчик.

### Свойства объекта события

Пример ниже демонстрирует использование объекта события:

```
<input type="button" value="Нажми меня" id="elem">

<script>
  elem.onclick = function(event) {
    // вывести тип события, элемент и координаты клика
    alert(event.type + " на " + event.currentTarget);
    alert(event.clientX + ":" + event.clientY);
  }
</script>
```

Свойства объекта`event`:

`event.type`

Тип события, в данном случае`click`

`event.currentTarget`

Элемент, на котором сработал обработчик. Значение – в точности такое же, как и у`this`, но бывают ситуации, когда обработчик является методом объекта, и его`this`при помощи`bind`привязан к этому объекту, тогда мы можем использовать`event.currentTarget`.

`event.clientX / event.clientY`

Координаты курсора в момент клика (относительно окна).

## Всплытие и перехват

Основной принцип всплытия:

**При наступлении события, обработчики сначала срабатывают на самом вложенном элементе, затем на его родителе, затем выше и так далее, вверх по цепочке вложенности.**

Например, есть 3 вложенных элемента`FORM > DIV > P` с обработчиком на каждом:

```
<style>
  body * {
    margin: 10px;
    border: 1px solid blue;
  }
</style>

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>
```

Всплытие гарантирует, что клик по внутреннему`<p>`вызовет обработчик`onclick`(если есть) сначала на самом`<p>`, затем на элементе`<div>,`далее на элементе`<form>`и так далее вверх по цепочке родителей до самого`document`.

![](/files/-LBffC8N6LF2YcHwLsiD)

Поэтому если в примере выше кликнуть на`P`, то последовательно выведутся`alert`:`p`→`div`→`form`.

Этот процесс называется *всплытием*, потому что события «всплывают» от внутреннего элемента вверх через родителей.

### Целевой элемент event.target

На каком бы элементе мы ни поймали событие, всегда можно узнать, где конкретно оно произошло.

**Самый глубокий элемент, который вызывает событие, называется&#x20;*****«целевым»*****&#x20;или&#x20;*****«исходным»*****&#x20;элементом и доступен как** `event.target`**.**

Отличия от`this`(=`event.currentTarget`):

* `event.target`– это **исходный элемент**, на котором произошло событие, в процессе всплытия он неизменен.
* `this`– это **текущий элемент**, до которого дошло всплытие, на нем сейчас выполняется обработчик.

Например, если стоит только один обработчик`form.onclick`, то он «поймает» все клики внутри формы. Где бы ни был клик внутри – он всплывет до элемента`<form>`, на котором сработает обработчик.

При этом:

* `this`(`=event.currentTarget`) всегда будет сама форма, так как обработчик сработал на ней.
* `event.target`будет содержать ссылку на конкретный элемент внутри формы, самый вложенный, на котором произошел клик.

Возможна и ситуация, когда`event.target`и`this`– один и тот же элемент, например, если в форме нет других тегов и клик был на самом элементе`<form>`.

### Прекращение всплытия

Всплытие идет прямо наверх. Обычно событие будет всплывать вверх и вверх, до элемента`<html>`, а затем до`document`, а иногда даже до`window`, вызывая все обработчики на своем пути.

**Но любой промежуточный обработчик может решить, что событие полностью обработано, и остановить всплытие.**

Для остановки всплытия нужно вызвать метод`event.stopPropagation()`.

Например, здесь при клике на кнопку обработчик`body.onclick`не сработает:

```
<body onclick="alert('сюда обработка не дойдёт')">
  <button onclick="event.stopPropagation()">Кликни меня</button>
</body>
```

### Погружение

В современном стандарте, кроме «всплытия» событий, предусмотрено еще и «погружение».

Оно гораздо менее востребовано, но иногда, очень редко, знание о нем может быть полезным.

Строго говоря, стандарт выделяет целых три стадии прохода события:

Событие сначала идет сверху вниз. Эта стадия называется

1. *«стадия перехвата»*(capturing stage).
2. Событие достигло целевого элемента. Это –*«стадия цели»*(target stage).
3. После этого событие начинает всплывать. Это –*«стадия всплытия»*(bubbling stage).

В [стандарте DOM Events 3](http://www.w3.org/TR/DOM-Level-3-Events/) это продемонстрировано так:

![](/files/-LBffDXB3dxdGZ2tQVQc)

То есть, при клике на`TD,`событие путешествует по цепочке родителей сначала вниз к элементу («погружается»), а потом наверх («всплывает»), задействуя обработчики.

Обработчики, добавленные через`on...`-свойство, ничего не знают о стадии перехвата, а начинают работать со всплытия.

Чтобы поймать событие на стадии перехвата, нужно использовать третий аргумент`addEventListener`:

Если аргумент

* `true`, то событие будет перехвачено по дороге вниз.
* Если аргумент`false`, то событие будет поймано при всплытии.

## Делегирование событий

Всплытие событий позволяет реализовать один из самых важных приемов разработки –*делегирование*.

Он заключается в том, что если у нас есть много элементов, события на которых нужно обрабатывать похожим образом, то вместо того, чтобы назначать обработчик каждому – мы ставим один обработчик на их общего предка. Из него можно получить целевой элемент`event.target`, понять, на каком именно потомке произошло событие, и обработать его.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://andersenlab.gitbook.io/javascript/js-in-browser/events.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
