1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
|
---
title: Введение в события
slug: Learn/JavaScript/Building_blocks/Events
tags:
- Изучение
- Обработчик событий
- Руководство
- события
translation_of: Learn/JavaScript/Building_blocks/Events
original_slug: Learn/JavaScript/Building_blocks/События
---
<div>{{LearnSidebar}}</div>
<div>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "Learn/JavaScript/Building_blocks")}}</div>
<p class="summary">События — это действия или случаи, возникающие в программируемой вами системе, о которых система сообщает вам для того, чтобы вы могли с ними взаимодействовать. Например, если пользователь нажимает кнопку на веб-странице, вы можете ответить на это действие, отобразив информационное окно. В этой статье мы обсудим некоторые важные концепции, связанные с событиями, и посмотрим, как они работают в браузерах. Эта статья не является исчерпывающим источником по этой теме — здесь только то, что вам нужно знать на этом этапе.</p>
<table class="learn-box standard-table">
<tbody>
<tr>
<th scope="row">Предпосылки:</th>
<td>Базовая компьютерная грамотность, базовое понимание HTML и CSS, <a href="/ru/docs/Learn/JavaScript/Первые_шаги">Первые шаги в JavaScript</a>.</td>
</tr>
<tr>
<th scope="row">Задача:</th>
<td>Понять фундаментальную теорию событий, как они работают в браузерах и как события могут различаться в разных средах программирования.</td>
</tr>
</tbody>
</table>
<h2 id="Серия_удачных_событий">Серия удачных событий</h2>
<p>При возникновении <strong>события</strong> система генерирует сигнал, а также предоставляет механизм, с помощью которого можно автоматически предпринимать какие-либо действия (например, выполнить определенный код), когда происходит событие. Например, в аэропорту, когда взлетно-посадочная полоса свободна для взлета самолета, сигнал передается пилоту, и в результате они приступают к взлету.</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/17376/MDN-mozilla-events-runway.png" style="height: 443px; width: 700px;"></p>
<p>В Web события запускаются внутри окна браузера и, как правило, прикрепляются к конкретному элементу, который в нем находится. Это может быть один элемент, набор элементов, документ HTML, загруженный на текущей вкладке, или все окно браузера. Существует множество различных видов событий, которые могут произойти, например:</p>
<ul>
<li>Пользователь кликает мышью или наводит курсор на определенный элемент.</li>
<li>Пользователь нажимает клавишу на клавиатуре.</li>
<li>Пользователь изменяет размер или закрывает окно браузера.</li>
<li>Завершение загрузки веб-страницы.</li>
<li>Отправка данных через формы.</li>
<li>Воспроизведение видео, пауза или завершение воспроизведения.</li>
<li>Произошла ошибка.</li>
</ul>
<p>Подробнее о событиях можно посмотреть в <a href="/ru/docs/Web/Events">Справочнике по событиям</a>.</p>
<p><span>Каждое доступное событие имеет </span><strong>обработчик событий<strong> </strong></strong>—<span> блок кода (обычно это функция JavaScript, вводимая вами в качестве разработчика), который будет запускаться при срабатывании события. Когда такой блок кода определен на запуск в ответ на возникновение события, мы говорим, что мы </span><strong>регистрируем обработчик событий</strong><span>. Обратите внимание, что обработчики событий иногда называют </span><strong>прослушивателями событий (event listeners).</strong><span> Они в значительной степени взаимозаменяемы для наших целей, хотя, строго говоря, они работают вместе. Прослушиватель отслеживает событие, а обработчик </span>— <span>это код, который запускается в ответ на событие.</span></p>
<div class="note">
<p><strong>Примечание:</strong> Важно отметить, что веб-события не являются частью основного языка JavaScript. Они определены как часть JavaScript-API, встроенных в браузер.</p>
</div>
<h3 id="Простой_пример">Простой пример</h3>
<p>Рассмотрим простой пример. Вы уже видели события и обработчики событий во многих примерах в этом курсе, но давайте повторим для закрепления информации. В этом примере у нас есть кнопка {{htmlelement ("button")}}, при нажатии которой цвет фона изменяется случайным образом:</p>
<pre class="brush: html notranslate"><button>Change color</button></pre>
<div class="hidden">
<pre class="brush: css notranslate">button { margin: 10px };</pre>
</div>
<p>JavaScript выглядит так:</p>
<pre class="brush: js notranslate">const btn = document.querySelector('button');
function random(number) {
return Math.floor(Math.random() * (number+1));
}
btn.onclick = function() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}</pre>
<p>В этом коде мы сохраняем ссылку на кнопку внутри переменной <code>btn</code> типа <code>const</code>, используя функцию {{domxref ("Document.querySelector()")}}. Мы также определяем функцию, которая возвращает случайное число. Третья часть кода — <a href="#Свойства_обработчика_событий">обработчик события</a>. Переменная <code>btn</code> указывает на элемент <code><button></code> — для этого типа объекта существуют возникающие при определенном взаимодействии с ним события, а значит, возможно использование обработчиков событий. Мы отслеживаем момент возникновения события при щелчке мышью, связывая свойство обработчика события <code>onclick</code> с анонимной функцией, генерирующей случайный цвет RGB и устанавливающей его в качестве цвета фона элемента <code><body></code>.</p>
<p>Этот код теперь будет запускаться всякий раз, когда возникает событие при нажатии на элемент <code><button></code> — всякий раз, когда пользователь щелкает по нему.</p>
<p>Пример вывода выглядит следующим образом:</p>
<p>{{ EmbedLiveSample('Простой_пример', '100%', 200, "") }}</p>
<h3 id="События_не_только_для_веб-страниц">События не только для веб-страниц</h3>
<p>События, как понятие, относятся не только к JavaScript — большинство языков программирования имеют модель событий, способ работы которой часто отличается от модели в JavaScript. Фактически, даже модель событий в JavaScript для веб-страниц отличается от модели событий для просто JavaScript, поскольку используются они в разных средах.</p>
<p>Например, <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs">Node.js</a> — очень популярная среда исполнения JavaScript, которая позволяет разработчикам использовать JavaScript для создания сетевых и серверных приложений. <a href="https://nodejs.org/docs/latest-v5.x/api/events.html">Модель событий Node.js</a> основана на том, что существуют прослушиватели, отслеживающие события, и эмиттеры (передатчики), которые периодически генерируют события. В общем-то, это похоже на модель событий в JavaScript для веб-страниц, но код совсем другой. В этой модели используется функция <code>on()</code> для регистрации прослушивателей событий, и функция <code>once()</code> для регистрации прослушивателя событий, который отключается после первого срабатывания. Хорошим примером использования являются протоколы событий <a href="https://nodejs.org/docs/latest-v8.x/api/http.html#http_event_connect">HTTP connect event docs</a>.</p>
<p>Вы также можете использовать JavaScript для создания кросс-браузерных расширений — улучшения функциональности браузера с помощью технологии <a href="https://developer.mozilla.org/ru/docs/Mozilla/Add-ons/WebExtensions">WebExtensions</a>. В отличии от модели веб-событий здесь свойства прослушивателей событий пишутся в так называемом регистре <a href="https://ru.wikipedia.org/wiki/CamelCase">CamelCase</a> (например, <code>onMessage</code>, а не <code>onmessage</code>) и должны сочетаться с функцией <code>addListener</code>. См. <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/onMessage#Examples">runtime.onMessage page</a> для примера.</p>
<p>На данном этапе обучения вам не нужно особо разбираться в различных средах программирования, однако важно понимать, что принцип работы <em>событий</em> в них отличается.</p>
<h2 id="Способы_использования_веб-событий">Способы использования веб-событий</h2>
<p>Существует множество различных способов добавления кода прослушивателя событий на веб-страницы так, чтобы он срабатывал при возникновении соответствующего события. В этом разделе мы рассмотрим различные механизмы и обсудим, какие из них следует использовать.</p>
<h3 id="Свойства_обработчика_событий">Свойства обработчика событий</h3>
<p>В этом курсе вы уже сталкивались со свойствами, связываемыми с алгоритмом работы обработчика событий. Вернемся к приведенному выше примеру:</p>
<pre class="brush: js notranslate">const btn = document.querySelector('button');
btn.onclick = function() {
var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}</pre>
<p>В данной ситуации свойство <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick">onclick</a></code> — это свойство обработчика события. В принципе это обычное свойство кнопки как элемента (наравне с <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent">btn.textContent</a></code> или <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style">btn.style</a></code>), но оно относится к особому типу. Если вы установите его равным какому-нибудь коду, этот код будет запущен при возникновении события (при нажатии на кнопку).</p>
<p>Для получения того же результата, Вы также можете присвоить свойству обработчика имя уже описанной функции (как мы видели в статье <a href="/ru/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">Создайте свою функцию</a>):</p>
<pre class="brush: js notranslate">const btn = document.querySelector('button');
function bgChange() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
btn.onclick = bgChange;</pre>
<p>Давайте теперь поэкспериментируем с другими свойствами обработчика событий.</p>
<p>Создайте локальную копию файла <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-eventhandlerproperty.html">random-color-eventhandlerproperty.html</a> и откройте ее в своем браузере. Это всего лишь копия простого примера про случайные цвета, который мы уже разобрали в этой статье. Теперь попробуйте изменить <code>btn.onclick</code> на следующие значения и понаблюдайте за результатами:</p>
<ul>
<li><code><a href="https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onfocus">btn.onfocus</a></code> и <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onblur">btn.onblur</a></code> — Цвет изменится, когда кнопка будет сфокусирована или не сфокусирована (попробуйте нажать Tab, чтобы выбрать кнопку или убрать выбор). Эти свойства часто применяются для отображения информации о том, как заполнить поля формы, когда они сфокусированы, или отобразить сообщение об ошибке, если поле формы было заполнено с неправильным значением.</li>
<li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/ondblclick">btn.ondblclick</a></code> — Цвет будет изменяться только при двойном щелчке.</li>
<li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onkeypress">window.onkeypress</a></code>, <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onkeydown">window.onkeydown</a></code>, <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onkeyup">window.onkeyup</a></code> — Цвет будет меняться при нажатии клавиши на клавиатуре, причём <code>keypress</code> ссылается на обычное нажатие (нажатие кнопки и последующее её отпускание <em>как одно целое</em>), в то время как <code>keydown</code> и <code>keyup</code> <em>разделяют</em> действия на нажатие клавиши и отпускание, и ссылаются на них соответственно. Обратите внимание, что это не работает, если вы попытаетесь зарегистрировать этот обработчик событий на самой кнопке - его нужно зарегистрировать на объекте <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window">window</a>, который представляет все окно браузера.</li>
<li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onmouseover">btn.onmouseover</a></code> и <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onmouseout">btn.onmouseout</a></code> — Цвет будет меняться при наведении указателя мыши на кнопку или когда указатель будет отводиться от кнопки соответственно.</li>
</ul>
<p>Некоторые события очень общие и доступны практически в любом месте (например, обработчик <code>onclick</code> может быть зарегистрирован практически для любого элемента), тогда как некоторые из них более конкретны и полезны только в определенных ситуациях (например, имеет смысл использовать <a href="https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/GlobalEventHandlers.onplay">onplay</a> только для определенных элементов, таких как {{htmlelement ("video")}}).</p>
<h3 id="Встроенные_обработчики_событий_-_не_используйте_их">Встроенные обработчики событий - не используйте их</h3>
<p>Самый ранний из введенных в сеть Web методов регистрации <em>обработчиков событий</em> базируется на <strong>HTML атрибутах </strong>(<strong>встроенные обработчики событий</strong>):</p>
<pre class="brush: html notranslate"><button onclick="bgChange()">Press me</button>
</pre>
<pre class="brush: js notranslate">function bgChange() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}</pre>
<div class="note">
<p><strong>Примечание</strong>: Вы можете найти <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-eventhandlerattributes.html">полный исходник кода</a> из этого примера на GitHub (также <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/random-color-eventhandlerattributes.html">взгляните на его выполнение</a>).</p>
</div>
<p>Значение атрибута — это буквально код JavaScript, который вы хотите запустить при возникновении события. В приведенном выше примере вызывается функция, определенная внутри элемента {{htmlelement ("script")}} на той же странице, но вы также можете вставить JavaScript непосредственно внутри атрибута, например:</p>
<pre class="brush: html notranslate"><button onclick="alert('Hello, this is my old-fashioned event handler!');">Press me</button></pre>
<p>Для многих свойств обработчика событий существуют эквиваленты в виде атрибутов HTML. Однако, не рекомендуется их использовать — это считается плохой практикой. Использование атрибутов для регистрации обработчика событий кажется простым и быстрым методом, но такое описание обработчиков также скоро становится неудобным и неэффективным.</p>
<p>Более того, не рекомендуется смешивать HTML и JavaScript файлы, так как в дальнейшем такой код становится сложнее с точки зрения обработки (парсинга). Лучше держать весь JavaScript в одном месте. Также, если он находится в отдельном файле, вы можете применить его к нескольким документам HTML.</p>
<p>Даже при работе только в одном файле использование встроенных обработчиков не является хорошей идеей. Ладно, если у Вас одна кнопка, но что, если у вас их 100? Вам нужно добавить в файл 100 атрибутов; обслуживание такого кода очень быстро превратится в кошмар. С помощью JavaScript вы можете легко добавить функцию обработчика событий ко всем кнопкам на странице независимо от того, сколько их было.</p>
<p>Например:</p>
<pre class="brush: js notranslate">const buttons = document.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = bgChange;
}</pre>
<p class="brush: js">Обратите внимание, что для перебора всех элементов, которые содержит объект <code><a href="/en-US/docs/Web/API/NodeList">NodeList</a></code>, можно воспользоваться встроенным методом <code><a href="/en-US/docs/Web/API/NodeList/forEach">forEach()</a></code>:</p>
<pre class="brush: js notranslate">buttons.forEach(function(button) {
button.onclick = bgChange;
});</pre>
<div class="note">
<p><strong>Примечание</strong>: Разделение логики Вашей программы и Вашего контента также делает Ваш сайт более дружественным к поисковым системам.</p>
</div>
<h3 id="Функции_addEventListener_и_removeEventListener">Функции addEventListener() и removeEventListener()</h3>
<p>Новый тип механизма событий определен в спецификации <a href="https://www.w3.org/TR/DOM-Level-2-Events/">Document Object Model (DOM) Level 2 Events</a>, которая предоставляет браузеру новую функцию — <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code>. Работает она аналогично свойствам обработчика событий, но синтаксис совсем другой. Наш пример со случайным цветом мог бы выглядеть и так:</p>
<pre class="brush: js notranslate">var btn = document.querySelector('button');
function bgChange() {
var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
btn.addEventListener('click', bgChange);</pre>
<div class="note">
<p><strong>Примечание</strong>: Вы можете найти <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-addeventlistener.html">исходный код</a> из этого примера на GitHub (также <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/random-color-addeventlistener.html">взгляните на его выполнение</a>).</p>
</div>
<p>Внутри функции <code>addEventListener()</code> мы указываем два параметра — имя события, для которого мы хотим зарегистрировать этот обработчик, и код, содержащий функцию обработчика, которую мы хотим запустить в ответ. Обратите внимание, что будет целесообразно поместить весь код внутри функции <code>addEventListener()</code> в анонимную функцию, например:</p>
<pre class="brush: js notranslate">btn.addEventListener('click', function() {
var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
});</pre>
<p>Этот механизм имеет некоторые преимущества по сравнению с более старыми механизмами, рассмотренными ранее. Например, существует аналогичная функция <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener">removeEventListener()</a></code>, которая удаляет ранее добавленный прослушиватель. Это приведет к удалению набора слушателей в первом блоке кода в этом разделе:</p>
<pre class="brush: js notranslate">btn.removeEventListener('click', bgChange);</pre>
<p>Это не важно для простых небольших программ, но для более крупных и более сложных программ он может повысить эффективность очистки старых неиспользуемых обработчиков событий. Кроме того, это позволяет вам иметь одну и ту же кнопку, выполняющую различные действия в разных обстоятельствах — все, что вам нужно сделать, это добавить/удалить обработчики событий, если это необходимо.</p>
<p>Также, вы можете зарегистрировать несколько обработчиков для одного и того же прослушивателя. Следующие два обработчика не будут применяться:</p>
<pre class="brush: js notranslate">myElement.onclick = functionA;
myElement.onclick = functionB;</pre>
<p>Поскольку вторая строка будет перезаписывать значение <code>onclick</code>, установленное первой. Однако, если:</p>
<pre class="brush: js notranslate">myElement.addEventListener('click', functionA);
myElement.addEventListener('click', functionB);</pre>
<p>Обе функции будут выполняться при щелчке элемента.</p>
<p>Кроме того, в этом механизме событий имеется ряд других мощных функций и опций. Эта тема выходит за рамки данной статьи, но если вы хотите изучить подробнее, переходите по ссылкам: <a href="/ru/docs/Web/API/EventTarget/addEventListener">Метод EventTarget.addEventListener()</a> и <a href="/ru/docs/Web/API/EventTarget/removeEventListener">Метод EventTarget.removeEventListener()</a>.</p>
<h3 id="Какой_механизм_мне_использовать">Какой механизм мне использовать?</h3>
<p>Из трех механизмов определенно не нужно использовать атрибуты событий HTML. Как упоминалось выше, это устаревшая и плохая практика.</p>
<p>Остальные два являются относительно взаимозаменяемыми, по крайней мере для простых целей</p>
<ul>
<li>Свойства обработчика событий имеют меньшую мощность и параметры, но лучше совместимость между браузерами (поддерживается еще в Internet Explorer 8). Вероятно, вам следует начать с них, когда вы учитесь.</li>
<li>События уровня 2 DOM (<code>addEventListener()</code> и т. д.) являются более мощными, но также могут стать более сложными и хуже поддерживаться (поддерживается еще в Internet Explorer 9). Вам также стоит поэкспериментировать с ними и стремиться использовать их там, где это возможно.</li>
</ul>
<p>Основные преимущества третьего механизма заключаются в том, что при необходимости можно удалить код обработчика событий, используя <code>removeEventListener()</code>, и так же можно добавить несколько элементов-слушателей того же типа к элементам. Например, вы можете вызвать <code>addEventListener('click', function() {...})</code> для элемента несколько раз, с разными функциями, указанными во втором аргументе. Это невозможно при использовании свойств обработчика событий, поскольку любые последующие попытки установить свойство будут перезаписывать более ранние, например:</p>
<pre class="brush: js notranslate">element.onclick = function1;
element.onclick = function2;
etc.</pre>
<div class="note">
<p><strong>Примечание:</strong> Если вам требуется поддержка браузеров старше Internet Explorer 8 в вашем проекте, вы можете столкнуться с трудностями, так как такие старые браузеры используют старые модели событий из более новых браузеров. Но не бойтесь, большинство библиотек JavaScript (например, <code>jQuery</code>) имеют встроенные функции, которые абстрагируют различия между браузерами. Не беспокойтесь об этом слишком много на этапе вашего учебного путешествия.</p>
</div>
<h2 id="Другие_концепции_событий">Другие концепции событий</h2>
<p>Рассмотрим некоторые современные концепции, имеющие отношение к событиям. На данный момент не обязательно понимать их полностью, но представление о них поможет лучше понять некоторые модели кода, с которыми вы, вероятно, столкнетесь.</p>
<h3 id="Объекты_событий"> Объекты событий</h3>
<p>Иногда внутри функции обработчика событий вы можете увидеть параметр, заданный с таким именем, как <code>event</code>, <code>evt</code> или просто <code>e</code>. Называется он <strong>объектом события</strong> и он автоматически передается обработчикам событий для предоставления дополнительных функций и информации. Например, давайте немного перепишем наш пример со случайным цветом:</p>
<pre class="brush: js notranslate">function bgChange(e) {
var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
e.target.style.backgroundColor = rndCol;
console.log(e);
}
btn.addEventListener('click', bgChange);</pre>
<div class="note">
<p><strong>Примечание</strong>: Вы можете найти <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-eventobject.html">исходник кода</a> для этого примера на GitHub (также <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/random-color-eventobject.html">взгляните на его выполнение</a>).</p>
</div>
<p>Итак в коде выше мы включаем объект события <strong><code>e</code></strong> в функцию, а в функции — настройку стиля фона для <code>e.target</code>, который является кнопкой. Свойство объекта события <code>target</code> всегда является ссылкой на элемент, с которым только что произошло событие. Поэтому в этом примере мы устанавливаем случайный цвет фона на кнопке, а не на странице.</p>
<div class="note">
<p><strong>Примечание:</strong> Вместо <code>e</code>/<code>evt</code>/<code>event</code> можно использовать любое имя для объекта события, которое затем можно использовать для ссылки на него в функции обработчика событий. <code>e</code>/<code>evt</code>/<code>event</code> чаще всего используются разработчиками, потому что они короткие и легко запоминаются. И хорошо придерживаться стандарта.</p>
</div>
<p><code>e.target</code> применяют, когда нужно установить один и тот же обработчик событий на несколько элементов и, когда на них происходит событие, применить определенное действие к ним ко всем. Например, у вас может быть набор из 16 плиток, которые исчезают при нажатии. Полезно всегда иметь возможность просто указать, чтобы объект исчез, как <code>e.target</code>, вместо того, чтобы выбирать его более сложным способом. В следующем примере (см. исходный код на <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/useful-eventtarget.html">useful-eventtarget.html</a>,а как он работает можно посмотреть <a href="https://mdn.github.io/learning-area/javascript/building-blocks/events/useful-eventtarget.html">здесь</a>), мы создаем 16 элементов {{htmlelement ("div")}} с использованием JavaScript. Затем мы выберем все из них, используя {{domxref ("document.querySelectorAll()")}}, и с помощью цикла <code>for</code> выберем каждый из них, добавив обработчик <code>onclick</code> к каждому так, чтобы случайный цвет применялся к каждому клику:</p>
<pre class="brush: js notranslate">var divs = document.querySelectorAll('div');
for (var i = 0; i < divs.length; i++) {
divs[i].onclick = function(e) {
e.target.style.backgroundColor = bgChange();
}
}</pre>
<p>Результат выглядит следующим образом (попробуйте щелкнуть по нему):</p>
<div class="hidden">
<h6 id="Hidden_example">Hidden example</h6>
<pre class="brush: html notranslate"><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Useful event target example</title>
<style>
div {
background-color: #ff6600;
height: 100px;
width: 25%;
float: left;
}
</style>
</head>
<body>
<script>
for (var i = 1; i <= 16; i++) {
var myDiv = document.createElement('div');
document.body.appendChild(myDiv);
}
function random(number) {
return Math.floor(Math.random()*number);
}
function bgChange() {
var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
return rndCol;
}
var divs = document.querySelectorAll('div');
for (var i = 0; i < divs.length; i++) {
divs[i].onclick = function(e) {
e.target.style.backgroundColor = bgChange();
}
}
</script>
</body>
</html></pre>
</div>
<p>{{ EmbedLiveSample('Hidden_example', '100%', 400) }}</p>
<p>Большинство обработчиков событий, с которыми вы столкнулись, имеют только стандартный набор свойств и функций (методов), доступных для объекта события (см. {{domxref("Event")}} для ссылки на полный список). Однако некоторые более продвинутые обработчики добавляют специальные свойства, содержащие дополнительные данные, которые им необходимо выполнять. Например, <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder_API">Media Recorder API</a> имеет событие, доступное для данных, которое срабатывает, когда записано какое-либо аудио или видео и доступно для выполнения чего-либо (например, для сохранения или воспроизведения). Соответствующий объект события <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/ondataavailable">ondataavailable</a> handler имеет свойство данных, содержащее записанные аудио- или видеоданные, чтобы вы могли получить к нему доступ и что-то сделать с ним.</p>
<h3 id="Предотвращение_поведения_по_умолчанию"> Предотвращение поведения по умолчанию</h3>
<p>Иногда бывают ситуации, когда нужно остановить событие, выполняющее то, что оно делает по умолчанию. Наиболее распространенным примером является веб-форма, например, пользовательская форма регистрации. Когда вы вводите данные и нажимаете кнопку отправки, естественное поведение заключается в том, что данные должны быть отправлены на указанную страницу на сервере для обработки, а браузер перенаправляется на страницу с сообщением об успехе (или остаться на той же странице, если другое не указано).</p>
<p>Но если пользователь отправил данные не правильно, как разработчик, вы хотите остановить отправку на сервер и выдать сообщение об ошибке с информацией о том, что не так и что нужно сделать. Некоторые браузеры поддерживают функции автоматической проверки данных формы, но, поскольку многие этого не делают, вам не следует полагаться на них и выполнять свои собственные проверки валидации. Давайте посмотрим на простой пример.</p>
<p>Простая форма HTML, в которой требуется ввести ваше имя и фамилию:</p>
<pre class="brush: html notranslate"><form>
<div>
<label for="fname">Имя: </label>
<input id="fname" type="text">
</div>
<div>
<label for="lname">Фамилия: </label>
<input id="lname" type="text">
</div>
<div>
<input id="submit" type="submit">
</div>
</form>
<p></p></pre>
<div class="hidden">
<pre class="brush: css notranslate">div {
margin-bottom: 10px;
}
</pre>
</div>
<p>В JavaScript мы реализуем очень простую проверку внутри обработчика события <a href="https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onsubmit">onsubmit</a> (событие "отправить" запускается в форме, когда оно отправлено), который проверяет, пусты ли текстовые поля. Если они пусты, мы вызываем функцию <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault">preventDefault()</a></code> объекта события, которая останавливает отправку формы, а затем выводит сообщение об ошибке в абзаце ниже нашей формы, чтобы сообщить пользователю, что не так:</p>
<pre class="brush: js notranslate">var form = document.querySelector('form');
var fname = document.getElementById('fname');
var lname = document.getElementById('lname');
var submit = document.getElementById('submit');
var para = document.querySelector('p');
form.onsubmit = function(e) {
if (fname.value === '' || lname.value === '') {
e.preventDefault();
para.textContent = 'Оба поля должны быть заполнены!';
}
}</pre>
<p>Очевидно, что это довольно слабая проверка формы - это не помешает пользователю отправить форму с пробелами или цифрами, введенными в поля, но для примера подходит. Вывод выглядит следующим образом:</p>
<p>{{ EmbedLiveSample('Предотвращение_поведения_по_умолчанию', '100%', 140) }}</p>
<div class="note">
<p><strong>Примечание</strong>: чтобы увидеть исходный код, откройте <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/preventdefault-validation.html">preventdefault-validation.html</a> (также <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/preventdefault-validation.html">запустите</a> здесь).</p>
</div>
<h3 id="Всплытие_и_перехват_событий">Всплытие и перехват событий</h3>
<p>Последним предметом для рассмотрения в этой теме является то, с чем вы не часто будете сталкиваться, но это может стать настоящей головной болью, если вы не поймете, как работает следующий механизм. <em>Всплытие</em> и <em>перехват событий</em> — два механизма, описывающих, что происходит, когда два обработчика одного и того же события активируются на одном элементе. Посмотрим на пример. Чтобы сделать это проще — откройте пример <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/show-video-box.html">show-video-box.html</a> в одной вкладке и <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box.html">исходный код </a>в другой вкладке. Он также представлен ниже:</p>
<div class="hidden">
<h6 id="Hidden_video_example">Hidden video example</h6>
<pre class="brush: html notranslate"><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Show video box example</title>
<style>
div {
position: absolute;
top: 50%;
transform: translate(-50%,-50%);
width: 550px;
height: 420px;
border-radius: 10px;
background-color: #eee;
background-image: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.4));
}
.hidden {
left: -50%;
}
.showing {
left: 50%;
}
div video {
display: block;
width: 400px;
margin: 50px auto;
}
</style>
</head>
<body>
<button>Display video</button>
<div class="hidden">
<video>
<source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.mp4" type="video/mp4">
<source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.webm" type="video/webm">
<p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
</video>
</div>
<script>
var btn = document.querySelector('button');
var videoBox = document.querySelector('div');
var video = document.querySelector('video');
btn.onclick = function() {
displayVideo();
}
function displayVideo() {
if(videoBox.getAttribute('class') === 'hidden') {
videoBox.setAttribute('class','showing');
}
}
videoBox.addEventListener('click',function() {
videoBox.setAttribute('class','hidden');
});
video.addEventListener('click',function() {
video.play();
});
</script>
</body>
</html></pre>
</div>
<p>{{ EmbedLiveSample('Hidden_video_example', '100%', 500) }}</p>
<p>Это довольно простой пример, который показывает и скрывает {{htmlelement ("div")}} с элементом {{htmlelement ("video")}} внутри него:</p>
<pre class="brush: html notranslate"><button>Display video</button>
<div class="hidden">
<video>
<source src="rabbit320.mp4" type="video/mp4">
<source src="rabbit320.webm" type="video/webm">
<p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
</video>
</div></pre>
<p>При нажатии на кнопку {{htmlelement ("button")}}, изменяется атрибут класса элемента <code><div></code> с <code>hidden</code> на <code>showing</code> (CSS примера содержит эти два класса, которые размещают блок вне экрана и на экране соответственно):</p>
<pre class="brush: css notranslate">div {
position: absolute;
top: 50%;
transform: translate(-50%,-50%);
...
}
<font color="#6f42c1" face="SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, monospace">.hidden {
left: -50%;
}
.showing {
left: 50%;
}</font></pre>
<pre class="brush: js notranslate">var btn = document.querySelector('button');
btn.onclick = function() {
videoBox.setAttribute('class', 'showing');
}</pre>
<p>Затем мы добавляем еще пару обработчиков событий <code>onclick.</code> Первый к <code><div></code>, а второй к <code><video></code>. Идея заключается в том, чтобы при щелчке по области <code><div></code> вне зоны видео поле снова скрылось, а при клике в области <code><video></code> видео начало воспроизводиться.</p>
<pre class="brush: js notranslate">var videoBox = document.querySelector('div');
var video = document.querySelector('video');
videoBox.onclick = function() {
videoBox.setAttribute('class', 'hidden');
};
video.onclick = function() {
video.play();
};</pre>
<p>Но есть проблема: когда вы нажимаете на видео, оно начинает воспроизводиться, но одновременно вызывает скрытие <code><div></code>. Это связано с тем, что видео находится внутри <code><div>,</code> это часть его, поэтому нажатие на видео фактически запускает оба вышеуказанных обработчика событий.</p>
<h4 id="Всплытие_и_перехват_событий_—_концепция_выполнения"><strong>Всплытие и перехват событий — концепция выполнения</strong></h4>
<p>Когда событие инициируется элементом, который имеет родительские элементы (например, {{htmlelement ("video")}} в нашем случае), современные браузеры выполняют две разные фазы — фазу <strong>захвата</strong> и фазу <strong>всплытия</strong>.</p>
<p>На стадии <strong>захвата </strong>происходит следующее:</p>
<ul>
<li>Браузер проверяет, имеет ли самый внешний элемент ({{htmlelement ("html")}}) обработчик события <code>onclick</code>, зарегистрированный на нем на этапе захвата и запускает его, если это так.</li>
<li>Затем он переходит к следующему элементу внутри <code><html></code> и выполняет то же самое, затем следующее и так далее, пока не достигнет элемента, на который на самом деле нажали.</li>
</ul>
<p>На стадии <strong>всплытия</strong> происходит полная противоположность:</p>
<ul>
<li>Браузер проверяет, имеет ли элемент, который был фактически нажат, обработчик события <code>onclick</code>, зарегистрированный на нем в фазе всплытия, и запускает его, если это так.</li>
<li>Затем он переходит к следующему непосредственному родительскому элементу и выполняет то же самое, затем следующее и так далее, пока не достигнет элемента <code><html></code>.</li>
</ul>
<p><a href="https://mdn.mozillademos.org/files/14075/bubbling-capturing.png"><img alt="" src="https://mdn.mozillademos.org/files/14075/bubbling-capturing.png" style="display: block; height: 452px; margin: 0px auto; width: 960px;"></a></p>
<p>(Нажмите на изображение, чтобы увеличить диаграмму)</p>
<p>В современных браузерах по умолчанию все обработчики событий регистрируются в фазе <strong><em>всплытия</em></strong>. Итак, в нашем текущем примере, когда вы нажимаете видео, событие click вызывается из элемента <code><video></code> наружу, в элемент <code><html></code>. По пути:</p>
<ul>
<li>Он находит обработчик <code>video.onclick...</code> и запускает его, поэтому видео сначала начинает воспроизводиться.</li>
<li>Затем он находит обработчик <code>videoBox.onclick...</code> и запускает его, поэтому видео также скрывается.</li>
</ul>
<h4 id="Исправление_проблемы_с_помощью_stopPropagation">Исправление проблемы с помощью stopPropagation()</h4>
<p>Чтобы исправить это раздражающее поведение, стандартный объект события имеет функцию, называемую <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation">stopPropagation()</a></code>, которая при вызове в обработчике событий объекта делает так, чтобы обработчик выполнялся, но событие не всплывало дальше по цепочке, поэтому не будут запускаться другие обработчики.</p>
<p>Поэтому мы можем исправить нашу текущую проблему, изменив вторую функцию-обработчик в предыдущем блоке кода:</p>
<pre class="brush: js notranslate">video.onclick = function(e) {
e.stopPropagation();
video.play();
};</pre>
<p>Вы можете попробовать создать локальную копию <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box.html">show-video-box.html</a> и попробовать его самостоятельно исправить или просмотреть исправленный результат в <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/show-video-box-fixed.html">show-video-box-fixed.html</a> (также см. <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box-fixed.html">исходный код здесь</a>).</p>
<div class="note"><strong>Примечание</strong>: Зачем беспокоиться как с захватом, так и с всплыванием? Что ж, в старые добрые времена, когда браузеры были менее совместимы, чем сейчас, Netscape использовал только захват событий, а Internet Explorer использовал только всплывающие события. Когда W3C решил попытаться стандартизировать поведение и достичь консенсуса, они в итоге получили эту систему, которая включала в себя и то, и другое, что реализовано в одном из современных браузеров.</div>
<div class="note">
<p><strong>Примечание</strong>: Как упоминалось выше, по умолчанию все обработчики событий регистрируются в фазе всплытия и это имеет смысл в большинстве случаев. Если вы действительно хотите зарегистрировать событие в фазе захвата, вы можете сделать это, зарегистрировав обработчик с помощью <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code> и установив для третьего дополнительного свойства значение <code>true</code>.</p>
</div>
<h4 id="Делегирование_события"><strong>Делегирование</strong> события</h4>
<p>Всплытие также позволяет нам использовать <strong>делегирование событий. </strong> Если у какого-либо родительского элемента есть множество дочерних элементов, и вы хотите, чтобы определенный код выполнялся при щелчке (событии) на каждом из дочерних элементов, можно установить прослушиватель событий на родительском элементе и события, происходящие на дочерних элементах будут всплывать до их родителя. При этом не нужно устанавливать прослушивателя событий на каждом дочернем элементе.</p>
<p>Хорошим примером является серия элементов списка. Если вы хотите, чтобы каждый из них, например, отображал сообщение при нажатии, вы можете установить прослушиватель событий <code>click</code> для родительского элемента <code><ul></code> и он будет всплывать в элементах списка.</p>
<p>Эту концепцию объясняет в своем блоге Дэвид Уолш, где приводит несколько примеров. (см. <a href="https://davidwalsh.name/event-delegate">How JavaScript Event Delegation Works</a>.)</p>
<h2 id="Вывод">Вывод</h2>
<p>Это все, что вам нужно знать о веб-событиях на этом этапе. Как уже упоминалось, события не являются частью основного JavaScript — они определены в веб-интерфейсах браузера (<a href="/ru/docs/Web/API">Web API</a>).</p>
<p>Кроме того, важно понимать, что различные контексты, в которых используется JavaScript, обычно имеют разные модели событий — от веб-API до других областей, таких как браузерные WebExtensions и Node.js (серверный JavaScript). Может сейчас вы не особо в этом разбираетесь, но по мере изучения веб-разработки начнет приходить более ясное понимание тематики.</p>
<p>Если у вас возникли вопросы, попробуйте прочесть статью снова или <a href="https://developer.mozilla.org/en-US/docs/Learn#Contact_us">обратитесь за помощью к нам</a>.</p>
<h2 id="См._также">См. также</h2>
<ul>
<li><a href="http://www.quirksmode.org/js/events_order.html">Event order</a> (обсуждение захвата и всплытий) — превосходно детализированная часть от Peter-Paul Koch.</li>
<li><a href="http://www.quirksmode.org/js/events_access.html">Event accessing</a> (discussing of the event object) — another excellently detailed piece by Peter-Paul Koch.</li>
<li><a href="/en-US/docs/Web/Events">Event reference</a></li>
</ul>
<p>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "Learn/JavaScript/Building_blocks")}}</p>
<h2 id="В_этом_модуле">В этом модуле</h2>
<ul>
<li><a href="/ru/docs/Learn/JavaScript/Building_blocks/conditionals">Принятие решений в Вашем коде — условные конструкции</a></li>
<li><a href="/ru/docs/Learn/JavaScript/Building_blocks/Looping_code">Зацикливание кода</a></li>
<li><a href="/ru/docs/Learn/JavaScript/Building_blocks/Functions">Функции — Переиспользуемые блоки кода</a></li>
<li><a href="/ru/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">Создайте свою собственную функцию</a></li>
<li><a href="/ru/docs/Learn/JavaScript/Building_blocks/Return_values">Возвращаемое значение функции</a></li>
<li><a href="/ru/docs/Learn/JavaScript/Building_blocks/События">Введение в события</a></li>
<li><a href="/ru/docs/Learn/JavaScript/Building_blocks/Image_gallery">Создание галереи</a></li>
</ul>
<div class="s3gt_translate_tooltip_mini_box" id="s3gt_translate_tooltip_mini" style="background: initial !important; border: initial !important; border-radius: initial !important; border-collapse: initial !important; direction: ltr !important; font-weight: initial !important; height: initial !important; letter-spacing: initial !important; max-width: initial !important; min-height: initial !important; margin: auto !important; outline: initial !important; padding: initial !important; position: absolute; text-align: initial !important; text-shadow: initial !important; width: initial !important; display: initial !important; color: inherit !important; font-size: 13px !important; font-family: X-LocaleSpecific, sans-serif, Tahoma, Helvetica !important; line-height: 13px !important; vertical-align: top !important; white-space: inherit !important; left: 416px; top: 2989px; opacity: 0.35;">
<div class="s3gt_translate_tooltip_mini" id="s3gt_translate_tooltip_mini_sound" style="width: 12px; height: 12px; border-radius: 4px;" title="Прослушать"></div>
<div class="s3gt_translate_tooltip_mini" id="s3gt_translate_tooltip_mini_copy" style="width: 12px; height: 12px; border-radius: 4px;" title="Скопировать текст в буфер обмена"></div>
</div>
|