diff options
Diffstat (limited to 'files/ru/learn/javascript/building_blocks/события/index.html')
-rw-r--r-- | files/ru/learn/javascript/building_blocks/события/index.html | 606 |
1 files changed, 606 insertions, 0 deletions
diff --git a/files/ru/learn/javascript/building_blocks/события/index.html b/files/ru/learn/javascript/building_blocks/события/index.html new file mode 100644 index 0000000000..db13cec676 --- /dev/null +++ b/files/ru/learn/javascript/building_blocks/события/index.html @@ -0,0 +1,606 @@ +--- +title: Введение в события +slug: Learn/JavaScript/Building_blocks/События +tags: + - Изучение + - Обработчик событий + - Руководство + - события +translation_of: Learn/JavaScript/Building_blocks/Events +--- +<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> |